DTN Callbacks¶
DTN Callbacks are the mechanism for handling responses from AI requests. Every AI request requires two callback functions: one for success and one for failure. Understanding how to implement and use callbacks is essential for building robust AI-powered applications.
What are DTN Callbacks?¶
DTN Callbacks are functions that are automatically called by the DTN network when an AI request is completed. They allow your contract to:
- Process successful AI responses
- Handle errors and failures
- Update contract state based on results
- Trigger additional actions
Callback Structure¶
Every AI request requires two callbacks:
IDtnAi.CallBack(
this.successCallback.selector, // Function called on success
this.errorCallback.selector, // Function called on failure
address(this) // Contract that owns the callbacks
)
Callback Function Requirements¶
Function Signature¶
All callback functions must follow this exact signature:
Required Modifier¶
All callback functions must use the onlyDtn
modifier to ensure only DTN contracts can call them:
Success Callbacks¶
Success callbacks are called when an AI request completes successfully.
Basic Success Callback¶
function onSuccess(bytes32 requestId) external onlyDtn {
// Fetch the response from the AI system
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
// Parse the response based on your expected data type
string memory result = abi.decode(response, (string));
// Update your contract state
lastResult = result;
lastRequestId = requestId;
// Emit an event
emit AIResponseReceived(requestId, result);
}
Handling Different Response Types¶
Text Responses¶
function onTextSuccess(bytes32 requestId) external onlyDtn {
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
string memory textResult = abi.decode(response, (string));
textResults[requestId] = textResult;
}
Image Responses (IPFS)¶
function onImageSuccess(bytes32 requestId) external onlyDtn {
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
string memory ipfsCid = abi.decode(response, (string));
imageResults[requestId] = ipfsCid;
}
Structured Data Responses¶
function onStructuredSuccess(bytes32 requestId) external onlyDtn {
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
// Decode structured data
(uint256 score, string memory analysis) = abi.decode(response, (uint256, string));
analysisResults[requestId] = Analysis(score, analysis);
}
Error Callbacks¶
Error callbacks are called when an AI request fails or encounters an error.
Basic Error Callback¶
function onError(bytes32 requestId) external onlyDtn {
// Fetch error information
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
// Store error information
lastError = message;
lastErrorRequestId = requestId;
// Emit error event
emit AIRequestFailed(requestId, message);
}
Error Handling Strategies¶
Retry Logic¶
function onErrorWithRetry(bytes32 requestId) external onlyDtn {
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
// Check if we should retry
if (retryCount[requestId] < maxRetries) {
retryCount[requestId]++;
// Retry the request
retryRequest(requestId);
} else {
// Give up after max retries
failedRequests[requestId] = message;
emit MaxRetriesExceeded(requestId, message);
}
}
Fallback Logic¶
function onErrorWithFallback(bytes32 requestId) external onlyDtn {
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
// Try a different model or approach
if (bytes(message).length > 0) {
// Use fallback model
makeFallbackRequest(requestId);
}
}
Response Status Handling¶
The fetchResponse
function returns a status that indicates the response state:
enum ResponseStatus {
PENDING, // Request is still being processed
SUCCESS, // Request completed successfully
FAILED, // Request failed
TIMEOUT // Request timed out
}
Status-Based Handling¶
function handleResponse(bytes32 requestId) external onlyDtn {
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
if (status == IDtnAi.ResponseStatus.SUCCESS) {
// Handle successful response
string memory result = abi.decode(response, (string));
processSuccess(requestId, result);
} else if (status == IDtnAi.ResponseStatus.FAILED) {
// Handle failure
processFailure(requestId, message);
} else if (status == IDtnAi.ResponseStatus.TIMEOUT) {
// Handle timeout
processTimeout(requestId);
}
// PENDING status means the request is still being processed
}
Gas Considerations¶
Gas Requirements¶
Callbacks must have sufficient gas to execute. The gas sent with the original request must cover:
- The original request execution
- The callback execution
- Any state changes in the callback
Gas Optimization¶
function gasOptimizedCallback(bytes32 requestId) external onlyDtn {
// Minimize storage operations
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
// Use events instead of storage when possible
emit ResponseProcessed(requestId, status, message);
// Only store essential data
if (status == IDtnAi.ResponseStatus.SUCCESS) {
essentialData[requestId] = abi.decode(response, (uint256));
}
}
Security Considerations¶
Access Control¶
Always use the onlyDtn
modifier to prevent unauthorized access:
Input Validation¶
Validate callback parameters:
function secureCallback(bytes32 requestId) external onlyDtn {
require(requestId != bytes32(0), "Invalid request ID");
// Process the response
(IDtnAi.ResponseStatus status, string memory message, bytes memory response) =
ai.fetchResponse(requestId);
// Validate response data
require(response.length > 0, "Empty response");
}
Best Practices¶
1. Error Handling¶
- Always implement both success and error callbacks
- Handle all possible response statuses
- Provide meaningful error messages
- Implement retry logic when appropriate
2. State Management¶
- Update contract state atomically
- Use events for important state changes
- Avoid complex logic in callbacks
- Keep callbacks gas-efficient
3. Response Processing¶
- Validate response data before processing
- Handle different response formats
- Implement fallback mechanisms
- Log important events
4. Gas Optimization¶
- Minimize storage operations in callbacks
- Use events for non-critical data
- Batch operations when possible
- Estimate gas requirements accurately
Common Issues¶
"Insufficient gas" Error¶
Cause: Callback doesn't have enough gas to execute Solution: Include more gas with the original request
"Only DTN can call" Error¶
Cause: Missing onlyDtn
modifier or incorrect AI contract address Solution: Ensure proper modifier and contract setup
"Invalid response format" Error¶
Cause: Trying to decode response with wrong data type Solution: Match decode type with expected response format
"Callback not found" Error¶
Cause: Incorrect function selector or missing callback function Solution: Verify function exists and selector is correct
Next Steps¶
- Basic AI Call Example - See callbacks in action
- Error Handling - Advanced error handling techniques
- API Reference - Complete callback interface documentation