initial commit

This commit is contained in:
Yun 2019-06-24 18:14:23 +09:00
parent 2187ab6df2
commit 1d0ba63c44
11 changed files with 634 additions and 0 deletions

83
.gitignore vendored
View File

@ -59,3 +59,86 @@ typings/
# next.js build output
.next
node_modules/
.node_modules/
built/*
tests/cases/rwc/*
tests/cases/test262/*
tests/cases/perf/*
!tests/cases/webharness/compilerToString.js
test-args.txt
~*.docx
\#*\#
.\#*
tests/baselines/local/*
tests/baselines/local.old/*
tests/services/baselines/local/*
tests/baselines/prototyping/local/*
tests/baselines/rwc/*
tests/baselines/test262/*
tests/baselines/reference/projectOutput/*
tests/baselines/local/projectOutput/*
tests/baselines/reference/testresults.tap
tests/services/baselines/prototyping/local/*
tests/services/browser/typescriptServices.js
src/harness/*.js
src/compiler/diagnosticInformationMap.generated.ts
src/compiler/diagnosticMessages.generated.json
src/parser/diagnosticInformationMap.generated.ts
src/parser/diagnosticMessages.generated.json
rwc-report.html
*.swp
build.json
*.actual
tests/webTestServer.js
tests/webTestServer.js.map
tests/webhost/*.d.ts
tests/webhost/webtsc.js
tests/cases/**/*.js
tests/cases/**/*.js.map
*.config
scripts/debug.bat
scripts/run.bat
scripts/word2md.js
scripts/buildProtocol.js
scripts/ior.js
scripts/authors.js
scripts/configurePrerelease.js
scripts/open-user-pr.js
scripts/open-cherry-pick-pr.js
scripts/processDiagnosticMessages.d.ts
scripts/processDiagnosticMessages.js
scripts/produceLKG.js
scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js
scripts/generateLocalizedDiagnosticMessages.js
scripts/*.js.map
scripts/typings/
coverage/
internal/
**/.DS_Store
.settings
**/.vs
**/.vscode
!**/.vscode/tasks.json
!tests/cases/projects/projectOption/**/node_modules
!tests/cases/projects/NodeModulesSearch/**/*
!tests/baselines/reference/project/nodeModules*/**/*
.idea
yarn.lock
yarn-error.log
.parallelperf.*
tests/cases/user/*/package-lock.json
tests/cases/user/*/node_modules/
tests/cases/user/*/**/*.js
tests/cases/user/*/**/*.js.map
tests/cases/user/*/**/*.d.ts
!tests/cases/user/zone.js/
!tests/cases/user/bignumber.js/
!tests/cases/user/discord.js/
tests/baselines/reference/dt
.failed-tests
TEST-results.xml
package-lock.json
unsignedTx.json

1
dist/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export {};

151
dist/index.js vendored Normal file
View File

@ -0,0 +1,151 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core = require("@terra-money/core");
const rest_1 = require("./utils/rest");
const big_js_1 = require("big.js");
const fs = require("fs");
const rest = new rest_1.default("http://127.0.0.1:1317");
const outputFile = "./unsignedTx.json";
const foundationAddress = "terra1dp0taj85ruc299rkdvzp4z5pfg6z6swaed74e6";
const goliathValAddress = "terravaloper163phlen6dn7sp9khhjar2gqqx6kga0ly8d7h9g";
const marineValAddress = "terravaloper1d3hatwcsvkktgwp3elglw9glca0h42yg6xy4lp";
const ghostValAddress = "terravaloper1rgu3qmm6rllfxlrfk94pgxa0jm37902dynqehm";
const wraithValAddress = "terravaloper1eutun6vh83lmyq0wmyf9vgghvurze2xanl9sq6";
async function loadFoundationRewards() {
const promises = [];
promises.push(rest.loadDelegatorRewards(foundationAddress));
promises.push(rest.loadValidatorRewards(goliathValAddress));
promises.push(rest.loadValidatorRewards(marineValAddress));
promises.push(rest.loadValidatorRewards(ghostValAddress));
promises.push(rest.loadValidatorRewards(wraithValAddress));
const rewardMap = {};
await Promise.all(promises)
.then(res => {
for (let i in res) {
const rewards = res[i];
if (rewards && rewards.length > 0) {
for (let j in rewards) {
const denom = rewards[j].denom;
const amount = rewards[j].amount.split('.')[0];
if (rewardMap[denom]) {
rewardMap[denom] = big_js_1.default(rewardMap[denom]).plus(amount).toString();
}
else {
rewardMap[denom] = amount;
}
}
}
}
});
const totalRewards = [];
for (let denom in rewardMap) {
totalRewards.push({
denom: denom,
amount: rewardMap[denom]
});
}
return totalRewards;
}
const validatorBonusRate = 0.2;
function computeValidatorsRewardRatio(rewardRatioMap, validators) {
let totalBondedToken = big_js_1.default(0);
for (let i in validators) {
totalBondedToken = totalBondedToken.plus(validators[i].tokens);
}
for (let i in validators) {
const validator = validators[i];
const address = core.convertValAddressToAccAddress(validator.operator_address);
rewardRatioMap[address] = big_js_1.default(validator.tokens).div(totalBondedToken).mul(validatorBonusRate).toString();
}
return;
}
async function computeDelegatorRewardRatio(rewardRatioMap, validators) {
let totalBondedToken = big_js_1.default(0);
for (let i in validators) {
totalBondedToken = totalBondedToken.plus(validators[i].tokens);
}
for (let i in validators) {
const validator = validators[i];
const delegations = await rest.loadDelegations(validator.operator_address);
if (!delegations)
continue;
for (let j in delegations) {
const delegation = delegations[j];
const tokens = big_js_1.default(validator.tokens).mul(delegations[j].shares).div(validator.delegator_shares);
const ratio = big_js_1.default(tokens).div(totalBondedToken).mul(1 - validatorBonusRate).toString();
if (rewardRatioMap[delegation.delegator_address]) {
rewardRatioMap[delegation.delegator_address]
= big_js_1.default(rewardRatioMap[delegation.delegator_address])
.plus(ratio).toString();
}
else {
rewardRatioMap[delegation.delegator_address] = ratio;
}
}
}
return;
}
async function main() {
const foundationRewards = await loadFoundationRewards();
console.debug("Foundation Rewards:", foundationRewards);
const validators = await rest.loadValidators();
if (!validators) {
console.error("no validator found");
return process.exit(-1);
}
const rewardRatioMap = {};
computeValidatorsRewardRatio(rewardRatioMap, validators);
console.info("Validator Bonus Rewards: ", rewardRatioMap);
await computeDelegatorRewardRatio(rewardRatioMap, validators);
console.info("Total Rewards: ", rewardRatioMap);
// Rotate reward ratio and build msg input
let totalRatio = big_js_1.default(0);
const outputs = [];
for (let addr in rewardRatioMap) {
const ratio = rewardRatioMap[addr];
totalRatio = totalRatio.plus(ratio);
const coins = [];
for (let i in foundationRewards) {
coins.push({
denom: foundationRewards[i].denom,
amount: big_js_1.default(foundationRewards[i].amount).mul(ratio).toFixed(0)
});
}
outputs.push({
address: addr,
coins: coins
});
}
if (totalRatio.gt(1)) {
console.error(`Total Reward Ratio(${totalRatio}) is bigger than 1`);
return process.exit(-1);
}
const inputs = [];
const coins = [];
for (let i in foundationRewards) {
coins.push({
denom: foundationRewards[i].denom,
amount: big_js_1.default(foundationRewards[i].amount).toFixed(0)
});
}
inputs.push({
address: foundationAddress,
coins: coins
});
const multiSendMsg = core.buildMultiSend(inputs, outputs);
const unSingedTx = core.buildStdTx([multiSendMsg], { gas: "1000000", amount: [{
denom: "ukrw",
amount: "1000000"
}] }, "reward distribution");
fs.writeFile(outputFile, JSON.stringify(unSingedTx, null, 4), function (err) {
if (err) {
console.error("Writing Failed", err);
}
else {
console.info("Writing Succeed", `Please check ${outputFile}`);
}
});
return;
}
main();
//# sourceMappingURL=index.js.map

1
dist/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

36
dist/utils/rest.d.ts vendored Normal file
View File

@ -0,0 +1,36 @@
declare class RestInterface {
lcdAddress: string;
constructor(lcdAddress: string);
loadValidators(): Promise<void | Array<Validator>>;
loadValidatorRewards(valAddress: string): Promise<void | Array<Coin>>;
loadDelegations(valAddress: string): Promise<void | Array<Delegation>>;
loadDelegatorRewards(delAddress: string): Promise<void | Array<Coin>>;
}
export interface Validator {
operator_address: string;
consensus_pubkey: string;
jailed: boolean;
status: number;
tokens: string;
delegator_shares: string;
description: [Object];
unbonding_height: string;
unbonding_time: string;
commission: [Object];
min_self_delegation: string;
}
export interface Delegation {
delegator_address: string;
validator_address: string;
shares: string;
}
export interface Coin {
denom: string;
amount: string;
}
export interface ValidatorRewardsInfo {
operator_address: string;
self_bond_rewards: [Coin];
val_commission: [Coin];
}
export default RestInterface;

51
dist/utils/rest.js vendored Normal file
View File

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = require("axios");
class RestInterface {
constructor(lcdAddress) {
this.lcdAddress = lcdAddress;
}
async loadValidators() {
const queryValidatorsURL = `${this.lcdAddress}/staking/validators`;
try {
const res = await axios_1.default.get(queryValidatorsURL);
return res.data ? res.data : [];
}
catch (error) {
console.error("[LoadValidators]", error);
}
}
async loadValidatorRewards(valAddress) {
const queryValidatorRewardsInfoURL = `${this.lcdAddress}/distribution/validators/${valAddress}`;
try {
const res = await axios_1.default.get(queryValidatorRewardsInfoURL);
// foundation validators have 100% commission, so ignore self_bond_rewards
return res.data ? res.data.val_commission : [];
}
catch (error) {
console.error("[LoadValidatorRewardsInfoURL]", error);
}
}
async loadDelegations(valAddress) {
const queryDelegatorsURL = `${this.lcdAddress}/staking/validators/${valAddress}/delegations`;
try {
const res = await axios_1.default.get(queryDelegatorsURL);
return res.data ? res.data : [];
}
catch (error) {
console.error("[LoadDelegations]", error);
}
}
async loadDelegatorRewards(delAddress) {
const queryDelegatorRewardsURL = `${this.lcdAddress}/distribution/delegators/${delAddress}/rewards`;
try {
const res = await axios_1.default.get(queryDelegatorRewardsURL);
return res.data ? res.data : [];
}
catch (error) {
console.error("[LoadDelegatorRewards]", error);
}
}
}
exports.default = RestInterface;
//# sourceMappingURL=rest.js.map

1
dist/utils/rest.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"rest.js","sourceRoot":"src/","sources":["utils/rest.ts"],"names":[],"mappings":";;AAAA,iCAA4C;AAE5C,MAAM,aAAa;IAGjB,YAAY,UAAkB;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,kBAAkB,GAAG,GAAG,IAAI,CAAC,UAAU,qBAAqB,CAAA;QAClE,IAAI;YACF,MAAM,GAAG,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;YAC/C,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;SAChC;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;SAC1C;IACH,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,UAAkB;QAC3C,MAAM,4BAA4B,GAAG,GAAG,IAAI,CAAC,UAAU,4BAA4B,UAAU,EAAE,CAAA;QAC/F,IAAI;YACF,MAAM,GAAG,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAA;YACzD,0EAA0E;YAC1E,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAA;SAC/C;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;SACvD;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,UAAkB;QACtC,MAAM,kBAAkB,GAAG,GAAG,IAAI,CAAC,UAAU,uBAAuB,UAAU,cAAc,CAAA;QAC5F,IAAI;YACF,MAAM,GAAG,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;YAC/C,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;SAChC;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;SAC3C;IACH,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,UAAkB;QAC3C,MAAM,wBAAwB,GAAG,GAAG,IAAI,CAAC,UAAU,4BAA4B,UAAU,UAAU,CAAA;QACnG,IAAI;YACF,MAAM,GAAG,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAA;YACrD,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;SAChC;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;SAChD;IACH,CAAC;CACF;AAiCD,kBAAe,aAAa,CAAA"}

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "reward-distributor",
"version": "1.0.0",
"description": "terra foundation reward distributor to validators & delegators",
"main": "dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc --declaration",
"start": "node ./dist/index.js"
},
"author": "YunSuk Yeo <sw.yunsuk@gmail.com>",
"license": "ISC",
"dependencies": {
"@terra-money/core": "^1.0.2",
"@types/axios": "^0.14.0",
"@types/big.js": "^4.0.5",
"@types/loglevel": "^1.5.4",
"axios": "^0.19.0",
"big.js": "^5.2.2",
"loglevel": "^1.6.3"
}
}

176
src/index.ts Normal file
View File

@ -0,0 +1,176 @@
import * as core from "@terra-money/core"
import {default as RestInterface, Coin, Validator, ValidatorRewardsInfo} from "./utils/rest"
import bn from "big.js"
import * as fs from "fs"
const rest = new RestInterface("http://127.0.0.1:1317")
const outputFile = "./unsignedTx.json"
const foundationAddress = "terra1dp0taj85ruc299rkdvzp4z5pfg6z6swaed74e6"
const goliathValAddress = "terravaloper163phlen6dn7sp9khhjar2gqqx6kga0ly8d7h9g"
const marineValAddress = "terravaloper1d3hatwcsvkktgwp3elglw9glca0h42yg6xy4lp"
const ghostValAddress = "terravaloper1rgu3qmm6rllfxlrfk94pgxa0jm37902dynqehm"
const wraithValAddress = "terravaloper1eutun6vh83lmyq0wmyf9vgghvurze2xanl9sq6"
async function loadFoundationRewards(): Promise<Array<Coin>> {
const promises: Array<Promise<void | Array<Coin>>> = []
promises.push(rest.loadDelegatorRewards(foundationAddress))
promises.push(rest.loadValidatorRewards(goliathValAddress))
promises.push(rest.loadValidatorRewards(marineValAddress))
promises.push(rest.loadValidatorRewards(ghostValAddress))
promises.push(rest.loadValidatorRewards(wraithValAddress))
const rewardMap = {}
await Promise.all(promises)
.then(res => {
for (let i in res) {
const rewards = res[i]
if (rewards && rewards.length > 0) {
for (let j in rewards) {
const denom = rewards[j].denom
const amount = rewards[j].amount.split('.')[0]
if (rewardMap[denom]) {
rewardMap[denom] = bn(rewardMap[denom]).plus(amount).toString()
} else {
rewardMap[denom] = amount
}
}
}
}
})
const totalRewards: Array<Coin> = []
for (let denom in rewardMap) {
totalRewards.push({
denom: denom,
amount: rewardMap[denom]
})
}
return totalRewards
}
const validatorBonusRate = 0.2
function computeValidatorsRewardRatio(rewardRatioMap: object, validators: Array<Validator>) {
let totalBondedToken = bn(0)
for (let i in validators) {
totalBondedToken = totalBondedToken.plus(validators[i].tokens)
}
for (let i in validators) {
const validator = validators[i]
const address = core.convertValAddressToAccAddress(validator.operator_address)
rewardRatioMap[address] = bn(validator.tokens).div(totalBondedToken).mul(validatorBonusRate).toString()
}
return
}
async function computeDelegatorRewardRatio(rewardRatioMap: object, validators: Array<Validator>): Promise<void> {
let totalBondedToken = bn(0)
for (let i in validators) {
totalBondedToken = totalBondedToken.plus(validators[i].tokens)
}
for (let i in validators) {
const validator = validators[i]
const delegations = await rest.loadDelegations(validator.operator_address)
if (!delegations) continue
for (let j in delegations) {
const delegation = delegations[j]
const tokens = bn(validator.tokens).mul(delegations[j].shares).div(validator.delegator_shares)
const ratio = bn(tokens).div(totalBondedToken).mul(1 - validatorBonusRate).toString()
if (rewardRatioMap[delegation.delegator_address]) {
rewardRatioMap[delegation.delegator_address]
= bn(rewardRatioMap[delegation.delegator_address])
.plus(ratio).toString()
} else {
rewardRatioMap[delegation.delegator_address] = ratio
}
}
}
return
}
async function main() {
const foundationRewards = await loadFoundationRewards()
console.debug("Foundation Rewards:", foundationRewards)
const validators = await rest.loadValidators()
if (!validators) {
console.error("no validator found")
return process.exit(-1)
}
const rewardRatioMap = {}
computeValidatorsRewardRatio(rewardRatioMap, validators)
console.info("Validator Bonus Rewards: ", rewardRatioMap)
await computeDelegatorRewardRatio(rewardRatioMap, validators)
console.info("Total Rewards: ", rewardRatioMap)
// Rotate reward ratio and build msg input
let totalRatio = bn(0)
const outputs: Array<core.InOut> = []
for (let addr in rewardRatioMap) {
const ratio = rewardRatioMap[addr]
totalRatio = totalRatio.plus(ratio)
const coins: Array<core.Coin> = []
for (let i in foundationRewards) {
coins.push({
denom: foundationRewards[i].denom,
amount: bn(foundationRewards[i].amount).mul(ratio).toFixed(0)
})
}
outputs.push({
address: addr,
coins: coins
})
}
if (totalRatio.gt(1)) {
console.error(`Total Reward Ratio(${totalRatio}) is bigger than 1`)
return process.exit(-1)
}
const inputs: Array<core.InOut> = []
const coins: Array<core.Coin> = []
for (let i in foundationRewards) {
coins.push({
denom: foundationRewards[i].denom,
amount: bn(foundationRewards[i].amount).toFixed(0)
})
}
inputs.push({
address: foundationAddress,
coins: coins
})
const multiSendMsg = core.buildMultiSend(inputs, outputs)
const unSingedTx = core.buildStdTx([multiSendMsg], {gas: "1000000", amount: [{
denom: "ukrw",
amount: "1000000"
}]}, "reward distribution")
fs.writeFile(outputFile, JSON.stringify(unSingedTx, null, 4), function(err) {
if (err) {
console.error("Writing Failed", err);
} else {
console.info("Writing Succeed", `Please check ${outputFile}`)
}
})
return
}
main()

83
src/utils/rest.ts Normal file
View File

@ -0,0 +1,83 @@
import axios, { AxiosResponse } from "axios"
class RestInterface {
lcdAddress: string
constructor(lcdAddress: string) {
this.lcdAddress = lcdAddress
}
async loadValidators(): Promise<void | Array<Validator>> {
const queryValidatorsURL = `${this.lcdAddress}/staking/validators`
try {
const res = await axios.get(queryValidatorsURL)
return res.data ? res.data : []
} catch (error) {
console.error("[LoadValidators]", error);
}
}
async loadValidatorRewards(valAddress: string): Promise<void | Array<Coin>> {
const queryValidatorRewardsInfoURL = `${this.lcdAddress}/distribution/validators/${valAddress}`
try {
const res = await axios.get(queryValidatorRewardsInfoURL)
// foundation validators have 100% commission, so ignore self_bond_rewards
return res.data ? res.data.val_commission : []
} catch (error) {
console.error("[LoadValidatorRewardsInfoURL]", error);
}
}
async loadDelegations(valAddress: string): Promise<void | Array<Delegation>> {
const queryDelegatorsURL = `${this.lcdAddress}/staking/validators/${valAddress}/delegations`
try {
const res = await axios.get(queryDelegatorsURL)
return res.data ? res.data : []
} catch (error) {
console.error("[LoadDelegations]", error);
}
}
async loadDelegatorRewards(delAddress: string): Promise<void | Array<Coin>> {
const queryDelegatorRewardsURL = `${this.lcdAddress}/distribution/delegators/${delAddress}/rewards`
try {
const res = await axios.get(queryDelegatorRewardsURL)
return res.data ? res.data : []
} catch (error) {
console.error("[LoadDelegatorRewards]", error);
}
}
}
export interface Validator {
operator_address: string
consensus_pubkey: string
jailed: boolean
status: number
tokens: string
delegator_shares: string
description: [Object]
unbonding_height: string
unbonding_time: string
commission: [Object]
min_self_delegation: string
}
export interface Delegation {
delegator_address: string
validator_address: string
shares: string
}
export interface Coin {
denom: string
amount: string
}
export interface ValidatorRewardsInfo {
operator_address:string
self_bond_rewards:[Coin]
val_commission:[Coin]
}
export default RestInterface

29
tsconfig.json Normal file
View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"strict": false,
"allowJs": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"skipLibCheck": true,
"strictNullChecks": true,
"sourceMap": true,
"baseUrl": "src",
"sourceRoot": "src",
"outDir": "dist",
"typeRoots": [
"./src/typings",
"./node_modules/@types"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"dist"
]
}