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
5 changes: 5 additions & 0 deletions .changeset/fix-balance-truncation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@walletconnect/staking-cli": patch
---

Fix balance display rounding up, causing full-balance stake to revert. `formatWCT` now truncates to 2 decimal places instead of rounding. `stake` checks on-chain balance before approving to avoid wasting gas.
10 changes: 10 additions & 0 deletions packages/staking-cli/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ export async function stake(

console.log(`\nAdding ${amount} WCT${extendingTime ? `, extending lock to ${formatDate(Number(effectiveUnlockTime))}` : ""}...`);

// Check balance before approving to avoid wasting gas on a doomed stake
const bal = await readUint256(buildBalanceOfCallData(address));
if (bal < amountWei) {
console.error(
`\nInsufficient balance: you have ${formatWCT(bal)} WCT but tried to stake ${amount} WCT.` +
`\nUse the displayed balance (or less) to avoid a reverted transaction.`,
);
return;
}

// Check allowance and approve if needed
const allowance = await readUint256(buildAllowanceCallData(address, STAKE_WEIGHT_ADDRESS));
if (allowance < amountWei) {
Expand Down
11 changes: 9 additions & 2 deletions packages/staking-cli/src/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ import {
MAX_LOCK_WEEKS,
} from "./constants.js";

/** Format a bigint WCT amount as a human-readable string with 2 decimals */
/** Format a bigint WCT amount as a human-readable string with 2 decimals (truncated, never rounded up) */
export function formatWCT(amount: bigint): string {
const raw = formatUnits(amount, WCT_DECIMALS);
const num = parseFloat(raw);
// Truncate to 2 decimal places instead of rounding to avoid displaying
// a value larger than the actual on-chain balance
const dotIndex = raw.indexOf(".");
const truncated =
dotIndex === -1
? `${raw}.00`
: raw.slice(0, dotIndex + 3).padEnd(dotIndex + 3, "0");
const num = parseFloat(truncated);
return num.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
Expand Down
10 changes: 10 additions & 0 deletions packages/staking-cli/test/format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ describe("formatWCT", () => {
// 1.5 WCT
expect(formatWCT(1500000000000000000n)).toBe("1.50");
});

it("truncates instead of rounding up", () => {
// 112.887964254423744364 WCT — must display 112.88, not 112.89
expect(formatWCT(112887964254423744364n)).toBe("112.88");
});

it("truncates .999 without rounding to next integer", () => {
// 9.999 WCT — must display 9.99, not 10.00
expect(formatWCT(9999000000000000000n)).toBe("9.99");
});
});

describe("formatDate", () => {
Expand Down