@@ -65,11 +65,31 @@ export function mandateDigest(payload: SessionMandatePayload): Buffer {
6565}
6666
6767export function usdcToStroops ( amount : number ) : bigint {
68- return BigInt ( Math . round ( amount * 1e7 ) ) ;
68+ if ( ! Number . isFinite ( amount ) ) {
69+ throw new MandateError ( `usdc amount must be a finite number, got ${ amount } ` ) ;
70+ }
71+ if ( amount < 0 ) {
72+ throw new MandateError ( `usdc amount must be non-negative, got ${ amount } ` ) ;
73+ }
74+ const scaled = Math . round ( amount * 1e7 ) ;
75+ if ( ! Number . isSafeInteger ( scaled ) ) {
76+ throw new MandateError ( `usdc amount ${ amount } is out of stroop-safe range` ) ;
77+ }
78+ return BigInt ( scaled ) ;
6979}
7080
7181export function stroopsToUsdc ( stroops : bigint ) : number {
72- return Number ( stroops ) / 1e7 ;
82+ if ( typeof stroops !== "bigint" ) {
83+ throw new MandateError ( `stroops must be a bigint, got ${ typeof stroops } ` ) ;
84+ }
85+ if ( stroops < 0n ) {
86+ throw new MandateError ( `stroops must be non-negative, got ${ stroops } ` ) ;
87+ }
88+ // Split whole/fraction to avoid `Number(bigint)` precision loss for large
89+ // escrow/bid values that exceed Number's safe integer range.
90+ const whole = Number ( stroops / 10_000_000n ) ;
91+ const frac = Number ( stroops % 10_000_000n ) / 1e7 ;
92+ return whole + frac ;
7393}
7494
7595export interface CreateMandateParams {
@@ -178,6 +198,26 @@ export function assertAppraisalSpendAllowed(
178198 }
179199}
180200
201+ /** Remaining x402 appraisal budget (stroops) before the mandate cap is hit. */
202+ export function remainingAppraisalSpend (
203+ mandate : SessionMandate ,
204+ spentSoFarStroops : bigint = 0n ,
205+ ) : bigint {
206+ if ( typeof spentSoFarStroops !== "bigint" || spentSoFarStroops < 0n ) {
207+ throw new MandateError (
208+ `spentSoFarStroops must be a non-negative bigint, got ${ String ( spentSoFarStroops ) } ` ,
209+ ) ;
210+ }
211+ const cap = BigInt ( mandate . maxAppraisalSpendStroops ) ;
212+ const remaining = cap - spentSoFarStroops ;
213+ if ( remaining < 0n ) {
214+ throw new MandateCapError (
215+ `appraisal spend ${ spentSoFarStroops } already exceeds mandate cap ${ cap } ` ,
216+ ) ;
217+ }
218+ return remaining ;
219+ }
220+
181221/** Refuse a bid/escrow pair that exceeds mandate caps (agent-side guard). */
182222export function assertBidWithinMandate (
183223 mandate : SessionMandate ,
0 commit comments