From 3d24c697780e0d92ce45a068e32500a71af1d204 Mon Sep 17 00:00:00 2001 From: Jonathan <59397732+jonathanchw@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:30:00 -0300 Subject: [PATCH 1/6] fix: add principal to default position API response (#11) --- positions/positions.service.ts | 1 + positions/positions.types.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/positions/positions.service.ts b/positions/positions.service.ts index 7ef7093..f07d230 100644 --- a/positions/positions.service.ts +++ b/positions/positions.service.ts @@ -45,6 +45,7 @@ export class PositionsService { expiration: cached.expiration, reserveContribution: cached.reserveContribution, annualInterestPPM: cached.annualInterestPPM, + principal: cached.principal, }; } diff --git a/positions/positions.types.ts b/positions/positions.types.ts index f9e680a..42cb7a1 100644 --- a/positions/positions.types.ts +++ b/positions/positions.types.ts @@ -129,4 +129,5 @@ export type ApiPositionDefault = { expiration: number; reserveContribution: number; annualInterestPPM: number; + principal: string; }; From 9d13f59755fa9d3fc2af3bac5d0442e24b4ce0a0 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Fri, 9 Jan 2026 19:47:39 +0100 Subject: [PATCH 2/6] Add CODEOWNERS file for branch protection (#12) Require approval from @Danswar or @TaprootFreak for all PRs to protected branches. Co-authored-by: Danswar <48102227+Danswar@users.noreply.github.com> --- .github/CODEOWNERS | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..5fa91f4 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# Code Owners for JuiceDollar/api +# These users must approve all pull requests to protected branches + +# Default owners for everything in the repo +* @Danswar @TaprootFreak From 7ddbe266d4dd31052308ec7b1f16875b4919d8c4 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Sat, 10 Jan 2026 13:11:39 +0100 Subject: [PATCH 3/6] fix: use real Coingecko prices for testnet tokens (#14) * fix: use real Coingecko prices for testnet tokens - Add TESTNET_COINGECKO_MAPPING to map token symbols to Coingecko IDs - WCBTC now fetches real Bitcoin price (~$100k) instead of fake $1 - Fixes incorrect collateralization display (was 0.01%, now ~200%) - Fallback to $1 for stablecoins and unknown tokens * fix: add null safety and improve error logging * fix: return only USD from Coingecko, let updatePrices() calculate JUSD price The 'eur' field is actually the price in JUSD (protocol stablecoin), not Euro. By only returning USD, the correct JUSD price calculation in updatePrices() is triggered: eur = usd / protocolStablecoinPrice * chore: remove unused randRef variable * Remove EUR/JUSD pricing complexity - use USD only Since 1 JUSD = 1 USD (dollar stablecoin), there's no need for currency conversion. This simplifies the pricing system: - Remove /prices/eur endpoint from controller - Remove getEuroPrice(), fetchEuroPrice() from service - Remove euroPrice state variable - Simplify PriceQueryCurrencies to just { usd?, btc? } - Remove jusd calculations from ecosystem collateral stats The API now works exclusively with USD, which is equivalent to JUSD at 1:1 ratio. * chore: use bitcoin ID for WBTC, remove dead code * ci: add PR checks workflow for build and lint * chore: rebrand d-EURO to JuiceDollar in telegram messages * chore: update LICENSE copyright * perf: skip Coingecko for JUSD - stablecoin is always $1 * fix: handle missing Coingecko price data gracefully - Add explicit check for undefined price data on mainnet - Use price?.usd checks instead of price === null - Log warning when no price data available - Prevents NaN calculations from undefined prices * fix: prevent TypeError when poolSharesPrice is null --- .github/workflows/ci.yaml | 31 +++++ LICENSE | 3 +- ecosystem/ecosystem.collateral.service.ts | 12 +- prices/prices.controller.ts | 8 -- prices/prices.service.ts | 127 ++++++++---------- prices/prices.types.ts | 2 - socialmedia/telegram/messages/Help.message.ts | 6 +- .../telegram/messages/StartUp.message.ts | 6 +- 8 files changed, 98 insertions(+), 97 deletions(-) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..b733717 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,31 @@ +name: CI + +on: + pull_request: + branches: [develop, main] + push: + branches: [develop, main] + +jobs: + build: + name: Build & Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Build + run: yarn build + + - name: Lint + run: yarn lint + continue-on-error: true diff --git a/LICENSE b/LICENSE index 2b368ce..a147b3b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,8 @@ MIT License Copyright (c) 2024 frankencoin -Copyright (c) 2024 samclassix Copyright (c) 2024 deuro -Copyright (c) 2025 juicedollar +Copyright (c) 2026 juicedollar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ecosystem/ecosystem.collateral.service.ts b/ecosystem/ecosystem.collateral.service.ts index fd49aac..28fee13 100644 --- a/ecosystem/ecosystem.collateral.service.ts +++ b/ecosystem/ecosystem.collateral.service.ts @@ -85,8 +85,7 @@ export class EcosystemCollateralService { const protocolStablecoinAddress = this.pricesService.getMint()?.address; if (!protocolStablecoinAddress) return null; - const protocolStablecoinPrice = prices[protocolStablecoinAddress.toLowerCase()]?.price?.usd as number; - if (!protocolStablecoinPrice) return null; + if (!prices[protocolStablecoinAddress.toLowerCase()]?.price?.usd) return null; const ecosystemTotalValueLocked: PriceQueryCurrencies = {}; const map: { [key: Address]: ApiEcosystemCollateralStatsItem } = {}; @@ -106,7 +105,6 @@ export class EcosystemCollateralService { const totalBalanceNumUsd = parseInt(formatUnits(totalBalance, c.decimals)) * price; const totalValueLocked: PriceQueryCurrencies = { usd: totalBalanceNumUsd, - eur: totalBalanceNumUsd / protocolStablecoinPrice, }; // upsert ecosystemTotalValueLocked usd @@ -116,13 +114,6 @@ export class EcosystemCollateralService { ecosystemTotalValueLocked.usd += totalValueLocked.usd; } - // upsert ecosystemTotalValueLocked eur - if (!ecosystemTotalValueLocked.eur) { - ecosystemTotalValueLocked.eur = totalValueLocked.eur; - } else { - ecosystemTotalValueLocked.eur += totalValueLocked.eur; - } - // upsert map map[c.address.toLowerCase() as Address] = { address: c.address, @@ -142,7 +133,6 @@ export class EcosystemCollateralService { totalValueLocked, price: { usd: price, - eur: Math.round((price / protocolStablecoinPrice) * 100) / 100, }, }; } diff --git a/prices/prices.controller.ts b/prices/prices.controller.ts index 4e79c7d..294d0d2 100644 --- a/prices/prices.controller.ts +++ b/prices/prices.controller.ts @@ -49,14 +49,6 @@ export class PricesController { return this.pricesService.getCollateral(); } - @Get('eur') - @ApiResponse({ - description: 'Returns the price of EUR in USD', - }) - getEuroPrice(): Promise { - return this.pricesService.getEuroPrice(); - } - @Get('poolshares') @ApiResponse({ description: `Returns the current price of the ${POOL_SHARES_SYMBOL} token`, diff --git a/prices/prices.service.ts b/prices/prices.service.ts index c8eab20..2aed006 100644 --- a/prices/prices.service.ts +++ b/prices/prices.service.ts @@ -15,13 +15,20 @@ import { PriceQueryObjectArray, } from './prices.types'; -const randRef: number = Math.random() * 0.4 + 0.8; +// Mapping of testnet token symbols to Coingecko IDs for real price fetching +const TESTNET_COINGECKO_MAPPING: Record = { + WCBTC: 'bitcoin', + WBTC: 'bitcoin', + WETH: 'ethereum', + ETH: 'ethereum', + BTC: 'bitcoin', + JUSD: null, // Stablecoin, use hardcoded $1 +}; @Injectable() export class PricesService { private readonly logger = new Logger(this.constructor.name); private fetchedPrices: PriceQueryObjectArray = {}; - private euroPrice: PriceQueryCurrencies = {}; private poolSharesPrice: PriceQueryCurrencies = {}; constructor( @@ -58,13 +65,11 @@ export class PricesService { } async getPoolSharesPrice(): Promise { - if (!this.poolSharesPrice) this.poolSharesPrice = await this.fetchFromEcosystemSharePools(this.getPoolShares()); - if (!this.euroPrice) this.euroPrice = await this.fetchEuroPrice(); - + if (!this.poolSharesPrice?.usd) { + this.poolSharesPrice = await this.fetchFromEcosystemSharePools(this.getPoolShares()); + } return { - usd: Number(this.poolSharesPrice.usd.toFixed(4)), - eur: Number(this.poolSharesPrice.eur.toFixed(4)), - btc: Number((this.poolSharesPrice.eur * this.euroPrice.btc).toFixed(9)), + usd: Number(this.poolSharesPrice?.usd?.toFixed(4) || 0), }; } @@ -84,63 +89,51 @@ export class PricesService { return c; } - async getEuroPrice(): Promise { - if (!this.euroPrice) this.euroPrice = await this.fetchEuroPrice(); - - return { - usd: Number(this.euroPrice.usd.toFixed(4)), - eur: Number(this.euroPrice.eur.toFixed(4)), - btc: Number(this.euroPrice.btc.toFixed(9)), - }; - } - async fetchFromEcosystemSharePools(erc: ERC20Info): Promise { const price = this.poolShares.getEcosystemPoolSharesInfo()?.values?.price; if (!price) return null; - const protocolStablecoinAddress = ADDRESS[VIEM_CHAIN.id].juiceDollar.toLowerCase(); - const quote = this.euroPrice?.usd || this.fetchedPrices[protocolStablecoinAddress]?.price?.usd; - const usdPrice = quote ? price * quote : price; - - this.poolSharesPrice = { usd: usdPrice, eur: price }; + // Price from ecosystem is already in JUSD, which equals USD (1 JUSD = 1 USD) + this.poolSharesPrice = { usd: price }; return this.poolSharesPrice; } async fetchSourcesCoingecko(erc: ERC20Info): Promise { // all mainnet addresses if ((VIEM_CHAIN.id as number) === 1) { - const url = `/api/v3/simple/token_price/ethereum?contract_addresses=${erc.address}&vs_currencies=usd%2Ceur`; + const url = `/api/v3/simple/token_price/ethereum?contract_addresses=${erc.address}&vs_currencies=usd`; const data = await (await COINGECKO_CLIENT(url)).json(); if (data.status) { this.logger.debug(data.status?.error_message || 'Error fetching price from coingecko'); return null; } - return Object.values(data)[0] as { usd: number; eur: number }; + const result = Object.values(data)[0] as { usd: number } | undefined; + if (!result?.usd) { + this.logger.warn(`No price data from Coingecko for ${erc.symbol} (${erc.address})`); + return null; + } + return { usd: result.usd }; } else { - // all other chain addresses (test deployments) - const calc = (value: number) => { - const ref: number = 1718033809979; - return value * randRef * (1 + ((Date.now() - ref) / (3600 * 24 * 365)) * 0.001 + Math.random() * 0.01); - }; - // @dev: this is just for testnet soft price mapping - let price = { usd: calc(1) }; - return price; - } - } + // Testnet: Map token symbols to real Coingecko prices + const symbol = erc.symbol?.toUpperCase(); + const coingeckoId = symbol ? TESTNET_COINGECKO_MAPPING[symbol] : null; + + if (coingeckoId) { + try { + const url = `/api/v3/simple/price?ids=${coingeckoId}&vs_currencies=usd`; + const data = await (await COINGECKO_CLIENT(url)).json(); + if (data[coingeckoId]?.usd) { + this.logger.debug(`Fetched real price for ${erc.symbol} via ${coingeckoId}: $${data[coingeckoId].usd}`); + return { usd: data[coingeckoId].usd }; + } + } catch (error) { + this.logger.warn(`Failed to fetch price for ${erc.symbol}: ${error.message || error}`); + } + } - async fetchEuroPrice(): Promise { - const url = `/api/v3/simple/price?ids=usd&vs_currencies=eur%2Cbtc`; - const data = await (await COINGECKO_CLIENT(url)).json(); - if (data.status) { - this.logger.debug(data.status?.error_message || 'Error fetching price from coingecko'); - return null; + // Fallback for stablecoins and unknown tokens + return { usd: 1 }; } - - return { - eur: 1, - usd: 1 / Number(data.usd.eur), - btc: 1 / Number(data.usd.eur / data.usd.btc), - }; } async fetchPrice(erc: ERC20Info): Promise { @@ -154,15 +147,24 @@ export class PricesService { async updatePrices() { this.logger.debug('Updating Prices'); - const euroPrice = await this.fetchEuroPrice(); - if (euroPrice) this.euroPrice = euroPrice; - const poolShares = this.getPoolShares(); - const m = this.getMint(); + const mint = this.getMint(); const c = this.getCollateral(); - if (!m || Object.values(c).length == 0) return; - const a = [poolShares, m, ...Object.values(c)]; + if (!mint || Object.values(c).length == 0) return; + + // JUSD is always $1 (stablecoin) - no need to fetch from Coingecko + const mintAddr = mint.address.toLowerCase() as Address; + if (!this.fetchedPrices[mintAddr]) { + this.fetchedPrices[mintAddr] = { + ...mint, + timestamp: Date.now(), + price: { usd: 1 }, + }; + } + + // Only fetch prices for poolShares and collateral tokens + const a = [poolShares, ...Object.values(c)]; const pricesQuery: PriceQueryObjectArray = {}; let pricesQueryNewCount: number = 0; @@ -178,12 +180,12 @@ export class PricesService { pricesQueryNewCount += 1; this.logger.debug(`Price for ${erc.name} not available, trying to fetch...`); const price = await this.fetchPrice(erc); - if (!price) pricesQueryNewCountFailed += 1; + if (!price?.usd) pricesQueryNewCountFailed += 1; pricesQuery[addr] = { ...erc, - timestamp: price === null ? 0 : Date.now(), - price: price === null ? { usd: 1 } : price, + timestamp: price?.usd ? Date.now() : 0, + price: price?.usd ? price : { usd: 1 }, }; } else if (oldEntry.timestamp + 300_000 < Date.now()) { // needs to update => try to fetch @@ -191,7 +193,7 @@ export class PricesService { this.logger.debug(`Price for ${erc.name} out of date, trying to fetch...`); const price = await this.fetchPrice(erc); - if (!price) { + if (!price?.usd) { pricesQueryUpdateCountFailed += 1; } else { pricesQuery[addr] = { @@ -201,17 +203,6 @@ export class PricesService { }; } } - - const protocolStablecoinPrice: number = - this.euroPrice?.usd || this.fetchedPrices[ADDRESS[VIEM_CHAIN.id].juiceDollar.toLowerCase()]?.price?.usd; - - if (protocolStablecoinPrice) { - const priceUsd = pricesQuery[addr]?.price?.usd; - const priceEur = pricesQuery[addr]?.price?.eur; - if (priceUsd && !priceEur) { - pricesQuery[addr].price.eur = Math.round((priceUsd / protocolStablecoinPrice) * 100) / 100; - } - } } const updatesCnt = pricesQueryNewCount + pricesQueryUpdateCount; diff --git a/prices/prices.types.ts b/prices/prices.types.ts index 90e2a36..94feaec 100644 --- a/prices/prices.types.ts +++ b/prices/prices.types.ts @@ -13,10 +13,8 @@ export type ERC20Info = { decimals: number; }; -// TODO: Implement other currencies export type PriceQueryCurrencies = { usd?: number; - eur?: number; btc?: number; }; diff --git a/socialmedia/telegram/messages/Help.message.ts b/socialmedia/telegram/messages/Help.message.ts index 7b85f64..7745691 100644 --- a/socialmedia/telegram/messages/Help.message.ts +++ b/socialmedia/telegram/messages/Help.message.ts @@ -5,9 +5,9 @@ export function HelpMessage(groups: string[], group: string, handles: string[]): const isSubscribed = groups.includes(group); return ` -*Welcome to the d-EURO API Bot* +*Welcome to the JuiceDollar API Bot* -I am listening to changes within the d-EURO ecosystem. +I am listening to changes within the JuiceDollar ecosystem. *Available commands:* ${handles.join('\n')} @@ -21,6 +21,6 @@ Chain/Network: ${CONFIG.chain.name} (${CONFIG.chain.id}) Time: ${new Date().toString().split(' ').slice(0, 5).join(' ')} [Goto App](${AppUrl('')}) -[Github Api](https://github.com/d-EURO/api) +[Github Api](https://github.com/JuiceDollar/api) `; } diff --git a/socialmedia/telegram/messages/StartUp.message.ts b/socialmedia/telegram/messages/StartUp.message.ts index 21b56ed..ef295b4 100644 --- a/socialmedia/telegram/messages/StartUp.message.ts +++ b/socialmedia/telegram/messages/StartUp.message.ts @@ -3,9 +3,9 @@ import { AppUrl } from 'utils/func-helper'; export function StartUpMessage(handles: string[]): string { return ` -*Hello again, from the d-EURO API Bot!* +*Hello again, from the JuiceDollar API Bot!* -I have updated and restarted and am back online, listening to changes within the d-EURO ecosystem. +I have updated and restarted and am back online, listening to changes within the JuiceDollar ecosystem. *Available subscription handles:* ${handles.join('\n')} @@ -16,6 +16,6 @@ Chain/Network: ${CONFIG.chain.name} (${CONFIG.chain.id}) Time: ${new Date().toString().split(' ').slice(0, 5).join(' ')} [Goto App](${AppUrl('')}) -[Github Api](https://github.com/d-EURO/api) +[Github Api](https://github.com/JuiceDollar/api) `; } From 64b2fcb791c45d52e05835c4ae92fa81e9cee9c8 Mon Sep 17 00:00:00 2001 From: bernd2022 <104787072+bernd2022@users.noreply.github.com> Date: Fri, 23 Jan 2026 20:42:18 +0100 Subject: [PATCH 4/6] Add dual container app deployment support (#15) Deploy to both jdta and jdma container apps in DEV and PRD workflows. --- .github/workflows/api-dev.yaml | 13 ++++++++++--- .github/workflows/api-prd.yaml | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/api-dev.yaml b/.github/workflows/api-dev.yaml index ab54b87..83d2736 100644 --- a/.github/workflows/api-dev.yaml +++ b/.github/workflows/api-dev.yaml @@ -8,7 +8,8 @@ on: env: DOCKER_TAGS: dfxswiss/juicedollar-api:beta AZURE_RESOURCE_GROUP: rg-dfx-api-dev - AZURE_CONTAINER_APP: ca-dfx-jdta-dev + AZURE_CONTAINER_APP_1: ca-dfx-jdta-dev + AZURE_CONTAINER_APP_2: ca-dfx-jdma-dev DEPLOY_INFO: ${{ github.ref_name }}-${{ github.sha }} jobs: @@ -43,11 +44,17 @@ jobs: with: creds: ${{ secrets.DFX_DEV_CREDENTIALS }} - - name: Update Azure Container App + - name: Update Azure Container App 1 uses: azure/CLI@v2 with: inlineScript: | - az containerapp update --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --name ${{ env.AZURE_CONTAINER_APP }} --image ${{ env.DOCKER_TAGS }} --set-env-vars DEPLOY_INFO=${{ env.DEPLOY_INFO }} + az containerapp update --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --name ${{ env.AZURE_CONTAINER_APP_1 }} --image ${{ env.DOCKER_TAGS }} --set-env-vars DEPLOY_INFO=${{ env.DEPLOY_INFO }} + + - name: Update Azure Container App 2 + uses: azure/CLI@v2 + with: + inlineScript: | + az containerapp update --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --name ${{ env.AZURE_CONTAINER_APP_2 }} --image ${{ env.DOCKER_TAGS }} --set-env-vars DEPLOY_INFO=${{ env.DEPLOY_INFO }} - name: Logout from Azure run: | diff --git a/.github/workflows/api-prd.yaml b/.github/workflows/api-prd.yaml index 92df1d3..a07684e 100644 --- a/.github/workflows/api-prd.yaml +++ b/.github/workflows/api-prd.yaml @@ -8,7 +8,8 @@ on: env: DOCKER_TAGS: dfxswiss/juicedollar-api:latest AZURE_RESOURCE_GROUP: rg-dfx-api-prd - AZURE_CONTAINER_APP: ca-dfx-jdta-prd + AZURE_CONTAINER_APP_1: ca-dfx-jdta-prd + AZURE_CONTAINER_APP_2: ca-dfx-jdma-prd DEPLOY_INFO: ${{ github.ref_name }}-${{ github.sha }} jobs: @@ -43,11 +44,17 @@ jobs: with: creds: ${{ secrets.DFX_PRD_CREDENTIALS }} - - name: Update Azure Container App + - name: Update Azure Container App 1 uses: azure/CLI@v2 with: inlineScript: | - az containerapp update --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --name ${{ env.AZURE_CONTAINER_APP }} --image ${{ env.DOCKER_TAGS }} --set-env-vars DEPLOY_INFO=${{ env.DEPLOY_INFO }} + az containerapp update --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --name ${{ env.AZURE_CONTAINER_APP_1 }} --image ${{ env.DOCKER_TAGS }} --set-env-vars DEPLOY_INFO=${{ env.DEPLOY_INFO }} + + - name: Update Azure Container App 2 + uses: azure/CLI@v2 + with: + inlineScript: | + az containerapp update --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --name ${{ env.AZURE_CONTAINER_APP_2 }} --image ${{ env.DOCKER_TAGS }} --set-env-vars DEPLOY_INFO=${{ env.DEPLOY_INFO }} - name: Logout from Azure run: | From 1bf547254a8bfdb8abe0f49e8b1e0baed8ae6c8d Mon Sep 17 00:00:00 2001 From: David May Date: Sat, 24 Jan 2026 13:23:00 +0100 Subject: [PATCH 5/6] feat: mainnet support --- .env.example | 3 ++- api.config.ts | 12 +++++++----- chains.ts | 7 +++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index f851d1d..6214cc1 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,8 @@ CONFIG_INDEXER_URL=https://dev.ponder.testnet.juicedollar.com/ CONFIG_INDEXER_FALLBACK_URL=https://dev.ponder.testnet.juicedollar.com/ CONFIG_CHAIN=testnet -RPC_URL_MAINNET=https://rpc.testnet.juiceswap.com/ +RPC_URL_MAINNET=https://rpc.citrea.xyz +RPC_URL_TESTNET=https://rpc.testnet.citrea.xyz COINGECKO_API_KEY=[API-KEY] diff --git a/api.config.ts b/api.config.ts index 379793b..1eb8538 100644 --- a/api.config.ts +++ b/api.config.ts @@ -1,12 +1,14 @@ import { Chain, createPublicClient, http } from 'viem'; import { Logger } from '@nestjs/common'; -import { testnet } from 'chains'; +import { mainnet, testnet } from 'chains'; import * as dotenv from 'dotenv'; dotenv.config(); // Verify environment -if (process.env.RPC_URL_MAINNET === undefined) throw new Error('RPC_URL_MAINNET not available'); +const isMainnet = process.env.CONFIG_CHAIN === 'mainnet'; +if (isMainnet && process.env.RPC_URL_MAINNET === undefined) throw new Error('RPC_URL_MAINNET not available'); +if (!isMainnet && process.env.RPC_URL_TESTNET === undefined) throw new Error('RPC_URL_TESTNET not available'); if (process.env.COINGECKO_API_KEY === undefined) throw new Error('COINGECKO_API_KEY not available'); // Config type @@ -40,10 +42,10 @@ export const CONFIG: ConfigType = { indexer: process.env.CONFIG_INDEXER_URL, indexerFallback: process.env.CONFIG_INDEXER_FALLBACK_URL, coingeckoApiKey: process.env.COINGECKO_API_KEY, - chain: testnet, + chain: isMainnet ? mainnet : testnet, network: { mainnet: process.env.RPC_URL_MAINNET, - testnet: process.env.RPC_URL_MAINNET, + testnet: process.env.RPC_URL_TESTNET, }, telegram: { botToken: process.env.TELEGRAM_BOT_TOKEN, @@ -72,7 +74,7 @@ process.env.NTBA_FIX_350 = 'true'; export const VIEM_CHAIN = CONFIG.chain; export const VIEM_CONFIG = createPublicClient({ chain: VIEM_CHAIN, - transport: http(CONFIG.network.mainnet), + transport: http(isMainnet ? CONFIG.network.mainnet : CONFIG.network.testnet), batch: { multicall: { wait: 200, diff --git a/chains.ts b/chains.ts index 7b80f2c..6274eec 100644 --- a/chains.ts +++ b/chains.ts @@ -12,15 +12,14 @@ export const testnet = defineChain({ }, }); -// Juice Mainnet - To define later, same as testnet for now export const mainnet = defineChain({ - id: 62831, + id: 4114, name: 'Mainnet', nativeCurrency: { name: 'cBTC', symbol: 'cBTC', decimals: 18 }, rpcUrls: { - default: { http: ['https://rpc.testnet.citrea.xyz'] }, + default: { http: ['https://rpc.citrea.xyz'] }, }, blockExplorers: { - default: { name: 'Juice Explorer', url: 'https://explorer.testnet.citrea.xyz' }, + default: { name: 'Citrea Explorer', url: 'https://explorer.citrea.xyz' }, }, }); From 0a8d0f90682b9c95bf19a94af17424047360fe61 Mon Sep 17 00:00:00 2001 From: Danswar <48102227+Danswar@users.noreply.github.com> Date: Sun, 25 Jan 2026 09:24:58 -0300 Subject: [PATCH 6/6] Version 1.1.0 (#16) --- package.json | 4 ++-- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index daa6567..1372aa4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@juicedollar/api", - "version": "0.0.1", + "version": "1.1.0", "private": false, "license": "MIT", "homepage": "https://api.juicedollar.com", @@ -32,7 +32,7 @@ }, "dependencies": { "@apollo/client": "^3.10.5", - "@juicedollar/jusd": "1.0.6", + "@juicedollar/jusd": "^1.1.0", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", diff --git a/yarn.lock b/yarn.lock index bbd417a..3b3cddb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -902,10 +902,10 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@juicedollar/jusd@1.0.6": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@juicedollar/jusd/-/jusd-1.0.6.tgz#5e28a415a71b1abd77d1c278ae2816b95fc72bb3" - integrity sha512-5fp4n5rAyq/pw6rEbCWEdJ0U5v50NHh/cR63gG+Km8aNj8GO1wxG53/cNuXku/45TWGGFsboDci18C4X8tdEOg== +"@juicedollar/jusd@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@juicedollar/jusd/-/jusd-1.1.0.tgz#0f85fd081873ebb90854bd76bb29aeca1272239e" + integrity sha512-+rMzg1bFgtUJRloEUFJDWBbb+0AUNOFwKS2Qc7JMP14Ngu1VhvAs00pBqC1O+OjQu7cc1twU7VS7W1XhVnYdMw== dependencies: "@openzeppelin/contracts" "^5.1.0" hardhat-abi-exporter "^2.10.0"