/api/transactions/transfer

GET

Transfer coins between wallets with automatic change-making and coin breaking.

Description

The `/api/transactions/transfer` endpoint transfers a specified amount of CloudCoins from one wallet to another. The operation moves coin files directly between wallets (not copy-delete), preserving folder structure (Bank→Bank, Fracked→Fracked), and automatically makes change by breaking coins if exact denominations aren't available.

💡 Automatic Change-Making

If exact change cannot be made with available coins, the system automatically breaks larger coins (up to 10 iterations) to create the denominations needed for the transfer. If change-making fails after maximum attempts, the transfer is aborted and an error is returned.

📝 Use Cases
  • Move coins between wallets for organization or security
  • Distribute coins across multiple wallet locations
  • Consolidate coins from multiple wallets into one
  • Transfer specific amounts while maintaining exact change
  • Automated wallet-to-wallet transfers in applications
⚠️ Important Notes
  • File Movement: Coin files are moved (not copied), ensuring they exist in only one location at a time.
  • Folder Preservation: Coins in Bank folder stay in Bank; coins in Fracked folder stay in Fracked.
  • No Authentication: Coins are transferred regardless of authentication status (Bank or Fracked).
  • Exact Amount: The system ensures exactly the requested amount is transferred using automatic change-making.
  • Transaction Logging: Both wallets receive transaction log entries with opposite symbols (withdraw/deposit).

Parameters

Query Parameters

from_wallet_path string required
Full path to the source wallet. Coins will be withdrawn from this wallet. Example: E:\Data\Wallets\Default
to_wallet_path string required
Full path to the destination wallet. Coins will be deposited to this wallet. Example: E:\Data\Wallets\Savings
amount number required
Amount in CloudCoin units to transfer (supports decimals: 0.00000001 to 1000000). Examples: 100, 250.5, 0.1

Response

Returns a JSON object containing the transfer details and task ID for tracking.

Success Response Properties

command string
Always "transaction-transfer" - identifies the API command executed.
success boolean
Always true for successful transfers.
task_id string
Unique task identifier (format: "Nov-13-25_09-24-05-PM-1537"). Used for tracking in transaction logs and receipts.
from_wallet_path string
Full path to the source wallet.
to_wallet_path string
Full path to the destination wallet.
amount number
Amount transferred in CloudCoin units.
message string
Success confirmation message.

Success Response Example

{
  "command": "transaction-transfer",
  "success": true,
  "task_id": "Nov-13-25_09-24-05-PM-1537",
  "from_wallet_path": "E:\\Data\\Wallets\\Default",
  "to_wallet_path": "E:\\Data\\Wallets\\Savings",
  "amount": 100,
  "message": "Transfer completed successfully"
}

Error Responses

The endpoint returns different error codes depending on the failure reason.

HTTP 400 - Missing Parameters

{
  "error": true,
  "message": "Missing 'from_wallet_path' parameter",
  "code": 400
}

HTTP 400 - Invalid Wallet Path

{
  "error": true,
  "message": "Invalid wallet path 'E:\\Data\\Wallets\\Invalid'. The wallet does not exist or is not accessible.",
  "code": 400
}

HTTP 400 - Cannot Make Exact Change

{
  "error": true,
  "message": "Cannot make exact change after breaking coins. Please transfer whole notes.",
  "code": 400
}

HTTP 400 - Insufficient Balance

{
  "error": true,
  "message": "Insufficient balance in source wallet",
  "code": 400
}

HTTP 500 - File Move Failed

{
  "error": true,
  "message": "Failed to move coin files between wallets",
  "code": 500
}

Examples

JavaScript (fetch)

const API_HOST = 'http://localhost:8080';

async function transferCoins(fromWalletPath, toWalletPath, amount) {
    try {
        const url = `${API_HOST}/api/transactions/transfer?` +
                   `from_wallet_path=${encodeURIComponent(fromWalletPath)}&` +
                   `to_wallet_path=${encodeURIComponent(toWalletPath)}&` +
                   `amount=${amount}`;

        const response = await fetch(url);
        const result = await response.json();

        if (result.success) {
            console.log(`✓ ${result.message}`);
            console.log(`Task ID: ${result.task_id}`);
            console.log(`From: ${result.from_wallet}`);
            console.log(`To: ${result.to_wallet}`);
            console.log(`Amount: ${result.amount} CloudCoins`);
            return result.task_id;
        } else {
            console.error(`Error: ${result.message}`);
            return null;
        }
    } catch (error) {
        console.error('Request failed:', error);
        return null;
    }
}

// Transfer 100 CloudCoins from Default wallet to Savings wallet
transferCoins('E:\\Data\\Wallets\\Default', 'E:\\Data\\Wallets\\Savings', 100);

// Transfer with decimals (250.5 CloudCoins)
transferCoins('E:\\Data\\Wallets\\Default', 'E:\\Data\\Wallets\\Backup', 250.5);

cURL

# Transfer 100 CloudCoins from Default wallet to Savings wallet
curl -X GET "http://localhost:8080/api/transactions/transfer?from_wallet_path=E:\Data\Wallets\Default&to_wallet_path=E:\Data\Wallets\Savings&amount=100"

# Transfer with decimal amount (250.5 CloudCoins)
curl -X GET "http://localhost:8080/api/transactions/transfer?from_wallet_path=E:\Data\Wallets\Default&to_wallet_path=E:\Data\Wallets\Backup&amount=250.5"

# With verbose output to see headers
curl -v -X GET "http://localhost:8080/api/transactions/transfer?from_wallet_path=E:\Data\Wallets\Savings&to_wallet_path=E:\Data\Wallets\Default&amount=50"

# Pipe to jq for formatted JSON output
curl -s "http://localhost:8080/api/transactions/transfer?from_wallet_path=E:\Data\Wallets\Default&to_wallet_path=E:\Data\Wallets\Savings&amount=100" | jq .

Go

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
)

const ApiHost = "http://localhost:8080"

type TransferResponse struct {
    Command        string  `json:"command"`
    Success        bool    `json:"success"`
    TaskID         string  `json:"task_id"`
    FromWalletPath string  `json:"from_wallet_path"`
    ToWalletPath   string  `json:"to_wallet_path"`
    Amount         float64 `json:"amount"`
    Message        string  `json:"message"`
}

type ErrorResponse struct {
    Error   bool   `json:"error"`
    Message string `json:"message"`
    Code    int    `json:"code"`
}

func transferCoins(fromWalletPath, toWalletPath string, amount float64) (string, error) {
    // Build URL with query parameters
    params := url.Values{}
    params.Add("from_wallet_path", fromWalletPath)
    params.Add("to_wallet_path", toWalletPath)
    params.Add("amount", fmt.Sprintf("%.2f", amount))

    apiUrl := fmt.Sprintf("%s/api/transactions/transfer?%s", ApiHost, params.Encode())

    resp, err := http.Get(apiUrl)
    if err != nil {
        return "", fmt.Errorf("request failed: %w", err)
    }
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)

    // Check for success
    if resp.StatusCode == http.StatusOK {
        var result TransferResponse
        if err := json.Unmarshal(body, &result); err != nil {
            return "", err
        }

        fmt.Printf("✓ %s\n", result.Message)
        fmt.Printf("Task ID: %s\n", result.TaskID)
        fmt.Printf("From: %s\n", result.FromWalletPath)
        fmt.Printf("To: %s\n", result.ToWalletPath)
        fmt.Printf("Amount: %.2f CloudCoins\n", result.Amount)

        return result.TaskID, nil
    }

    // Handle error response
    var errResult ErrorResponse
    if err := json.Unmarshal(body, &errResult); err != nil {
        return "", err
    }

    return "", fmt.Errorf("error %d: %s", errResult.Code, errResult.Message)
}

func main() {
    // Transfer 100 CloudCoins from Default wallet to Savings wallet
    taskID, err := transferCoins("E:\\Data\\Wallets\\Default", "E:\\Data\\Wallets\\Savings", 100)
    if err != nil {
        fmt.Printf("Transfer failed: %v\n", err)
        return
    }

    fmt.Printf("\nTransfer completed successfully!\n")
    fmt.Printf("Track this transfer using Task ID: %s\n", taskID)
}

Implementation Details

Understanding how the transfer operation works internally:

  1. Parameter Validation: Validates that all required parameters (from_wallet_path, to_wallet_path, amount) are provided.
  2. Wallet Path Validation: Ensures both wallet paths exist and are accessible.
  3. Balance Check: Scans source wallet to verify sufficient balance for the transfer.
  4. Automatic Change-Making: Uses greedy algorithm to find exact change from available coins.
  5. Coin Breaking: If exact change cannot be made, automatically breaks larger coins (up to 10 iterations) to create needed denominations.
  6. File Movement: Moves selected coin files from source to destination using rename() operation (atomic on same filesystem).
  7. Folder Preservation: Coins in Bank folder are moved to destination's Bank; Fracked coins go to Fracked.
  8. Transaction Logging: Logs withdrawal in source wallet's transactions.csv and deposit in destination wallet's transactions.csv.
  9. Task ID Generation: Creates unique task ID (format: "MMM-DD-YY_HH-MM-SS-AM/PM-XXXX") for tracking.
💡 Change-Making Algorithm

The transfer uses a greedy algorithm (largest coins first) to make exact change. If that fails, it automatically breaks the smallest coin larger than the remaining needed amount, then retries. This process repeats up to 10 times to ensure exact change can be made. This prevents over-sending and ensures precise transfers.

📊 Transaction Logging

Both wallets receive entries in their transactions.csv file:

  • Source wallet: "◀" symbol (Transfer Out) with amount withdrawn
  • Destination wallet: "▶" symbol (Transfer In) with amount deposited
  • Both entries share the same task_id for correlation

Related Endpoints

/api/wallets

View all configured wallets to get the wallet paths needed for transfers.

/api/wallet/balance

Check wallet balance before initiating a transfer.

/api/transactions/export

Export coins to external files instead of another wallet.

/api/transactions/import

Import coins from external files into a wallet.