replaced subgraph with ponder
This commit is contained in:
parent
5e6e21878c
commit
ba64e725dc
45 changed files with 386 additions and 7275 deletions
35
AGENTS.md
35
AGENTS.md
|
|
@ -5,9 +5,14 @@
|
|||
- `ponder/`: New indexer replacing The Graph. `ponder.config.ts` selects network/contracts, `src/` holds TypeScript event handlers, `ponder.schema.ts` defines tables, `README.md` documents usage. Generated artifacts in `generated/` are auto-created by Ponder.
|
||||
- `subgraph/base_sepolia/`: Legacy AssemblyScript implementation kept for reference during migration. Do not modify unless syncing schema changes between stacks.
|
||||
- `landing/`: Vue 3 app (Vite + TypeScript) for the public launch site and forthcoming staking UI. See `src/views/` for pages, `env.d.ts` for injected globals.
|
||||
- `services/txnBot/`: Node service that consumes the GraphQL API (`.graphclient`) to trigger `recenter()` and `payTax()` on-chain when profitable.
|
||||
- `services/txnBot/`: Node service that consumes the Ponder GraphQL API to trigger `recenter()` and `payTax()` on-chain when profitable.
|
||||
- `kraiken-lib/`: Shared TypeScript helpers (e.g., `bytesToUint256`) consumed by the landing app + bots.
|
||||
|
||||
## Environment Profiles
|
||||
- `BASE_SEPOLIA_LOCAL_FORK`: Anvil fork bootstrapped by `scripts/local_env.sh`
|
||||
- `BASE_SEPOLIA`: Public Base Sepolia testnet
|
||||
- `BASE`: Base mainnet
|
||||
|
||||
## Execution Workflow
|
||||
1. **Contracts**
|
||||
- Build/test via `forge build` and `forge test` inside `onchain/`.
|
||||
|
|
@ -16,23 +21,25 @@
|
|||
|
||||
2. **Indexer (Ponder)**
|
||||
- Install deps (`npm install`).
|
||||
- Configure environment via `PONDER_NETWORK` (`local`, `baseSepolia`, or `base`). Update `ponder.config.ts` addresses if deployments change.
|
||||
- Configure environment via `PONDER_NETWORK` (`BASE_SEPOLIA_LOCAL_FORK`, `BASE_SEPOLIA`, or `BASE`). Update `ponder.config.ts` addresses if deployments change.
|
||||
- Run dev mode with `npm run dev`; GraphQL served at `http://localhost:42069/graphql`.
|
||||
- Handlers in `src/kraiken.ts` and `src/stake.ts` maintain rolling supply stats, ring-buffered hourly metrics, and position state.
|
||||
|
||||
3. **Frontend**
|
||||
- `npm install` then `npm run dev` in `landing/`. Currently static marketing copy with placeholders for wallet/staking flows.
|
||||
- Configure GraphQL endpoints via `VITE_PONDER_BASE_SEPOLIA`, `VITE_PONDER_BASE`, and optionally override `VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK` (defaults to `http://127.0.0.1:42069/graphql`).
|
||||
|
||||
4. **Automation Bot**
|
||||
- Requires `.env` with RPC + key. Queries indexer (Graph or Ponder) to decide when to pay tax or recenter liquidity.
|
||||
- Requires `.env` with RPC + key. Queries the Ponder GraphQL endpoint to decide when to pay tax or recenter liquidity.
|
||||
- `scripts/local_env.sh start` provisions and launches the bot automatically when running against the local fork.
|
||||
|
||||
## Migration Notes (Subgraph → Ponder)
|
||||
- Schema parity: ensure any entity changes land in both `ponder.schema.ts` and legacy Graph `schema.graphql` until cutover.
|
||||
- Event coverage: `Stake` events (`PositionCreated/Removed/Shrunk/TaxPaid/RateHiked`) mirrored from AssemblyScript handlers. Kraiken `Transfer` powers mint/burn/tax/UBI tracking.
|
||||
- Ring buffer logic in `kraiken.ts` depends on block timestamps being monotonic; gaps >168 hours zero out the buffer. Verify `startBlock` in `ponder.config.ts` to avoid reprocessing genesis noise.
|
||||
- Local deployment requires updating `ponder.config.ts` local contract addresses after each run or injecting via env overrides.
|
||||
- Local deployment requires updating `ponder.config.ts` local contract addresses after each run or injecting via env overrides. The `BASE_SEPOLIA_LOCAL_FORK` profile assumes addresses generated by `DeployLocal.sol`.
|
||||
- Ponder v0.13 exposes helpers via `context.client` / `context.contracts`; use these to seed stats from on-chain `totalSupply` and refresh `outstandingStake` straight from the `Stake` contract. All hourly projections now run off a JSON ring buffer on the `stats` row.
|
||||
- Subgraph naming differs from the new schema (`stats_collection`, decimal shares). Until the web-app switches to Ponder’s GraphQL endpoint, keep the legacy entity shape in mind. Plan to provide either a compatibility layer or update `web-app/src/composables/useStatCollection.ts` to hit the new schema.
|
||||
- Subgraph naming differs from the new schema (`stats_collection`, decimal shares). The web-app and txn bot now target the Ponder schema directly; keep legacy Graph compatibility only until the AssemblyScript stack is fully retired.
|
||||
- Tax accounting must listen to `PositionTaxPaid` instead of guessing via hard-coded addresses. The old subgraph missed this; Ponder now increments the ring buffer segment on each tax payment.
|
||||
- Liquidity bootstrap still depends on the Uniswap pool stepping through `recenter()`. On current Base-Sepolia forks the LM reverts with `LOK`. Investigate before relying on `scripts/local_env.sh start` for an unattended setup.
|
||||
|
||||
|
|
@ -42,7 +49,7 @@
|
|||
1. `anvil --fork-url https://sepolia.base.org` (or base mainnet RPC) in terminal 1.
|
||||
2. From `onchain/`, deploy with `forge script script/DeployLocal.sol --fork-url http://127.0.0.1:8545 --broadcast`.
|
||||
3. Fund LiquidityManager, call `recenter()`, and execute sample trades (KRK buy, stake, unstake) using `cast` or Foundry scripts.
|
||||
4. Start Ponder (`PONDER_NETWORK=local npm run dev`) and watch logs for handler errors.
|
||||
4. Start Ponder (`PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK npm run dev`) and watch logs for handler errors.
|
||||
5. Query GraphQL (`stats`, `positions`) to confirm indexed state.
|
||||
|
||||
## Gotchas & Tips
|
||||
|
|
@ -50,16 +57,24 @@
|
|||
- VWAP/ethScarcity logic expects squared price format; do not convert to sqrt unintentionally.
|
||||
- LiquidityManager `recenter()` reverts unless funded with WETH (Base WETH address `0x4200...006`). Use `cast send` with sufficient ETH when testing locally.
|
||||
- Ponder’s SQLite store lives in `.ponder/` (gitignored). Delete between runs if schema changes.
|
||||
- Legacy subgraph still powers `services/txnBot` until cutover—coordinate endpoint switch when Ponder is production-ready.
|
||||
- `web-app/` currently points at The Graph Studio URLs (see `src/config.ts`). When Ponder replaces the subgraph, update those envs and mirror the expected GraphQL shape (the app queries `stats_collection` with camelCase fields). Consider adding an adapter layer or bumping the frontend to the new schema before deleting `subgraph/`.
|
||||
- Legacy subgraph is maintained for reference only; all active services now read from Ponder.
|
||||
- `web-app/` reads from Ponder by default (see `src/config.ts`). Update the environment specific GraphQL URLs when deployments move.
|
||||
|
||||
## Useful Commands
|
||||
- `foundryup` / `forge clean` / `forge snapshot`
|
||||
- `anvil --fork-url https://sepolia.base.org`
|
||||
- `cast call <POOL> "slot0()"`
|
||||
- `PONDER_NETWORK=local npm run dev`
|
||||
- `PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK npm run dev`
|
||||
- `curl -X POST http://localhost:42069/graphql -d '{"query":"{ stats(id:\"0x01\"){kraikenTotalSupply}}"}'`
|
||||
- `./scripts/local_env.sh start` boots Anvil+contracts+ponder+frontend; stop with Ctrl+C or `./scripts/local_env.sh stop`.
|
||||
- `curl http://127.0.0.1:43069/status`
|
||||
- `./scripts/local_env.sh start` boots Anvil+contracts+ponder+frontend+txnBot; stop with Ctrl+C or `./scripts/local_env.sh stop`.
|
||||
|
||||
## Refactor Backlog
|
||||
- Replace the temporary `any` shims in `ponder/src/kraiken.ts` and `ponder/src/stake.ts` by importing the official 0.13 handler types instead of stubbing `ponder-env.d.ts`.
|
||||
- Drop the custom `ponder:api` / `ponder:registry` module declarations once the generator emits them; if it cannot, declare precise interfaces rather than `any` to keep autocomplete and future upgrades safe.
|
||||
- Convert the JSON ABI imports in `ponder/ponder.config.ts` to typed loaders (e.g., `satisfies Abi`) so config drift is caught at compile time instead of via `as Abi` casts.
|
||||
- Move the snatch-selection logic out of `web-app/src/components/StakeHolder.vue` into a dedicated composable that both the stake form and charts can reuse, and memoise the `assetsToShares` conversions there.
|
||||
- Split `kraiken-lib/src/helpers.ts` into focused modules (ids, tax rates, snatch selection) so consumers can tree-shake and each helper stays small and testable.
|
||||
|
||||
## Contacts & Artifacts
|
||||
- Deployment addresses recorded in `onchain/deployments-local.json` (local) and broadcast traces under `onchain/broadcast/`.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# Network configuration
|
||||
# Options: local, baseSepolia, base
|
||||
PONDER_NETWORK=local
|
||||
# Options: BASE_SEPOLIA_LOCAL_FORK, BASE_SEPOLIA, BASE
|
||||
PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK
|
||||
|
||||
# RPC URLs (optional - defaults provided)
|
||||
PONDER_RPC_URL_BASE_SEPOLIA_LOCAL_FORK=http://127.0.0.1:8545
|
||||
PONDER_RPC_URL_BASE=https://base.llamarpc.com
|
||||
PONDER_RPC_URL_BASE_SEPOLIA=https://sepolia.base.org
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Auto-generated by local_env.sh
|
||||
PONDER_NETWORK=local
|
||||
PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK
|
||||
KRAIKEN_ADDRESS=0x56186c1e64ca8043def78d06aff222212ea5df71
|
||||
STAKE_ADDRESS=0x056e4a859558a3975761abd7385506bc4d8a8e60
|
||||
START_BLOCK=31435585
|
||||
START_BLOCK=31438430
|
||||
# Use PostgreSQL connection
|
||||
DATABASE_URL=postgresql://ponder:ponder_local@localhost/ponder_local
|
||||
DATABASE_SCHEMA=ponder_local_31435585
|
||||
DATABASE_SCHEMA=ponder_local_31438430
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Perfect for testing with mainnet state without spending gas.
|
|||
anvil --fork-url https://base.llamarpc.com
|
||||
|
||||
# Terminal 2: Start Ponder
|
||||
export PONDER_NETWORK=local
|
||||
export PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK
|
||||
npm run dev
|
||||
```
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ Access GraphQL at: http://localhost:42069/graphql
|
|||
For integration testing with live testnet.
|
||||
|
||||
```bash
|
||||
export PONDER_NETWORK=baseSepolia
|
||||
export PONDER_NETWORK=BASE_SEPOLIA
|
||||
export PONDER_RPC_URL_BASE_SEPOLIA=https://sepolia.base.org # or your RPC
|
||||
npm run dev
|
||||
```
|
||||
|
|
@ -32,7 +32,7 @@ npm run dev
|
|||
For production deployment.
|
||||
|
||||
```bash
|
||||
export PONDER_NETWORK=base
|
||||
export PONDER_NETWORK=BASE
|
||||
export PONDER_RPC_URL_BASE=https://base.llamarpc.com # Use paid RPC for production
|
||||
export DATABASE_URL=postgresql://user:pass@host:5432/kraiken_ponder
|
||||
npm run start
|
||||
|
|
@ -147,7 +147,7 @@ services:
|
|||
- "42069:42069"
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://postgres:password@db:5432/kraiken
|
||||
- PONDER_NETWORK=base
|
||||
- PONDER_NETWORK=BASE
|
||||
|
||||
db:
|
||||
image: postgres:15
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ cp .env.example .env
|
|||
```
|
||||
|
||||
Edit `.env` to select your network:
|
||||
- `PONDER_NETWORK=local` - Local Anvil fork
|
||||
- `PONDER_NETWORK=baseSepolia` - Base Sepolia testnet
|
||||
- `PONDER_NETWORK=base` - Base mainnet
|
||||
- `PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK` - Local Anvil fork managed by `scripts/local_env.sh`
|
||||
- `PONDER_NETWORK=BASE_SEPOLIA` - Base Sepolia testnet
|
||||
- `PONDER_NETWORK=BASE` - Base mainnet
|
||||
|
||||
### 3. Local Development (Anvil Fork)
|
||||
|
||||
|
|
@ -37,19 +37,19 @@ Edit `.env` to select your network:
|
|||
anvil --fork-url https://base.llamarpc.com
|
||||
|
||||
# Terminal 2: Start Ponder indexer
|
||||
PONDER_NETWORK=local npm run dev
|
||||
PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK npm run dev
|
||||
```
|
||||
|
||||
### 4. Testnet Deployment (Base Sepolia)
|
||||
|
||||
```bash
|
||||
PONDER_NETWORK=baseSepolia npm run dev
|
||||
PONDER_NETWORK=BASE_SEPOLIA npm run dev
|
||||
```
|
||||
|
||||
### 5. Production Deployment (Base Mainnet)
|
||||
|
||||
```bash
|
||||
PONDER_NETWORK=base npm run start
|
||||
PONDER_NETWORK=BASE npm run start
|
||||
```
|
||||
|
||||
## GraphQL Queries
|
||||
|
|
@ -157,7 +157,7 @@ WORKDIR /app
|
|||
COPY package*.json ./
|
||||
RUN npm ci --only=production
|
||||
COPY . .
|
||||
ENV PONDER_NETWORK=base
|
||||
ENV PONDER_NETWORK=BASE
|
||||
CMD ["npm", "run", "start"]
|
||||
```
|
||||
|
||||
|
|
|
|||
88
ponder/package-lock.json
generated
88
ponder/package-lock.json
generated
|
|
@ -1905,23 +1905,6 @@
|
|||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
|
|
@ -2556,18 +2539,6 @@
|
|||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "8.21.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz",
|
||||
|
|
@ -3139,22 +3110,6 @@
|
|||
"real-require": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tsconfck": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz",
|
||||
|
|
@ -3284,23 +3239,20 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz",
|
||||
"integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==",
|
||||
"version": "5.4.20",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz",
|
||||
"integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3",
|
||||
"postcss": "^8.5.6",
|
||||
"rollup": "^4.43.0",
|
||||
"tinyglobby": "^0.2.15"
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
"rollup": "^4.20.0"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||
|
|
@ -3309,25 +3261,19 @@
|
|||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^20.19.0 || >=22.12.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "^4.0.0",
|
||||
"@types/node": "^18.0.0 || >=20.0.0",
|
||||
"less": "*",
|
||||
"lightningcss": "^1.21.0",
|
||||
"sass": "^1.70.0",
|
||||
"sass-embedded": "^1.70.0",
|
||||
"stylus": ">=0.54.8",
|
||||
"sugarss": "^5.0.0",
|
||||
"terser": "^5.16.0",
|
||||
"tsx": "^4.8.1",
|
||||
"yaml": "^2.4.2"
|
||||
"sass": "*",
|
||||
"sass-embedded": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"terser": "^5.4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"jiti": {
|
||||
"optional": true
|
||||
},
|
||||
"less": {
|
||||
"optional": true
|
||||
},
|
||||
|
|
@ -3348,12 +3294,6 @@
|
|||
},
|
||||
"terser": {
|
||||
"optional": true
|
||||
},
|
||||
"tsx": {
|
||||
"optional": true
|
||||
},
|
||||
"yaml": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
},
|
||||
"overrides": {
|
||||
"esbuild": "^0.25.10",
|
||||
"vite": "^7.1.7"
|
||||
"vite": "^5.4.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
|
|
|||
11
ponder/ponder-env.d.ts
vendored
11
ponder/ponder-env.d.ts
vendored
|
|
@ -9,17 +9,6 @@ declare module "ponder:schema" {
|
|||
export * from "./ponder.schema.ts";
|
||||
}
|
||||
|
||||
declare module "ponder:registry" {
|
||||
const ponder: any;
|
||||
export { ponder };
|
||||
}
|
||||
|
||||
declare module "ponder:api" {
|
||||
export const db: any;
|
||||
const api: any;
|
||||
export default api;
|
||||
}
|
||||
|
||||
// This file enables type checking and editor autocomplete for this Ponder project.
|
||||
// After upgrading, you may find that changes have been made to this file.
|
||||
// If this happens, please commit the changes. Do not manually edit this file.
|
||||
|
|
|
|||
|
|
@ -3,20 +3,18 @@ import type { Abi } from "viem";
|
|||
import KraikenAbi from "./abis/Kraiken.json";
|
||||
import StakeAbi from "./abis/Stake.json";
|
||||
|
||||
// Network configurations
|
||||
// Network configurations keyed by canonical environment name
|
||||
const networks = {
|
||||
// Local development (Anvil fork)
|
||||
local: {
|
||||
BASE_SEPOLIA_LOCAL_FORK: {
|
||||
chainId: 31337,
|
||||
rpc: "http://127.0.0.1:8545",
|
||||
rpc: process.env.PONDER_RPC_URL_BASE_SEPOLIA_LOCAL_FORK || "http://127.0.0.1:8545",
|
||||
contracts: {
|
||||
kraiken: process.env.KRAIKEN_ADDRESS || "0x56186c1E64cA8043dEF78d06AfF222212eA5df71",
|
||||
stake: process.env.STAKE_ADDRESS || "0x056E4a859558A3975761ABd7385506BC4D8A8E60",
|
||||
startBlock: parseInt(process.env.START_BLOCK || "31425917"),
|
||||
},
|
||||
},
|
||||
// Base Sepolia testnet
|
||||
baseSepolia: {
|
||||
BASE_SEPOLIA: {
|
||||
chainId: 84532,
|
||||
rpc: process.env.PONDER_RPC_URL_BASE_SEPOLIA || "https://sepolia.base.org",
|
||||
contracts: {
|
||||
|
|
@ -25,8 +23,7 @@ const networks = {
|
|||
startBlock: 20940337,
|
||||
},
|
||||
},
|
||||
// Base mainnet
|
||||
base: {
|
||||
BASE: {
|
||||
chainId: 8453,
|
||||
rpc: process.env.PONDER_RPC_URL_BASE || "https://base.llamarpc.com",
|
||||
contracts: {
|
||||
|
|
@ -35,16 +32,20 @@ const networks = {
|
|||
startBlock: 26038614,
|
||||
},
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
// Select network based on environment variable
|
||||
const NETWORK = process.env.PONDER_NETWORK || "local";
|
||||
const NETWORK = (process.env.PONDER_NETWORK as keyof typeof networks) || "BASE_SEPOLIA_LOCAL_FORK";
|
||||
const selectedNetwork = networks[NETWORK as keyof typeof networks];
|
||||
|
||||
if (!selectedNetwork) {
|
||||
throw new Error(`Invalid network: ${NETWORK}. Valid options: ${Object.keys(networks).join(", ")}`);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[ponder.config] Network=${NETWORK}, chainId=${selectedNetwork.chainId}, startBlock=${selectedNetwork.contracts.startBlock}`,
|
||||
);
|
||||
|
||||
export default createConfig({
|
||||
// Use PostgreSQL if DATABASE_URL is set, otherwise use PGlite
|
||||
database: process.env.DATABASE_URL ? {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ ANVIL_PID=$!
|
|||
sleep 5
|
||||
|
||||
echo "Starting Ponder indexer for local network..."
|
||||
export PONDER_NETWORK=local
|
||||
export PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK
|
||||
timeout 30 npm run dev
|
||||
|
||||
# Cleanup
|
||||
|
|
|
|||
|
|
@ -9,15 +9,19 @@ LOG_DIR="$STATE_DIR/logs"
|
|||
ANVIL_PID_FILE="$STATE_DIR/anvil.pid"
|
||||
PONDER_PID_FILE="$STATE_DIR/ponder.pid"
|
||||
WEBAPP_PID_FILE="$STATE_DIR/webapp.pid"
|
||||
TXNBOT_PID_FILE="$STATE_DIR/txnBot.pid"
|
||||
|
||||
ANVIL_LOG="$LOG_DIR/anvil.log"
|
||||
PONDER_LOG="$LOG_DIR/ponder.log"
|
||||
WEBAPP_LOG="$LOG_DIR/webapp.log"
|
||||
TXNBOT_LOG="$LOG_DIR/txnBot.log"
|
||||
SETUP_LOG="$LOG_DIR/setup.log"
|
||||
TXNBOT_ENV_FILE="$STATE_DIR/txnBot.env"
|
||||
|
||||
FORK_URL=${FORK_URL:-"https://sepolia.base.org"}
|
||||
ANVIL_RPC="http://127.0.0.1:8545"
|
||||
GRAPHQL_HEALTH="http://127.0.0.1:42069/health"
|
||||
GRAPHQL_ENDPOINT="http://127.0.0.1:42069/graphql"
|
||||
FRONTEND_URL="http://127.0.0.1:5173"
|
||||
|
||||
FOUNDRY_BIN=${FOUNDRY_BIN:-"$HOME/.foundry/bin"}
|
||||
|
|
@ -31,6 +35,13 @@ FEE_DEST="0xf6a3eef9088A255c32b6aD2025f83E57291D9011"
|
|||
WETH="0x4200000000000000000000000000000000000006"
|
||||
SWAP_ROUTER="0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4"
|
||||
|
||||
DEFAULT_TXNBOT_PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
|
||||
DEFAULT_TXNBOT_ADDRESS="0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
|
||||
|
||||
TXNBOT_PRIVATE_KEY=${TXNBOT_PRIVATE_KEY:-$DEFAULT_TXNBOT_PRIVATE_KEY}
|
||||
TXNBOT_ADDRESS=${TXNBOT_ADDRESS:-$DEFAULT_TXNBOT_ADDRESS}
|
||||
TXNBOT_FUND_VALUE=${TXNBOT_FUND_VALUE:-1ether}
|
||||
|
||||
MAX_UINT="0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
|
||||
SKIP_CLEANUP=false
|
||||
|
|
@ -39,6 +50,7 @@ COMMAND="start"
|
|||
cleanup() {
|
||||
stop_process "Frontend" "$WEBAPP_PID_FILE"
|
||||
stop_process "Ponder" "$PONDER_PID_FILE"
|
||||
stop_process "TxnBot" "$TXNBOT_PID_FILE"
|
||||
stop_process "Anvil" "$ANVIL_PID_FILE"
|
||||
|
||||
if [[ "$SKIP_CLEANUP" == true ]]; then
|
||||
|
|
@ -123,6 +135,13 @@ ensure_dependencies() {
|
|||
npm install >>"$SETUP_LOG" 2>&1
|
||||
popd >/dev/null
|
||||
fi
|
||||
|
||||
if [[ ! -d "$ROOT_DIR/services/txnBot/node_modules" ]]; then
|
||||
log "Installing txnBot dependencies"
|
||||
pushd "$ROOT_DIR/services/txnBot" >/dev/null
|
||||
npm install >>"$SETUP_LOG" 2>&1
|
||||
popd >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
start_anvil() {
|
||||
|
|
@ -177,7 +196,7 @@ extract_addresses() {
|
|||
# Create .env.local for Ponder with deployed addresses
|
||||
cat > "$ROOT_DIR/ponder/.env.local" <<EOF
|
||||
# Auto-generated by local_env.sh
|
||||
PONDER_NETWORK=local
|
||||
PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK
|
||||
KRAIKEN_ADDRESS=$KRAIKEN
|
||||
STAKE_ADDRESS=$STAKE
|
||||
START_BLOCK=$deploy_block
|
||||
|
|
@ -231,6 +250,59 @@ prepare_application_state() {
|
|||
"($WETH,$KRAIKEN,10000,$DEPLOYER_ADDR,10000000000000000,0,0)" >>"$SETUP_LOG" 2>&1
|
||||
}
|
||||
|
||||
prime_chain_for_indexing() {
|
||||
log "Pre-mining blocks for indexer stability"
|
||||
for _ in {1..1200}; do
|
||||
"$CAST" rpc --rpc-url "$ANVIL_RPC" evm_mine > /dev/null 2>&1 || true
|
||||
done
|
||||
}
|
||||
|
||||
write_txnbot_env() {
|
||||
source_addresses || { log "Contract addresses not found"; exit 1; }
|
||||
|
||||
cat > "$TXNBOT_ENV_FILE" <<EOF
|
||||
ENVIRONMENT=BASE_SEPOLIA_LOCAL_FORK
|
||||
PROVIDER_URL=$ANVIL_RPC
|
||||
PRIVATE_KEY=$TXNBOT_PRIVATE_KEY
|
||||
LM_CONTRACT_ADDRESS=$LIQUIDITY_MANAGER
|
||||
STAKE_CONTRACT_ADDRESS=$STAKE
|
||||
GRAPHQL_ENDPOINT=$GRAPHQL_ENDPOINT
|
||||
WALLET_ADDRESS=$TXNBOT_ADDRESS
|
||||
# Expose a non-conflicting status port for local dev
|
||||
PORT=43069
|
||||
EOF
|
||||
}
|
||||
|
||||
fund_txnbot_wallet() {
|
||||
source_addresses || { log "Contract addresses not found"; exit 1; }
|
||||
|
||||
if [[ -z "$TXNBOT_ADDRESS" ]]; then
|
||||
log "TxnBot wallet address not provided; skipping funding"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Funding txnBot wallet $TXNBOT_ADDRESS with $TXNBOT_FUND_VALUE"
|
||||
"$CAST" send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
|
||||
"$TXNBOT_ADDRESS" --value "$TXNBOT_FUND_VALUE" >>"$SETUP_LOG" 2>&1 || \
|
||||
log "Funding txnBot wallet failed (see setup log)"
|
||||
}
|
||||
|
||||
start_txnbot() {
|
||||
if [[ -f "$TXNBOT_PID_FILE" ]]; then
|
||||
log "txnBot already running (pid $(cat \"$TXNBOT_PID_FILE\"))"
|
||||
return
|
||||
fi
|
||||
|
||||
write_txnbot_env
|
||||
fund_txnbot_wallet
|
||||
|
||||
log "Starting txnBot automation"
|
||||
pushd "$ROOT_DIR/services/txnBot" >/dev/null
|
||||
TXN_BOT_ENV_FILE="$TXNBOT_ENV_FILE" node service.js >"$TXNBOT_LOG" 2>&1 &
|
||||
popd >/dev/null
|
||||
echo $! >"$TXNBOT_PID_FILE"
|
||||
}
|
||||
|
||||
start_ponder() {
|
||||
if [[ -f "$PONDER_PID_FILE" ]]; then
|
||||
log "Ponder already running (pid $(cat "$PONDER_PID_FILE"))"
|
||||
|
|
@ -239,7 +311,7 @@ start_ponder() {
|
|||
|
||||
log "Starting Ponder indexer"
|
||||
pushd "$ROOT_DIR/ponder" >/dev/null
|
||||
PONDER_NETWORK=local npm run dev >"$PONDER_LOG" 2>&1 &
|
||||
PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK npm run dev >"$PONDER_LOG" 2>&1 &
|
||||
popd >/dev/null
|
||||
echo $! >"$PONDER_PID_FILE"
|
||||
|
||||
|
|
@ -270,7 +342,9 @@ start_environment() {
|
|||
extract_addresses
|
||||
bootstrap_liquidity_manager
|
||||
prepare_application_state
|
||||
prime_chain_for_indexing
|
||||
start_ponder # Re-enabled with PostgreSQL
|
||||
start_txnbot
|
||||
start_frontend
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +360,7 @@ start_and_wait() {
|
|||
log " Frontend: $FRONTEND_URL"
|
||||
log "Press Ctrl+C to shut everything down"
|
||||
|
||||
wait "$(cat "$ANVIL_PID_FILE")" "$(cat "$PONDER_PID_FILE")" "$(cat "$WEBAPP_PID_FILE")"
|
||||
wait "$(cat "$ANVIL_PID_FILE")" "$(cat "$PONDER_PID_FILE")" "$(cat "$WEBAPP_PID_FILE")" "$(cat "$TXNBOT_PID_FILE")"
|
||||
}
|
||||
|
||||
stop_environment() {
|
||||
|
|
@ -343,6 +417,7 @@ status_environment() {
|
|||
service_status "Anvil" "$ANVIL_PID_FILE" "$ANVIL_LOG"
|
||||
service_status "Ponder" "$PONDER_PID_FILE" "$PONDER_LOG"
|
||||
service_status "Frontend" "$WEBAPP_PID_FILE" "$WEBAPP_LOG"
|
||||
service_status "TxnBot" "$TXNBOT_PID_FILE" "$TXNBOT_LOG"
|
||||
|
||||
if [[ -f "$STATE_DIR/contracts.env" ]]; then
|
||||
printf '\nContracts:\n'
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
# .graphclientrc.yml
|
||||
sources:
|
||||
- name: harberg
|
||||
handler:
|
||||
graphql:
|
||||
endpoint: https://api.studio.thegraph.com/query/47986/harberg-base-sepolia/version/latest
|
||||
transforms:
|
||||
- autoPagination:
|
||||
# You might want to disable schema validation for faster startup
|
||||
validateSchema: true
|
||||
|
||||
documents:
|
||||
- ./.graphql
|
||||
|
|
@ -1,5 +1,38 @@
|
|||
## RUN
|
||||
yarn
|
||||
yarn graphclient --fileType json build
|
||||
node service.js
|
||||
# txnBot
|
||||
|
||||
Automation worker that monitors staking positions and calls `recenter()` / `payTax()` when profitable.
|
||||
|
||||
## Environments
|
||||
|
||||
The bot supports three environments shared across the stack:
|
||||
|
||||
- `BASE_SEPOLIA_LOCAL_FORK` – Anvil fork started by `scripts/local_env.sh`
|
||||
- `BASE_SEPOLIA` – Public Base Sepolia testnet
|
||||
- `BASE` – Base mainnet
|
||||
|
||||
Set `ENVIRONMENT` alongside the other variables listed below to target the desired chain.
|
||||
|
||||
## Configuration
|
||||
|
||||
Create an environment file (e.g. `.env`) with:
|
||||
|
||||
```
|
||||
ENVIRONMENT=BASE_SEPOLIA
|
||||
PROVIDER_URL=<rpc-url>
|
||||
PRIVATE_KEY=<hex-private-key>
|
||||
LM_CONTRACT_ADDRESS=<liquidity-manager-address>
|
||||
STAKE_CONTRACT_ADDRESS=<stake-contract-address>
|
||||
GRAPHQL_ENDPOINT=<ponder-graphql-url>
|
||||
# Optional: PORT=43069
|
||||
```
|
||||
|
||||
`scripts/local_env.sh start` generates these values automatically for the local fork, writes them to a temporary file, and keeps the process running in the background.
|
||||
|
||||
## Local Run
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
The service exposes a lightweight status endpoint at `GET /status` reporting uptime, balances, and the most recent automation activity.
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
"version": "0.0.1",
|
||||
"main": "index.js",
|
||||
"license": "GPL3",
|
||||
"scripts": {
|
||||
"start": "node service.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5",
|
||||
"ethers": "^6.13.2",
|
||||
"express": "^5.0.0",
|
||||
"kraiken-lib": "file:../../kraiken-lib"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphprotocol/client-cli": "^3.0.7"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,48 @@
|
|||
require('dotenv').config();
|
||||
const path = require('path');
|
||||
const dotenvPath = process.env.TXN_BOT_ENV_FILE
|
||||
? path.resolve(process.env.TXN_BOT_ENV_FILE)
|
||||
: path.resolve(__dirname, '.env');
|
||||
|
||||
require('dotenv').config({ path: dotenvPath });
|
||||
const { ethers } = require('ethers');
|
||||
const express = require('express');
|
||||
const { execute } = require('./.graphclient');
|
||||
const { decodePositionId, isPositionDelinquent } = require('kraiken-lib');
|
||||
|
||||
const myQuery = `
|
||||
query GetPositions {
|
||||
positions(first: 5000, where: {status: "Active"}) {
|
||||
const ACTIVE_POSITIONS_QUERY = `
|
||||
query ActivePositions {
|
||||
positionss(where: { status: "Active" }, limit: 1000) {
|
||||
items {
|
||||
id
|
||||
share
|
||||
lastTaxTime
|
||||
taxRate
|
||||
status
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
`;
|
||||
|
||||
// Load environment variables
|
||||
const PROVIDER_URL = process.env.PROVIDER_URL;
|
||||
const PRIVATE_KEY = process.env.PRIVATE_KEY;
|
||||
const LM_CONTRACT_ADDRESS = process.env.LM_CONTRACT_ADDRESS;
|
||||
const STAKE_CONTRACT_ADDRESS = process.env.STAKE_CONTRACT_ADDRESS;
|
||||
const GRAPHQL_ENDPOINT = process.env.GRAPHQL_ENDPOINT;
|
||||
const ENVIRONMENT = process.env.ENVIRONMENT || 'UNSPECIFIED';
|
||||
|
||||
const requiredEnv = {
|
||||
PROVIDER_URL,
|
||||
PRIVATE_KEY,
|
||||
LM_CONTRACT_ADDRESS,
|
||||
STAKE_CONTRACT_ADDRESS,
|
||||
GRAPHQL_ENDPOINT,
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(requiredEnv)) {
|
||||
if (!value) {
|
||||
throw new Error(`Missing required environment variable: ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
const LM_ABI = [
|
||||
{"type":"function","name":"recenter","inputs":[],"outputs":[],"stateMutability":"nonpayable"}
|
||||
|
|
@ -40,6 +62,26 @@ let startTime = new Date();
|
|||
let lastRecenterTime = null;
|
||||
let lastLiquidationTime = null;
|
||||
|
||||
async function fetchActivePositions() {
|
||||
const response = await fetch(GRAPHQL_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ query: ACTIVE_POSITIONS_QUERY }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GraphQL request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
const payload = await response.json();
|
||||
if (payload.errors && payload.errors.length > 0) {
|
||||
const messages = payload.errors.map((error) => error.message).join('; ');
|
||||
throw new Error(`GraphQL responded with errors: ${messages}`);
|
||||
}
|
||||
|
||||
return payload.data?.positionss?.items ?? [];
|
||||
}
|
||||
|
||||
async function checkFunds() {
|
||||
const balance = await provider.getBalance(wallet.address);
|
||||
return ethers.formatEther(balance);
|
||||
|
|
@ -102,20 +144,20 @@ async function liquidityLoop() {
|
|||
async function liquidationLoop() {
|
||||
let counter = 0;
|
||||
try {
|
||||
const result = await execute(myQuery, {});
|
||||
for (const position of result.data.positions) {
|
||||
const positions = await fetchActivePositions();
|
||||
for (const position of positions) {
|
||||
counter = counter + await checkPosition(position);
|
||||
}
|
||||
if (counter == 0) {
|
||||
console.log(`No tax can be claimed at the moment. - ${(new Date()).toISOString()}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in liquididation loop:', error);
|
||||
console.error('Error in liquidation loop:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Service started...');
|
||||
console.log(`txnBot service started for environment ${ENVIRONMENT}`);
|
||||
|
||||
await liquidityLoop();
|
||||
await liquidationLoop();
|
||||
|
|
|
|||
4
subgraph/base_sepolia/.gitignore
vendored
4
subgraph/base_sepolia/.gitignore
vendored
|
|
@ -1,4 +0,0 @@
|
|||
build/
|
||||
node_modules/
|
||||
generated/
|
||||
yarn.lock
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
# Subgraph - CLAUDE.md
|
||||
|
||||
The Graph Protocol subgraph for indexing KRAIKEN protocol events and state.
|
||||
|
||||
## Overview
|
||||
|
||||
This subgraph indexes:
|
||||
- Token transfers and supply changes
|
||||
- Staking positions and Harberger tax events
|
||||
- Liquidity manager recentering events
|
||||
- User statistics and protocol metrics
|
||||
|
||||
## Schema Design
|
||||
|
||||
### Core Entities
|
||||
|
||||
**Stats** - Global protocol statistics
|
||||
- Token supplies (KRAIKEN and STAKE)
|
||||
- Minting/burning metrics with hourly projections
|
||||
- Tax collection statistics
|
||||
- Staking percentages and average rates
|
||||
|
||||
**Position** - Individual staking positions
|
||||
- Holder address and staked amount
|
||||
- Self-assessed tax rate
|
||||
- Last update timestamp
|
||||
|
||||
**Snatch** - Harberger tax position takeover events
|
||||
- Old and new holder details
|
||||
- Tax rate changes
|
||||
- Transaction metadata
|
||||
|
||||
**User** - Aggregated user statistics
|
||||
- Total taxes paid/received
|
||||
- Staking history
|
||||
- Minting/burning activity
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Generate AssemblyScript types from schema
|
||||
npm run codegen
|
||||
|
||||
# Build the subgraph
|
||||
npm run build
|
||||
|
||||
# Deploy to The Graph Studio
|
||||
npm run deploy
|
||||
|
||||
# Local development (requires graph-node)
|
||||
npm run create-local
|
||||
npm run deploy-local
|
||||
```
|
||||
|
||||
## Event Handlers
|
||||
|
||||
### Kraiken Contract Events
|
||||
- `Transfer` - Track token movements and supply changes
|
||||
- `TaxPaid` - Record tax collection events
|
||||
- `Mint/Burn` - Update supply statistics
|
||||
|
||||
### Stake Contract Events
|
||||
- `Staked` - Create/update position entities
|
||||
- `Snatched` - Record position takeovers
|
||||
- `TaxRateChanged` - Update position tax rates
|
||||
|
||||
## Query Examples
|
||||
|
||||
```graphql
|
||||
# Get current protocol statistics
|
||||
query GetStats {
|
||||
stats(id: "0x00") {
|
||||
kraikenTotalSupply
|
||||
percentageStaked
|
||||
averageTaxRate
|
||||
mintedLastDay
|
||||
}
|
||||
}
|
||||
|
||||
# Get top staking positions
|
||||
query TopStakers {
|
||||
positions(first: 10, orderBy: amount, orderDirection: desc) {
|
||||
holder
|
||||
amount
|
||||
rate
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
- **Network**: Base Sepolia (testnet)
|
||||
- **Start Block**: Configured in subgraph.yaml
|
||||
- **API Endpoint**: Available after deployment to Studio
|
||||
|
||||
## Code Quality
|
||||
|
||||
- All handlers must be idempotent
|
||||
- Use BigInt for all numeric values
|
||||
- Handle null cases explicitly
|
||||
- Keep entity updates minimal for performance
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
|
||||
|
||||
## deployment
|
||||
|
||||
```
|
||||
yarn codegen
|
||||
yarn build
|
||||
yarn deploy
|
||||
```
|
||||
|
||||
## deployment-script
|
||||
networks are defined in deploy.js
|
||||
This solution overwrites the subgraph.yaml with the template and enables the possible to upgrade the same contract on different chains
|
||||
```
|
||||
node deploy.js base
|
||||
```
|
||||
|
||||
## queries
|
||||
|
||||
for stats
|
||||
```
|
||||
{
|
||||
stats(id:"0x01") {
|
||||
harbTotalSupply
|
||||
stakeTotalSupply
|
||||
outstandingStake
|
||||
totalMinted
|
||||
mintedLastWeek
|
||||
mintedLastDay
|
||||
mintNextHourProjected
|
||||
totalBurned
|
||||
burnedLastWeek
|
||||
burnedLastDay
|
||||
burnNextHourProjected
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,102 +0,0 @@
|
|||
const fs = require('fs');
|
||||
const { exec } = require("child_process");
|
||||
const readline = require("readline");
|
||||
|
||||
// Netzwerkspezifische Informationen
|
||||
const networks = {
|
||||
'base-sepolia': {
|
||||
NETWORK: 'base-sepolia',
|
||||
CONTRACT_ADDRESS_HARB: '0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8',
|
||||
CONTRACT_ADDRESS_STAKE: '0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2',
|
||||
START_BLOCK: 20940337,
|
||||
SUBGRAPH_NAME: "harb-base-sepolia"
|
||||
},
|
||||
'sepolia': {
|
||||
NETWORK: 'sepolia',
|
||||
CONTRACT_ADDRESS_HARB: '0x087F256D11fe533b0c7d372e44Ee0F9e47C89dF9',
|
||||
CONTRACT_ADDRESS_STAKE: '0xCd21a41a137BCAf8743E47D048F57D92398f7Da9',
|
||||
START_BLOCK: 6746241,
|
||||
SUBGRAPH_NAME: "harb"
|
||||
},
|
||||
'base': {
|
||||
NETWORK: 'base',
|
||||
CONTRACT_ADDRESS_HARB: '0x45caa5929f6ee038039984205bdecf968b954820',
|
||||
CONTRACT_ADDRESS_STAKE: '0xed70707fab05d973ad41eae8d17e2bcd36192cfc',
|
||||
START_BLOCK: 26038614,
|
||||
// START_BLOCK: 25895309,
|
||||
SUBGRAPH_NAME: "kraiken-base"
|
||||
},
|
||||
'base2': {
|
||||
NETWORK: 'base',
|
||||
CONTRACT_ADDRESS_HARB: '0x45caa5929f6ee038039984205bdecf968b954820',
|
||||
CONTRACT_ADDRESS_STAKE: '0xed70707fab05d973ad41eae8d17e2bcd36192cfc',
|
||||
START_BLOCK: 26038614,
|
||||
// START_BLOCK: 25895309,
|
||||
SUBGRAPH_NAME: "kraiken-base-test"
|
||||
},
|
||||
};
|
||||
|
||||
// Netzwerk über Kommandozeilenargument auswählen
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length === 0) {
|
||||
console.error('Kein Netzwerk angegeben, z. B.: node deploy.js sepolia');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const selectedNetwork = networks[args[0]];
|
||||
if (!selectedNetwork) {
|
||||
console.error(`Kein gülitges Netzwerk angegeben: ${args[0]}. Verfügbare Netzwerke: ${Object.keys(networks).join(', ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Template-Datei laden
|
||||
const subgraphTemplate = fs.readFileSync('subgraph_template.yaml', 'utf-8');
|
||||
|
||||
// Platzhalter ersetzen
|
||||
let subgraphConfig = subgraphTemplate.replace(/{{NETWORK}}/g, selectedNetwork.NETWORK);
|
||||
subgraphConfig = subgraphConfig.replace(/{{CONTRACT_ADDRESS_STAKE}}/g, selectedNetwork.CONTRACT_ADDRESS_STAKE);
|
||||
subgraphConfig = subgraphConfig.replace(/{{CONTRACT_ADDRESS_HARB}}/g, selectedNetwork.CONTRACT_ADDRESS_HARB);
|
||||
subgraphConfig = subgraphConfig.replace(/{{START_BLOCK}}/g, selectedNetwork.START_BLOCK);
|
||||
|
||||
// Neue Datei generieren
|
||||
fs.writeFileSync('subgraph.yaml', subgraphConfig);
|
||||
|
||||
console.log(`subgraph.yaml für das Netzwerk ${selectedNetwork.NETWORK} wurde erfolgreich generiert.`);
|
||||
|
||||
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
rl.question('Which version label to use1? (e.g. "v0.0.1"): ', (versionLabel) => {
|
||||
|
||||
if (!versionLabel) {
|
||||
console.error("Kein Versionslabel angegeben. Abbruch.");
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
const serverAdress = "https://api.studio.thegraph.com/deploy/";
|
||||
console.log(`Starte Deployment mit Versionslabel "${versionLabel}"...`);
|
||||
|
||||
// Spawn-Prozess starten
|
||||
// const deployProcess = spawn("graph", ["deploy", "--node", serverAdress, selectedNetwork.SUBGRAPH_NAME]);
|
||||
|
||||
const command = `graph deploy --node ${serverAdress} ${selectedNetwork.SUBGRAPH_NAME} --version-label ${versionLabel}`;
|
||||
console.log(`Auszuführender Befehl: ${command}`);
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`Fehler: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
if (stderr) {
|
||||
console.error(`stderr: ${stderr}`);
|
||||
}
|
||||
|
||||
console.log(`stdout: ${stdout}`);
|
||||
});
|
||||
});
|
||||
5904
subgraph/base_sepolia/package-lock.json
generated
5904
subgraph/base_sepolia/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"name": "kraiken",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"codegen": "graph codegen",
|
||||
"build": "graph build",
|
||||
"deploy": "graph deploy --node https://api.studio.thegraph.com/deploy/ kraiken-base-sepolia",
|
||||
"create-local": "graph create --node http://localhost:8020/ kraiken",
|
||||
"remove-local": "graph remove --node http://localhost:8020/ kraiken",
|
||||
"deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 kraiken",
|
||||
"test": "graph test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@graphprotocol/graph-cli": "0.69.0",
|
||||
"@graphprotocol/graph-ts": "0.34.0",
|
||||
"assemblyscript": "0.27.25"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.30",
|
||||
"matchstick-as": "0.5.0",
|
||||
"typescript": "^5.4.3"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
# scalar Bytes
|
||||
# scalar BigInt
|
||||
# scalar BigDecimal
|
||||
|
||||
type Stats @entity {
|
||||
id: Bytes!
|
||||
kraikenTotalSupply: BigInt! # uint256
|
||||
stakeTotalSupply: BigInt! # uint256
|
||||
outstandingStake: BigInt! # uint256
|
||||
ringBuffer: [BigInt!]! # Ring buffer to store daily totals
|
||||
ringBufferPointer: Int! # Pointer to the current day in the ring buffer
|
||||
lastUpdatedHour: Int! # The last updated day boundary (timestamp in days)
|
||||
totalMinted: BigInt!
|
||||
mintedLastWeek: BigInt!
|
||||
mintedLastDay: BigInt!
|
||||
mintNextHourProjected: BigInt!
|
||||
|
||||
totalBurned: BigInt!
|
||||
burnedLastWeek: BigInt!
|
||||
burnedLastDay: BigInt!
|
||||
burnNextHourProjected: BigInt!
|
||||
|
||||
}
|
||||
|
||||
enum PositionStatus {
|
||||
Active
|
||||
Closed
|
||||
}
|
||||
|
||||
type Position @entity {
|
||||
id: Bytes!
|
||||
owner: Bytes! # address
|
||||
share: BigDecimal!
|
||||
# harb at start
|
||||
# current harb value
|
||||
creationTime: Int!
|
||||
lastTaxTime: Int
|
||||
taxRate: BigDecimal!
|
||||
taxPaid: BigInt!
|
||||
kraikenDeposit: BigInt!
|
||||
snatched: Int!
|
||||
totalSupplyInit: BigInt!
|
||||
totalSupplyEnd: BigInt
|
||||
status: PositionStatus!
|
||||
payout: BigInt!
|
||||
}
|
||||
|
||||
# type Query {
|
||||
# stats: [Stats!]
|
||||
# positions: [Position!]
|
||||
#}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
import { Address, dataSource } from '@graphprotocol/graph-ts'
|
||||
|
||||
export enum ChainId {
|
||||
BASE = 8453,
|
||||
BASE_SEPOLIA = 84532,
|
||||
}
|
||||
|
||||
const BASE_NETWORK_NAME = 'base'
|
||||
const BASE_SEPOLIA_NETWORK_NAME = 'base-sepolia'
|
||||
|
||||
|
||||
export class SubgraphConfig {
|
||||
|
||||
kraikenAddress: Address
|
||||
|
||||
stakeAddress: Address
|
||||
|
||||
}
|
||||
|
||||
export function getSubgraphConfig(): SubgraphConfig {
|
||||
// Update this value to the corresponding chain you want to deploy
|
||||
const selectedNetwork = dataSource.network()
|
||||
|
||||
// subgraph does not support case switch with strings, hence this if else block
|
||||
if (selectedNetwork == BASE_NETWORK_NAME) {
|
||||
return {
|
||||
kraikenAddress: Address.fromString('0x45caa5929f6ee038039984205bdecf968b954820'),
|
||||
stakeAddress: Address.fromString('0xed70707fab05d973ad41eae8d17e2bcd36192cfc'),
|
||||
}
|
||||
} else if (selectedNetwork == BASE_SEPOLIA_NETWORK_NAME) {
|
||||
return {
|
||||
kraikenAddress: Address.fromString('0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8'),
|
||||
stakeAddress: Address.fromString('0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2'),
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unsupported Network')
|
||||
}
|
||||
}
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
import {
|
||||
Approval as ApprovalEvent,
|
||||
EIP712DomainChanged as EIP712DomainChangedEvent,
|
||||
Transfer as TransferEvent,
|
||||
Kraiken
|
||||
} from "../generated/Kraiken/Kraiken";
|
||||
import { BigInt, Bytes, ethereum, Address, log } from "@graphprotocol/graph-ts";
|
||||
import { Stats } from "../generated/schema";
|
||||
import { getSubgraphConfig } from './chains';
|
||||
|
||||
let stakeTotalSupply: BigInt = BigInt.fromString("10000000000000000000000000");
|
||||
|
||||
// Helper function to get or create Stats entity
|
||||
function getOrCreateStats(): Stats {
|
||||
let stats = Stats.load(Bytes.fromHexString("0x01") as Bytes);
|
||||
if (stats == null) {
|
||||
log.info("Stats not found, creating a new one", []);
|
||||
stats = new Stats(Bytes.fromHexString("0x01") as Bytes);
|
||||
let kraiken = Kraiken.bind(getSubgraphConfig().kraikenAddress);
|
||||
|
||||
|
||||
|
||||
stats.kraikenTotalSupply = kraiken.totalSupply();
|
||||
stats.stakeTotalSupply = stakeTotalSupply;
|
||||
stats.outstandingStake = BigInt.zero();
|
||||
|
||||
// Minted
|
||||
stats.totalMinted = BigInt.zero();
|
||||
stats.mintedLastWeek = BigInt.zero();
|
||||
stats.mintedLastDay = BigInt.zero();
|
||||
stats.mintNextHourProjected = BigInt.zero();
|
||||
|
||||
// Burned
|
||||
stats.totalBurned = BigInt.zero();
|
||||
stats.burnedLastWeek = BigInt.zero();
|
||||
stats.burnedLastDay = BigInt.zero();
|
||||
stats.burnNextHourProjected = BigInt.zero();
|
||||
|
||||
|
||||
|
||||
stats.ringBuffer = new Array<BigInt>(168 * 4).fill(BigInt.zero());
|
||||
stats.ringBufferPointer = 0;
|
||||
stats.lastUpdatedHour = 0;
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
export function handleTransfer(event: TransferEvent): void {
|
||||
let ZERO_ADDRESS = Address.fromString("0x0000000000000000000000000000000000000000");
|
||||
let TAX_POOL_ADDR = Address.fromString("0x0000000000000000000000000000000000000002");
|
||||
|
||||
let stats = getOrCreateStats();
|
||||
|
||||
// Get a copy of the ring buffer
|
||||
let ringBuffer = stats.ringBuffer;
|
||||
|
||||
if (event.params.from == TAX_POOL_ADDR) {
|
||||
|
||||
// Add the UBI amount to the current hour's total in the ring buffer
|
||||
let ubiBufferIndex = (stats.ringBufferPointer * 4) + 0; // UBI is at index 0
|
||||
ringBuffer[ubiBufferIndex] = ringBuffer[ubiBufferIndex].plus(event.params.value);
|
||||
|
||||
} else if (event.params.from == ZERO_ADDRESS) {
|
||||
// Mint event
|
||||
stats.totalMinted = stats.totalMinted.plus(event.params.value);
|
||||
|
||||
// Add the minted amount to the current hour's total in the ring buffer
|
||||
let mintBufferIndex = (stats.ringBufferPointer * 4) + 1; // Minted tokens are at index 1
|
||||
ringBuffer[mintBufferIndex] = ringBuffer[mintBufferIndex].plus(event.params.value);
|
||||
|
||||
stats.kraikenTotalSupply = stats.kraikenTotalSupply.plus(event.params.value);
|
||||
|
||||
} else if (event.params.to == ZERO_ADDRESS) {
|
||||
// Burn event
|
||||
stats.totalBurned = stats.totalBurned.plus(event.params.value);
|
||||
|
||||
// Add the burned amount to the current hour's total in the ring buffer
|
||||
let burnBufferIndex = (stats.ringBufferPointer * 4) + 2; // Burned tokens are at index 2
|
||||
ringBuffer[burnBufferIndex] = ringBuffer[burnBufferIndex].plus(event.params.value);
|
||||
|
||||
stats.kraikenTotalSupply = stats.kraikenTotalSupply.minus(event.params.value);
|
||||
|
||||
} else if (event.params.to == TAX_POOL_ADDR) {
|
||||
|
||||
// Add the burned amount to the current hour's total in the ring buffer
|
||||
let taxBufferIndex = (stats.ringBufferPointer * 4) + 3; // tax paid is at index 3
|
||||
ringBuffer[taxBufferIndex] = ringBuffer[taxBufferIndex].plus(event.params.value);
|
||||
|
||||
}
|
||||
|
||||
// Update the ring buffer in the stats entity
|
||||
stats.ringBuffer = ringBuffer;
|
||||
|
||||
// Save the updated Stats entity
|
||||
stats.save();
|
||||
}
|
||||
|
||||
// Block handler to aggregate stats
|
||||
export function handleBlock(block: ethereum.Block): void {
|
||||
// Update Stats entity
|
||||
let stats = getOrCreateStats();
|
||||
|
||||
// Get a copy of the ring buffer
|
||||
let ringBuffer = stats.ringBuffer;
|
||||
|
||||
// Calculate the current hour
|
||||
let currentHour = block.timestamp.toI32() / 3600;
|
||||
|
||||
// Check if we've moved to a new hour
|
||||
if (currentHour > stats.lastUpdatedHour) {
|
||||
// Move the ring buffer pointer forward
|
||||
stats.ringBufferPointer = (stats.ringBufferPointer + 1) % 168;
|
||||
|
||||
// Reset the new current hour in the ring buffer
|
||||
let baseIndex = stats.ringBufferPointer * 4;
|
||||
ringBuffer[baseIndex] = BigInt.zero(); // UBI claimed
|
||||
ringBuffer[baseIndex + 1] = BigInt.zero(); // Minted tokens
|
||||
ringBuffer[baseIndex + 2] = BigInt.zero(); // Burned tokens
|
||||
|
||||
// Update the last updated hour
|
||||
stats.lastUpdatedHour = currentHour;
|
||||
|
||||
// Recalculate the sum of the last 7 days and 24 hours
|
||||
|
||||
let mintedLastWeek = BigInt.zero();
|
||||
let burnedLastWeek = BigInt.zero();
|
||||
|
||||
let mintedLastDay = BigInt.zero();
|
||||
let burnedLastDay = BigInt.zero();
|
||||
|
||||
|
||||
for (let i = 0; i < 168; i++) {
|
||||
let index = ((stats.ringBufferPointer - i + 168) % 168) * 4;
|
||||
mintedLastWeek = mintedLastWeek.plus(ringBuffer[index + 1]);
|
||||
burnedLastWeek = burnedLastWeek.plus(ringBuffer[index + 2]);
|
||||
|
||||
if (i < 24) {
|
||||
mintedLastDay = mintedLastDay.plus(ringBuffer[index + 1]);
|
||||
burnedLastDay = burnedLastDay.plus(ringBuffer[index + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stats.mintedLastWeek = mintedLastWeek;
|
||||
stats.burnedLastWeek = burnedLastWeek;
|
||||
|
||||
|
||||
stats.mintedLastDay = mintedLastDay;
|
||||
stats.burnedLastDay = burnedLastDay;
|
||||
|
||||
// Update the ring buffer in the stats entity
|
||||
stats.ringBuffer = ringBuffer;
|
||||
|
||||
} else {
|
||||
// update projected stats with every block
|
||||
|
||||
// Calculate the elapsed time in the current hour
|
||||
let currentTimestamp = block.timestamp.toI32();
|
||||
let startOfHour = (currentTimestamp / 3600) * 3600;
|
||||
let elapsedSeconds = currentTimestamp - startOfHour;
|
||||
|
||||
for (var i = 0; i <= 3; i++) {
|
||||
let bufferIndex = (stats.ringBufferPointer * 4) + i;
|
||||
// Project the current hour's total based on the average rate of burned tokens in the current hour
|
||||
let projectedTotal = ringBuffer[bufferIndex].times(BigInt.fromI32(3600)).div(BigInt.fromI32(elapsedSeconds));
|
||||
|
||||
// Calculate the medium between the previous hour and the projection
|
||||
let previousHourTotal = ringBuffer[((stats.ringBufferPointer - 1 + 168) % 168) * 4 + i];
|
||||
|
||||
log.info("projecting stats : {} projected total: {}. previous hour Total: {}. medium: {}", [
|
||||
i.toString(),
|
||||
projectedTotal.toString(),
|
||||
previousHourTotal.toString(),
|
||||
previousHourTotal.plus(projectedTotal).div(BigInt.fromI32(2)).toString(),
|
||||
]);
|
||||
|
||||
let medium = previousHourTotal.plus(projectedTotal).div(BigInt.fromI32(2));
|
||||
|
||||
if (i == 1) {
|
||||
stats.mintNextHourProjected = (medium > BigInt.zero()) ? medium : stats.mintedLastWeek.div(BigInt.fromI32(7));
|
||||
} else if (i == 2) {
|
||||
stats.burnNextHourProjected = (medium > BigInt.zero()) ? medium : stats.burnedLastWeek.div(BigInt.fromI32(7));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Save the updated Stats entity
|
||||
stats.save();
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
query GetPositions {
|
||||
positions {
|
||||
id
|
||||
owner
|
||||
share
|
||||
creationTime
|
||||
lastTaxTime
|
||||
taxRate
|
||||
status
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
import {
|
||||
PositionCreated as PositionCreatedEvent,
|
||||
PositionRemoved as PositionRemovedEvent,
|
||||
PositionTaxPaid as PositionTaxPaidEvent,
|
||||
PositionShrunk as PositionShrunkEvent,
|
||||
PositionRateHiked as PositionRateHikedEvent,
|
||||
} from "../generated/Stake/Stake";
|
||||
import { Kraiken } from "../generated/Kraiken/Kraiken";
|
||||
import { Stake } from "../generated/Stake/Stake";
|
||||
import { getSubgraphConfig, SubgraphConfig } from './chains';
|
||||
import { BigDecimal, BigInt, Bytes } from "@graphprotocol/graph-ts";
|
||||
import { Position, Stats } from "../generated/schema";
|
||||
|
||||
let decimals: BigDecimal = BigDecimal.fromString("1000000000000000000");
|
||||
let totalSupply: BigDecimal = BigDecimal.fromString("10000000000000000000000000");
|
||||
let taxRates: Array<string> = ["0.01", "0.03", "0.05", "0.08", "0.12", "0.18", "0.24", "0.30", "0.40", "0.50", "0.60", "0.80", "1", "1.3", "1.8", "2.5", "3.2", "4.2", "5.4", "7", "9.2", "12", "16", "20", "26", "34", "44", "57", "75", "97"];
|
||||
|
||||
export function handlePositionCreated(event: PositionCreatedEvent): void {
|
||||
let position = new Position(Bytes.fromI32(event.params.positionId.toI32()));
|
||||
position.owner = event.params.owner;
|
||||
position.share = event.params.share.toBigDecimal().div(totalSupply);
|
||||
position.creationTime = event.block.timestamp.toI32();
|
||||
position.lastTaxTime = event.block.timestamp.toI32();
|
||||
position.taxRate = BigDecimal.fromString(taxRates[event.params.taxRate.toI32()]);
|
||||
position.kraikenDeposit = event.params.kraikenDeposit;
|
||||
position.status = "Active";
|
||||
position.snatched = 0;
|
||||
position.payout = BigInt.fromString("0");
|
||||
position.taxPaid = BigInt.fromString("0");
|
||||
let kraiken = Kraiken.bind(getSubgraphConfig().kraikenAddress);
|
||||
position.totalSupplyInit = kraiken.totalSupply();
|
||||
position.save();
|
||||
let stake = Stake.bind(event.address);
|
||||
let stats = Stats.load(Bytes.fromHexString("0x01") as Bytes);
|
||||
if (stats != null) {
|
||||
stats.outstandingStake = stake.outstandingStake();
|
||||
stats.save();
|
||||
}
|
||||
}
|
||||
|
||||
export function handlePositionRemoved(event: PositionRemovedEvent): void {
|
||||
let position = Position.load(Bytes.fromI32(event.params.positionId.toI32()));
|
||||
if (position != null) {
|
||||
position.status = "Closed";
|
||||
let kraiken = Kraiken.bind(getSubgraphConfig().kraikenAddress);
|
||||
position.totalSupplyEnd = kraiken.totalSupply();
|
||||
// position.payout = position.payout.plus(event.params.kraikenPayout);
|
||||
position.save();
|
||||
}
|
||||
let stake = Stake.bind(event.address);
|
||||
let stats = Stats.load(Bytes.fromHexString("0x01") as Bytes);
|
||||
if (stats != null) {
|
||||
stats.outstandingStake = stake.outstandingStake();
|
||||
stats.save();
|
||||
}
|
||||
}
|
||||
|
||||
export function handlePositionShrunk(event: PositionShrunkEvent): void {
|
||||
let position = Position.load(Bytes.fromI32(event.params.positionId.toI32()));
|
||||
if (position != null) {
|
||||
position.share = event.params.newShares.toBigDecimal().div(totalSupply);
|
||||
position.kraikenDeposit = position.kraikenDeposit.minus(event.params.kraikenPayout);
|
||||
position.snatched = position.snatched++;
|
||||
|
||||
// position.payout = position.payout.plus(event.params.kraikenPayout);
|
||||
position.save();
|
||||
}
|
||||
}
|
||||
|
||||
export function handleTaxPaid(event: PositionTaxPaidEvent): void {
|
||||
let position = Position.load(Bytes.fromI32(event.params.positionId.toI32()));
|
||||
if (position != null) {
|
||||
position.taxPaid = position.taxPaid.plus(event.params.taxPaid);
|
||||
position.taxRate = BigDecimal.fromString(taxRates[event.params.taxRate.toI32()]);
|
||||
position.lastTaxTime = event.block.timestamp.toI32();
|
||||
position.save();
|
||||
}
|
||||
}
|
||||
|
||||
export function handlePositionRateHiked(event: PositionRateHikedEvent): void {
|
||||
let position = Position.load(Bytes.fromI32(event.params.positionId.toI32()));
|
||||
if (position != null) {
|
||||
position.taxRate = BigDecimal.fromString(taxRates[event.params.newTaxRate.toI32()]);
|
||||
position.save();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
specVersion: 0.0.4
|
||||
repository: http://gitea.loseyourip.com:4000/dark-meme-society/harb.git
|
||||
description: Kraiken Token
|
||||
schema:
|
||||
file: ./schema.graphql
|
||||
dataSources:
|
||||
- kind: ethereum
|
||||
name: Kraiken
|
||||
network: base
|
||||
source:
|
||||
address: "0x45caa5929f6ee038039984205bdecf968b954820"
|
||||
abi: Kraiken
|
||||
startBlock: 26038614
|
||||
mapping:
|
||||
kind: ethereum/events
|
||||
apiVersion: 0.0.7
|
||||
language: wasm/assemblyscript
|
||||
entities:
|
||||
- Stats
|
||||
- UbiClaim
|
||||
abis:
|
||||
- name: Kraiken
|
||||
file: ./abis/Kraiken.json
|
||||
eventHandlers:
|
||||
- event: Transfer(indexed address,indexed address,uint256)
|
||||
handler: handleTransfer
|
||||
blockHandlers:
|
||||
- handler: handleBlock
|
||||
file: ./src/kraiken.ts
|
||||
- kind: ethereum
|
||||
name: Stake
|
||||
network: base
|
||||
source:
|
||||
address: "0xed70707fab05d973ad41eae8d17e2bcd36192cfc"
|
||||
abi: Stake
|
||||
startBlock: 26038614
|
||||
mapping:
|
||||
kind: ethereum/events
|
||||
apiVersion: 0.0.7
|
||||
language: wasm/assemblyscript
|
||||
entities:
|
||||
- Position
|
||||
abis:
|
||||
- name: Stake
|
||||
file: ./abis/Stake.json
|
||||
- name: Kraiken
|
||||
file: ./abis/Kraiken.json
|
||||
eventHandlers:
|
||||
- event: PositionCreated(indexed uint256,indexed address,uint256,uint256,uint32)
|
||||
handler: handlePositionCreated
|
||||
- event: PositionRemoved(indexed uint256,indexed address,uint256)
|
||||
handler: handlePositionRemoved
|
||||
- event: PositionShrunk(indexed uint256,indexed address,uint256,uint256)
|
||||
handler: handlePositionShrunk
|
||||
- event: PositionTaxPaid(indexed uint256,indexed address,uint256,uint256,uint256)
|
||||
handler: handleTaxPaid
|
||||
- event: PositionRateHiked(indexed uint256,indexed address,uint256)
|
||||
handler: handlePositionRateHiked
|
||||
file: ./src/stake.ts
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
specVersion: 0.0.4
|
||||
repository: http://gitea.loseyourip.com:4000/dark-meme-society/harb.git
|
||||
description: Harberger Tax Token
|
||||
schema:
|
||||
file: ./schema.graphql
|
||||
dataSources:
|
||||
- kind: ethereum
|
||||
name: Harb
|
||||
network: {{NETWORK}}
|
||||
source:
|
||||
address: "{{CONTRACT_ADDRESS_HARB}}"
|
||||
abi: Harb
|
||||
startBlock: {{START_BLOCK}}
|
||||
mapping:
|
||||
kind: ethereum/events
|
||||
apiVersion: 0.0.7
|
||||
language: wasm/assemblyscript
|
||||
entities:
|
||||
- Stats
|
||||
- UbiClaim
|
||||
abis:
|
||||
- name: Harb
|
||||
file: ./abis/Harb.json
|
||||
eventHandlers:
|
||||
- event: Transfer(indexed address,indexed address,uint256)
|
||||
handler: handleTransfer
|
||||
blockHandlers:
|
||||
- handler: handleBlock
|
||||
file: ./src/harb.ts
|
||||
- kind: ethereum
|
||||
name: Stake
|
||||
network: {{NETWORK}}
|
||||
source:
|
||||
address: "{{CONTRACT_ADDRESS_STAKE}}"
|
||||
abi: Stake
|
||||
startBlock: {{START_BLOCK}}
|
||||
mapping:
|
||||
kind: ethereum/events
|
||||
apiVersion: 0.0.7
|
||||
language: wasm/assemblyscript
|
||||
entities:
|
||||
- Position
|
||||
abis:
|
||||
- name: Stake
|
||||
file: ./abis/Stake.json
|
||||
- name: Harb
|
||||
file: ./abis/Harb.json
|
||||
eventHandlers:
|
||||
- event: PositionCreated(indexed uint256,indexed address,uint256,uint256,uint32)
|
||||
handler: handlePositionCreated
|
||||
- event: PositionRemoved(indexed uint256,indexed address,uint256)
|
||||
handler: handlePositionRemoved
|
||||
- event: PositionShrunk(indexed uint256,indexed address,uint256,uint256)
|
||||
handler: handlePositionShrunk
|
||||
- event: PositionTaxPaid(indexed uint256,indexed address,uint256,uint256,uint256)
|
||||
handler: handleTaxPaid
|
||||
- event: PositionRateHiked(indexed uint256,indexed address,uint256)
|
||||
handler: handlePositionRateHiked
|
||||
file: ./src/stake.ts
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
specVersion: 0.0.4
|
||||
repository: http://gitea.loseyourip.com:4000/dark-meme-society/harb.git
|
||||
description: Harberger Tax Token
|
||||
schema:
|
||||
file: ./schema.graphql
|
||||
dataSources:
|
||||
- kind: ethereum
|
||||
name: Harb
|
||||
network: base-sepolia
|
||||
source:
|
||||
address: "0x54838DC097E7fC4736B801bF1c1FCf1597348265"
|
||||
abi: Harb
|
||||
startBlock: 15399960
|
||||
mapping:
|
||||
kind: ethereum/events
|
||||
apiVersion: 0.0.7
|
||||
language: wasm/assemblyscript
|
||||
entities:
|
||||
- Stats
|
||||
- UbiClaim
|
||||
abis:
|
||||
- name: Harb
|
||||
file: ./abis/Harb.json
|
||||
eventHandlers:
|
||||
- event: Transfer(indexed address,indexed address,uint256)
|
||||
handler: handleTransfer
|
||||
blockHandlers:
|
||||
- handler: handleBlock
|
||||
file: ./src/harb.ts
|
||||
- kind: ethereum
|
||||
name: Stake
|
||||
network: base-sepolia
|
||||
source:
|
||||
address: "0xd7728173F73C748944d29EA77b56f09b8FEc8F33"
|
||||
abi: Stake
|
||||
startBlock: 15399960
|
||||
mapping:
|
||||
kind: ethereum/events
|
||||
apiVersion: 0.0.7
|
||||
language: wasm/assemblyscript
|
||||
entities:
|
||||
- Position
|
||||
abis:
|
||||
- name: Stake
|
||||
file: ./abis/Stake.json
|
||||
- name: Harb
|
||||
file: ./abis/Harb.json
|
||||
eventHandlers:
|
||||
- event: PositionCreated(indexed uint256,indexed address,uint256,uint256,uint32)
|
||||
handler: handlePositionCreated
|
||||
- event: PositionRemoved(indexed uint256,indexed address,uint256)
|
||||
handler: handlePositionRemoved
|
||||
- event: PositionShrunk(indexed uint256,indexed address,uint256,uint256)
|
||||
handler: handlePositionShrunk
|
||||
- event: PositionTaxPaid(indexed uint256,indexed address,uint256,uint256,uint256)
|
||||
handler: handleTaxPaid
|
||||
- event: PositionRateHiked(indexed uint256,indexed address,uint256)
|
||||
handler: handlePositionRateHiked
|
||||
file: ./src/stake.ts
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import { newMockEvent } from "matchstick-as"
|
||||
import { ethereum, Address, BigInt } from "@graphprotocol/graph-ts"
|
||||
import { Approval, EIP712DomainChanged, Transfer } from "../generated/Harb/Harb"
|
||||
|
||||
export function createApprovalEvent(
|
||||
owner: Address,
|
||||
spender: Address,
|
||||
value: BigInt
|
||||
): Approval {
|
||||
let approvalEvent = changetype<Approval>(newMockEvent())
|
||||
|
||||
approvalEvent.parameters = new Array()
|
||||
|
||||
approvalEvent.parameters.push(
|
||||
new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner))
|
||||
)
|
||||
approvalEvent.parameters.push(
|
||||
new ethereum.EventParam("spender", ethereum.Value.fromAddress(spender))
|
||||
)
|
||||
approvalEvent.parameters.push(
|
||||
new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value))
|
||||
)
|
||||
|
||||
return approvalEvent
|
||||
}
|
||||
|
||||
export function createEIP712DomainChangedEvent(): EIP712DomainChanged {
|
||||
let eip712DomainChangedEvent = changetype<EIP712DomainChanged>(newMockEvent())
|
||||
|
||||
eip712DomainChangedEvent.parameters = new Array()
|
||||
|
||||
return eip712DomainChangedEvent
|
||||
}
|
||||
|
||||
export function createTransferEvent(
|
||||
from: Address,
|
||||
to: Address,
|
||||
value: BigInt
|
||||
): Transfer {
|
||||
let transferEvent = changetype<Transfer>(newMockEvent())
|
||||
|
||||
transferEvent.parameters = new Array()
|
||||
|
||||
transferEvent.parameters.push(
|
||||
new ethereum.EventParam("from", ethereum.Value.fromAddress(from))
|
||||
)
|
||||
transferEvent.parameters.push(
|
||||
new ethereum.EventParam("to", ethereum.Value.fromAddress(to))
|
||||
)
|
||||
transferEvent.parameters.push(
|
||||
new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value))
|
||||
)
|
||||
|
||||
return transferEvent
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import {
|
||||
assert,
|
||||
describe,
|
||||
test,
|
||||
clearStore,
|
||||
beforeAll,
|
||||
afterAll
|
||||
} from "matchstick-as/assembly/index"
|
||||
import { Address, BigInt } from "@graphprotocol/graph-ts"
|
||||
import { Approval } from "../generated/schema"
|
||||
import { Approval as ApprovalEvent } from "../generated/Harb/Harb"
|
||||
import { handleApproval } from "../src/harb"
|
||||
import { createApprovalEvent } from "./harb-utils"
|
||||
|
||||
// Tests structure (matchstick-as >=0.5.0)
|
||||
// https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0
|
||||
|
||||
describe("Describe entity assertions", () => {
|
||||
beforeAll(() => {
|
||||
let owner = Address.fromString("0x0000000000000000000000000000000000000001")
|
||||
let spender = Address.fromString(
|
||||
"0x0000000000000000000000000000000000000001"
|
||||
)
|
||||
let value = BigInt.fromI32(234)
|
||||
let newApprovalEvent = createApprovalEvent(owner, spender, value)
|
||||
handleApproval(newApprovalEvent)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
clearStore()
|
||||
})
|
||||
|
||||
// For more test scenarios, see:
|
||||
// https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test
|
||||
|
||||
test("Approval created and stored", () => {
|
||||
assert.entityCount("Approval", 1)
|
||||
|
||||
// 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function
|
||||
assert.fieldEquals(
|
||||
"Approval",
|
||||
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1",
|
||||
"owner",
|
||||
"0x0000000000000000000000000000000000000001"
|
||||
)
|
||||
assert.fieldEquals(
|
||||
"Approval",
|
||||
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1",
|
||||
"spender",
|
||||
"0x0000000000000000000000000000000000000001"
|
||||
)
|
||||
assert.fieldEquals(
|
||||
"Approval",
|
||||
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1",
|
||||
"value",
|
||||
"234"
|
||||
)
|
||||
|
||||
// More assert options:
|
||||
// https://thegraph.com/docs/en/developer/matchstick/#asserts
|
||||
})
|
||||
})
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
import { newMockEvent } from "matchstick-as"
|
||||
import { ethereum, BigInt, Address } from "@graphprotocol/graph-ts"
|
||||
import { PositionCreated, PositionRemoved } from "../generated/Stake/Stake"
|
||||
|
||||
export function createPositionCreatedEvent(
|
||||
positionId: BigInt,
|
||||
owner: Address,
|
||||
share: BigInt,
|
||||
creationTime: BigInt,
|
||||
taxRate: BigInt
|
||||
): PositionCreated {
|
||||
let positionCreatedEvent = changetype<PositionCreated>(newMockEvent())
|
||||
|
||||
positionCreatedEvent.parameters = new Array()
|
||||
|
||||
positionCreatedEvent.parameters.push(
|
||||
new ethereum.EventParam(
|
||||
"positionId",
|
||||
ethereum.Value.fromUnsignedBigInt(positionId)
|
||||
)
|
||||
)
|
||||
positionCreatedEvent.parameters.push(
|
||||
new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner))
|
||||
)
|
||||
positionCreatedEvent.parameters.push(
|
||||
new ethereum.EventParam("share", ethereum.Value.fromUnsignedBigInt(share))
|
||||
)
|
||||
positionCreatedEvent.parameters.push(
|
||||
new ethereum.EventParam(
|
||||
"creationTime",
|
||||
ethereum.Value.fromUnsignedBigInt(creationTime)
|
||||
)
|
||||
)
|
||||
positionCreatedEvent.parameters.push(
|
||||
new ethereum.EventParam(
|
||||
"taxRate",
|
||||
ethereum.Value.fromUnsignedBigInt(taxRate)
|
||||
)
|
||||
)
|
||||
|
||||
return positionCreatedEvent
|
||||
}
|
||||
|
||||
export function createPositionRemovedEvent(
|
||||
positionId: BigInt,
|
||||
share: BigInt,
|
||||
lastTaxTime: BigInt
|
||||
): PositionRemoved {
|
||||
let positionRemovedEvent = changetype<PositionRemoved>(newMockEvent())
|
||||
|
||||
positionRemovedEvent.parameters = new Array()
|
||||
|
||||
positionRemovedEvent.parameters.push(
|
||||
new ethereum.EventParam(
|
||||
"positionId",
|
||||
ethereum.Value.fromUnsignedBigInt(positionId)
|
||||
)
|
||||
)
|
||||
positionRemovedEvent.parameters.push(
|
||||
new ethereum.EventParam("share", ethereum.Value.fromUnsignedBigInt(share))
|
||||
)
|
||||
positionRemovedEvent.parameters.push(
|
||||
new ethereum.EventParam(
|
||||
"lastTaxTime",
|
||||
ethereum.Value.fromUnsignedBigInt(lastTaxTime)
|
||||
)
|
||||
)
|
||||
|
||||
return positionRemovedEvent
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
import {
|
||||
assert,
|
||||
describe,
|
||||
test,
|
||||
clearStore,
|
||||
beforeAll,
|
||||
afterAll
|
||||
} from "matchstick-as/assembly/index"
|
||||
import { BigInt, Address } from "@graphprotocol/graph-ts"
|
||||
import { PositionCreated } from "../generated/schema"
|
||||
import { PositionCreated as PositionCreatedEvent } from "../generated/Stake/Stake"
|
||||
import { handlePositionCreated } from "../src/stake"
|
||||
import { createPositionCreatedEvent } from "./stake-utils"
|
||||
|
||||
// Tests structure (matchstick-as >=0.5.0)
|
||||
// https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0
|
||||
|
||||
describe("Describe entity assertions", () => {
|
||||
beforeAll(() => {
|
||||
let positionId = BigInt.fromI32(234)
|
||||
let owner = Address.fromString("0x0000000000000000000000000000000000000001")
|
||||
let share = BigInt.fromI32(234)
|
||||
let creationTime = BigInt.fromI32(234)
|
||||
let taxRate = BigInt.fromI32(234)
|
||||
let newPositionCreatedEvent = createPositionCreatedEvent(
|
||||
positionId,
|
||||
owner,
|
||||
share,
|
||||
creationTime,
|
||||
taxRate
|
||||
)
|
||||
handlePositionCreated(newPositionCreatedEvent)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
clearStore()
|
||||
})
|
||||
|
||||
// For more test scenarios, see:
|
||||
// https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test
|
||||
|
||||
test("PositionCreated created and stored", () => {
|
||||
assert.entityCount("PositionCreated", 1)
|
||||
|
||||
// 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function
|
||||
assert.fieldEquals(
|
||||
"PositionCreated",
|
||||
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1",
|
||||
"positionId",
|
||||
"234"
|
||||
)
|
||||
assert.fieldEquals(
|
||||
"PositionCreated",
|
||||
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1",
|
||||
"owner",
|
||||
"0x0000000000000000000000000000000000000001"
|
||||
)
|
||||
assert.fieldEquals(
|
||||
"PositionCreated",
|
||||
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1",
|
||||
"share",
|
||||
"234"
|
||||
)
|
||||
assert.fieldEquals(
|
||||
"PositionCreated",
|
||||
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1",
|
||||
"creationTime",
|
||||
"234"
|
||||
)
|
||||
assert.fieldEquals(
|
||||
"PositionCreated",
|
||||
"0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1",
|
||||
"taxRate",
|
||||
"234"
|
||||
)
|
||||
|
||||
// More assert options:
|
||||
// https://thegraph.com/docs/en/developer/matchstick/#asserts
|
||||
})
|
||||
})
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"extends": "@graphprotocol/graph-ts/types/tsconfig.base.json",
|
||||
"include": ["src", "tests"]
|
||||
}
|
||||
|
|
@ -211,16 +211,17 @@ watchEffect(() => {
|
|||
stakeSlots.value = (supplyFreeze.value * 1000)?.toFixed(2);
|
||||
});
|
||||
|
||||
// const stakeAbleHarbAmount = computed(() => statCollection.harbTotalSupply / 5n);
|
||||
// const stakeAbleHarbAmount = computed(() => statCollection.kraikenTotalSupply / 5n);
|
||||
//war das mal so, wurde das geändert --> funktioniert nicht mehr
|
||||
// const minStake = computed(() => stakeAbleHarbAmount.value / 600n);
|
||||
|
||||
|
||||
const tokenIssuance = computed(() => {
|
||||
if (statCollection.harbTotalSupply === 0n) {
|
||||
if (statCollection.kraikenTotalSupply === 0n) {
|
||||
return 0n;
|
||||
}
|
||||
|
||||
return (statCollection.nettoToken7d / statCollection.harbTotalSupply) * 100n;
|
||||
return (statCollection.nettoToken7d / statCollection.kraikenTotalSupply) * 100n;
|
||||
});
|
||||
|
||||
function getMinFloorTax() {
|
||||
|
|
@ -280,7 +281,7 @@ onMounted(async () => {
|
|||
}
|
||||
});
|
||||
// async function getGraphData(): Promise<Array<any>> {
|
||||
// let res = await axios.post("https://api.studio.thegraph.com/query/47986/harb/version/latest", {
|
||||
// let res = await axios.post("http://127.0.0.1:42069/graphql", {
|
||||
// query: "query MyQuery {\n positions {\n id\n lastTaxTime\n owner\n share\n status\n taxRate\n creationTime\n }\n}",
|
||||
// });
|
||||
// return res.data.data.positions;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ console.log("minStake", minStake.value);
|
|||
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
||||
});
|
||||
|
||||
const stakeAbleHarbAmount = computed(() => statCollection.harbTotalSupply / 5n);
|
||||
const stakeAbleHarbAmount = computed(() => statCollection.kraikenTotalSupply / 5n);
|
||||
|
||||
const minStake = computed(() => stakeAbleHarbAmount.value / 600n);
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ const statCollection = useStatCollection();
|
|||
const snatchPositions = computed(() => {
|
||||
if (
|
||||
bigInt2Number(statCollection.outstandingStake, 18) + stake.stakingAmountNumber <=
|
||||
bigInt2Number(statCollection.harbTotalSupply, 18) * 0.2
|
||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ if (
|
|||
const difference =
|
||||
bigInt2Number(statCollection.outstandingStake, 18) +
|
||||
stake.stakingAmountNumber -
|
||||
bigInt2Number(statCollection.harbTotalSupply, 18) * 0.2;
|
||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2;
|
||||
console.log("difference", difference);
|
||||
|
||||
//Division ohne Rest, um zu schauen wie viele Positionen gesnatched werden könnten
|
||||
|
|
|
|||
|
|
@ -162,14 +162,14 @@ async function loadActivePositionData() {
|
|||
|
||||
const multiplier =
|
||||
Number(formatUnits(props.position.totalSupplyInit, 18)) /
|
||||
Number(formatUnits(statCollection.harbTotalSupply, 18));
|
||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
||||
console.log("props.position.totalSupplyInit", props.position.totalSupplyInit);
|
||||
|
||||
console.log("multiplier", multiplier);
|
||||
|
||||
profit.value =
|
||||
Number(formatUnits(statCollection.harbTotalSupply, 18)) * multiplier -
|
||||
Number(formatUnits(statCollection.harbTotalSupply, 18));
|
||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18)) * multiplier -
|
||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -108,32 +108,37 @@ const tresholdValue = computed(() => {
|
|||
|
||||
export async function loadActivePositions() {
|
||||
logger.info(`loadActivePositions for chain: ${chainData.value?.path}`);
|
||||
if (!chainData.value?.thegraph) {
|
||||
if (!chainData.value?.graphql) {
|
||||
return [];
|
||||
}
|
||||
console.log("chainData.value?.thegraph", chainData.value?.thegraph);
|
||||
console.log("chainData.value?.graphql", chainData.value?.graphql);
|
||||
|
||||
const res = await axios.post(chainData.value?.thegraph, {
|
||||
query: `query MyQuery {
|
||||
positions(where: {status: Active}, orderBy: taxRate, orderDirection: asc) {
|
||||
id
|
||||
lastTaxTime
|
||||
owner
|
||||
payout
|
||||
share
|
||||
harbDeposit
|
||||
snatched
|
||||
status
|
||||
taxPaid
|
||||
taxRate
|
||||
totalSupplyEnd
|
||||
totalSupplyInit
|
||||
}
|
||||
}`,
|
||||
const res = await axios.post(chainData.value?.graphql, {
|
||||
query: `query ActivePositions {
|
||||
positionss(where: { status: "Active" }, orderBy: "taxRate", orderDirection: "asc", limit: 1000) {
|
||||
items {
|
||||
id
|
||||
lastTaxTime
|
||||
owner
|
||||
payout
|
||||
share
|
||||
kraikenDeposit
|
||||
snatched
|
||||
status
|
||||
taxPaid
|
||||
taxRate
|
||||
totalSupplyEnd
|
||||
totalSupplyInit
|
||||
}
|
||||
}
|
||||
}`,
|
||||
});
|
||||
console.log("res", res.data);
|
||||
|
||||
return res.data.data.positions as Position[];
|
||||
const items = res.data?.data?.positionss?.items ?? [];
|
||||
return items.map((item: any) => ({
|
||||
...item,
|
||||
harbDeposit: item.kraikenDeposit ?? "0",
|
||||
})) as Position[];
|
||||
}
|
||||
|
||||
function formatId(id: Hex) {
|
||||
|
|
@ -144,33 +149,38 @@ function formatId(id: Hex) {
|
|||
|
||||
export async function loadMyClosedPositions(account: GetAccountReturnType) {
|
||||
logger.info(`loadMyClosedPositions for chain: ${chainData.value?.path}`);
|
||||
if (!chainData.value?.thegraph) {
|
||||
if (!chainData.value?.graphql) {
|
||||
return [];
|
||||
}
|
||||
const res = await axios.post(chainData.value?.thegraph, {
|
||||
query: `query MyQuery {
|
||||
positions(where: {status: Closed, owner: "${account.address?.toLowerCase()}"}) {
|
||||
id
|
||||
lastTaxTime
|
||||
owner
|
||||
payout
|
||||
share
|
||||
harbDeposit
|
||||
snatched
|
||||
status
|
||||
taxPaid
|
||||
taxRate
|
||||
totalSupplyEnd
|
||||
totalSupplyInit
|
||||
}
|
||||
}`,
|
||||
const res = await axios.post(chainData.value?.graphql, {
|
||||
query: `query ClosedPositions {
|
||||
positionss(where: { status: "Closed", owner: "${account.address?.toLowerCase()}" }, limit: 1000) {
|
||||
items {
|
||||
id
|
||||
lastTaxTime
|
||||
owner
|
||||
payout
|
||||
share
|
||||
kraikenDeposit
|
||||
snatched
|
||||
status
|
||||
taxPaid
|
||||
taxRate
|
||||
totalSupplyEnd
|
||||
totalSupplyInit
|
||||
}
|
||||
}
|
||||
}`,
|
||||
});
|
||||
if (res.data.errors?.length > 0) {
|
||||
console.error("todo nur laden, wenn eingeloggt");
|
||||
return [];
|
||||
}
|
||||
const positions: Position[] = res.data.data.positions;
|
||||
return positions;
|
||||
const items = res.data?.data?.positionss?.items ?? [];
|
||||
return items.map((item: any) => ({
|
||||
...item,
|
||||
harbDeposit: item.kraikenDeposit ?? "0",
|
||||
})) as Position[];
|
||||
}
|
||||
|
||||
export async function loadPositions() {
|
||||
|
|
|
|||
|
|
@ -8,72 +8,80 @@ import type { WatchBlocksReturnType } from "viem";
|
|||
import { bigInt2Number } from "@/utils/helper";
|
||||
const demo = sessionStorage.getItem("demo") === "true";
|
||||
|
||||
interface statsCollection {
|
||||
burnNextHourProjected: bigint;
|
||||
burnedLastDay: bigint;
|
||||
burnedLastWeek: bigint;
|
||||
interface StatsRecord {
|
||||
burnNextHourProjected: string;
|
||||
burnedLastDay: string;
|
||||
burnedLastWeek: string;
|
||||
id: string;
|
||||
lastUpdatedHour: number;
|
||||
mintNextHourProjected: bigint;
|
||||
mintedLastDay: bigint;
|
||||
mintedLastWeek: bigint;
|
||||
outstandingStake: bigint;
|
||||
harbTotalSupply: bigint;
|
||||
stakeTotalSupply: bigint;
|
||||
mintNextHourProjected: string;
|
||||
mintedLastDay: string;
|
||||
mintedLastWeek: string;
|
||||
outstandingStake: string;
|
||||
kraikenTotalSupply: string;
|
||||
stakeTotalSupply: string;
|
||||
ringBufferPointer: number;
|
||||
totalBurned: bigint;
|
||||
totalMinted: bigint;
|
||||
totalBurned: string;
|
||||
totalMinted: string;
|
||||
}
|
||||
|
||||
const rawStatsCollections = ref<Array<statsCollection>>([]);
|
||||
const rawStatsCollections = ref<Array<StatsRecord>>([]);
|
||||
const loading = ref(false);
|
||||
const initialized = ref(false);
|
||||
|
||||
export async function loadStatsCollection() {
|
||||
logger.info(`loadStatsCollection for chain: ${chainData.value?.path}`);
|
||||
if (!chainData.value?.thegraph) {
|
||||
if (!chainData.value?.graphql) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const res = await axios.post(chainData.value?.thegraph, {
|
||||
query: `query MyQuery {
|
||||
stats_collection {
|
||||
burnNextHourProjected
|
||||
burnedLastDay
|
||||
burnedLastWeek
|
||||
id
|
||||
lastUpdatedHour
|
||||
mintNextHourProjected
|
||||
mintedLastDay
|
||||
mintedLastWeek
|
||||
outstandingStake
|
||||
harbTotalSupply
|
||||
stakeTotalSupply
|
||||
ringBufferPointer
|
||||
totalBurned
|
||||
totalMinted
|
||||
}
|
||||
}`,
|
||||
const res = await axios.post(chainData.value?.graphql, {
|
||||
query: `query StatsQuery {
|
||||
stats(id: "0x01") {
|
||||
burnNextHourProjected
|
||||
burnedLastDay
|
||||
burnedLastWeek
|
||||
id
|
||||
mintNextHourProjected
|
||||
mintedLastDay
|
||||
mintedLastWeek
|
||||
outstandingStake
|
||||
kraikenTotalSupply
|
||||
stakeTotalSupply
|
||||
ringBufferPointer
|
||||
totalBurned
|
||||
totalMinted
|
||||
}
|
||||
}`,
|
||||
});
|
||||
console.groupEnd();
|
||||
return res.data?.data?.stats_collection;
|
||||
|
||||
const stats = res.data?.data?.stats as StatsRecord | undefined;
|
||||
if (!stats) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [{ ...stats, kraikenTotalSupply: stats.kraikenTotalSupply }];
|
||||
}
|
||||
|
||||
const profit7d = computed(() => {
|
||||
if (!statsCollection.value) {
|
||||
return 0n;
|
||||
}
|
||||
return (
|
||||
(BigInt(statsCollection.value?.mintedLastWeek) - BigInt(statsCollection.value?.burnedLastWeek)) /
|
||||
(BigInt(statsCollection.value?.totalMinted) - BigInt(statsCollection.value?.totalBurned))
|
||||
);
|
||||
const mintedLastWeek = BigInt(statsCollection.value.mintedLastWeek);
|
||||
const burnedLastWeek = BigInt(statsCollection.value.burnedLastWeek);
|
||||
const totalMinted = BigInt(statsCollection.value.totalMinted);
|
||||
const totalBurned = BigInt(statsCollection.value.totalBurned);
|
||||
const denominator = totalMinted - totalBurned;
|
||||
if (denominator === 0n) {
|
||||
return 0n;
|
||||
}
|
||||
return (mintedLastWeek - burnedLastWeek) / denominator;
|
||||
});
|
||||
const nettoToken7d = computed(() => {
|
||||
if (!statsCollection.value) {
|
||||
return 0n;
|
||||
}
|
||||
|
||||
return BigInt(statsCollection.value?.mintedLastWeek) - BigInt(statsCollection.value.burnedLastWeek);
|
||||
return BigInt(statsCollection.value.mintedLastWeek) - BigInt(statsCollection.value.burnedLastWeek);
|
||||
});
|
||||
|
||||
const statsCollection = computed(() => {
|
||||
|
|
@ -96,9 +104,9 @@ const outstandingStake = computed(() => {
|
|||
}
|
||||
});
|
||||
|
||||
const harbTotalSupply = computed(() => {
|
||||
const kraikenTotalSupply = computed(() => {
|
||||
if (rawStatsCollections.value?.length > 0) {
|
||||
return BigInt(rawStatsCollections.value[0].harbTotalSupply);
|
||||
return BigInt(rawStatsCollections.value[0].kraikenTotalSupply);
|
||||
} else {
|
||||
return 0n;
|
||||
}
|
||||
|
|
@ -115,7 +123,10 @@ const stakeTotalSupply = computed(() => {
|
|||
//Total Supply Change / 7d=mintedLastWeek−burnedLastWeek
|
||||
const totalSupplyChange7d = computed(() => {
|
||||
if (rawStatsCollections.value?.length > 0) {
|
||||
return BigInt(rawStatsCollections.value[0].mintedLastWeek - rawStatsCollections.value[0].burnedLastWeek);
|
||||
return (
|
||||
BigInt(rawStatsCollections.value[0].mintedLastWeek) -
|
||||
BigInt(rawStatsCollections.value[0].burnedLastWeek)
|
||||
);
|
||||
} else {
|
||||
return 0n;
|
||||
}
|
||||
|
|
@ -123,15 +134,18 @@ const totalSupplyChange7d = computed(() => {
|
|||
|
||||
//totalsupply Change7d / harbtotalsupply
|
||||
const inflation7d = computed(() => {
|
||||
if (rawStatsCollections.value?.length > 0 && rawStatsCollections.value[0].harbTotalSupply > 0) {
|
||||
return BigInt(rawStatsCollections.value[0].mintedLastWeek - rawStatsCollections.value[0].burnedLastWeek);
|
||||
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
||||
return (
|
||||
BigInt(rawStatsCollections.value[0].mintedLastWeek) -
|
||||
BigInt(rawStatsCollections.value[0].burnedLastWeek)
|
||||
);
|
||||
} else {
|
||||
return 0n;
|
||||
}
|
||||
});
|
||||
|
||||
const stakeableSupply = computed(() => {
|
||||
if (rawStatsCollections.value?.length > 0 && rawStatsCollections.value[0].harbTotalSupply > 0) {
|
||||
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
||||
console.log("rawStatsCollections.value[0]", rawStatsCollections.value[0]);
|
||||
|
||||
return stakeTotalSupply.value / 5n;
|
||||
|
|
@ -142,7 +156,7 @@ const stakeableSupply = computed(() => {
|
|||
|
||||
//maxSlots
|
||||
const maxSlots = computed(() => {
|
||||
if (rawStatsCollections.value?.length > 0 && rawStatsCollections.value[0].harbTotalSupply > 0) {
|
||||
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
||||
console.log("rawStatsCollections.value[0]", rawStatsCollections.value[0]);
|
||||
|
||||
return (bigInt2Number(stakeTotalSupply.value, 18) * 0.2) / 100;
|
||||
|
|
@ -206,7 +220,7 @@ export function useStatCollection() {
|
|||
nettoToken7d,
|
||||
inflation7d,
|
||||
outstandingStake,
|
||||
harbTotalSupply,
|
||||
kraikenTotalSupply,
|
||||
stakeTotalSupply,
|
||||
totalSupplyChange7d,
|
||||
initialized,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { getAllowance, HarbContract, getNonce } from "@/contracts/harb";
|
|||
import logger from "@/utils/logger";
|
||||
import { setHarbContract } from "@/contracts/harb";
|
||||
import { setStakeContract } from "@/contracts/stake";
|
||||
import {chainsData} from "@/config"
|
||||
import {chainsData, DEFAULT_CHAIN_ID} from "@/config"
|
||||
|
||||
const balance = ref<GetBalanceReturnType>({
|
||||
value: 0n,
|
||||
|
|
@ -28,8 +28,10 @@ const account = ref<GetAccountReturnType>({
|
|||
status: "disconnected",
|
||||
});
|
||||
|
||||
const selectedChainId = computed(() => account.value.chainId ?? DEFAULT_CHAIN_ID);
|
||||
|
||||
export const chainData = computed(() => {
|
||||
return chainsData.find((obj) => obj.id === account.value.chainId)
|
||||
return chainsData.find((obj) => obj.id === selectedChainId.value)
|
||||
})
|
||||
|
||||
let unwatch: any = null;
|
||||
|
|
|
|||
|
|
@ -1,31 +1,35 @@
|
|||
const LOCAL_PONDER_URL = "http://127.0.0.1:42069/graphql";
|
||||
|
||||
export const DEFAULT_CHAIN_ID = Number(import.meta.env.VITE_DEFAULT_CHAIN_ID ?? 84532);
|
||||
|
||||
export const chainsData = [
|
||||
{
|
||||
//sepolia
|
||||
// sepolia
|
||||
id: 11155111,
|
||||
thegraph: "https://api.studio.thegraph.com/query/71271/harb/v0.0.50",
|
||||
graphql: import.meta.env.VITE_PONDER_SEPOLIA ?? "",
|
||||
path: "sepolia",
|
||||
stake: "0xCd21a41a137BCAf8743E47D048F57D92398f7Da9",
|
||||
harb: "0x087F256D11fe533b0c7d372e44Ee0F9e47C89dF9",
|
||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE"
|
||||
}, {
|
||||
//base-sepolia
|
||||
// base-sepolia (local dev default)
|
||||
id: 84532,
|
||||
thegraph: "https://api.studio.thegraph.com/query/71271/harb-base-sepolia/v0.0.12",
|
||||
graphql: import.meta.env.VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK ?? LOCAL_PONDER_URL,
|
||||
path: "sepoliabase",
|
||||
stake: "0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2",
|
||||
harb: "0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8",
|
||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE"
|
||||
},
|
||||
{
|
||||
//base
|
||||
// base mainnet
|
||||
id: 8453,
|
||||
thegraph: import.meta.env.VITE_BASE_URL,
|
||||
graphql: import.meta.env.VITE_PONDER_BASE ?? "",
|
||||
path: "base",
|
||||
stake: "0xed70707fab05d973ad41eae8d17e2bcd36192cfc",
|
||||
harb: "0x45caa5929f6ee038039984205bdecf968b954820",
|
||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE"
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
export function getChain(id:number){
|
||||
return chainsData.find((obj) => obj.id === id)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const minStakeAmount = computed(() => {
|
|||
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
||||
});
|
||||
|
||||
const stakeAbleHarbAmount = computed(() => statCollection.harbTotalSupply / 5n);
|
||||
const stakeAbleHarbAmount = computed(() => statCollection.kraikenTotalSupply / 5n);
|
||||
|
||||
const minStake = computed(() => stakeAbleHarbAmount.value / 600n);
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ const statCollection = useStatCollection();
|
|||
const snatchPositions = computed(() => {
|
||||
if (
|
||||
bigInt2Number(statCollection.outstandingStake, 18) + stake.stakingAmountNumber <=
|
||||
bigInt2Number(statCollection.harbTotalSupply, 18) * 0.2
|
||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ const snatchPositions = computed(() => {
|
|||
const difference =
|
||||
bigInt2Number(statCollection.outstandingStake, 18) +
|
||||
stake.stakingAmountNumber -
|
||||
bigInt2Number(statCollection.harbTotalSupply, 18) * 0.2;
|
||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2;
|
||||
console.log("difference", difference);
|
||||
|
||||
//Division ohne Rest, um zu schauen wie viele Positionen gesnatched werden könnten
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue