Skip to main content

Overview

This page documents edge cases, failure modes, and Daya’s guarantees when things don’t go as planned.

Deposit Timing Edge Cases

Late Deposits (After Onramp Expiry)

Scenario: User transfers NGN after the 25-minute onramp window What happens:
  1. Deposit detected in virtual account
  2. Status immediately set to FLAGGED
  3. flag_reason: "Deposit received after onramp expiry"
  4. NGN held in suspense (not automatically processed)
  5. Webhook: deposit.flagged
Resolution:
  • Requires manual operations review (1-2 business days)
  • Ops may process at current rate (not original rate_id)
  • Or ops may reverse NGN to sender’s bank
Late deposits do not execute at the original locked rate_id. They process at current rates (if approved) or are reversed.
Example:
Onramp expires: 15:30
User transfers:  15:35 (5 minutes late)
Result:          FLAGGED, manual review
Merchant action:
  • Inform user their deposit is under review
  • Set expectation: 1-2 business days
  • Provide support contact

Late Deposits (After Rate Expiry)

Scenario: User transfers after the rate_id expires (but onramp still active) What happens:
  • Same as onramp expiry: FLAGGED
  • flag_reason: "Deposit received after rate expiry"
Example:
Onramp created:  15:05 with rate_abc (expires 15:35)
Onramp expires:  15:30 (permanent VA: never)
User transfers:  15:40
Rate expires:    15:35
Result:          FLAGGED

Deposit Before Onramp Fully Provisioned

Scenario: User transfers NGN before virtual account is fully activated (rare) What happens:
  • Deposit may be delayed or rejected by payment provider
  • If received, processed normally once VA is active
Prevention:
  • Show VA details only after onramp creation response received
  • Do not display VA details during loading state

FX Conversion Edge Cases

No Valid Rate Available

Scenario: FX venue is temporarily unavailable Impact:
  • GET /v1/rates returns 503 error
  • POST /v1/onramp fails with rate_unavailable
What happens:
{
  "error": {
    "code": "rate_unavailable",
    "message": "No valid exchange rates available at this time",
    "details": "Please try again in a few minutes"
  }
}
Merchant action:
  • Show user-friendly error: “Exchange rates temporarily unavailable”
  • Retry after 1-5 minutes
  • Alert your operations team if persists > 15 minutes

FX Execution Failure

Scenario: FX conversion fails (no liquidity, internal error) What happens:
  1. Deposit status → FAILED or FLAGGED (depending on cause)
  2. NGN held in clearing account
  3. Webhook: deposit.failed or deposit.flagged
Common causes:
CauseStatusResolution
Temporary liquidity issueFLAGGEDRetry automatically or manually
Permanent liquidity issueFAILEDReverse NGN to sender
Internal errorFLAGGEDManual review
Merchant action:
  • For FAILED: Inform user to retry with new onramp
  • For FLAGGED: Inform user of review (1-2 days)

On-Chain Withdrawal Edge Cases

Gas Price Spike

Scenario: Ethereum gas prices spike during withdrawal What happens:
  • Withdrawal may be delayed until gas prices normalize
  • Deposit stays in PENDING_WITHDRAWAL longer than usual
  • Eventually settles when gas becomes reasonable
Typical delay: 5-30 minutes (vs. normal 2-5 minutes) Merchant action:
  • Show estimated wait time based on chain
  • If > 15 minutes, alert operations

Chain Outage

Scenario: Target blockchain has downtime (network issues, hard fork, etc.) What happens:
  • Deposit status → FLAGGED or remains PENDING_WITHDRAWAL
  • No on-chain broadcast until chain recovers
  • Manual retry by operations once chain is healthy
Merchant action:
  • Check chain status (e.g., basescan.org status page)
  • Inform user of network delay
  • Provide estimated resolution time (if known)

Invalid Destination Address

Scenario: Destination address is invalid for specified chain What happens:
  • Caught at onramp creation time
  • POST /v1/onramp returns error
{
  "error": {
    "code": "invalid_address",
    "message": "destination_address is not valid for chain BASE",
    "details": "Ensure the address format matches the specified chain"
  }
}
Prevention:
  • Validate address format client-side before submitting
  • Use blockchain libraries (ethers.js, web3.js)
import { isAddress } from 'ethers';

if (!isAddress(destinationAddress)) {
  throw new Error('Invalid Ethereum address');
}

Withdrawal Rejected by Risk Engine

Scenario: Risk engine flags withdrawal What happens:
  1. Deposit status → FLAGGED
  2. NGN and converted USD held
  3. Manual ops review required
Common triggers:
  • Destination address on sanctions list (future)
  • Unusual velocity (many deposits to same address)
  • Merchant under investigation
Merchant action:
  • Contact support with deposit_id
  • Provide context if known legitimate use

Bank Reversal

Scenario: Sending bank reverses the original NGN transfer (rare) What happens:
  1. Daya receives reversal notification
  2. If deposit already SETTLED → status changes to REVERSED
  3. If deposit not yet settled → status changes to FAILED or FLAGGED
Impact:
  • If payout already sent on-chain: Daya absorbs the loss (v0.1)
  • Merchant not charged back (v0.1)
  • May affect merchant’s risk score
Future (post-v0.1):
  • Potential merchant liability for reversals
  • Insurance or reserves to cover reversals
Merchant action:
  • None required (Daya handles)
  • If user claims legitimate reversal, contact support

Duplicate Deposits

Scenario: User accidentally transfers NGN twice to same VA For throwaway onramps:
  • First deposit → Processes normally → SETTLED
  • Second deposit → FLAGGED (throwaway should only receive one deposit)
  • Manual review required
For permanent onramps:
  • First deposit → SETTLED
  • Second deposit → SETTLED (multiple deposits expected)
Idempotency is based on bank’s bank_reference, not amount. Two transfers of same amount are treated as separate deposits if bank_reference differs.

Webhook Delivery Failures

Scenario: Your webhook endpoint is down or returns errors What happens:
  • Daya retries with exponential backoff
  • Up to 10 retries over 24 hours
  • After 24 hours, delivery marked as failed
Your responsibility:
  • Monitor webhook endpoint health
  • Set up alerts for failures
  • Query GET /v1/deposits to recover missed events
Recovery:
// Poll for missed deposits
const lastProcessedTime = getLastProcessedTime();
const deposits = await fetch(
  `https://api.daya.xyz/v1/deposits?from=${lastProcessedTime}`,
  { headers: { 'Authorization': 'Bearer API_KEY' } }
);

Merchant Account Frozen

Scenario: Merchant account is frozen (manually or automatically) Impact:
  • All new onramp creation blocked
  • Existing onramps may be disabled
  • No FX execution for new deposits
  • No withdrawals
What happens:
  • POST /v1/onramp returns 403 error
  • Deposits on existing onramps → FLAGGED
Causes:
  • Exceeded 1,000 onramps/day limit
  • Manual freeze by operations (risk/compliance)
Resolution:
  • Contact [email protected]
  • Provide context and explanation
  • Await manual review and unfreeze

Guarantees

What Daya Guarantees

For deposits within validity window, the locked rate_id is honored. No slippage.
FX conversion and settlement are atomic. Either both succeed or both fail. No partial execution.
Duplicate bank transfers (same bank_reference) create only one deposit. Webhooks may deliver multiple times but event_id ensures deduplication.
Onramp settlement configuration cannot change after creation. Prevents unauthorized modification attacks.
Every deposit lifecycle event triggers at least one webhook delivery (may be multiple).

What Daya Does NOT Guarantee

  • No reversal protection (v0.1): Bank reversals after payout are absorbed by Daya
  • No uptime SLA: Sandbox and production have no uptime guarantees in v0.1
  • No rate guarantee for permanent VAs: Uses current rate at settlement time
  • No protection from chain issues: On-chain delays/failures are outside Daya’s control

Best Practices

1

Always verify webhook signatures

Prevent spoofing attacks by validating HMAC signatures.
2

Implement idempotency

Use event_id and deposit_id to deduplicate events and deposits.
3

Show clear error messages

For each failure mode, explain what happened and what the user should do.
4

Monitor for unusual delays

Alert operations if deposits stay in PENDING_* states > 15 minutes.
5

Design for eventual consistency

Don’t assume instant settlement. Use webhooks to know when deposits complete.

Next Steps