@@ -177,7 +177,7 @@ class TokenApiProvider extends ActionProvider<WalletProvider> { // Use WalletPro
177177
178178 @CreateAction ( {
179179 name : "get-token-transfers" ,
180- description : "Fetches token transfers involving a specific address or contract. Can filter by sender, receiver, date range, etc." ,
180+ description : "Fetches token transfers involving a specific address or contract. Can filter by sender, receiver, date range, etc. If addressRole is not specified, defaults to showing incoming transfers (receiver). If no time filters are provided, automatically applies a 7-day lookback to prevent API timeouts. " ,
181181 schema : GetTokenTransfersAgentParamsSchema ,
182182 } )
183183 async getTokenTransfers (
@@ -186,37 +186,71 @@ class TokenApiProvider extends ActionProvider<WalletProvider> { // Use WalletPro
186186 ) : Promise < string > {
187187 console . log ( `Action: getTokenTransfers, Args: ${ JSON . stringify ( args ) } ` ) ;
188188
189- const { address, addressRole, fromAddress, toAddress, ...otherParams } = args ;
189+ const { address, addressRole, fromAddress, toAddress, age , startTime , endTime , ...otherParams } = args ;
190190
191191 let finalToAddress : string | undefined = toAddress ;
192192 let finalFromAddress : string | undefined = fromAddress ;
193193
194194 if ( address ) {
195- if ( addressRole === "receiver" && ! finalToAddress ) {
195+ // Default to "receiver" if no role is specified (most common use case)
196+ const role = addressRole || "receiver" ;
197+
198+ if ( role === "receiver" && ! finalToAddress ) {
196199 finalToAddress = address ;
197- } else if ( addressRole === "sender" && ! finalFromAddress ) {
200+ } else if ( role === "sender" && ! finalFromAddress ) {
198201 finalFromAddress = address ;
199- } else if ( addressRole === "either" ) {
200- // If role is 'either', and specific from/to are not set,
201- // this basic setup will use the address for 'to' in fetchTokenTransfers (first arg),
202- // and potentially for 'from' in its params if finalFromAddress is still undefined.
203- // A true 'either' might require two API calls or specific backend support.
202+ } else if ( role === "either" ) {
203+ // For "either", we'll default to receiver for now
204+ // TODO: In the future, this could make two API calls and merge results
204205 if ( ! finalToAddress ) finalToAddress = address ;
205- if ( ! finalFromAddress ) finalFromAddress = address ;
206+ console . log ( `📝 Note: Using address as receiver for "either" role. Consider making separate queries for complete results.` ) ;
206207 }
207208 }
208209
209- // If after all logic, neither toAddress nor fromAddress is set, and no contract is specified,
210- // the query might be too broad. The utility has a placeholder for this check.
210+ // More specific error message with guidance
211211 if ( ! finalToAddress && ! finalFromAddress && ! otherParams . contract ) {
212- return "Error: Token transfers query is too broad. Please specify an address (with role), from/to address, or a contract address." ;
212+ return `Error: Please specify one of the following:
213+ - An address with a role (receiver/sender/either)
214+ - A fromAddress (sender)
215+ - A toAddress (receiver)
216+ - A contract address to filter by
217+
218+ Examples:
219+ - To see incoming transfers: specify addressRole as "receiver" (this is the default)
220+ - To see outgoing transfers: specify addressRole as "sender"
221+ - To see transfers for a specific token: provide the contract address` ;
222+ }
223+
224+ // Handle time filtering - prefer startTime/endTime over age to avoid timeouts
225+ let finalStartTime : number | undefined = startTime ;
226+ let finalEndTime : number | undefined = endTime ;
227+ let finalAge : number | undefined = age ;
228+
229+ // If age is provided but no explicit start/end times, convert age to timestamps
230+ // This helps avoid API timeouts for large datasets
231+ if ( age && ! startTime && ! endTime ) {
232+ const now = Math . floor ( Date . now ( ) / 1000 ) ; // Current time in seconds
233+ finalEndTime = now ;
234+ finalStartTime = now - ( age * 24 * 60 * 60 ) ; // Convert days to seconds
235+ finalAge = undefined ; // Remove age since we're using timestamps
236+ }
237+
238+ // If NO time filtering is provided at all, default to last 7 days to prevent timeouts
239+ if ( ! age && ! startTime && ! endTime ) {
240+ const now = Math . floor ( Date . now ( ) / 1000 ) ;
241+ finalEndTime = now ;
242+ finalStartTime = now - ( 7 * 24 * 60 * 60 ) ; // Default to 7 days
243+ console . log ( `📅 No time filter provided. Defaulting to last 7 days to prevent API timeout.` ) ;
213244 }
214245
215246 // Prepare parameters for the fetchTokenTransfers utility
216247 // The utility expects `toAddress` as first arg, and other params (including `from`) in the second.
217248 const utilityParams : Omit < TokenTransfersParams , 'to' > = {
218- ...otherParams , // network_id, contract, limit, age, etc.
249+ ...otherParams , // network_id, contract, limit, etc.
219250 from : finalFromAddress , // This can be undefined, and fetchTokenTransfers handles it
251+ age : finalAge , // Only use age if startTime/endTime are not set
252+ startTime : finalStartTime ,
253+ endTime : finalEndTime ,
220254 } ;
221255
222256 try {
@@ -228,7 +262,12 @@ class TokenApiProvider extends ActionProvider<WalletProvider> { // Use WalletPro
228262 }
229263
230264 if ( ! response . data || ! response . data . transfers || response . data . transfers . length === 0 ) {
231- return `No token transfers found matching the criteria.` ;
265+ const roleText = addressRole || "receiver" ;
266+ return `No token transfers found for address ${ address } as ${ roleText } on ${ otherParams . network_id || 'mainnet' } . Try:
267+ - Different addressRole (sender/receiver/either)
268+ - Different network
269+ - Longer time period
270+ - Check if the address has any token activity` ;
232271 }
233272
234273 // The response.data from fetchTokenTransfers includes { transfers: [], pagination: {}, ... }
0 commit comments