Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 18 additions & 22 deletions .github/workflows/api-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,13 @@ on:
workflow_dispatch:

env:
DOCKER_TAGS: dfxswiss/deuro-api:beta
AZURE_RESOURCE_GROUP: rg-dfx-api-dev
AZURE_CONTAINER_APP: ca-dfx-dea-dev
DEPLOY_INFO: ${{ github.ref_name }}-${{ github.sha }}
DOCKER_TAGS: deurocom/api:beta
DEPLOY_SERVICE: deuro-api

jobs:
build-and-deploy:
name: Build, test and deploy to DEV
runs-on: ubuntu-latest
defaults:
run:
working-directory: .
name: Build and deploy to DEV
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -37,19 +32,20 @@ jobs:
context: .
push: true
tags: ${{ env.DOCKER_TAGS }}
platforms: linux/arm64

- name: Log in to Azure
uses: azure/login@v2
with:
creds: ${{ secrets.DFX_DEV_CREDENTIALS }}

- name: Update Azure Container App
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 }}
- name: Install cloudflared
run: |
curl -fsSL https://github.com/cloudflare/cloudflared/releases/download/2025.4.0/cloudflared-linux-arm64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared

- name: Logout from Azure
- name: Deploy to server
run: |
az logout
if: always()
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_DEV_SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
echo "${{ secrets.DEPLOY_DEV_SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
ssh -i ~/.ssh/deploy_key \
-o ProxyCommand="cloudflared access ssh --hostname ${{ secrets.DEPLOY_DEV_HOST }}" \
${{ secrets.DEPLOY_DEV_USER }}@${{ secrets.DEPLOY_DEV_HOST }} \
"${{ env.DEPLOY_SERVICE }}"
38 changes: 17 additions & 21 deletions .github/workflows/api-prd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,13 @@ on:
workflow_dispatch:

env:
DOCKER_TAGS: dfxswiss/deuro-api:latest
AZURE_RESOURCE_GROUP: rg-dfx-api-prd
AZURE_CONTAINER_APP: ca-dfx-dea-prd
DEPLOY_INFO: ${{ github.ref_name }}-${{ github.sha }}
DOCKER_TAGS: deurocom/api:latest
DEPLOY_SERVICE: deuro-api

jobs:
build-and-deploy:
name: Build, test and deploy to PRD
runs-on: ubuntu-latest
defaults:
run:
working-directory: .
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -37,19 +32,20 @@ jobs:
context: .
push: true
tags: ${{ env.DOCKER_TAGS }}
platforms: linux/arm64

- name: Log in to Azure
uses: azure/login@v2
with:
creds: ${{ secrets.DFX_PRD_CREDENTIALS }}

- name: Update Azure Container App
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 }}
- name: Install cloudflared
run: |
curl -fsSL https://github.com/cloudflare/cloudflared/releases/download/2025.4.0/cloudflared-linux-arm64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared

- name: Logout from Azure
- name: Deploy to server
run: |
az logout
if: always()
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_PRD_SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
echo "${{ secrets.DEPLOY_PRD_SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
ssh -i ~/.ssh/deploy_key \
-o ProxyCommand="cloudflared access ssh --hostname ${{ secrets.DEPLOY_PRD_HOST }}" \
${{ secrets.DEPLOY_PRD_USER }}@${{ secrets.DEPLOY_PRD_HOST }} \
"${{ env.DEPLOY_SERVICE }}"
4 changes: 3 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ node_modules

.env
.env.local
.env.**.local
.env.**.local

.claude/
3 changes: 2 additions & 1 deletion analytics/analytics.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export class AnalyticsService {
const equityAdjusted: number = expo.general.equityInReserve;
const otherContributions: number =
equityAdjusted - minterProposalFees - investFees - redeemFees - positionProposalFees - otherProfitClaims;
const savingsInfo = this.save.getInfo();

return {
minterProposalFees,
Expand All @@ -162,7 +163,7 @@ export class AnalyticsService {
otherProfitClaims,
otherContributions,

savingsInterestCosts: this.save.getInfo().totalInterest,
savingsInterestCosts: savingsInfo.totalInterest,
otherLossClaims: this.deps.getEcosystemDepsInfo().earnings.loss,
};
}
Expand Down
14 changes: 13 additions & 1 deletion api.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Chain, createPublicClient, http } from 'viem';
import { Address, Chain, createPublicClient, http, zeroAddress } from 'viem';
import { ADDRESS } from '@deuro/eurocoin';
import { mainnet, polygon } from 'viem/chains';

import { Logger } from '@nestjs/common';
Expand Down Expand Up @@ -89,3 +90,14 @@ export const COINGECKO_CLIENT = (query: string) => {
const uri: string = `https://pro-api.coingecko.com${query}`;
return fetch(`${uri}${hasParams ? '&' : '?'}x_cg_pro_api_key=${CONFIG.coingeckoApiKey}`);
};

// Contract addresses for the active chain
export const ADDR = ADDRESS[CONFIG.chain.id];

export function isDeployed(addr: string | undefined): addr is Address {
return !!addr && addr !== zeroAddress;
}

export function isV3Hub(hubAddress: Address): boolean {
return isDeployed(ADDR.mintingHub) && hubAddress.toLowerCase() === ADDR.mintingHub.toLowerCase();
}
1 change: 1 addition & 0 deletions api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export class ApiService {
await timeTask('updateBidV2s', () => this.challenges.updateBidV2s()).catch(() => {}),
await timeTask('updateChallengesPrices', () => this.challenges.updateChallengesPrices()).catch(() => {}),
await timeTask('updateSavingsUserLeaderboard', () => this.savings.updateSavingsUserLeaderboard()).catch(() => {}),
await timeTask('updateSavingsBalances', () => this.savings.updateSavingsBalances()).catch(() => {}),
];

await Promise.all(promises);
Expand Down
30 changes: 19 additions & 11 deletions challenges/challenges.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import {
ChallengesQueryStatus,
} from './challenges.types';
import { Address } from 'viem';
import { ADDRESS, MintingHubGatewayABI } from '@deuro/eurocoin';
import { MintingHubGatewayV2ABI, MintingHubV3ABI } from '@deuro/eurocoin';
import { isV3Hub } from '../api.config';

@Injectable()
export class ChallengesService {
Expand Down Expand Up @@ -178,16 +179,21 @@ export class ChallengesService {

// mapping active challenge -> prices
const challengesPrices: ChallengesPricesMapping = {};
const id = VIEM_CONFIG.chain.id;
for (const c of active) {
const price = await VIEM_CONFIG.readContract({
abi: MintingHubGatewayABI,
address: ADDRESS[id].mintingHubGateway,
functionName: 'price',
args: [parseInt(c.number.toString())],
});
try {
const abi = isV3Hub(c.mintingHubAddress) ? MintingHubV3ABI : MintingHubGatewayV2ABI;

challengesPrices[c.id] = price.toString();
const price = await VIEM_CONFIG.readContract({
abi,
address: c.mintingHubAddress,
functionName: 'price',
args: [BigInt(c.number)],
});

challengesPrices[c.id] = price.toString();
} catch (e) {
this.logger.warn(`Failed to fetch price for challenge ${c.id}: ${e.message}`);
}
}

// upsert
Expand All @@ -205,6 +211,7 @@ export class ChallengesService {
items {
id
position
mintingHubAddress
number
challenger
start
Expand Down Expand Up @@ -232,7 +239,7 @@ export class ChallengesService {
const mapped: ChallengesQueryItemMapping = {};
for (const i of list) {
mapped[i.id] = i;
mapped[i.id].version = 2;
mapped[i.id].version = isV3Hub(i.mintingHubAddress) ? 3 : 2;
}

// upsert
Expand All @@ -250,6 +257,7 @@ export class ChallengesService {
items {
id
position
mintingHubAddress
number
numberBid
bidder
Expand All @@ -276,7 +284,7 @@ export class ChallengesService {
const mapped: BidsQueryItemMapping = {};
for (const i of list) {
mapped[i.id] = i;
mapped[i.id].version = 2;
mapped[i.id].version = isV3Hub(i.mintingHubAddress) ? 3 : 2;
}

// upsert
Expand Down
2 changes: 2 additions & 0 deletions challenges/challenges.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type ChallengesQueryItem = {

id: ChallengesId;
position: Address;
mintingHubAddress: Address;
number: bigint;

challenger: Address;
Expand All @@ -27,6 +28,7 @@ export type BidsQueryItem = {

id: BidsId;
position: Address;
mintingHubAddress: Address;
number: bigint;
numberBid: bigint;

Expand Down
6 changes: 3 additions & 3 deletions ecosystem/ecosystem.deps.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class EcosystemDepsService {
fetchPolicy: 'no-cache',
query: gql`
query GetDEPS {
dEPSs(orderBy: "id", limit: 1000) {
depss(orderBy: "id", limit: 1000) {
items {
id
profits
Expand All @@ -65,12 +65,12 @@ export class EcosystemDepsService {
`,
});

if (!profitLossPonder.data || !profitLossPonder.data.dEPSs.items.length) {
if (!profitLossPonder.data || !profitLossPonder.data.depss.items.length) {
this.logger.warn('No profitLossPonder data found.');
return;
}

const d = profitLossPonder.data.dEPSs.items.at(0);
const d = profitLossPonder.data.depss.items.at(0);
const unrealizedProfit = this.getUnrealizedProfit();
const earningsData: ApiEcosystemDepsInfo['earnings'] = {
profit: parseFloat(formatUnits(d.profits, 18)),
Expand Down
1 change: 1 addition & 0 deletions frontendcode/frontendcode.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class FrontendCodeService {
where: {
created_gt: "${checkTimestamp}"
amount_gte: "${minAmount}"
frontendCode_not: null
}
) {
items {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@deuro/api",
"version": "0.3.3",
"version": "0.3.4",
"private": false,
"license": "MIT",
"homepage": "https://api.deuro.com",
Expand All @@ -24,7 +24,7 @@
},
"dependencies": {
"@apollo/client": "^3.10.5",
"@deuro/eurocoin": "^1.0.13",
"@deuro/eurocoin": "^2.1.0",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.0.0",
Expand Down
23 changes: 15 additions & 8 deletions positions/positions.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { gql } from '@apollo/client/core';
import { ADDRESS, PositionV2ABI, SavingsABI } from '@deuro/eurocoin';
import { PositionV2ABI, SavingsGatewayV2ABI } from '@deuro/eurocoin';
import { Injectable, Logger } from '@nestjs/common';
import { FIVEDAYS_MS } from 'utils/const-helper';
import { Address, erc20Abi, getAddress } from 'viem';
import { CONFIG, VIEM_CONFIG } from '../api.config';
import { ADDR, isV3Hub, VIEM_CONFIG } from '../api.config';
import { PONDER_CLIENT } from '../api.apollo.config';
import {
ApiMintingUpdateListing,
Expand Down Expand Up @@ -119,6 +119,7 @@ export class PositionsService {

fixedAnnualRatePPM
principal
mintingHubAddress
}
}
}
Expand All @@ -136,9 +137,10 @@ export class PositionsService {
const virtualPriceDataPromises: Promise<bigint>[] = [];
const interestPromises: Promise<bigint>[] = [];

const leadrate = await VIEM_CONFIG.readContract({
address: ADDRESS[CONFIG.chain.id].savingsGateway,
abi: SavingsABI,
// V2 leadrate must succeed — failure aborts the update so stale-but-correct data is served
const v2Leadrate = await VIEM_CONFIG.readContract({
address: ADDR.savingsGateway,
abi: SavingsGatewayV2ABI,
functionName: 'currentRatePPM',
});

Expand Down Expand Up @@ -196,13 +198,16 @@ export class PositionsService {
const v = (virtualPriceData[idx] as PromiseFulfilledResult<bigint>).value;
const i = (interestData[idx] as PromiseFulfilledResult<bigint>).value;

const annualInterestPPM = isV3Hub(p.mintingHubAddress) ? p.fixedAnnualRatePPM : v2Leadrate + p.riskPremiumPPM;

const entry: PositionQuery = {
version: 2,
version: isV3Hub(p.mintingHubAddress) ? 3 : 2,

position: getAddress(p.position),
owner: getAddress(p.owner),
deuro: getAddress(p.deuro),
collateral: getAddress(p.collateral),
mintingHubAddress: getAddress(p.mintingHubAddress),
price: p.price,

created: p.created,
Expand All @@ -213,7 +218,7 @@ export class PositionsService {
original: getAddress(p.original),

minimumCollateral: p.minimumCollateral,
annualInterestPPM: leadrate + p.riskPremiumPPM,
annualInterestPPM,
riskPremiumPPM: p.riskPremiumPPM,
reserveContribution: p.reserveContribution,
start: p.start,
Expand Down Expand Up @@ -297,6 +302,7 @@ export class PositionsService {
feeTimeframe
feePPM
feePaid
mintingHubAddress
}
}
}
Expand All @@ -318,7 +324,7 @@ export class PositionsService {
if (list[k] === undefined) list[k] = [];

const entry: MintingUpdateQuery = {
version: 2,
version: isV3Hub(m.mintingHubAddress) ? 3 : 2,

id: m.id,
txHash: m.txHash,
Expand All @@ -343,6 +349,7 @@ export class PositionsService {
feeTimeframe: m.feeTimeframe,
feePPM: m.feePPM,
feePaid: m.feePaid,
mintingHubAddress: getAddress(m.mintingHubAddress),
};

list[k].push(entry);
Expand Down
Loading
Loading