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
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A clear description of what you expected `lock()`, `close()`, or `yieldForAmount
**Minimal reproduction**

```js
import { StakingService, InMemoryBalanceProvider, EXAMPLE_TIERS } from '@flashylabs/wdk-staking-kit'
import { StakingService, InMemoryBalanceProvider, EXAMPLE_TIERS } from '@flashyfinancial/wdk-staking-kit'

const provider = new InMemoryBalanceProvider({ alice: '1000' })
const staking = new StakingService({ provider, tiers: EXAMPLE_TIERS })
Expand All @@ -25,7 +25,7 @@ The smaller this is, the faster it gets fixed.

**Package version**

Output of `npm ls @flashylabs/wdk-staking-kit`.
Output of `npm ls @flashyfinancial/wdk-staking-kit`.

**Environment**

Expand Down
52 changes: 52 additions & 0 deletions .github/workflows/deprecate-old-scope.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Deprecate old npm scope

# One-off, human-triggered. The package moved from @flashylabs/wdk-staking-kit
# to @flashyfinancial/wdk-staking-kit (finance packages move to the
# FlashyFinancial scope). This workflow runs `npm deprecate` against every
# version ever published under the old scope, pointing consumers at the new
# name — it does not touch the new scope, and it never runs on its own
# schedule. A human with the npm token triggers it, once, after confirming the
# new scope is actually live on the registry.
#
# Deliberately workflow_dispatch only:
# - no `push` trigger — this must never fire as a side effect of a commit
# - no `schedule` — deprecating is a one-time announcement, not a recurring job
#
# Uses the same NPM_TOKEN secret the publish workflow already uses. This
# workflow does not create, read, or need any other credential.

on:
workflow_dispatch:

permissions:
contents: read

jobs:
deprecate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 20
registry-url: https://registry.npmjs.org

- name: Verify the npm token can publish before doing anything else
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_FLASHYOS_CI || secrets.NPM_TOKEN }}
run: |
if [ -z "$NODE_AUTH_TOKEN" ]; then
echo "::error::No npm token found. Add one with publish rights on the old @flashylabs scope as a repo secret named NPM_TOKEN_FLASHYOS_CI or NPM_TOKEN."
exit 1
fi
if ! WHO=$(npm whoami 2>&1); then
echo "::error::The npm token is set but not valid for this scope — npm said: ${WHO}. Confirm this token has publish rights on the @flashylabs scope."
exit 1
fi
echo "Authenticated to npm as ${WHO}"

- name: Deprecate every version of the old scope's package
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_FLASHYOS_CI || secrets.NPM_TOKEN }}
run: npm deprecate @flashylabs/wdk-staking-kit@* "moved to @flashyfinancial/wdk-staking-kit"
12 changes: 6 additions & 6 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Publish to npm

# Publishes @flashylabs/wdk-staking-kit when its version changes on main.
# Publishes @flashyfinancial/wdk-staking-kit when its version changes on main.
# A version already on the registry is skipped rather than failing on
# "cannot publish over the previously published version" — makes a re-run
# idempotent.
Expand Down Expand Up @@ -38,11 +38,11 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_FLASHYOS_CI || secrets.NPM_TOKEN }}
run: |
if [ -z "$NODE_AUTH_TOKEN" ]; then
echo "::error::No npm token found. Add one with publish rights on the @flashylabs scope as a repo secret named NPM_TOKEN_FLASHYOS_CI or NPM_TOKEN."
echo "::error::No npm token found. Add one with publish rights on the @flashyfinancial scope as a repo secret named NPM_TOKEN_FLASHYOS_CI or NPM_TOKEN."
exit 1
fi
if ! WHO=$(npm whoami 2>&1); then
echo "::error::The npm token is set but not valid for this scope — npm said: ${WHO}. Confirm this token has publish rights on the @flashylabs scope."
echo "::error::The npm token is set but not valid for this scope — npm said: ${WHO}. Confirm this token has publish rights on the @flashyfinancial scope."
exit 1
fi
echo "Authenticated to npm as ${WHO}"
Expand All @@ -51,12 +51,12 @@ jobs:
id: check
run: |
V=$(node -p "require('./package.json').version")
if [ -n "$(npm view "@flashylabs/wdk-staking-kit@$V" version 2>/dev/null || true)" ]; then
if [ -n "$(npm view "@flashyfinancial/wdk-staking-kit@$V" version 2>/dev/null || true)" ]; then
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "::notice::@flashylabs/wdk-staking-kit@$V is already published."
echo "::notice::@flashyfinancial/wdk-staking-kit@$V is already published."
else
echo "publish=true" >> "$GITHUB_OUTPUT"
echo "::notice::@flashylabs/wdk-staking-kit@$V will be published."
echo "::notice::@flashyfinancial/wdk-staking-kit@$V will be published."
fi

- name: Test
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ All notable changes to this project are documented here. Format loosely follows

The published version is the one in [`package.json`](package.json); the topmost entry below must match it (a test pins this — see `test/doc-pins.test.mjs`), so the changelog can never quietly fall behind the package again.

## Unreleased
## 0.3.0 — 2026-10-30

**Changed (breaking):** the npm scope moved from `@flashylabs/wdk-staking-kit` to `@flashyfinancial/wdk-staking-kit` — this estate's brand-scoped reorg moves finance packages to the FlashyFinancial scope, the same move `@flashylabs/ledger` made to `@flashyos/ledger` at `1.0.1`. The package's name, every import and install instruction in the README and issue templates, the published manifest's `name`/`module.package` fields, and the publish workflow's scope now read `@flashyfinancial`. Nothing about the package's behaviour, its exports, or its GitHub repository location changed — `package.json`'s `repository`, `homepage` and `bugs` fields still name `github.com/FlashyLabs/wdk-staking-kit`, because this is a scope rename, not a repository move. A one-off, `workflow_dispatch`-only workflow, `.github/workflows/deprecate-old-scope.yml`, runs `npm deprecate @flashylabs/wdk-staking-kit@* "moved to @flashyfinancial/wdk-staking-kit"` against the old scope once a human with the npm token triggers it — it is not run automatically and was not run as part of this change. A consumer on `@flashylabs/wdk-staking-kit` must update their import and their `package.json` dependency; nothing on the old scope will receive this or any future release.

**Fixed:** two `close()` calls for one matured position started together both fulfilled and `stake.closed` fired twice — the status check and the write sat either side of the provider's `credit()` await, the same shape as the lock defect fixed at 0.2.0. Only the provider's idempotency key on `staking-yield:${positionId}` prevented a second payment. `close()` now runs through the same per-holder chain as `lock()`, so exactly one close succeeds and the rest are refused `AlreadyClosedError`. Found by the harness below on the day it was wired, on the code that had just fixed the other three.

Expand Down
2 changes: 1 addition & 1 deletion INVARIANTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Invariants — `@flashylabs/wdk-staking-kit`
# Invariants — `@flashyfinancial/wdk-staking-kit`

What this component guarantees about money, each enforced by a named piece of
code and proved by a named test. The document is itself checked:
Expand Down
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# @flashylabs/wdk-staking-kit
# @flashyfinancial/wdk-staking-kit

```
██
Expand All @@ -14,8 +14,8 @@ A reference staking primitive for wallets built on [Tether's WDK](https://github
[![tests](https://github.com/FlashyLabs/wdk-staking-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/FlashyLabs/wdk-staking-kit/actions/workflows/ci.yml)
[![CodeQL](https://github.com/FlashyLabs/wdk-staking-kit/actions/workflows/codeql.yml/badge.svg)](https://github.com/FlashyLabs/wdk-staking-kit/actions/workflows/codeql.yml)
[![Scorecard](https://github.com/FlashyLabs/wdk-staking-kit/actions/workflows/scorecard.yml/badge.svg)](https://github.com/FlashyLabs/wdk-staking-kit/security/code-scanning)
[![npm version](https://img.shields.io/npm/v/@flashylabs/wdk-staking-kit.svg)](https://www.npmjs.com/package/@flashylabs/wdk-staking-kit)
[![npm downloads](https://img.shields.io/npm/dm/@flashylabs/wdk-staking-kit.svg)](https://www.npmjs.com/package/@flashylabs/wdk-staking-kit)
[![npm version](https://img.shields.io/npm/v/@flashyfinancial/wdk-staking-kit.svg)](https://www.npmjs.com/package/@flashyfinancial/wdk-staking-kit)
[![npm downloads](https://img.shields.io/npm/dm/@flashyfinancial/wdk-staking-kit.svg)](https://www.npmjs.com/package/@flashyfinancial/wdk-staking-kit)
[![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)
[![node](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org)

Expand All @@ -30,13 +30,13 @@ As of this package's first release, no WDK module offers staking — every walle
## Install

```bash
npm install @flashylabs/wdk-staking-kit
npm install @flashyfinancial/wdk-staking-kit
```

## Quickstart

```js
import { StakingService, InMemoryBalanceProvider, EXAMPLE_TIERS } from '@flashylabs/wdk-staking-kit'
import { StakingService, InMemoryBalanceProvider, EXAMPLE_TIERS } from '@flashyfinancial/wdk-staking-kit'

const provider = new InMemoryBalanceProvider({ alice: '100' })
const staking = new StakingService({ provider, tiers: EXAMPLE_TIERS })
Expand All @@ -63,7 +63,7 @@ const closed = await staking.close(position.id)
`EXAMPLE_TIERS` is exactly that — an example. Publish your own:

```js
import { publishedTerms, checkTerms } from '@flashylabs/wdk-staking-kit'
import { publishedTerms, checkTerms } from '@flashyfinancial/wdk-staking-kit'

const MY_TIERS = [
{ id: 'flex-7', termDays: 7, aprBasisPoints: 150, minAmount: '10' },
Expand Down Expand Up @@ -131,12 +131,14 @@ const unsubscribe = staking.on('stake.closed', ({ position }) => { /* ... */ })

## Status

Pre-1.0 (`0.2.0`). The tier and position shapes are not yet frozen. Watch [`CHANGELOG.md`](CHANGELOG.md) across a version bump before pinning a wider range than `^0.2.0`.
Pre-1.0 (`0.3.0`). The tier and position shapes are not yet frozen. Watch [`CHANGELOG.md`](CHANGELOG.md) across a version bump before pinning a wider range than `^0.3.0`.

**The npm scope changed at `0.3.0`.** This package now publishes as `@flashyfinancial/wdk-staking-kit` — finance packages in this estate moved to the FlashyFinancial scope. `@flashylabs/wdk-staking-kit` will receive no further releases and is deprecated on the registry once a human with publish rights runs `deprecate-old-scope.yml`; update your import and your `package.json` dependency to the new name.

## Testing

```bash
npm test # 90 tests, node's built-in test runner, no external services
npm test # 102 tests, node's built-in test runner, no external services
npm run check # confirms the generated manifest is current
npm run invariants # INVARIANTS.md holds against the suite (invariants/1)
```
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@flashylabs/wdk-staking-kit",
"version": "0.2.0",
"name": "@flashyfinancial/wdk-staking-kit",
"version": "0.3.0",
"description": "A reference staking primitive for wallets built on Tether's WDK (or any wallet SDK): lock a balance for a fixed term at a published rate, without touching an on-chain contract or a ledger's internals \u2014 a lock earmarks a balance a provider already credits, and only yield is a real write, on close. No first-party WDK staking module exists; this is a generalized reference implementation, open for anyone to adopt or adapt.",
"type": "module",
"main": "./src/index.js",
Expand All @@ -12,7 +12,7 @@
}
},
"scripts": {
"test": "node --test test/balance-provider.test.mjs test/charter-conformance.test.mjs test/doc-pins.test.mjs test/invariants.test.mjs test/manifest.test.mjs test/mesh-refs.test.mjs test/staking.test.mjs test/strike.test.mjs test/terms.test.mjs test/test-script.test.mjs test/vendor-invariants.test.mjs test/workflow-pins.test.mjs",
"test": "node --test test/balance-provider.test.mjs test/charter-conformance.test.mjs test/doc-pins.test.mjs test/invariants.test.mjs test/manifest.test.mjs test/mesh-refs.test.mjs test/scope.test.mjs test/staking.test.mjs test/strike.test.mjs test/terms.test.mjs test/test-script.test.mjs test/vendor-invariants.test.mjs test/workflow-pins.test.mjs",
"manifest": "node src/manifest.js --write",
"check": "node src/manifest.js --check",
"invariants": "node vendor-invariants.mjs check .",
Expand All @@ -33,6 +33,10 @@
],
"author": "Flashy Labs",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"type": "git",
"url": "git+https://github.com/FlashyLabs/wdk-staking-kit.git"
Expand Down
2 changes: 1 addition & 1 deletion src/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function build() {
const terms = publishedTerms({ tiers: EXAMPLE_TIERS, unit: 'example-unit', version: '0.1.0' })
return {
contract: 'wdk-staking-kit-manifest/1',
name: '@flashylabs/wdk-staking-kit',
name: '@flashyfinancial/wdk-staking-kit',
module: { package: pkg.name, version: pkg.version },
posture: {
stage: 'balance-provider-first',
Expand Down
2 changes: 1 addition & 1 deletion test-types/consumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import {
StakingService, InMemoryBalanceProvider, EXAMPLE_TIERS, tierById, yieldForAmount, checkTerms,
type Tier, type BalanceProvider,
} from '@flashylabs/wdk-staking-kit'
} from '@flashyfinancial/wdk-staking-kit'

// A plain object satisfying the BalanceProvider interface, not an instance
// of InMemoryBalanceProvider — this is the exact case the interface exists
Expand Down
2 changes: 1 addition & 1 deletion test-types/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@flashylabs/wdk-staking-kit": ["../dist/index.d.ts"]
"@flashyfinancial/wdk-staking-kit": ["../dist/index.d.ts"]
}
},
"include": ["consumer.ts"]
Expand Down
2 changes: 1 addition & 1 deletion test/manifest.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ test('build(): matches the committed wdk-staking-kit.manifest.json — run `npm
test('build(): contract id and module name', () => {
const doc = build()
assert.equal(doc.contract, 'wdk-staking-kit-manifest/1')
assert.equal(doc.module.package, '@flashylabs/wdk-staking-kit')
assert.equal(doc.module.package, '@flashyfinancial/wdk-staking-kit')
})

test('build(): the embedded example terms pass their own offline verifier', () => {
Expand Down
Loading
Loading