@@ -8,28 +8,69 @@ export interface WalletActivitySnapshot {
88 readonly transfers : readonly {
99 readonly transaction_hash : string ;
1010 readonly log_index : number ;
11+ readonly sender ?: string ;
12+ readonly token_contract ?: string ;
13+ readonly block_number ?: string ;
14+ readonly block_timestamp ?: string ;
15+ readonly network ?: 'eip155:5042002' ;
1116 readonly recipient : string ;
1217 readonly amount_atomic : string ;
1318 } [ ] ;
1419 } ;
1520}
1621
1722export interface WalletActivityPort {
18- refresh ( ) : Promise < WalletActivitySnapshot > ;
23+ refresh ( wallets ?: readonly string [ ] ) : Promise < WalletActivitySnapshot > ;
1924}
2025
21- const QUERY = `query OneShotWalletActivity($sender: Bytes!) { settlementCandidates(first: 100, orderBy: blockNumber, orderDirection: desc, where: { sender: $sender }) { transactionHash logIndex recipient amountAtomic } _meta { deployment hasIndexingErrors block { number } } }` ;
26+ const QUERY = `query OneShotWalletActivity($senders: [Bytes!]!) { settlementCandidates(first: 100, orderBy: blockNumber, orderDirection: desc, where: { sender_in: $senders }) { transactionHash logIndex sender tokenContract blockNumber blockTimestamp network recipient amountAtomic } _meta { deployment hasIndexingErrors block { number } } }` ;
27+
28+ function graphBlockTimestamp ( value : unknown ) : string | undefined {
29+ if ( value === undefined ) return undefined ;
30+ if (
31+ typeof value === 'string' &&
32+ / ^ \d { 4 } - \d { 2 } - \d { 2 } T \d { 2 } : \d { 2 } : \d { 2 } (?: \. \d { 1 , 9 } ) ? Z $ / u. test ( value )
33+ ) {
34+ return value ;
35+ }
36+ if ( typeof value !== 'string' || ! / ^ ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) $ / u. test ( value ) ) {
37+ throw new Error ( 'Graph activity block timestamp failed validation' ) ;
38+ }
39+ const seconds = Number ( value ) ;
40+ if ( ! Number . isSafeInteger ( seconds ) || seconds < 0 ) {
41+ throw new Error ( 'Graph activity block timestamp failed validation' ) ;
42+ }
43+ const timestamp = new Date ( seconds * 1_000 ) ;
44+ if ( Number . isNaN ( timestamp . getTime ( ) ) ) {
45+ throw new Error ( 'Graph activity block timestamp failed validation' ) ;
46+ }
47+ return timestamp . toISOString ( ) ;
48+ }
2249
2350export class StudioWalletActivityPort implements WalletActivityPort {
2451 constructor (
2552 private readonly options : {
2653 readonly endpoint : string ;
27- readonly wallet : string ;
54+ readonly wallet ? : string ;
2855 readonly apiKey ?: string ;
2956 readonly fetchFn ?: typeof fetch ;
3057 } ,
3158 ) { }
32- async refresh ( ) : Promise < WalletActivitySnapshot > {
59+ async refresh ( wallets : readonly string [ ] = [ ] ) : Promise < WalletActivitySnapshot > {
60+ const senders = [ ...( this . options . wallet ? [ this . options . wallet ] : [ ] ) , ...wallets ] . reduce <
61+ string [ ]
62+ > ( ( unique , wallet ) => {
63+ const address = asEvmAddress ( wallet ) . toLowerCase ( ) ;
64+ if ( ! unique . includes ( address ) ) unique . push ( address ) ;
65+ return unique ;
66+ } , [ ] ) ;
67+ if ( senders . length === 0 ) {
68+ return {
69+ freshness : 'UNAVAILABLE' ,
70+ coverageNote : 'No site payer wallet is recorded for this workspace yet.' ,
71+ payload : { transfers : [ ] } ,
72+ } ;
73+ }
3374 const response = await ( this . options . fetchFn ?? fetch ) ( this . options . endpoint , {
3475 method : 'POST' ,
3576 headers : {
@@ -38,7 +79,7 @@ export class StudioWalletActivityPort implements WalletActivityPort {
3879 } ,
3980 body : JSON . stringify ( {
4081 query : QUERY ,
41- variables : { sender : asEvmAddress ( this . options . wallet ) } ,
82+ variables : { senders } ,
4283 } ) ,
4384 } ) ;
4485 if ( ! response . ok ) throw new Error ( 'Graph activity query is unavailable' ) ;
@@ -70,6 +111,17 @@ export class StudioWalletActivityPort implements WalletActivityPort {
70111 return {
71112 transaction_hash : asTransactionHash ( row . transactionHash ) ,
72113 log_index : index ,
114+ ...( typeof row . sender === 'string' ? { sender : asEvmAddress ( row . sender ) } : { } ) ,
115+ ...( typeof row . tokenContract === 'string'
116+ ? { token_contract : asEvmAddress ( row . tokenContract ) }
117+ : { } ) ,
118+ ...( typeof row . blockNumber === 'string' && / ^ ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) $ / u. test ( row . blockNumber )
119+ ? { block_number : row . blockNumber }
120+ : { } ) ,
121+ ...( row . blockTimestamp === undefined
122+ ? { }
123+ : { block_timestamp : graphBlockTimestamp ( row . blockTimestamp ) ! } ) ,
124+ ...( row . network === 'eip155:5042002' ? { network : 'eip155:5042002' as const } : { } ) ,
73125 recipient : asEvmAddress ( row . recipient ) ,
74126 amount_atomic : row . amountAtomic ,
75127 } ;
0 commit comments