rebrand(lethean): update branding, ports, and config for Lethean blockchain
- Coin: Zano → Lethean, ticker: ZAN/ZANO → LTHN - Ports: 11211 → 36941 (mainnet RPC), 46941 (testnet RPC) - Wallet: 11212 → 36944/46944 - Address prefix: iTHN - URLs: zano.org → lethean.io - Explorer links: explorer.lthn.io Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9304bf57a9
commit
1623966c32
14 changed files with 138 additions and 108 deletions
12
.env.example
12
.env.example
|
|
@ -1,8 +1,12 @@
|
|||
PORT="3001"
|
||||
PGUSER="postgres"
|
||||
PGPASSWORD="root"
|
||||
PGPASSWORD="CHANGE_ME"
|
||||
PGHOST="127.0.0.1"
|
||||
PGDATABASE="trade"
|
||||
PGDATABASE="lethean_trade"
|
||||
PGPORT="5432"
|
||||
JWT_SECRET="your JWT secret here"
|
||||
OWNER_ALIAS="your alias here"
|
||||
JWT_SECRET="CHANGE_ME"
|
||||
OWNER_ALIAS="your alias here"
|
||||
# Lethean daemon RPC — testnet: 46941, mainnet: 36941
|
||||
DAEMON_RPC_URL="http://127.0.0.1:46941/json_rpc"
|
||||
# Lethean wallet RPC — testnet: 46944, mainnet: 36944
|
||||
WALLET_RPC_URL="http://127.0.0.1:46944/json_rpc"
|
||||
53
README.md
53
README.md
|
|
@ -1,38 +1,31 @@
|
|||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
# Lethean Trade Backend
|
||||
|
||||
Backend service for the Lethean decentralised exchange (DEX). Built with Express, Socket.IO, and PostgreSQL.
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy `.env.example` to `.env` and set real values:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
### Lethean RPC Endpoints
|
||||
|
||||
| Service | Testnet | Mainnet |
|
||||
|---------|---------|---------|
|
||||
| Daemon RPC | `http://127.0.0.1:46941/json_rpc` | `http://127.0.0.1:36941/json_rpc` |
|
||||
| Wallet RPC | `http://127.0.0.1:46944/json_rpc` | `http://127.0.0.1:36944/json_rpc` |
|
||||
|
||||
Set `DAEMON_RPC_URL` and `WALLET_RPC_URL` in `.env` accordingly.
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
## Address Format
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
Lethean addresses use the `iTHN` prefix.
|
||||
|
|
|
|||
18
package-lock.json
generated
18
package-lock.json
generated
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "zano-trade-backend",
|
||||
"name": "lethean-trade-backend",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "zano-trade-backend",
|
||||
"name": "lethean-trade-backend",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.4.0",
|
||||
|
|
@ -739,7 +739,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",
|
||||
"integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/core": "^0.22.12"
|
||||
}
|
||||
|
|
@ -776,7 +775,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz",
|
||||
"integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12"
|
||||
},
|
||||
|
|
@ -789,7 +787,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.12.tgz",
|
||||
"integrity": "sha512-S0vJADTuh1Q9F+cXAwFPlrKWzDj2F9t/9JAbUvaaDuivpyWuImEKXVz5PUZw2NbpuSHjwssbTpOZ8F13iJX4uw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12"
|
||||
},
|
||||
|
|
@ -814,7 +811,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz",
|
||||
"integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12",
|
||||
"tinycolor2": "^1.6.0"
|
||||
|
|
@ -858,7 +854,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz",
|
||||
"integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12"
|
||||
},
|
||||
|
|
@ -982,7 +977,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz",
|
||||
"integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12"
|
||||
},
|
||||
|
|
@ -995,7 +989,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz",
|
||||
"integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12"
|
||||
},
|
||||
|
|
@ -1011,7 +1004,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz",
|
||||
"integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12"
|
||||
},
|
||||
|
|
@ -1540,7 +1532,6 @@
|
|||
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
|
|
@ -1994,7 +1985,6 @@
|
|||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
|
@ -3614,7 +3604,6 @@
|
|||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
|
|
@ -3828,7 +3817,6 @@
|
|||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
|
|
@ -7120,7 +7108,6 @@
|
|||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
||||
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.9.1",
|
||||
"pg-pool": "^3.10.1",
|
||||
|
|
@ -8782,7 +8769,6 @@
|
|||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "zano-trade-backend",
|
||||
"name": "lethean-trade-backend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class StatsController {
|
|||
(order) => order.buy_orders && order.buy_orders.length > 0,
|
||||
);
|
||||
|
||||
const volumeZano = filteredOrders.reduce(
|
||||
const volumeLthean = filteredOrders.reduce(
|
||||
(acc, order) =>
|
||||
order?.buy_orders?.reduce(
|
||||
(innerAcc, tx) =>
|
||||
|
|
@ -119,20 +119,20 @@ class StatsController {
|
|||
const firstPrice = new Decimal(filteredOrders[0]?.price || 0);
|
||||
const lastPrice = new Decimal(filteredOrders.at(-1)?.price || 0);
|
||||
|
||||
const zanoPriceData = exchangeModel.getZanoPriceData();
|
||||
const ltheanPriceData = exchangeModel.getLtheanPriceData();
|
||||
|
||||
const zanoPriceForTimestamp = (
|
||||
await exchangeModel.getZanoPriceForTimestamp(from_timestamp_parsed)
|
||||
const ltheanPriceForTimestamp = (
|
||||
await exchangeModel.getLtheanPriceForTimestamp(from_timestamp_parsed)
|
||||
)?.data;
|
||||
|
||||
if (!zanoPriceForTimestamp) {
|
||||
throw new Error('Failed to fetch Zano price data for the given timestamp');
|
||||
if (!ltheanPriceForTimestamp) {
|
||||
throw new Error('Failed to fetch Lethean price data for the given timestamp');
|
||||
}
|
||||
|
||||
const firstZanoPriceDecimal = new Decimal(zanoPriceForTimestamp || '1');
|
||||
const firstLtheanPriceDecimal = new Decimal(ltheanPriceForTimestamp || '1');
|
||||
|
||||
const firstPriceUSD = firstPrice.mul(firstZanoPriceDecimal);
|
||||
const lastPriceUSD = lastPrice.mul(new Decimal(zanoPriceData.now || '1'));
|
||||
const firstPriceUSD = firstPrice.mul(firstLtheanPriceDecimal);
|
||||
const lastPriceUSD = lastPrice.mul(new Decimal(ltheanPriceData.now || '1'));
|
||||
|
||||
let priceChangePercent = new Decimal(0);
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ class StatsController {
|
|||
|
||||
const period_data = {
|
||||
price_change_percent: priceChangePercent.toString(),
|
||||
volume: volumeZano.toString(),
|
||||
volume: volumeLthean.toString(),
|
||||
};
|
||||
|
||||
response.period_data = period_data;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,23 @@
|
|||
import { body } from 'express-validator';
|
||||
|
||||
// Lethean addresses use the iTHN prefix
|
||||
const LETHEAN_ADDRESS_PREFIX = 'iTHN';
|
||||
|
||||
interface RequestAuthBody {
|
||||
address: string;
|
||||
alias: string;
|
||||
}
|
||||
|
||||
export const requestAuthBodyValidator = [
|
||||
body('address').isString().notEmpty(),
|
||||
body('address')
|
||||
.isString()
|
||||
.notEmpty()
|
||||
.custom((value: string) => {
|
||||
if (!value.startsWith(LETHEAN_ADDRESS_PREFIX)) {
|
||||
throw new Error(`Address must start with ${LETHEAN_ADDRESS_PREFIX} prefix`);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
body('alias').isString().notEmpty(),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
import AuthData from '@/interfaces/bodies/user/AuthData';
|
||||
import axios from 'axios';
|
||||
|
||||
// Lethean address prefix — all valid addresses start with iTHN
|
||||
const LETHEAN_ADDRESS_PREFIX = 'iTHN';
|
||||
|
||||
// Lethean daemon RPC (testnet: 46941, mainnet: 36941)
|
||||
const DAEMON_RPC_DEFAULT = 'http://127.0.0.1:46941/json_rpc';
|
||||
|
||||
async function validateWallet(authData: AuthData) {
|
||||
async function fetchZanoApi(method: string, params: object) {
|
||||
async function fetchLtheanApi(method: string, params: object) {
|
||||
try {
|
||||
return await axios
|
||||
.post('http://37.27.100.59:10500/json_rpc', {
|
||||
.post(process.env.DAEMON_RPC_URL || DAEMON_RPC_DEFAULT, {
|
||||
id: 0,
|
||||
jsonrpc: '2.0',
|
||||
method,
|
||||
|
|
@ -23,7 +29,12 @@ async function validateWallet(authData: AuthData) {
|
|||
return false;
|
||||
}
|
||||
|
||||
const response = await fetchZanoApi('validate_signature', {
|
||||
// Validate Lethean address prefix
|
||||
if (!address || !address.startsWith(LETHEAN_ADDRESS_PREFIX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const response = await fetchLtheanApi('validate_signature', {
|
||||
buff: Buffer.from(message).toString('base64'),
|
||||
alias,
|
||||
sig: signature,
|
||||
|
|
@ -35,7 +46,7 @@ async function validateWallet(authData: AuthData) {
|
|||
return false;
|
||||
}
|
||||
|
||||
const aliasDetailsResponse = await fetchZanoApi('get_alias_details', {
|
||||
const aliasDetailsResponse = await fetchLtheanApi('get_alias_details', {
|
||||
alias,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ const defaultRateLimitMiddleware = rateLimit({
|
|||
class Middleware {
|
||||
async verifyToken(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const userData = jwt.verify(req.body.token, process.env.JWT_SECRET || '') as UserData;
|
||||
if (!process.env.JWT_SECRET) throw new Error('JWT_SECRET not configured');
|
||||
const userData = jwt.verify(req.body.token, process.env.JWT_SECRET) as UserData;
|
||||
req.body.userData = userData;
|
||||
next();
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ async function socketMiddleware(event: Event, next: (_err?: Error | undefined) =
|
|||
let userData: UserData;
|
||||
|
||||
try {
|
||||
userData = jwt.verify(data.token, process.env.JWT_SECRET || '') as UserData;
|
||||
userData = jwt.verify(data.token, process.env.JWT_SECRET!) as UserData;
|
||||
} catch {
|
||||
return next(new Error('Unauthorized'));
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ export function verifyUser(paths: string[]) {
|
|||
let userData;
|
||||
|
||||
try {
|
||||
userData = jwt.verify(data.token, process.env.JWT_SECRET || '') as UserData;
|
||||
userData = jwt.verify(data.token, process.env.JWT_SECRET!) as UserData;
|
||||
} catch {
|
||||
return next(new Error('Unauthorized'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ interface OrderWithTransactions extends Order {
|
|||
sell_orders: Transaction[];
|
||||
}
|
||||
|
||||
const PRICE_BASE_URL = 'https://explorer.zano.org/api/get_historical_zano_price?timestamp=';
|
||||
const PRICE_BASE_URL = 'https://explorer.lethean.org/api/get_historical_lethean_price?timestamp=';
|
||||
|
||||
class ExchangeModel {
|
||||
private zano_price_data: {
|
||||
private lthn_price_data: {
|
||||
now: string | null;
|
||||
back24hr: string | null;
|
||||
} = {
|
||||
|
|
@ -29,14 +29,14 @@ class ExchangeModel {
|
|||
};
|
||||
|
||||
constructor() {
|
||||
this.runZanoPriceDaemon();
|
||||
this.runLtheanPriceDaemon();
|
||||
}
|
||||
|
||||
getZanoPriceData() {
|
||||
return this.zano_price_data;
|
||||
getLtheanPriceData() {
|
||||
return this.lthn_price_data;
|
||||
}
|
||||
|
||||
async getZanoPriceForTimestamp(timestamp: number) {
|
||||
async getLtheanPriceForTimestamp(timestamp: number) {
|
||||
try {
|
||||
const priceData = await fetch(`${PRICE_BASE_URL}${timestamp}`).then((res) =>
|
||||
res.json(),
|
||||
|
|
@ -47,7 +47,7 @@ class ExchangeModel {
|
|||
if (!priceParsed) {
|
||||
console.log(priceData);
|
||||
|
||||
throw new Error('Failed to fetch Zano price data for timestamp');
|
||||
throw new Error('Failed to fetch Lethean price data for timestamp');
|
||||
}
|
||||
|
||||
return { success: true, data: priceParsed };
|
||||
|
|
@ -57,7 +57,7 @@ class ExchangeModel {
|
|||
}
|
||||
}
|
||||
|
||||
async updateZanoPrice() {
|
||||
async updateLtheanPrice() {
|
||||
try {
|
||||
const priceDataNow = await fetch(`${PRICE_BASE_URL}${Date.now()}`).then((res) =>
|
||||
res.json(),
|
||||
|
|
@ -73,10 +73,10 @@ class ExchangeModel {
|
|||
if (!priceNowParsed || !priceBack24hrParsed) {
|
||||
console.log(priceDataNow, priceDataBack24hr);
|
||||
|
||||
throw new Error('Failed to fetch Zano price data');
|
||||
throw new Error('Failed to fetch Lethean price data');
|
||||
}
|
||||
|
||||
this.zano_price_data = {
|
||||
this.lthn_price_data = {
|
||||
now: priceNowParsed,
|
||||
back24hr: priceBack24hrParsed,
|
||||
};
|
||||
|
|
@ -85,9 +85,9 @@ class ExchangeModel {
|
|||
}
|
||||
}
|
||||
|
||||
async runZanoPriceDaemon() {
|
||||
async runLtheanPriceDaemon() {
|
||||
while (true) {
|
||||
await this.updateZanoPrice();
|
||||
await this.updateLtheanPrice();
|
||||
await new Promise((resolve) => setTimeout(resolve, 30 * 1000));
|
||||
}
|
||||
}
|
||||
|
|
@ -144,11 +144,11 @@ class ExchangeModel {
|
|||
|
||||
private async calculatePairStats(pairId: string) {
|
||||
try {
|
||||
if (!this.zano_price_data.now || !this.zano_price_data.back24hr) {
|
||||
await this.updateZanoPrice();
|
||||
if (!this.lthn_price_data.now || !this.lthn_price_data.back24hr) {
|
||||
await this.updateLtheanPrice();
|
||||
|
||||
if (!this.zano_price_data.now || !this.zano_price_data.back24hr) {
|
||||
throw new Error('Failed to fetch Zano price data');
|
||||
if (!this.lthn_price_data.now || !this.lthn_price_data.back24hr) {
|
||||
throw new Error('Failed to fetch Lethean price data');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,11 +201,11 @@ class ExchangeModel {
|
|||
const lastOrderPrice = allTransactionsWithPrices.at(-1)?.buy_order_price || NaN;
|
||||
|
||||
const firstPriceInUSD = new Decimal(firstOrderPrice || '0').mul(
|
||||
new Decimal(this.zano_price_data.back24hr || '1'),
|
||||
new Decimal(this.lthn_price_data.back24hr || '1'),
|
||||
);
|
||||
|
||||
const lastPriceInUSD = new Decimal(lastOrderPrice || '0').mul(
|
||||
new Decimal(this.zano_price_data.now || '1'),
|
||||
new Decimal(this.lthn_price_data.now || '1'),
|
||||
);
|
||||
|
||||
const change_coefficient = lastPriceInUSD
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import userModel from './User.js';
|
|||
import Offer from '../schemes/Offer.js';
|
||||
import Currency from '../schemes/Currency.js';
|
||||
import User from '../schemes/User.js';
|
||||
import { ZANO_ASSET_ID } from '../workers/assetsUpdateChecker.js';
|
||||
import { LTHN_ASSET_ID } from '../workers/assetsUpdateChecker.js';
|
||||
|
||||
function generateOrderNumber(length: number) {
|
||||
let result = '';
|
||||
|
|
@ -55,9 +55,9 @@ class OffersModel {
|
|||
|
||||
let isFull = true;
|
||||
|
||||
const ZanoCurrencyID = await Currency.findOne({
|
||||
const LetheanCurrencyID = await Currency.findOne({
|
||||
where: {
|
||||
asset_id: ZANO_ASSET_ID,
|
||||
asset_id: LTHN_ASSET_ID,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ class OffersModel {
|
|||
|
||||
if (
|
||||
!depositCurrency &&
|
||||
parseInt(body.offerData.deposit_currency_id, 10) !== ZanoCurrencyID?.id
|
||||
parseInt(body.offerData.deposit_currency_id, 10) !== LetheanCurrencyID?.id
|
||||
) {
|
||||
isFull = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import Pair from '@/schemes/Pair';
|
|||
import Currency from '@/schemes/Currency';
|
||||
import { alwaysActiveTokens } from '@/config/config';
|
||||
|
||||
export const MIN_VOLUME_THRESHOLD = 1000; // volume in zano per month
|
||||
export const MIN_VOLUME_THRESHOLD = 1000; // volume in lethean per month
|
||||
class StatsModel {
|
||||
private alwaysActivePairIds: number[] = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dotenv/config';
|
||||
|
||||
import express from 'express';
|
||||
import http from 'http';
|
||||
import { Server } from 'socket.io';
|
||||
|
|
@ -16,7 +17,7 @@ import transactionsRouter from './routes/transactions.router';
|
|||
import adminRouter from './routes/admin.router';
|
||||
|
||||
import { socketStart } from './socket/main';
|
||||
import assetsUpdateChecker, { ZANO_ASSET_ID } from './workers/assetsUpdateChecker';
|
||||
import assetsUpdateChecker, { LTHN_ASSET_ID } from './workers/assetsUpdateChecker';
|
||||
import initdb from './database';
|
||||
import sequelize from './sequelize';
|
||||
import Currency, { Asset } from './schemes/Currency';
|
||||
|
|
@ -27,6 +28,29 @@ import { setupAssociations } from './schemes/Associations';
|
|||
import statsModel from './models/Stats';
|
||||
import ordersModerationService from './workers/ordersModerationService';
|
||||
|
||||
// Refuse to start without required configuration
|
||||
const required = ['JWT_SECRET', 'PGUSER', 'PGPASSWORD', 'PGDATABASE'];
|
||||
for (const key of required) {
|
||||
if (!process.env[key] || process.env[key] === 'CHANGE_ME') {
|
||||
console.error(
|
||||
`FATAL: ${key} is not configured. Copy .env.example to .env and set real values.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Warn if Lethean RPC endpoints are not explicitly configured
|
||||
if (!process.env.DAEMON_RPC_URL) {
|
||||
console.warn(
|
||||
'WARN: DAEMON_RPC_URL not set, defaulting to testnet http://127.0.0.1:46941/json_rpc',
|
||||
);
|
||||
}
|
||||
if (!process.env.WALLET_RPC_URL) {
|
||||
console.warn(
|
||||
'WARN: WALLET_RPC_URL not set, defaulting to testnet http://127.0.0.1:46944/json_rpc',
|
||||
);
|
||||
}
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
const app = express();
|
||||
|
|
@ -49,24 +73,24 @@ process.on('unhandledRejection', (reason, promise) => {
|
|||
await sequelize.sync();
|
||||
await setupAssociations();
|
||||
|
||||
const zanoRow = await Currency.findOne({ where: { asset_id: ZANO_ASSET_ID } });
|
||||
const ltheanRow = await Currency.findOne({ where: { asset_id: LTHN_ASSET_ID } });
|
||||
|
||||
if (!zanoRow) {
|
||||
if (!ltheanRow) {
|
||||
await Currency.create({
|
||||
name: 'ZANO',
|
||||
code: 'zano',
|
||||
name: 'LTHN',
|
||||
code: 'lethean',
|
||||
type: 'crypto',
|
||||
asset_id: ZANO_ASSET_ID,
|
||||
asset_id: LTHN_ASSET_ID,
|
||||
auto_parsed: false,
|
||||
asset_info: {
|
||||
decimal_point: 12,
|
||||
},
|
||||
});
|
||||
} else if (!zanoRow.asset_info) {
|
||||
zanoRow.asset_info = {
|
||||
} else if (!ltheanRow.asset_info) {
|
||||
ltheanRow.asset_info = {
|
||||
decimal_point: 12,
|
||||
} as Asset;
|
||||
await zanoRow.save();
|
||||
await ltheanRow.save();
|
||||
}
|
||||
|
||||
if (process.env.OWNER_ALIAS) {
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@ import Currency, { Asset } from '../schemes/Currency';
|
|||
import Pair from '../schemes/Pair';
|
||||
import sequelize from '../sequelize';
|
||||
|
||||
export const ZANO_ASSET_ID = 'd6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a';
|
||||
export const LTHN_ASSET_ID = 'd6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a';
|
||||
const CHECKING_INTERVAL = 60 * 60 * 1000; // 1 hr
|
||||
const amountPerIteration = 100;
|
||||
|
||||
async function fetchWhitelisted() {
|
||||
return fetch(`https://api.zano.org/assets_whitelist.json`)
|
||||
return fetch(`https://api.lethean.org/assets_whitelist.json`)
|
||||
.then((res) => res.json())
|
||||
.then((res) => res.assets);
|
||||
}
|
||||
|
||||
async function fetchAssets(from: number, to: number) {
|
||||
return fetch(`https://explorer.zano.org/api/get_assets/${from}/${to}`)
|
||||
return fetch(`https://explorer.lethean.org/api/get_assets/${from}/${to}`)
|
||||
.then((res) => res.json())
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
|
|
@ -74,16 +74,16 @@ class AssetsUpdateChecker {
|
|||
where: { asset_id: asset.asset_id },
|
||||
}).then((res) => res?.id);
|
||||
|
||||
const zano_id = await Currency.findOne({
|
||||
where: { asset_id: ZANO_ASSET_ID },
|
||||
const lthn_id = await Currency.findOne({
|
||||
where: { asset_id: LTHN_ASSET_ID },
|
||||
}).then((res) => res?.id);
|
||||
|
||||
const pairAlreadyExists = !!(await Pair.findOne({
|
||||
where: { first_currency_id, second_currency_id: zano_id },
|
||||
where: { first_currency_id, second_currency_id: lthn_id },
|
||||
}));
|
||||
|
||||
if (!pairAlreadyExists) {
|
||||
await Pair.create({ first_currency_id, second_currency_id: zano_id });
|
||||
await Pair.create({ first_currency_id, second_currency_id: lthn_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue