synor/sdk/go/wallet/wallet.go
Gulshan Yadav 59a7123535 feat(sdk): implement Phase 1 SDKs for Wallet, RPC, and Storage
Implements comprehensive SDK support for three core services across
four programming languages (JavaScript/TypeScript, Python, Go, Rust).

## New SDKs

### Wallet SDK
- Key management (create, import, export)
- Transaction signing
- Message signing and verification
- Balance and UTXO queries
- Stealth address support

### RPC SDK
- Block and transaction queries
- Chain state information
- Fee estimation
- Mempool information
- WebSocket subscriptions for real-time updates

### Storage SDK
- Content upload and download
- Pinning operations
- CAR file support
- Directory management
- Gateway URL generation

## Shared Infrastructure

- JSON Schema definitions for all 11 services
- Common type definitions (Address, Amount, UTXO, etc.)
- Unified error handling patterns
- Builder patterns for configuration

## Package Updates

- JavaScript: Updated to @synor/sdk with module exports
- Python: Updated to synor-sdk with websockets dependency
- Go: Added gorilla/websocket dependency
- Rust: Added base64, urlencoding, multipart support

## Fixes

- Fixed Tensor Default trait implementation
- Fixed ProcessorType enum casing
2026-01-27 00:46:24 +05:30

507 lines
14 KiB
Go

// Package wallet provides a Go SDK for Synor Wallet operations.
//
// Manage wallets, sign transactions, and query balances on the Synor blockchain.
//
// Example:
//
// client := wallet.NewClient("your-api-key")
// result, err := client.CreateWallet(ctx, wallet.Standard)
// fmt.Println("Address:", result.Wallet.Address)
// fmt.Println("Mnemonic:", result.Mnemonic) // Store securely!
package wallet
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
// Version of the SDK.
const Version = "0.1.0"
// DefaultEndpoint is the default API endpoint.
const DefaultEndpoint = "https://wallet.synor.cc/api/v1"
// Network represents the blockchain network.
type Network string
const (
Mainnet Network = "mainnet"
Testnet Network = "testnet"
)
// WalletType represents the type of wallet.
type WalletType string
const (
Standard WalletType = "standard"
Multisig WalletType = "multisig"
Stealth WalletType = "stealth"
Hardware WalletType = "hardware"
)
// Priority represents transaction priority levels.
type Priority string
const (
Low Priority = "low"
Medium Priority = "medium"
High Priority = "high"
Urgent Priority = "urgent"
)
// Config holds client configuration.
type Config struct {
APIKey string
Endpoint string
Network Network
Timeout time.Duration
Debug bool
DerivationPath string
}
// DefaultConfig returns a default configuration.
func DefaultConfig(apiKey string) Config {
return Config{
APIKey: apiKey,
Endpoint: DefaultEndpoint,
Network: Mainnet,
Timeout: 30 * time.Second,
Debug: false,
}
}
// Client is the Synor Wallet client.
type Client struct {
config Config
httpClient *http.Client
}
// NewClient creates a new client with the given API key.
func NewClient(apiKey string) *Client {
return NewClientWithConfig(DefaultConfig(apiKey))
}
// NewClientWithConfig creates a new client with custom configuration.
func NewClientWithConfig(config Config) *Client {
return &Client{
config: config,
httpClient: &http.Client{
Timeout: config.Timeout,
},
}
}
// Wallet represents a wallet instance.
type Wallet struct {
ID string `json:"id"`
Address string `json:"address"`
PublicKey string `json:"publicKey"`
Type WalletType `json:"type"`
CreatedAt int64 `json:"createdAt"`
}
// CreateWalletResult contains the created wallet and mnemonic.
type CreateWalletResult struct {
Wallet Wallet `json:"wallet"`
Mnemonic string `json:"mnemonic"`
}
// StealthAddress represents a stealth address for private payments.
type StealthAddress struct {
Address string `json:"address"`
ViewKey string `json:"viewKey"`
SpendKey string `json:"spendKey"`
EphemeralKey string `json:"ephemeralKey,omitempty"`
}
// TransactionInput represents a transaction input.
type TransactionInput struct {
TxID string `json:"txid"`
Vout int `json:"vout"`
Amount string `json:"amount"`
ScriptSig string `json:"scriptSig,omitempty"`
}
// TransactionOutput represents a transaction output.
type TransactionOutput struct {
Address string `json:"address"`
Amount string `json:"amount"`
ScriptPubKey string `json:"scriptPubKey,omitempty"`
}
// Transaction represents an unsigned transaction.
type Transaction struct {
Version int `json:"version"`
Inputs []TransactionInput `json:"inputs"`
Outputs []TransactionOutput `json:"outputs"`
LockTime int `json:"lockTime,omitempty"`
Fee string `json:"fee,omitempty"`
}
// SignedTransaction represents a signed transaction.
type SignedTransaction struct {
Raw string `json:"raw"`
TxID string `json:"txid"`
Size int `json:"size"`
Weight int `json:"weight,omitempty"`
}
// SignedMessage represents a signed message.
type SignedMessage struct {
Signature string `json:"signature"`
PublicKey string `json:"publicKey"`
Address string `json:"address"`
}
// UTXO represents an unspent transaction output.
type UTXO struct {
TxID string `json:"txid"`
Vout int `json:"vout"`
Amount string `json:"amount"`
Address string `json:"address"`
Confirmations int `json:"confirmations"`
ScriptPubKey string `json:"scriptPubKey,omitempty"`
}
// Balance represents balance information.
type Balance struct {
Confirmed string `json:"confirmed"`
Unconfirmed string `json:"unconfirmed"`
Total string `json:"total"`
}
// TokenBalance represents a token balance.
type TokenBalance struct {
Token string `json:"token"`
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
Balance string `json:"balance"`
}
// BalanceResponse contains native and token balances.
type BalanceResponse struct {
Native Balance `json:"native"`
Tokens []TokenBalance `json:"tokens,omitempty"`
}
// FeeEstimate represents a fee estimation result.
type FeeEstimate struct {
Priority Priority `json:"priority"`
FeeRate string `json:"feeRate"`
EstimatedBlocks int `json:"estimatedBlocks"`
}
// ImportWalletOptions contains options for importing a wallet.
type ImportWalletOptions struct {
Mnemonic string
Passphrase string
Type WalletType
}
// BuildTransactionOptions contains options for building a transaction.
type BuildTransactionOptions struct {
To string
Amount string
FeeRate float64
UTXOs []UTXO
ChangeAddress string
}
// GetUtxosOptions contains options for querying UTXOs.
type GetUtxosOptions struct {
MinConfirmations int
MinAmount string
}
// CreateWallet creates a new wallet.
func (c *Client) CreateWallet(ctx context.Context, walletType WalletType) (*CreateWalletResult, error) {
body := map[string]interface{}{
"type": walletType,
"network": c.config.Network,
}
if c.config.DerivationPath != "" {
body["derivationPath"] = c.config.DerivationPath
}
var result CreateWalletResult
if err := c.request(ctx, "POST", "/wallets", body, &result); err != nil {
return nil, err
}
return &result, nil
}
// ImportWallet imports a wallet from mnemonic phrase.
func (c *Client) ImportWallet(ctx context.Context, opts ImportWalletOptions) (*Wallet, error) {
body := map[string]interface{}{
"mnemonic": opts.Mnemonic,
"passphrase": opts.Passphrase,
"type": opts.Type,
"network": c.config.Network,
}
if c.config.DerivationPath != "" {
body["derivationPath"] = c.config.DerivationPath
}
var resp struct {
Wallet Wallet `json:"wallet"`
}
if err := c.request(ctx, "POST", "/wallets/import", body, &resp); err != nil {
return nil, err
}
return &resp.Wallet, nil
}
// GetWallet gets a wallet by ID.
func (c *Client) GetWallet(ctx context.Context, walletID string) (*Wallet, error) {
var resp struct {
Wallet Wallet `json:"wallet"`
}
if err := c.request(ctx, "GET", "/wallets/"+walletID, nil, &resp); err != nil {
return nil, err
}
return &resp.Wallet, nil
}
// ListWallets lists all wallets for this account.
func (c *Client) ListWallets(ctx context.Context) ([]Wallet, error) {
var resp struct {
Wallets []Wallet `json:"wallets"`
}
if err := c.request(ctx, "GET", "/wallets", nil, &resp); err != nil {
return nil, err
}
return resp.Wallets, nil
}
// GetAddress gets an address at a specific index for a wallet.
func (c *Client) GetAddress(ctx context.Context, walletID string, index int) (string, error) {
path := fmt.Sprintf("/wallets/%s/addresses/%d", walletID, index)
var resp struct {
Address string `json:"address"`
}
if err := c.request(ctx, "GET", path, nil, &resp); err != nil {
return "", err
}
return resp.Address, nil
}
// GetStealthAddress generates a stealth address for receiving private payments.
func (c *Client) GetStealthAddress(ctx context.Context, walletID string) (*StealthAddress, error) {
path := fmt.Sprintf("/wallets/%s/stealth", walletID)
var resp struct {
StealthAddress StealthAddress `json:"stealthAddress"`
}
if err := c.request(ctx, "POST", path, nil, &resp); err != nil {
return nil, err
}
return &resp.StealthAddress, nil
}
// SignTransaction signs a transaction.
func (c *Client) SignTransaction(ctx context.Context, walletID string, tx *Transaction) (*SignedTransaction, error) {
path := fmt.Sprintf("/wallets/%s/sign", walletID)
body := map[string]interface{}{
"transaction": tx,
}
var resp struct {
SignedTransaction SignedTransaction `json:"signedTransaction"`
}
if err := c.request(ctx, "POST", path, body, &resp); err != nil {
return nil, err
}
return &resp.SignedTransaction, nil
}
// SignMessage signs a message.
func (c *Client) SignMessage(ctx context.Context, walletID, message, format string) (*SignedMessage, error) {
if format == "" {
format = "text"
}
path := fmt.Sprintf("/wallets/%s/sign-message", walletID)
body := map[string]interface{}{
"message": message,
"format": format,
}
var result SignedMessage
if err := c.request(ctx, "POST", path, body, &result); err != nil {
return nil, err
}
return &result, nil
}
// VerifyMessage verifies a signed message.
func (c *Client) VerifyMessage(ctx context.Context, message, signature, address string) (bool, error) {
body := map[string]interface{}{
"message": message,
"signature": signature,
"address": address,
}
var resp struct {
Valid bool `json:"valid"`
}
if err := c.request(ctx, "POST", "/verify-message", body, &resp); err != nil {
return false, err
}
return resp.Valid, nil
}
// GetBalance gets the balance for an address.
func (c *Client) GetBalance(ctx context.Context, address string, includeTokens bool) (*BalanceResponse, error) {
path := fmt.Sprintf("/balances/%s?includeTokens=%t", address, includeTokens)
var result BalanceResponse
if err := c.request(ctx, "GET", path, nil, &result); err != nil {
return nil, err
}
return &result, nil
}
// GetUTXOs gets UTXOs for an address.
func (c *Client) GetUTXOs(ctx context.Context, address string, opts *GetUtxosOptions) ([]UTXO, error) {
path := "/utxos/" + address
if opts != nil {
params := url.Values{}
if opts.MinConfirmations > 0 {
params.Set("minConfirmations", fmt.Sprintf("%d", opts.MinConfirmations))
}
if opts.MinAmount != "" {
params.Set("minAmount", opts.MinAmount)
}
if len(params) > 0 {
path += "?" + params.Encode()
}
}
var resp struct {
UTXOs []UTXO `json:"utxos"`
}
if err := c.request(ctx, "GET", path, nil, &resp); err != nil {
return nil, err
}
return resp.UTXOs, nil
}
// BuildTransaction builds a transaction without signing.
func (c *Client) BuildTransaction(ctx context.Context, walletID string, opts BuildTransactionOptions) (*Transaction, error) {
path := fmt.Sprintf("/wallets/%s/build-tx", walletID)
body := map[string]interface{}{
"to": opts.To,
"amount": opts.Amount,
}
if opts.FeeRate > 0 {
body["feeRate"] = opts.FeeRate
}
if len(opts.UTXOs) > 0 {
body["utxos"] = opts.UTXOs
}
if opts.ChangeAddress != "" {
body["changeAddress"] = opts.ChangeAddress
}
var resp struct {
Transaction Transaction `json:"transaction"`
}
if err := c.request(ctx, "POST", path, body, &resp); err != nil {
return nil, err
}
return &resp.Transaction, nil
}
// SendTransaction builds and signs a transaction in one step.
func (c *Client) SendTransaction(ctx context.Context, walletID string, opts BuildTransactionOptions) (*SignedTransaction, error) {
tx, err := c.BuildTransaction(ctx, walletID, opts)
if err != nil {
return nil, err
}
return c.SignTransaction(ctx, walletID, tx)
}
// EstimateFee estimates the transaction fee.
func (c *Client) EstimateFee(ctx context.Context, priority Priority) (*FeeEstimate, error) {
path := fmt.Sprintf("/fees/estimate?priority=%s", priority)
var result FeeEstimate
if err := c.request(ctx, "GET", path, nil, &result); err != nil {
return nil, err
}
return &result, nil
}
// GetAllFeeEstimates gets fee estimates for all priority levels.
func (c *Client) GetAllFeeEstimates(ctx context.Context) ([]FeeEstimate, error) {
var resp struct {
Estimates []FeeEstimate `json:"estimates"`
}
if err := c.request(ctx, "GET", "/fees/estimate/all", nil, &resp); err != nil {
return nil, err
}
return resp.Estimates, nil
}
func (c *Client) request(ctx context.Context, method, path string, body interface{}, result interface{}) error {
reqURL := c.config.Endpoint + path
var bodyReader io.Reader
if body != nil {
bodyBytes, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("failed to marshal request: %w", err)
}
bodyReader = bytes.NewReader(bodyBytes)
}
req, err := http.NewRequestWithContext(ctx, method, reqURL, bodyReader)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+c.config.APIKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Network", string(c.config.Network))
if c.config.Debug {
fmt.Printf("[SynorWallet] %s %s\n", method, path)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
var errResp struct {
Message string `json:"message"`
Code string `json:"code"`
}
json.NewDecoder(resp.Body).Decode(&errResp)
return &WalletError{
Message: errResp.Message,
StatusCode: resp.StatusCode,
Code: errResp.Code,
}
}
if result != nil {
if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
return fmt.Errorf("failed to decode response: %w", err)
}
}
return nil
}
// WalletError represents an API error.
type WalletError struct {
Message string
StatusCode int
Code string
}
func (e *WalletError) Error() string {
return fmt.Sprintf("wallet: %s (status %d)", e.Message, e.StatusCode)
}