From a7011a7f189258fd4b84a41e21dbf480c3130bd2 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:43:17 +0100 Subject: [PATCH 1/2] Fix stale BTC price in /prices endpoints (#72) The euroPrice cache (containing USD, EUR and BTC rates from CoinGecko) had no TTL and only refreshed when blockchain blocks were detected. If block detection stalled, the BTC rate could become hours stale, causing significant mispricing on BTC-denominated trading pairs. Add a 60-second TTL to euroPrice so it self-refreshes on endpoint access regardless of block detection. Also fix the truthy check on empty objects ({}) that prevented initial fetches. --- prices/prices.service.ts | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/prices/prices.service.ts b/prices/prices.service.ts index cb17069..fbe1c05 100644 --- a/prices/prices.service.ts +++ b/prices/prices.service.ts @@ -28,13 +28,30 @@ export class PricesService { private readonly logger = new Logger(this.constructor.name); private fetchedPrices: PriceQueryObjectArray = {}; private euroPrice: PriceQueryCurrencies = {}; + private euroPriceTimestamp: number = 0; private depsPrice: PriceQueryCurrencies = {}; + private static readonly EURO_PRICE_TTL = 60_000; // 60 seconds + constructor( private readonly positionsService: PositionsService, private readonly deps: EcosystemDepsService ) {} + private isEuroPriceStale(): boolean { + return !this.euroPrice?.usd || Date.now() - this.euroPriceTimestamp > PricesService.EURO_PRICE_TTL; + } + + private async refreshEuroPriceIfStale(): Promise { + if (this.isEuroPriceStale()) { + const fetched = await this.fetchEuroPrice(); + if (fetched) { + this.euroPrice = fetched; + this.euroPriceTimestamp = Date.now(); + } + } + } + getPrices(): ApiPriceListing { return Object.values(this.fetchedPrices); } @@ -64,8 +81,8 @@ export class PricesService { } async getDepsPrice(): Promise { - if (!this.depsPrice) this.depsPrice = await this.fetchFromEcosystemDeps(this.getDeps()); - if (!this.euroPrice) this.euroPrice = await this.fetchEuroPrice(); + if (!this.depsPrice?.usd) this.depsPrice = await this.fetchFromEcosystemDeps(this.getDeps()); + await this.refreshEuroPriceIfStale(); return { usd: Number(this.depsPrice.usd.toFixed(4)), @@ -91,7 +108,7 @@ export class PricesService { } async getEuroPrice(): Promise { - if (!this.euroPrice) this.euroPrice = await this.fetchEuroPrice(); + await this.refreshEuroPriceIfStale(); return { usd: Number(this.euroPrice.usd.toFixed(4)), @@ -205,7 +222,10 @@ export class PricesService { this.logger.debug('Updating Prices'); const euroPrice = await this.fetchEuroPrice(); - if (euroPrice) this.euroPrice = euroPrice; + if (euroPrice) { + this.euroPrice = euroPrice; + this.euroPriceTimestamp = Date.now(); + } const deps = this.getDeps(); const m = this.getMint(); From cd901a1de7b47fb1671dac35a01aa4dd484182d6 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:43:28 +0100 Subject: [PATCH 2/2] Add auto-release PR workflow (#71) --- .github/workflows/auto-release-pr.yaml | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/auto-release-pr.yaml diff --git a/.github/workflows/auto-release-pr.yaml b/.github/workflows/auto-release-pr.yaml new file mode 100644 index 0000000..cbb705b --- /dev/null +++ b/.github/workflows/auto-release-pr.yaml @@ -0,0 +1,70 @@ +name: Auto Release PR + +on: + push: + branches: [develop] + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + +concurrency: + group: auto-release-pr + cancel-in-progress: false + +jobs: + create-release-pr: + name: Create Release PR + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fetch main branch + run: git fetch origin main + + - name: Check for existing PR + id: check-pr + run: | + PR_COUNT=$(gh pr list --base main --head develop --state open --json number --jq 'length') + echo "pr_exists=$([[ $PR_COUNT -gt 0 ]] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT + echo "::notice::Open PRs from develop to main: $PR_COUNT" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check for differences + id: check-diff + if: steps.check-pr.outputs.pr_exists == 'false' + run: | + DIFF_COUNT=$(git rev-list --count origin/main..origin/develop) + echo "has_changes=$([[ $DIFF_COUNT -gt 0 ]] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT + echo "commit_count=$DIFF_COUNT" >> $GITHUB_OUTPUT + echo "::notice::Commits ahead of main: $DIFF_COUNT" + + - name: Create Release PR + if: steps.check-pr.outputs.pr_exists == 'false' && steps.check-diff.outputs.has_changes == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMMIT_COUNT: ${{ steps.check-diff.outputs.commit_count }} + run: | + printf '%s\n' \ + "## Automatic Release PR" \ + "" \ + "This PR was automatically created after changes were pushed to develop." \ + "" \ + "**Commits:** ${COMMIT_COUNT} new commit(s)" \ + "" \ + "### Checklist" \ + "- [ ] Review all changes" \ + "- [ ] Verify CI passes" \ + "- [ ] Approve and merge when ready for production" \ + > /tmp/pr-body.md + + gh pr create \ + --base main \ + --head develop \ + --title "Release: develop -> main" \ + --body-file /tmp/pr-body.md