Skip to content

Commit

Permalink
Fix Conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
qgatssdev committed Mar 5, 2025
1 parent ba26d80 commit 9e9d47b
Show file tree
Hide file tree
Showing 70 changed files with 2,394 additions and 173 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
},
{
"extends": ["bloq/node", "prettier"],
"files": ["token-prices/**/*.js"]
"files": ["staking-points/**/*.js", "token-prices/**/*.js"]
}
],
"root": true,
Expand Down
40 changes: 40 additions & 0 deletions .github/actions/deploy-portal/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ inputs:
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_CAMPAIGN:
required: true
description: Feature flag to enable Stake Campaign
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_POINTS:
required: true
description: Feature flag to enable Loading Stake Points
NEXT_PUBLIC_FEATURE_FLAG_SYNC_SUBGRAPH:
required: false
description: Feature flag to enable syncing the user history with Subgraphs
NEXT_PUBLIC_FEATURE_FLAG_SYNC_WITHDRAWALS_SUBGRAPH:
required: false
description: Feature flag to enable syncing withdrawals with the Subgraph
NEXT_PUBLIC_TOKEN_PRICES_URL:
required: false
description: Token prices URL
Expand All @@ -69,12 +75,36 @@ inputs:
NEXT_PUBLIC_SUBGRAPH_API_KEY:
required: false
description: Subgraph API key
NEXT_PUBLIC_SUBGRAPH_HEMI_URL:
required: false
description: Hemi subgraph URL
NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_URL:
required: false
description: Hemi Sepolia subgraph URL
NEXT_PUBLIC_SUBGRAPH_HEMI_ID:
required: false
description: Hemi subgraph ID
NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_ID:
required: false
description: Hemi Sepolia subgraph ID
NEXT_PUBLIC_SUBGRAPH_MAINNET_ID:
required: false
description: Mainnet subgraph ID
NEXT_PUBLIC_SUBGRAPH_SEPOLIA_ID:
required: false
description: Sepolia subgraph ID
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_ID:
required: false
description: Hemi Stake subgraph ID
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_ID:
required: false
description: Hemi Sepolia Stake subgraph ID
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_URL:
required: false
description: Hemi Stake subgraph URL
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_URL:
required: false
description: Hemi Sepolia Stake subgraph URL
NEXT_PUBLIC_SUBGRAPH_MAINNET_URL:
required: false
description: Mainnet subgraph URL
Expand Down Expand Up @@ -120,12 +150,22 @@ runs:
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_BTC_TUNNEL: ${{ inputs.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_BTC_TUNNEL }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_MAINNET: ${{ inputs.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_MAINNET }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_CAMPAIGN: ${{ inputs.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_CAMPAIGN }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_POINTS: ${{ inputs.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_POINTS }}
NEXT_PUBLIC_FEATURE_FLAG_SYNC_SUBGRAPH: ${{ inputs.NEXT_PUBLIC_FEATURE_FLAG_SYNC_SUBGRAPH }}
NEXT_PUBLIC_FEATURE_FLAG_SYNC_WITHDRAWALS_SUBGRAPH: ${{ inputs.NEXT_PUBLIC_FEATURE_FLAG_SYNC_WITHDRAWALS_SUBGRAPH }}
NEXT_PUBLIC_TOKEN_PRICES_URL: ${{ inputs.NEXT_PUBLIC_TOKEN_PRICES_URL }}
NEXT_PUBLIC_SENTRY_DSN: ${{ inputs.NEXT_PUBLIC_SENTRY_DSN }}
NEXT_PUBLIC_SUBGRAPH_API_KEY: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_API_KEY }}
NEXT_PUBLIC_SUBGRAPH_HEMI_URL: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_HEMI_URL }}
NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_URL: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_URL }}
NEXT_PUBLIC_SUBGRAPH_HEMI_ID: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_HEMI_ID }}
NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_ID: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_MAINNET_ID: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_MAINNET_ID }}
NEXT_PUBLIC_SUBGRAPH_SEPOLIA_ID: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_ID: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_ID: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_URL: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_URL }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_URL: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_URL }}
NEXT_PUBLIC_SUBGRAPH_MAINNET_URL: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_MAINNET_URL }}
NEXT_PUBLIC_SUBGRAPH_SEPOLIA_URL: ${{ inputs.NEXT_PUBLIC_SUBGRAPH_SEPOLIA_URL }}
NEXT_PUBLIC_TRACES_SAMPLE_RATE: ${{ inputs.NEXT_PUBLIC_TRACES_SAMPLE_RATE }}
Expand Down
38 changes: 38 additions & 0 deletions .github/actions/docker-build-push/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Docker Build and Push

description: Build and push Docker images

inputs:
context:
description: 'The build context'
required: false
default: '.'
dockerHubPassword:
description: 'Docker Hub token'
required: true
dockerHubUsername:
description: 'Docker Hub username'
required: true
images:
description: 'The Docker image name'
required: true

runs:
using: composite
steps:
- uses: docker/setup-buildx-action@v3
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ inputs.images }}
- uses: docker/login-action@v3
with:
password: ${{ inputs.dockerHubPassword }}
username: ${{ inputs.dockerHubUsername }}
- uses: docker/build-push-action@v6
with:
context: ${{ inputs.context }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
31 changes: 31 additions & 0 deletions .github/workflows/build-push-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Publish Docker images

on:
push:
branches:
- main
paths:
- 'staking-points/**'
- 'token-prices/**'
workflow_dispatch:

jobs:
build-and-push:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- context: staking-points
image: hemilabs/staking-points-api
- context: token-prices/api
image: hemilabs/token-prices-api
- context: token-prices/cron
image: hemilabs/token-prices-cron
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/docker-build-push
with:
context: ${{ matrix.context }}
dockerHubPassword: ${{ secrets.DOCKERHUB_TOKEN }}
dockerHubUsername: ${{ secrets.DOCKERHUB_USERNAME }}
images: ${{ matrix.image }}
20 changes: 20 additions & 0 deletions .github/workflows/hostinger-deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,21 @@ jobs:
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_BTC_TUNNEL: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_BTC_TUNNEL || 'false' }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_MAINNET: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_MAINNET || 'false' }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_CAMPAIGN: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_CAMPAIGN || 'false' }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_POINTS: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_POINTS || 'false '}}
NEXT_PUBLIC_FEATURE_FLAG_SYNC_SUBGRAPH: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_SYNC_SUBGRAPH || 'false' }}
NEXT_PUBLIC_FEATURE_FLAG_SYNC_WITHDRAWALS_SUBGRAPH: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_SYNC_WITHDRAWALS_SUBGRAPH || 'false' }}
NEXT_PUBLIC_TOKEN_PRICES_URL: ${{ vars.NEXT_PUBLIC_TOKEN_PRICES_URL }}
NEXT_PUBLIC_SUBGRAPH_API_KEY: ${{ vars.NEXT_PUBLIC_SUBGRAPH_API_KEY }}
NEXT_PUBLIC_SUBGRAPH_HEMI_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_HEMI_URL }}
NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_URL }}
NEXT_PUBLIC_SUBGRAPH_HEMI_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_HEMI_ID }}
NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_MAINNET_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_MAINNET_ID }}
NEXT_PUBLIC_SUBGRAPH_SEPOLIA_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_URL }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_URL }}
NEXT_PUBLIC_SUBGRAPH_MAINNET_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_MAINNET_URL }}
NEXT_PUBLIC_SUBGRAPH_SEPOLIA_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_SEPOLIA_URL }}

Expand Down Expand Up @@ -86,12 +96,22 @@ jobs:
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_BTC_TUNNEL: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_BTC_TUNNEL || 'false' }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_MAINNET: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_MAINNET || 'false' }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_CAMPAIGN: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_CAMPAIGN || 'false' }}
NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_POINTS: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_ENABLE_STAKE_POINTS || 'false '}}
NEXT_PUBLIC_FEATURE_FLAG_SYNC_SUBGRAPH: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_SYNC_SUBGRAPH || 'false' }}
NEXT_PUBLIC_FEATURE_FLAG_SYNC_WITHDRAWALS_SUBGRAPH: ${{ vars.NEXT_PUBLIC_FEATURE_FLAG_SYNC_WITHDRAWALS_SUBGRAPH || 'false' }}
NEXT_PUBLIC_TOKEN_PRICES_URL: ${{ vars.NEXT_PUBLIC_TOKEN_PRICES_URL }}
NEXT_PUBLIC_SENTRY_DSN: ${{ vars.NEXT_PUBLIC_SENTRY_DSN }}
NEXT_PUBLIC_SUBGRAPH_API_KEY: ${{ vars.NEXT_PUBLIC_SUBGRAPH_API_KEY }}
NEXT_PUBLIC_SUBGRAPH_HEMI_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_HEMI_URL}}
NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_URL}}
NEXT_PUBLIC_SUBGRAPH_HEMI_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_HEMI_ID }}
NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_HEMI_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_MAINNET_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_MAINNET_ID }}
NEXT_PUBLIC_SUBGRAPH_SEPOLIA_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_ID: ${{ vars.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_ID }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_URL }}
NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_STAKE_HEMI_SEPOLIA_URL }}
NEXT_PUBLIC_SUBGRAPH_MAINNET_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_MAINNET_URL }}
NEXT_PUBLIC_SUBGRAPH_SEPOLIA_URL: ${{ vars.NEXT_PUBLIC_SUBGRAPH_SEPOLIA_URL }}
NEXT_PUBLIC_TRACES_SAMPLE_RATE: ${{ vars.NEXT_PUBLIC_TRACES_SAMPLE_RATE }}
Expand Down
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ tsconfig.tsbuildinfo
# information from docker-compose
subgraphs/data
# Graph CLI generated artifacts
subgraphs/tunnel-deposits-subgraph/build
subgraphs/tunnel-deposits-subgraph/generated
subgraphs/hemi-stake-operations-subgraph/build
subgraphs/hemi-stake-operations-subgraph/generated
subgraphs/hemi-tunnel-withdrawals-subgraph/build
subgraphs/hemi-tunnel-withdrawals-subgraph/generated
subgraphs/hemi-tunnel-withdrawals-subgraph/generated
subgraphs/tunnel-deposits-subgraph/build
subgraphs/tunnel-deposits-subgraph/generated
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions staking-points/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM node:20.18.3-alpine

ENV NODE_ENV=production

WORKDIR /app

COPY package*.json .
RUN npm install --production

USER node

COPY . .

EXPOSE 3002

CMD ["npm", "start"]
37 changes: 37 additions & 0 deletions staking-points/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Staking Points API

This service provides the amount of points any user has earned through the Hemi Staking Campaign.
Data is provided by [Absinthe](https://absinthe.network/).

## Configuration

These environment variables control how the API works:

| Variable | Description | Default |
| ---------------- | --------------------------------------------------------------- | ------------------------------------------ |
| ABSINTHE_API_KEY | The JWT used to authenticate to the Absinthe GraphQL API. | |
| ABSINTHE_API_URL | The Absinthe GraphQL API URL. | `https://gql3.absinthe.network/v1/graphql` |
| ORIGINS | Comma-separated list of allowed origins. '\*' is not supported. | `http://localhos:3000` |
| PORT | The HTTP port the server listens for requests. | 3002 |

## Local development and testing

To start the service, run:

```sh
ABSINTHE_API_KEY=MY_API_KEY docker compose up -d
```

Alternatively, if the environment variables are stored in a `.env.local` file, run:

```sh
docker build --tag staking-points-api:local .
docker run --env-file .env.local --init --interactive --publish 3002:3002 --rm --tty staking-points-api:local
```

Then the points for a user can be obtained as follows:

```console
$ curl http://localhost:3002/0x85e0D9e73c12eFE889750f44422a77B544D48d17
{"points":270644}
```
5 changes: 5 additions & 0 deletions staking-points/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"engines": {
"node": ">=20"
}
}
85 changes: 85 additions & 0 deletions staking-points/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict'

const http = require('http')

const absintheApiKey = process.env.ABSINTHE_API_KEY
const absintheApiUrl =
process.env.ABSINTHE_API_URL || 'https://gql3.absinthe.network/v1/graphql'
const portStr = process.env.PORT || '3002'
const port = parseInt(portStr)
const originsStr = process.env.ORIGINS || 'http://localhost:3000'
const origins = originsStr.split(',')

async function getUserPoints(address) {
const res = await fetch(absintheApiUrl, {
body: JSON.stringify({
query: `query ($address: String!) {
query_address_points(args: { address: $address, client_season: "hemi" }) {
points
}
}`,
variables: { address },
}),
headers: {
'Authorization': `Bearer ${absintheApiKey}`,
'Content-Type': 'application/json',
},
method: 'POST',
})
if (!res.ok) {
throw new Error(`Points query failed: ${res.statusText}`)
}

const { data, errors } = await res.json()
if (errors && errors.length) {
const messages = errors.map(err => err.message).join(', ')
throw new Error(`Points query errored: ${messages}`)
}

return data?.query_address_points[0]?.points || 0
}

async function handleRequest(req, res) {
const { 'access-control-request-headers': headers, origin } = req.headers
if (headers) {
res.setHeader('Access-Control-Allow-Headers', headers)
}
res.setHeader('Access-Control-Allow-Methods', 'GET')
const allowOrigin = origins.includes(origin) ? origin : false
res.setHeader('Access-Control-Allow-Origin', allowOrigin)

if (req.method === 'OPTIONS') {
res.writeHead(204)
res.end()
return
}

if (req.method !== 'GET') {
res.writeHead(405, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ error: 'Method Not Allowed' }))
return
}

const address = req.url.slice(1)
if (!/^0x[0-9a-f]{40}$/i.test(address)) {
res.writeHead(400, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ error: 'Invalid address format' }))
return
}

let statusCode, data
try {
statusCode = 200
data = { points: await getUserPoints(address) }
} catch (err) {
// eslint-disable-next-line no-console
console.error(`Failed to handle request: ${err}`)
statusCode = 500
data = { error: 'Internal Server Error' }
}

res.writeHead(statusCode, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(data))
}

http.createServer(handleRequest).listen(port)
Loading

0 comments on commit 9e9d47b

Please sign in to comment.