Skip to content

Commit 5d635a8

Browse files
authored
chore: enable 0 amount labels with info warning (#185)
## Description This PR adds a warning label on names whose deposit was sent to the insurance funds, resulting in them having a 0 position in the escrow ## Type - [ ] Bug fix - [ ] Feature - [ ] Breaking change - [ ] Documentation - [x] Chore ## Package - [ ] `@parity/dotns-cli` - [ ] Root/monorepo - [ ] Documentation ## Related Issues ## Fixes ## Checklist ### Code - [x] Follows project style - [x] `bun run lint` passes - [x] `bun run format` passes - [x] `bun run typecheck` passes ### Documentation - [ ] README updated if needed - [ ] Types updated if needed ### Breaking Changes - [ ] No breaking changes - [ ] Breaking changes documented below **Breaking changes:** ## Testing How to test: 1. 2. ## Notes
1 parent c5e4bc0 commit 5d635a8

4 files changed

Lines changed: 45 additions & 16 deletions

File tree

packages/ui/src/components/profile/EscrowTab.vue

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,26 @@
9494
{{ formatWeiAsEther(p.amount) }} PAS
9595
</td>
9696
<td class="px-4 py-2.5">
97-
<span :class="positionStatusClass(p)">{{ positionStatusLabel(p) }}</span>
97+
<span class="inline-flex items-center gap-1">
98+
<span :class="positionStatusClass(p)">{{ positionStatusLabel(p) }}</span>
99+
<span
100+
v-if="isNonRefundable(p)"
101+
:title="NON_REFUNDABLE_HINT"
102+
class="cursor-help text-dot-text-tertiary"
103+
>
104+
<Icon name="Info" size="sm" />
105+
</span>
106+
</span>
98107
</td>
99108
<td class="px-4 py-2.5 text-right">
109+
<span
110+
v-if="isNonRefundable(p) && !p.released"
111+
class="text-xs text-dot-text-tertiary"
112+
>
113+
No refund
114+
</span>
100115
<Button
101-
v-if="!p.released"
116+
v-else-if="!p.released"
102117
size="sm"
103118
variant="secondary"
104119
:disabled="busyId === p.domain"
@@ -250,11 +265,17 @@ import {
250265
isWithdrawable as isWithdrawableAt,
251266
positionStatusLabel as positionStatusLabelAt,
252267
isRefundClaimable as isRefundClaimableAt,
268+
isRefundableDeposit,
253269
cooldownRemainingSeconds,
254270
formatCooldown,
255271
totalEscrowAmount,
256272
} from "@/lib/escrowStatus";
257273
274+
// Shown on names whose escrow position holds no deposit: paid for by another
275+
// account, or registered under a free personhood tier. There is no bond to reclaim.
276+
const NON_REFUNDABLE_HINT =
277+
"This name holds no refundable deposit. It was either paid for by another account or registered under a free personhood tier, so there is no bond for you to reclaim.";
278+
258279
const wallet = useWalletStore();
259280
const escrow = useEscrowStore();
260281
const userStore = useUserStoreManager();
@@ -298,10 +319,14 @@ function cooldownText(position: EscrowPosition): string {
298319
return formatCooldown(cooldownRemainingSeconds(position, now.value));
299320
}
300321
322+
function isNonRefundable(position: EscrowPosition): boolean {
323+
return !isRefundableDeposit(position);
324+
}
325+
301326
function positionStatusClass(position: EscrowPosition): string {
302327
const label = positionStatusLabel(position);
303328
if (label === "Withdrawable") return "text-success font-medium";
304-
if (label === "Cooldown") return "text-dot-text-tertiary";
329+
if (label === "Cooldown" || label === "Not refundable") return "text-dot-text-tertiary";
305330
return "text-dot-text-secondary";
306331
}
307332

packages/ui/src/lib/escrowStatus.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ describe("isAccountPosition", () => {
4040
expect(isAccountPosition({ recipient: other, amount: 5n }, recipient)).toBe(false);
4141
});
4242

43-
it("rejects a zero-amount position", () => {
44-
expect(isAccountPosition({ recipient, amount: 0n }, recipient)).toBe(false);
43+
it("keeps a zero-amount position so non-refundable names can be surfaced", () => {
44+
expect(isAccountPosition({ recipient, amount: 0n }, recipient)).toBe(true);
4545
});
4646
});
4747

@@ -120,6 +120,10 @@ describe("positionStatusLabel", () => {
120120
expect(positionStatusLabel(base, NOW)).toBe("Held");
121121
});
122122

123+
it("labels a zero-amount unreleased position as not refundable", () => {
124+
expect(positionStatusLabel({ ...base, amount: 0n }, NOW)).toBe("Not refundable");
125+
});
126+
123127
it("labels a position in cooldown", () => {
124128
expect(
125129
positionStatusLabel({ ...base, released: true, withdrawAvailableAt: NOW + 5n }, NOW),

packages/ui/src/lib/escrowStatus.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export function isWithdrawable(position: ReleaseState, nowSeconds: bigint): bool
1818

1919
export function positionStatusLabel(position: ReleaseState, nowSeconds: bigint): string {
2020
if (position.claimed) return "Claimed";
21+
if (!position.released && !isRefundableDeposit(position)) return "Not refundable";
2122
if (!position.released) return "Held";
2223
return isWithdrawable(position, nowSeconds) ? "Withdrawable" : "Cooldown";
2324
}
@@ -33,19 +34,17 @@ export function isRefundableDeposit(position: { amount: bigint }): boolean {
3334
return position.amount > 0n;
3435
}
3536

36-
// A read position belongs to `recipient` when the escrow still names them as the
37-
// refund recipient and the deposit is refundable. A null read (missing or failed)
38-
// never qualifies. Shared by the UI store and mirrors the CLI's filter so both
39-
// surface the same set of positions.
40-
export function isAccountPosition<T extends { recipient: string; amount: bigint }>(
37+
// A read position belongs to `recipient` when the escrow names them as the refund
38+
// recipient, regardless of the staked amount. A null read (missing or failed) never
39+
// qualifies. Zero-amount positions are kept so the UI can surface non-refundable
40+
// names (paid for by another account, or registered under a free personhood tier)
41+
// with a warning rather than hiding them; refundability is a display concern decided
42+
// by isRefundableDeposit, not a discovery filter.
43+
export function isAccountPosition<T extends { recipient: string }>(
4144
position: T | null,
4245
recipient: string,
4346
): position is T {
44-
return (
45-
position !== null &&
46-
isSameEvmAddress(position.recipient, recipient) &&
47-
isRefundableDeposit(position)
48-
);
47+
return position !== null && isSameEvmAddress(position.recipient, recipient);
4948
}
5049

5150
// Total still locked across positions. Withdrawn positions carry amount 0 (the

packages/ui/src/store/useEscrowStore.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ export const useEscrowStore = defineStore("useEscrowStore", () => {
8282
// Labels are mirror-on-transfer and never deleted, so a released name (now held
8383
// by the escrow contract) still resolves through the caller's own label set;
8484
// the recipient filter drops names transferred away whose position rebound to
85-
// someone else.
85+
// someone else. Zero-amount positions are retained (not filtered) so the UI can
86+
// mark non-refundable names; the refundable-amount check is a display concern.
8687
//
8788
// Reads run sequentially, mirroring the CLI. A parallel burst overruns the
8889
// chainHead_follow subscription, and every read that then sees "No active

0 commit comments

Comments
 (0)