Add Economics, Governance, and Mining SDKs for: - Java: Full SDK with CompletableFuture async operations - Kotlin: Coroutine-based SDK with suspend functions - Swift: Modern Swift SDK with async/await - Flutter/Dart: Complete Dart SDK with Future-based API - C: Header files and implementations with opaque handles - C++: Modern C++17 with std::future and PIMPL pattern - C#: Records, async/await Tasks, and IDisposable - Ruby: Struct-based types with Faraday HTTP client Also includes minor Dart lint fixes (const exceptions).
424 lines
13 KiB
C
424 lines
13 KiB
C
/**
|
|
* @file economics.c
|
|
* @brief Synor Economics SDK implementation
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include "synor/economics.h"
|
|
|
|
/* Forward declarations from common.c */
|
|
typedef struct synor_http_client synor_http_client_t;
|
|
synor_http_client_t* synor_http_client_create(
|
|
const char* api_key, const char* endpoint,
|
|
uint32_t timeout_ms, uint32_t retries, bool debug);
|
|
void synor_http_client_destroy(synor_http_client_t* client);
|
|
int synor_http_get(synor_http_client_t* client, const char* path,
|
|
char** response, size_t* response_size);
|
|
int synor_http_post(synor_http_client_t* client, const char* path,
|
|
const char* body, char** response, size_t* response_size);
|
|
char* synor_strdup(const char* s);
|
|
char* synor_url_encode(const char* s);
|
|
|
|
/* Internal economics structure */
|
|
struct synor_economics {
|
|
synor_http_client_t* http;
|
|
bool closed;
|
|
bool debug;
|
|
};
|
|
|
|
static const char* service_type_to_string(synor_service_type_t type) {
|
|
switch (type) {
|
|
case SYNOR_SERVICE_COMPUTE: return "compute";
|
|
case SYNOR_SERVICE_STORAGE: return "storage";
|
|
case SYNOR_SERVICE_DATABASE: return "database";
|
|
case SYNOR_SERVICE_HOSTING: return "hosting";
|
|
case SYNOR_SERVICE_RPC: return "rpc";
|
|
case SYNOR_SERVICE_BRIDGE: return "bridge";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
static const char* billing_period_to_string(synor_billing_period_t period) {
|
|
switch (period) {
|
|
case SYNOR_BILLING_HOURLY: return "hourly";
|
|
case SYNOR_BILLING_DAILY: return "daily";
|
|
case SYNOR_BILLING_WEEKLY: return "weekly";
|
|
case SYNOR_BILLING_MONTHLY: return "monthly";
|
|
default: return "monthly";
|
|
}
|
|
}
|
|
|
|
synor_economics_t* synor_economics_create(const synor_economics_config_t* config) {
|
|
if (!config || !config->api_key) {
|
|
return NULL;
|
|
}
|
|
|
|
synor_economics_t* economics = calloc(1, sizeof(synor_economics_t));
|
|
if (!economics) return NULL;
|
|
|
|
const char* endpoint = config->endpoint ?
|
|
config->endpoint : "https://economics.synor.io/v1";
|
|
uint32_t timeout = config->timeout_ms > 0 ? config->timeout_ms : 30000;
|
|
uint32_t retries = config->retries > 0 ? config->retries : 3;
|
|
|
|
economics->http = synor_http_client_create(
|
|
config->api_key, endpoint, timeout, retries, config->debug);
|
|
|
|
if (!economics->http) {
|
|
free(economics);
|
|
return NULL;
|
|
}
|
|
|
|
economics->closed = false;
|
|
economics->debug = config->debug;
|
|
return economics;
|
|
}
|
|
|
|
void synor_economics_destroy(synor_economics_t* economics) {
|
|
if (!economics) return;
|
|
economics->closed = true;
|
|
synor_http_client_destroy(economics->http);
|
|
free(economics);
|
|
}
|
|
|
|
bool synor_economics_is_closed(synor_economics_t* economics) {
|
|
return economics ? economics->closed : true;
|
|
}
|
|
|
|
bool synor_economics_health_check(synor_economics_t* economics) {
|
|
if (!economics || economics->closed) return false;
|
|
|
|
char* response = NULL;
|
|
size_t response_size = 0;
|
|
|
|
if (synor_http_get(economics->http, "/health", &response, &response_size) != 0) {
|
|
return false;
|
|
}
|
|
|
|
bool healthy = response && strstr(response, "\"healthy\"") != NULL;
|
|
free(response);
|
|
return healthy;
|
|
}
|
|
|
|
/* ==================== Pricing Operations ==================== */
|
|
|
|
synor_economics_error_t synor_economics_get_pricing(synor_economics_t* economics,
|
|
synor_service_type_t* service, synor_service_pricing_list_t* result) {
|
|
if (!economics || economics->closed) return SYNOR_ECONOMICS_ERROR_CLIENT_CLOSED;
|
|
(void)service; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_get_price(synor_economics_t* economics,
|
|
synor_service_type_t service, const synor_usage_metrics_t* usage,
|
|
synor_price_result_t* result) {
|
|
if (!economics || economics->closed) return SYNOR_ECONOMICS_ERROR_CLIENT_CLOSED;
|
|
if (!usage || !result) return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
|
|
char body[1024];
|
|
snprintf(body, sizeof(body),
|
|
"{\"service\":\"%s\",\"computeUnits\":%.2f,\"storageBytes\":%.2f,\"bandwidthBytes\":%.2f,\"durationSeconds\":%lld,\"requests\":%d}",
|
|
service_type_to_string(service),
|
|
usage->compute_units, usage->storage_bytes, usage->bandwidth_bytes,
|
|
(long long)usage->duration_seconds, usage->requests);
|
|
|
|
char* response = NULL;
|
|
size_t response_size = 0;
|
|
|
|
int err = synor_http_post(economics->http, "/pricing/calculate", body, &response, &response_size);
|
|
if (err != 0) {
|
|
free(response);
|
|
return SYNOR_ECONOMICS_ERROR_HTTP;
|
|
}
|
|
|
|
/* TODO: Parse JSON response */
|
|
free(response);
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_estimate_cost(synor_economics_t* economics,
|
|
const synor_usage_plan_t* plan, synor_cost_estimate_t* result) {
|
|
(void)economics; (void)plan; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
void synor_service_pricing_free(synor_service_pricing_t* pricing) {
|
|
if (!pricing) return;
|
|
free(pricing->unit);
|
|
free(pricing->price_per_unit);
|
|
free(pricing->currency);
|
|
}
|
|
|
|
void synor_service_pricing_list_free(synor_service_pricing_list_t* list) {
|
|
if (!list) return;
|
|
for (size_t i = 0; i < list->count; i++) {
|
|
synor_service_pricing_free(&list->prices[i]);
|
|
}
|
|
free(list->prices);
|
|
list->prices = NULL;
|
|
list->count = 0;
|
|
}
|
|
|
|
void synor_price_result_free(synor_price_result_t* result) {
|
|
if (!result) return;
|
|
free(result->amount);
|
|
free(result->currency);
|
|
}
|
|
|
|
void synor_cost_estimate_free(synor_cost_estimate_t* estimate) {
|
|
if (!estimate) return;
|
|
free(estimate->total);
|
|
free(estimate->currency);
|
|
for (size_t i = 0; i < estimate->breakdown_count; i++) {
|
|
free(estimate->breakdown[i].amount);
|
|
}
|
|
free(estimate->breakdown);
|
|
free(estimate->discount_applied);
|
|
}
|
|
|
|
/* ==================== Billing Operations ==================== */
|
|
|
|
synor_economics_error_t synor_economics_get_usage(synor_economics_t* economics,
|
|
synor_billing_period_t* period, synor_usage_t* result) {
|
|
if (!economics || economics->closed) return SYNOR_ECONOMICS_ERROR_CLIENT_CLOSED;
|
|
|
|
char path[256];
|
|
if (period) {
|
|
snprintf(path, sizeof(path), "/billing/usage?period=%s", billing_period_to_string(*period));
|
|
} else {
|
|
snprintf(path, sizeof(path), "/billing/usage");
|
|
}
|
|
|
|
char* response = NULL;
|
|
size_t response_size = 0;
|
|
|
|
int err = synor_http_get(economics->http, path, &response, &response_size);
|
|
if (err != 0) {
|
|
free(response);
|
|
return SYNOR_ECONOMICS_ERROR_HTTP;
|
|
}
|
|
|
|
/* TODO: Parse JSON response */
|
|
(void)result;
|
|
free(response);
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_get_invoices(synor_economics_t* economics,
|
|
synor_invoice_list_t* result) {
|
|
(void)economics; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_get_invoice(synor_economics_t* economics,
|
|
const char* invoice_id, synor_invoice_t* result) {
|
|
(void)economics; (void)invoice_id; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_get_balance(synor_economics_t* economics,
|
|
synor_account_balance_t* result) {
|
|
(void)economics; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
void synor_usage_free(synor_usage_t* usage) {
|
|
if (!usage) return;
|
|
for (size_t i = 0; i < usage->items_count; i++) {
|
|
free(usage->items[i].cost);
|
|
}
|
|
free(usage->items);
|
|
free(usage->total_cost);
|
|
free(usage->currency);
|
|
}
|
|
|
|
void synor_invoice_free(synor_invoice_t* invoice) {
|
|
if (!invoice) return;
|
|
free(invoice->id);
|
|
for (size_t i = 0; i < invoice->lines_count; i++) {
|
|
free(invoice->lines[i].service);
|
|
free(invoice->lines[i].description);
|
|
free(invoice->lines[i].unit_price);
|
|
free(invoice->lines[i].amount);
|
|
}
|
|
free(invoice->lines);
|
|
free(invoice->subtotal);
|
|
free(invoice->discount);
|
|
free(invoice->tax);
|
|
free(invoice->total);
|
|
free(invoice->currency);
|
|
free(invoice->pdf_url);
|
|
}
|
|
|
|
void synor_invoice_list_free(synor_invoice_list_t* list) {
|
|
if (!list) return;
|
|
for (size_t i = 0; i < list->count; i++) {
|
|
synor_invoice_free(&list->invoices[i]);
|
|
}
|
|
free(list->invoices);
|
|
list->invoices = NULL;
|
|
list->count = 0;
|
|
}
|
|
|
|
void synor_account_balance_free(synor_account_balance_t* balance) {
|
|
if (!balance) return;
|
|
free(balance->available);
|
|
free(balance->pending);
|
|
free(balance->staked);
|
|
free(balance->total);
|
|
free(balance->currency);
|
|
}
|
|
|
|
/* ==================== Staking Operations ==================== */
|
|
|
|
synor_economics_error_t synor_economics_stake(synor_economics_t* economics,
|
|
const char* amount, int32_t duration_days, synor_stake_receipt_t* result) {
|
|
if (!economics || economics->closed) return SYNOR_ECONOMICS_ERROR_CLIENT_CLOSED;
|
|
if (!amount || !result) return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
|
|
char body[256];
|
|
snprintf(body, sizeof(body), "{\"amount\":\"%s\",\"durationDays\":%d}", amount, duration_days);
|
|
|
|
char* response = NULL;
|
|
size_t response_size = 0;
|
|
|
|
int err = synor_http_post(economics->http, "/staking/stake", body, &response, &response_size);
|
|
if (err != 0) {
|
|
free(response);
|
|
return SYNOR_ECONOMICS_ERROR_HTTP;
|
|
}
|
|
|
|
/* TODO: Parse JSON response */
|
|
free(response);
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_unstake(synor_economics_t* economics,
|
|
const char* stake_id, synor_unstake_receipt_t* result) {
|
|
(void)economics; (void)stake_id; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_get_stakes(synor_economics_t* economics,
|
|
synor_stake_list_t* result) {
|
|
(void)economics; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_get_stake(synor_economics_t* economics,
|
|
const char* stake_id, synor_stake_t* result) {
|
|
(void)economics; (void)stake_id; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_get_staking_rewards(synor_economics_t* economics,
|
|
synor_staking_rewards_t* result) {
|
|
(void)economics; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_claim_rewards(synor_economics_t* economics,
|
|
synor_stake_receipt_t* result) {
|
|
(void)economics; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
void synor_stake_free(synor_stake_t* stake) {
|
|
if (!stake) return;
|
|
free(stake->id);
|
|
free(stake->amount);
|
|
free(stake->rewards_earned);
|
|
}
|
|
|
|
void synor_stake_list_free(synor_stake_list_t* list) {
|
|
if (!list) return;
|
|
for (size_t i = 0; i < list->count; i++) {
|
|
synor_stake_free(&list->stakes[i]);
|
|
}
|
|
free(list->stakes);
|
|
list->stakes = NULL;
|
|
list->count = 0;
|
|
}
|
|
|
|
void synor_stake_receipt_free(synor_stake_receipt_t* receipt) {
|
|
if (!receipt) return;
|
|
free(receipt->id);
|
|
free(receipt->amount);
|
|
free(receipt->tx_hash);
|
|
}
|
|
|
|
void synor_unstake_receipt_free(synor_unstake_receipt_t* receipt) {
|
|
if (!receipt) return;
|
|
free(receipt->id);
|
|
free(receipt->amount);
|
|
free(receipt->tx_hash);
|
|
}
|
|
|
|
void synor_staking_rewards_free(synor_staking_rewards_t* rewards) {
|
|
if (!rewards) return;
|
|
free(rewards->pending);
|
|
free(rewards->claimed);
|
|
free(rewards->total);
|
|
}
|
|
|
|
/* ==================== Discount Operations ==================== */
|
|
|
|
synor_economics_error_t synor_economics_apply_discount(synor_economics_t* economics,
|
|
const char* code, synor_discount_result_t* result) {
|
|
if (!economics || economics->closed) return SYNOR_ECONOMICS_ERROR_CLIENT_CLOSED;
|
|
if (!code || !result) return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
|
|
char body[256];
|
|
snprintf(body, sizeof(body), "{\"code\":\"%s\"}", code);
|
|
|
|
char* response = NULL;
|
|
size_t response_size = 0;
|
|
|
|
int err = synor_http_post(economics->http, "/discounts/apply", body, &response, &response_size);
|
|
if (err != 0) {
|
|
free(response);
|
|
return SYNOR_ECONOMICS_ERROR_HTTP;
|
|
}
|
|
|
|
/* TODO: Parse JSON response */
|
|
free(response);
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_remove_discount(synor_economics_t* economics,
|
|
const char* code) {
|
|
(void)economics; (void)code;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
synor_economics_error_t synor_economics_get_discounts(synor_economics_t* economics,
|
|
synor_discount_list_t* result) {
|
|
(void)economics; (void)result;
|
|
return SYNOR_ECONOMICS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
void synor_discount_free(synor_discount_t* discount) {
|
|
if (!discount) return;
|
|
free(discount->code);
|
|
free(discount->value);
|
|
free(discount->description);
|
|
free(discount->applicable_services);
|
|
}
|
|
|
|
void synor_discount_list_free(synor_discount_list_t* list) {
|
|
if (!list) return;
|
|
for (size_t i = 0; i < list->count; i++) {
|
|
synor_discount_free(&list->discounts[i]);
|
|
}
|
|
free(list->discounts);
|
|
list->discounts = NULL;
|
|
list->count = 0;
|
|
}
|
|
|
|
void synor_discount_result_free(synor_discount_result_t* result) {
|
|
if (!result) return;
|
|
synor_discount_free(&result->discount);
|
|
free(result->savings_estimate);
|
|
}
|