synor/sdk/c/src/governance/governance.c
Gulshan Yadav 6607223c9e feat(sdk): complete Phase 4 SDKs for all remaining languages
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).
2026-01-28 08:33:20 +05:30

524 lines
18 KiB
C

/**
* @file governance.c
* @brief Synor Governance SDK implementation
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "synor/governance.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 governance structure */
struct synor_governance {
synor_http_client_t* http;
bool closed;
bool debug;
};
static const synor_proposal_status_t FINAL_STATUSES[] = {
SYNOR_PROPOSAL_PASSED,
SYNOR_PROPOSAL_REJECTED,
SYNOR_PROPOSAL_EXECUTED,
SYNOR_PROPOSAL_CANCELLED
};
static const size_t FINAL_STATUSES_COUNT = sizeof(FINAL_STATUSES) / sizeof(FINAL_STATUSES[0]);
static bool is_final_status(synor_proposal_status_t status) {
for (size_t i = 0; i < FINAL_STATUSES_COUNT; i++) {
if (FINAL_STATUSES[i] == status) return true;
}
return false;
}
static const char* vote_choice_to_string(synor_vote_choice_t choice) {
switch (choice) {
case SYNOR_VOTE_FOR: return "for";
case SYNOR_VOTE_AGAINST: return "against";
case SYNOR_VOTE_ABSTAIN: return "abstain";
default: return "abstain";
}
}
static const char* dao_type_to_string(synor_dao_type_t type) {
switch (type) {
case SYNOR_DAO_TOKEN: return "token";
case SYNOR_DAO_MULTISIG: return "multisig";
case SYNOR_DAO_HYBRID: return "hybrid";
default: return "token";
}
}
synor_governance_t* synor_governance_create(const synor_governance_config_t* config) {
if (!config || !config->api_key) {
return NULL;
}
synor_governance_t* governance = calloc(1, sizeof(synor_governance_t));
if (!governance) return NULL;
const char* endpoint = config->endpoint ?
config->endpoint : "https://governance.synor.io/v1";
uint32_t timeout = config->timeout_ms > 0 ? config->timeout_ms : 30000;
uint32_t retries = config->retries > 0 ? config->retries : 3;
governance->http = synor_http_client_create(
config->api_key, endpoint, timeout, retries, config->debug);
if (!governance->http) {
free(governance);
return NULL;
}
governance->closed = false;
governance->debug = config->debug;
return governance;
}
void synor_governance_destroy(synor_governance_t* governance) {
if (!governance) return;
governance->closed = true;
synor_http_client_destroy(governance->http);
free(governance);
}
bool synor_governance_is_closed(synor_governance_t* governance) {
return governance ? governance->closed : true;
}
bool synor_governance_health_check(synor_governance_t* governance) {
if (!governance || governance->closed) return false;
char* response = NULL;
size_t response_size = 0;
if (synor_http_get(governance->http, "/health", &response, &response_size) != 0) {
return false;
}
bool healthy = response && strstr(response, "\"healthy\"") != NULL;
free(response);
return healthy;
}
/* ==================== Proposal Operations ==================== */
synor_governance_error_t synor_governance_create_proposal(synor_governance_t* governance,
const synor_proposal_draft_t* draft, synor_proposal_t* result) {
if (!governance || governance->closed) return SYNOR_GOVERNANCE_ERROR_CLIENT_CLOSED;
if (!draft || !result) return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
char body[4096];
snprintf(body, sizeof(body),
"{\"title\":\"%s\",\"description\":\"%s\"}",
draft->title, draft->description);
char* response = NULL;
size_t response_size = 0;
int err = synor_http_post(governance->http, "/proposals", body, &response, &response_size);
if (err != 0) {
free(response);
return SYNOR_GOVERNANCE_ERROR_HTTP;
}
/* TODO: Parse JSON response */
free(response);
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_proposal(synor_governance_t* governance,
const char* proposal_id, synor_proposal_t* result) {
(void)governance; (void)proposal_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_list_proposals(synor_governance_t* governance,
const synor_proposal_filter_t* filter, synor_proposal_list_t* result) {
(void)governance; (void)filter; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_cancel_proposal(synor_governance_t* governance,
const char* proposal_id, synor_proposal_t* result) {
(void)governance; (void)proposal_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_execute_proposal(synor_governance_t* governance,
const char* proposal_id, synor_proposal_t* result) {
(void)governance; (void)proposal_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_wait_for_proposal(synor_governance_t* governance,
const char* proposal_id, uint32_t poll_interval_ms, uint32_t max_wait_ms,
synor_proposal_t* result) {
(void)governance; (void)proposal_id; (void)poll_interval_ms;
(void)max_wait_ms; (void)result;
(void)is_final_status;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
void synor_proposal_free(synor_proposal_t* proposal) {
if (!proposal) return;
free(proposal->id);
free(proposal->title);
free(proposal->description);
free(proposal->discussion_url);
free(proposal->proposer);
free(proposal->vote_tally.for_votes);
free(proposal->vote_tally.against_votes);
free(proposal->vote_tally.abstain_votes);
free(proposal->vote_tally.quorum);
free(proposal->vote_tally.quorum_required);
for (size_t i = 0; i < proposal->actions_count; i++) {
free(proposal->actions[i].target);
free(proposal->actions[i].method);
free(proposal->actions[i].data);
free(proposal->actions[i].value);
}
free(proposal->actions);
free(proposal->dao_id);
}
void synor_proposal_list_free(synor_proposal_list_t* list) {
if (!list) return;
for (size_t i = 0; i < list->count; i++) {
synor_proposal_free(&list->proposals[i]);
}
free(list->proposals);
list->proposals = NULL;
list->count = 0;
}
/* ==================== Voting Operations ==================== */
synor_governance_error_t synor_governance_vote(synor_governance_t* governance,
const char* proposal_id, const synor_vote_t* vote, const char* weight,
synor_vote_receipt_t* result) {
if (!governance || governance->closed) return SYNOR_GOVERNANCE_ERROR_CLIENT_CLOSED;
if (!proposal_id || !vote || !result) return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
char body[512];
if (vote->reason && weight) {
snprintf(body, sizeof(body),
"{\"choice\":\"%s\",\"reason\":\"%s\",\"weight\":\"%s\"}",
vote_choice_to_string(vote->choice), vote->reason, weight);
} else if (vote->reason) {
snprintf(body, sizeof(body),
"{\"choice\":\"%s\",\"reason\":\"%s\"}",
vote_choice_to_string(vote->choice), vote->reason);
} else if (weight) {
snprintf(body, sizeof(body),
"{\"choice\":\"%s\",\"weight\":\"%s\"}",
vote_choice_to_string(vote->choice), weight);
} else {
snprintf(body, sizeof(body), "{\"choice\":\"%s\"}", vote_choice_to_string(vote->choice));
}
char path[256];
snprintf(path, sizeof(path), "/proposals/%s/vote", proposal_id);
char* response = NULL;
size_t response_size = 0;
int err = synor_http_post(governance->http, path, body, &response, &response_size);
if (err != 0) {
free(response);
return SYNOR_GOVERNANCE_ERROR_HTTP;
}
/* TODO: Parse JSON response */
free(response);
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_votes(synor_governance_t* governance,
const char* proposal_id, synor_vote_receipt_list_t* result) {
(void)governance; (void)proposal_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_my_vote(synor_governance_t* governance,
const char* proposal_id, synor_vote_receipt_t* result) {
(void)governance; (void)proposal_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_delegate(synor_governance_t* governance,
const char* delegatee, const char* amount, synor_delegation_receipt_t* result) {
if (!governance || governance->closed) return SYNOR_GOVERNANCE_ERROR_CLIENT_CLOSED;
if (!delegatee || !result) return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
char body[256];
if (amount) {
snprintf(body, sizeof(body), "{\"delegatee\":\"%s\",\"amount\":\"%s\"}", delegatee, amount);
} else {
snprintf(body, sizeof(body), "{\"delegatee\":\"%s\"}", delegatee);
}
char* response = NULL;
size_t response_size = 0;
int err = synor_http_post(governance->http, "/voting/delegate", body, &response, &response_size);
if (err != 0) {
free(response);
return SYNOR_GOVERNANCE_ERROR_HTTP;
}
/* TODO: Parse JSON response */
free(response);
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_undelegate(synor_governance_t* governance,
const char* delegatee, synor_delegation_receipt_t* result) {
(void)governance; (void)delegatee; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_voting_power(synor_governance_t* governance,
const char* address, synor_voting_power_t* result) {
(void)governance; (void)address; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_delegations(synor_governance_t* governance,
const char* address, synor_delegation_receipt_list_t* result) {
(void)governance; (void)address; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
void synor_vote_receipt_free(synor_vote_receipt_t* receipt) {
if (!receipt) return;
free(receipt->id);
free(receipt->proposal_id);
free(receipt->voter);
free(receipt->weight);
free(receipt->reason);
free(receipt->tx_hash);
}
void synor_vote_receipt_list_free(synor_vote_receipt_list_t* list) {
if (!list) return;
for (size_t i = 0; i < list->count; i++) {
synor_vote_receipt_free(&list->votes[i]);
}
free(list->votes);
list->votes = NULL;
list->count = 0;
}
void synor_voting_power_free(synor_voting_power_t* power) {
if (!power) return;
free(power->address);
free(power->delegated_power);
free(power->own_power);
free(power->total_power);
for (size_t i = 0; i < power->delegators_count; i++) {
free(power->delegators[i]);
}
free(power->delegators);
}
void synor_delegation_receipt_free(synor_delegation_receipt_t* receipt) {
if (!receipt) return;
free(receipt->id);
free(receipt->from);
free(receipt->to);
free(receipt->amount);
free(receipt->tx_hash);
}
void synor_delegation_receipt_list_free(synor_delegation_receipt_list_t* list) {
if (!list) return;
for (size_t i = 0; i < list->count; i++) {
synor_delegation_receipt_free(&list->delegations[i]);
}
free(list->delegations);
list->delegations = NULL;
list->count = 0;
}
/* ==================== DAO Operations ==================== */
synor_governance_error_t synor_governance_create_dao(synor_governance_t* governance,
const synor_dao_config_t* config, synor_dao_t* result) {
if (!governance || governance->closed) return SYNOR_GOVERNANCE_ERROR_CLIENT_CLOSED;
if (!config || !result) return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
char body[2048];
snprintf(body, sizeof(body),
"{\"name\":\"%s\",\"description\":\"%s\",\"type\":\"%s\",\"votingPeriodDays\":%d,\"timelockDays\":%d}",
config->name, config->description, dao_type_to_string(config->type),
config->voting_period_days, config->timelock_days);
char* response = NULL;
size_t response_size = 0;
int err = synor_http_post(governance->http, "/daos", body, &response, &response_size);
if (err != 0) {
free(response);
return SYNOR_GOVERNANCE_ERROR_HTTP;
}
/* TODO: Parse JSON response */
free(response);
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_dao(synor_governance_t* governance,
const char* dao_id, synor_dao_t* result) {
(void)governance; (void)dao_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_list_daos(synor_governance_t* governance,
int32_t limit, int32_t offset, synor_dao_list_t* result) {
(void)governance; (void)limit; (void)offset; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_dao_treasury(synor_governance_t* governance,
const char* dao_id, synor_dao_treasury_t* result) {
(void)governance; (void)dao_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_dao_members(synor_governance_t* governance,
const char* dao_id, char*** members, size_t* count) {
(void)governance; (void)dao_id; (void)members; (void)count;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
void synor_dao_free(synor_dao_t* dao) {
if (!dao) return;
free(dao->id);
free(dao->name);
free(dao->description);
free(dao->token_address);
free(dao->proposal_threshold);
free(dao->treasury_value);
}
void synor_dao_list_free(synor_dao_list_t* list) {
if (!list) return;
for (size_t i = 0; i < list->count; i++) {
synor_dao_free(&list->daos[i]);
}
free(list->daos);
list->daos = NULL;
list->count = 0;
}
void synor_dao_treasury_free(synor_dao_treasury_t* treasury) {
if (!treasury) return;
free(treasury->dao_id);
free(treasury->total_value);
for (size_t i = 0; i < treasury->tokens_count; i++) {
free(treasury->tokens[i].address);
free(treasury->tokens[i].balance);
free(treasury->tokens[i].name);
free(treasury->tokens[i].symbol);
}
free(treasury->tokens);
}
/* ==================== Vesting Operations ==================== */
synor_governance_error_t synor_governance_create_vesting(synor_governance_t* governance,
const synor_vesting_schedule_t* schedule, synor_vesting_contract_t* result) {
if (!governance || governance->closed) return SYNOR_GOVERNANCE_ERROR_CLIENT_CLOSED;
if (!schedule || !result) return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
char body[1024];
snprintf(body, sizeof(body),
"{\"beneficiary\":\"%s\",\"totalAmount\":\"%s\",\"startTime\":%lld,\"cliffDuration\":%lld,\"vestingDuration\":%lld,\"revocable\":%s}",
schedule->beneficiary, schedule->total_amount,
(long long)schedule->start_time, (long long)schedule->cliff_duration,
(long long)schedule->vesting_duration, schedule->revocable ? "true" : "false");
char* response = NULL;
size_t response_size = 0;
int err = synor_http_post(governance->http, "/vesting", body, &response, &response_size);
if (err != 0) {
free(response);
return SYNOR_GOVERNANCE_ERROR_HTTP;
}
/* TODO: Parse JSON response */
free(response);
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_vesting(synor_governance_t* governance,
const char* contract_id, synor_vesting_contract_t* result) {
(void)governance; (void)contract_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_list_vesting(synor_governance_t* governance,
const char* beneficiary, synor_vesting_contract_list_t* result) {
(void)governance; (void)beneficiary; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_claim_vested(synor_governance_t* governance,
const char* contract_id, synor_claim_receipt_t* result) {
(void)governance; (void)contract_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_revoke_vesting(synor_governance_t* governance,
const char* contract_id, synor_vesting_contract_t* result) {
(void)governance; (void)contract_id; (void)result;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
synor_governance_error_t synor_governance_get_releasable(synor_governance_t* governance,
const char* contract_id, char** amount) {
(void)governance; (void)contract_id; (void)amount;
return SYNOR_GOVERNANCE_ERROR_UNKNOWN;
}
void synor_vesting_contract_free(synor_vesting_contract_t* contract) {
if (!contract) return;
free(contract->id);
free(contract->beneficiary);
free(contract->grantor);
free(contract->total_amount);
free(contract->released_amount);
free(contract->releasable_amount);
}
void synor_vesting_contract_list_free(synor_vesting_contract_list_t* list) {
if (!list) return;
for (size_t i = 0; i < list->count; i++) {
synor_vesting_contract_free(&list->contracts[i]);
}
free(list->contracts);
list->contracts = NULL;
list->count = 0;
}
void synor_claim_receipt_free(synor_claim_receipt_t* receipt) {
if (!receipt) return;
free(receipt->id);
free(receipt->contract_id);
free(receipt->amount);
free(receipt->tx_hash);
}