Skip to content

Commit

Permalink
Initial commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
RuslanGlaznyov committed Jul 31, 2023
0 parents commit 8da145a
Show file tree
Hide file tree
Showing 41 changed files with 22,918 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
.env
.parcel-cache/
65 changes: 65 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

.parcel-cache
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# 0x
profile-*

# mac files
.DS_Store

# vim swap files
*.swp

# webstorm
.idea

# vscode
.vscode
*code-workspace

# clinic
profile*
*clinic*
*flamegraph*

build
# compiled app
dist
.env
deploy
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Use an official Node.js runtime as the base image
FROM node:20-alpine
RUN apk add g++ make py3-pip
# Set the working directory inside the container

WORKDIR /usr/src/app/app
COPY app .

WORKDIR /usr/src/app/client

# Copy package.json and package-lock.json to the container
COPY client/package*.json /usr/src/app/client
RUN npm i
COPY client .
RUN npm run build

WORKDIR /usr/src/app/app
# Expose the port on which your Node.js server listens

ENV NODE_ENV='production'
RUN npm ci --only=production
EXPOSE 5000

# Define the command to run your Node.js server
CMD [ "node", "/usr/src/app/app/cli/index.js" ]
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# [KyveRestake](https://kyve-restake.mellifera.network)

Open source restake service for Kyve protocol nodes.

Allows delegators to grant permission for a validator to compound their rewards.

Inspired by [REStake](https://github.com/eco-stake/restake) restake service, which do the same but for cosmos nodes

Build with [@kyvejs/sdk](https://github.com/KYVENetwork/kyvejs/tree/main/common/sdk)

## Installation
env file example
```env
# Generate a new hot wallet you will use to automatically carry out the staking transactions. The mnemonic will need to be provided to the app so use a dedicated wallet and only keep enough funds for transaction fees
GRANT_MNEMONIC=menemonic seed phrase with minimal funds for fee payment. This wallet will be used to pay for the transactions fee and restake delegators rewards
#Setup your validator address
VALIDATOR_ADDRESS=kyve1dsk7wfy6n2ylwj8x2el6txgsyff8av6wa5yl28
#Setup image url for your validator address
VALIDATOR_IMAGE_URL=https://s3.amazonaws.com/keybase_processed_uploads/e86fec4890dc435ba14cb684ab658e05_360_360.jpg
```
1. With Docker compose

You need docker installed
```yaml
version: '3'
services:
server:
image: mellifera/kyve-restake
restart: unless-stopped
ports:
- 5000:5000
command: node cli/index.js server
env_file:
- <path to .env file>
worker:
image: mellifera/kyve-restake
restart: unless-stopped
command: node cli/index.js worker
env_file:
- <path to .env file>
```
2. From source code
You need nodejs 20+ installed.
Add `.env` file to `app` directory or set env variables in your system

```bash
git clone <repo url> kyve-restake
cd kyve-restake/client
npm i
npm run build
cd ../app
npm i
# run server
node cli/index.js server
# run restake worker
node cli/index.js worker
```

## FAQ.
1. Why there is no limit for delegate grant (delegate address restriction, max delegate funds)?

- We use GenericAuthorization for message `/kyve.delegation.v1beta1.MsgDelegate`. To implement more restrictions for grants needs a custom `Authorization` by analogy [StakeAuthorization](https://docs.cosmos.network/main/modules/authz#stakeauthorization) on chain side.
After this will be implemented on chain side, we will add it to our service.

2. Can I run KyveRestake service for my protocol node?

- Of course, you can run it for your validator. Just leave references of creators of this service :)


## You want to get involved? 😍

Please submit a pull request or open an issue ❤️
3 changes: 3 additions & 0 deletions app/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
GRANT_MNEMONIC=menemonic seed phrase with minimal funds for fee payment. This wallet will be used to pay for the transactions fee and restake delegators rewards.
VALIDATOR_ADDRESS=kyve1dsk7wfy6n2ylwj8x2el6txgsyff8av6wa5yl28
VALIDATOR_IMAGE_URL=https://s3.amazonaws.com/keybase_processed_uploads/e86fec4890dc435ba14cb684ab658e05_360_360.jpg
3 changes: 3 additions & 0 deletions app/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/node_modules/**
**/build/**
**/dist/**
34 changes: 34 additions & 0 deletions app/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module.exports = {
env: {
commonjs: true,
es2021: true,
node: true
},

extends: 'standard',
overrides: [
{
env: {
node: true
},
files: [
'.eslintrc.{js,cjs}'
],
parserOptions: {
sourceType: 'script'
}
}
],
parserOptions: {
ecmaVersion: 'latest'
},
rules: {
'no-console': ['warn', { allow: ['info'] }],
indent: ['error', 2],
'no-unused-vars': ['error', {
args: 'all',
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}]
}
}
21 changes: 21 additions & 0 deletions app/cli/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const commander = require('commander')
const logger = require('../common/logger')
const log = logger('CLI')
const program = new commander.Command('KYVE RESTAKE CLI')
program
.command('server')
.description('Run a restake server')
.action(() => {
log.info('Restake server starting...')
require('../server/src/server.js')
})

program
.command('worker')
.description('Run a restake worker')
.action(() => {
log.info('Restake worker starting...')
require('../restake-worker/index.js')
})

program.parse()
45 changes: 45 additions & 0 deletions app/common/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const { envSchema } = require('env-schema')
const schema = {
type: 'object',
required: [
'GRANT_MNEMONIC',
'VALIDATOR_ADDRESS',
'VALIDATOR_IMAGE_URL',
'HUMAN_READABLE_RESTAKE_BY_CRON',
'RESTAKE_BY_CRON'
],
properties: {
NODE_ENV: {
type: 'string',
default: 'development'
},
PORT: {
type: 'number',
default: 5000
},
KYVE_ENV: {
type: 'string',
default: 'kyve-1'
},
GRANT_MNEMONIC: {
type: 'string'
},
VALIDATOR_ADDRESS: {
type: 'string'
},
VALIDATOR_IMAGE_URL: {
type: 'string'
},
RESTAKE_BY_CRON: {
type: 'string'
},
HUMAN_READABLE_RESTAKE_BY_CRON: {
type: 'string'
}
}
}

module.exports = envSchema({
schema,
dotenv: true
})
15 changes: 15 additions & 0 deletions app/common/cosmos-api.factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { fetch } = require('undici')
const CosmosApi = require('./depends-free/cosmos-api')

module.exports = (function () {
let apiInstance = null
return {
init (baseUrl) {
apiInstance = CosmosApi(baseUrl, fetch)
},
get api () {
if (!apiInstance) throw new Error('API not initialized')
return apiInstance
}
}
})()
33 changes: 33 additions & 0 deletions app/common/depends-free/common-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const DECIMAL = 6
const formatNumber = (amount) => {
if (amount) {
return `${amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ')}`
}
return '0'
}
const toHumamReadable = (amount) => {
if (amount) {
return (Number(BigInt(amount) * BigInt(100) / BigInt(10 ** DECIMAL)) / 100).toFixed(2).toString()
}

return '0'
}
const formatProcent = (procent) => {
const formatProcent = procent * 100
return formatProcent.toFixed(2).toString()
}
const formatAddress = (address) => {
if (!address) {
return '-'
}

if (address) {
return `${address.slice(0, 10)}...${address.slice(address.length - 6)}`
}
}
module.exports = {
formatNumber,
toHumamReadable,
formatProcent,
formatAddress
}
18 changes: 18 additions & 0 deletions app/common/depends-free/cosmos-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = function CosmosApi (baseUrl, reqFetch = fetch) {
const makeRequest = (subUrl, queryParams = null) => {
const filteredObject = queryParams && Object.keys(queryParams).reduce((acc, key) => {
if (queryParams[key] !== undefined) {
acc[key] = queryParams[key]
}
return acc
}, {})
const url = new URL(subUrl, baseUrl)
const urlToReq = filteredObject ? `${url}?${new URLSearchParams({ ...filteredObject })}` : url
return reqFetch(urlToReq).then(res => res.json())
}
return {
getPermissionsByGranterAndGrantee: (granter, grantee, msgTypeUrl) => makeRequest('/cosmos/authz/v1beta1/grants', { grantee, granter, msg_type_url: msgTypeUrl }),
findSendedTrx: (address) => makeRequest('/cosmos/tx/v1beta1/txs', { order_by: '2', events: `message.sender='${address}'` }),
getPermissionsByGrantee: (granteeAddress) => makeRequest(`/cosmos/authz/v1beta1/grants/grantee/${granteeAddress}`)
}
}
22 changes: 22 additions & 0 deletions app/common/kyve.factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const KyveSDK = require('@kyvejs/sdk').default

function kyveSDK () {
let kyve = null
async function init (mnemonic, env) {
const sdk = new KyveSDK(env)
kyve = {
kyveSdk: await sdk.fromMnemonic(mnemonic),
kyveApi: sdk.createLCDClient(env)
}
}
return {
init,
get instance () {
if (!kyve) {
throw new Error('kyveSDK not initialized')
}
return kyve
}
}
}
module.exports = kyveSDK()
7 changes: 7 additions & 0 deletions app/common/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const pino = require('pino')
module.exports = function (name) {
if (process.env.NODE_ENV === 'development') {
return pino({ name, transport: { target: 'pino-pretty' } })
}
return pino({ name })
}
Loading

0 comments on commit 8da145a

Please sign in to comment.