@@ -4,14 +4,21 @@ import ora from "ora";
44import type { Address } from "viem" ;
55import {
66 viewEscrowPosition ,
7+ listAccountPositions ,
8+ totalEscrowAmount ,
9+ formatPositionStatus ,
10+ formatPositionsTable ,
11+ cooldownRemainingSeconds ,
712 releaseDomain ,
813 withdrawDomain ,
914 claimWithdrawal ,
15+ getPendingWithdrawal ,
1016 listRefunds ,
1117 claimRefund ,
1218 claimRefundsBatch ,
1319 formatRefundEntryLine ,
1420} from "../../commands/escrow" ;
21+ import { listStoreNames } from "../../commands/storeManagement" ;
1522import { addAuthOptions } from "./authOptions" ;
1623import { prepareContext } from "../context" ;
1724import { prepareReadOnlyContext } from "./lookup" ;
@@ -81,6 +88,10 @@ export function attachEscrowCommands(root: Command) {
8188 ) ;
8289 console . log ( chalk . gray ( " released: " ) + chalk . white ( String ( position . released ) ) ) ;
8390 console . log ( chalk . gray ( " claimed: " ) + chalk . white ( String ( position . claimed ) ) ) ;
91+ const nowSeconds = BigInt ( Math . floor ( Date . now ( ) / 1000 ) ) ;
92+ console . log (
93+ chalk . gray ( " status: " ) + chalk . white ( formatPositionStatus ( position , nowSeconds ) ) ,
94+ ) ;
8495 if ( position . withdrawAvailableAt > 0n ) {
8596 const t = new Date ( Number ( position . withdrawAvailableAt ) * 1000 ) . toISOString ( ) ;
8697 console . log ( chalk . gray ( " withdraw: " ) + chalk . white ( t ) ) ;
@@ -95,6 +106,105 @@ export function attachEscrowCommands(root: Command) {
95106 } ,
96107 ) ;
97108
109+ // escrow balance
110+ const balanceCommand = escrowCommand
111+ . command ( "balance" )
112+ . description ( "Show the caller's claimable pull-payment balance" )
113+ . option ( "--recipient <address>" , "Recipient EVM address (defaults to caller)" )
114+ . option ( "--json" , "Output result as JSON (suppresses all other output)" , false ) ;
115+ addAuthOptions ( balanceCommand ) . action ( async ( options : RefundListOptions , command : Command ) => {
116+ const jsonOutput = getJsonFlag ( command ) ;
117+ try {
118+ const mergedOptions = getMergedOptions ( command , options ) ;
119+ const context = await maybeQuiet ( jsonOutput , ( ) =>
120+ prepareReadOnlyContext ( mergedOptions as any ) ,
121+ ) ;
122+
123+ const recipient = ( options . recipient ?? context . evmAddress ) as Address ;
124+
125+ if ( ! jsonOutput ) console . log ( chalk . bold ( "\n▶ Escrow balance\n" ) ) ;
126+ const spinner = ora ( ) ;
127+
128+ const balance = await maybeQuiet ( jsonOutput , ( ) =>
129+ getPendingWithdrawal ( context . clientWrapper ! , context . account . address , recipient , spinner ) ,
130+ ) ;
131+
132+ if ( ! emitJsonResult ( jsonOutput , { recipient, balance : balance . toString ( ) } ) ) {
133+ console . log ( chalk . gray ( " claimable: " ) + chalk . green ( formatWeiAsEther ( balance ) + " PAS" ) ) ;
134+ console . log ( chalk . green ( "\n✓ Complete\n" ) ) ;
135+ }
136+ process . exit ( 0 ) ;
137+ } catch ( error ) {
138+ handleCommandError ( jsonOutput , error ) ;
139+ }
140+ } ) ;
141+
142+ // escrow positions
143+ const positionsCommand = escrowCommand
144+ . command ( "positions" )
145+ . description ( "List all escrow positions for the caller and the total locked" )
146+ . option ( "--recipient <address>" , "Recipient EVM address (defaults to caller)" )
147+ . option ( "--json" , "Output result as JSON (suppresses all other output)" , false ) ;
148+ addAuthOptions ( positionsCommand ) . action ( async ( options : RefundListOptions , command : Command ) => {
149+ const jsonOutput = getJsonFlag ( command ) ;
150+ try {
151+ const mergedOptions = getMergedOptions ( command , options ) ;
152+ const context = await maybeQuiet ( jsonOutput , ( ) =>
153+ prepareReadOnlyContext ( mergedOptions as any ) ,
154+ ) ;
155+
156+ const recipient = ( options . recipient ?? context . evmAddress ) as Address ;
157+
158+ if ( ! jsonOutput ) console . log ( chalk . bold ( "\n▶ Escrow positions\n" ) ) ;
159+ const spinner = ora ( ) ;
160+
161+ const names = await maybeQuiet ( jsonOutput , ( ) =>
162+ listStoreNames ( context . clientWrapper ! , context . account . address , recipient ) ,
163+ ) ;
164+ const positions = await maybeQuiet ( jsonOutput , ( ) =>
165+ listAccountPositions (
166+ context . clientWrapper ! ,
167+ context . account . address ,
168+ recipient ,
169+ names ,
170+ spinner ,
171+ ) ,
172+ ) ;
173+ const total = totalEscrowAmount ( positions ) ;
174+ const nowSeconds = BigInt ( Math . floor ( Date . now ( ) / 1000 ) ) ;
175+
176+ const handled = emitJsonResult ( jsonOutput , {
177+ recipient,
178+ total : total . toString ( ) ,
179+ positions : positions . map ( ( position ) => ( {
180+ domain : position . domain ,
181+ tokenId : position . tokenId . toString ( ) ,
182+ amount : position . amount . toString ( ) ,
183+ released : position . released ,
184+ claimed : position . claimed ,
185+ withdrawAvailableAt : position . withdrawAvailableAt . toString ( ) ,
186+ status : formatPositionStatus ( position , nowSeconds ) ,
187+ cooldownSeconds : cooldownRemainingSeconds ( position , nowSeconds ) . toString ( ) ,
188+ } ) ) ,
189+ } ) ;
190+
191+ if ( ! handled ) {
192+ if ( positions . length === 0 ) {
193+ console . log ( chalk . gray ( " no escrow positions" ) ) ;
194+ } else {
195+ for ( const line of formatPositionsTable ( positions , nowSeconds ) ) console . log ( " " + line ) ;
196+ }
197+ console . log (
198+ chalk . gray ( "\n total in escrow: " ) + chalk . green ( formatWeiAsEther ( total ) + " PAS" ) ,
199+ ) ;
200+ console . log ( chalk . green ( "\n✓ Complete\n" ) ) ;
201+ }
202+ process . exit ( 0 ) ;
203+ } catch ( error ) {
204+ handleCommandError ( jsonOutput , error ) ;
205+ }
206+ } ) ;
207+
98208 // escrow release <name>
99209 const releaseCommand = escrowCommand
100210 . command ( "release <name>" )
@@ -189,6 +299,23 @@ export function attachEscrowCommands(root: Command) {
189299 if ( ! jsonOutput ) console . log ( chalk . bold ( "\n▶ Escrow claim-withdrawal\n" ) ) ;
190300 const spinner = ora ( ) ;
191301
302+ const balance = await maybeQuiet ( jsonOutput , ( ) =>
303+ getPendingWithdrawal (
304+ context . clientWrapper ! ,
305+ context . substrateAddress ,
306+ context . evmAddress as Address ,
307+ spinner ,
308+ ) ,
309+ ) ;
310+
311+ if ( balance === 0n ) {
312+ if ( ! emitJsonResult ( jsonOutput , { ok : true , txHash : null , balance : "0" } ) ) {
313+ console . log ( chalk . gray ( " nothing to claim; pull-payment balance is 0" ) ) ;
314+ console . log ( chalk . green ( "\n✓ Complete\n" ) ) ;
315+ }
316+ process . exit ( 0 ) ;
317+ }
318+
192319 const txHash = await maybeQuiet ( jsonOutput , ( ) =>
193320 claimWithdrawal (
194321 context . clientWrapper ! ,
@@ -198,7 +325,7 @@ export function attachEscrowCommands(root: Command) {
198325 ) ,
199326 ) ;
200327
201- if ( ! emitJsonResult ( jsonOutput , { ok : true , txHash } ) ) {
328+ if ( ! emitJsonResult ( jsonOutput , { ok : true , txHash, balance : balance . toString ( ) } ) ) {
202329 console . log ( chalk . gray ( " tx: " ) + chalk . blue ( txHash ) ) ;
203330 console . log ( chalk . green ( "\n✓ Complete\n" ) ) ;
204331 }
@@ -242,7 +369,7 @@ export function attachEscrowCommands(root: Command) {
242369 throw new Error ( `limit must be between 1 and ${ MAX_REFUND_PAGE_SIZE } ` ) ;
243370 }
244371
245- const recipient = ( options . recipient ?? context . account . address ) as Address ;
372+ const recipient = ( options . recipient ?? context . evmAddress ) as Address ;
246373
247374 if ( ! jsonOutput ) console . log ( chalk . bold ( "\n▶ Refund ledger\n" ) ) ;
248375 const spinner = ora ( ) ;
0 commit comments