mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
Feat/add networks (#1303)
* No commit suggestions generated * No commit suggestions generated * No commit suggestions generated
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
# Sonr Script Library
|
||||
|
||||
This directory contains modular helper scripts for Sonr blockchain operations. These helpers provide reusable functions for common tasks like environment setup, configuration management, key handling, and transaction operations.
|
||||
|
||||
## Library Structure
|
||||
|
||||
### `env.sh` - Environment Management
|
||||
Centralizes default environment variables and provides utility functions for logging, validation, and system checks.
|
||||
|
||||
**Key Functions:**
|
||||
- `init_env()` - Initialize environment with required tools and cleanup
|
||||
- `log_info()`, `log_warn()`, `log_error()`, `log_success()` - Colored logging
|
||||
- `require_cmd()`, `ensure_file()`, `ensure_binary()` - Validation helpers
|
||||
- `is_docker()` - Check if running in Docker container
|
||||
|
||||
**Environment Variables:**
|
||||
- `DENOM=usnr` - Default denomination
|
||||
- `CHAIN_BIN=snrd` - Binary name
|
||||
- `CHAIN_DIR=~/.sonr` - Chain data directory
|
||||
- `CHAIN_ID=sonrtest_1-1` - Default chain ID
|
||||
|
||||
### `jq_patch.sh` - JSON Operations
|
||||
Provides safe JSON patching operations using `jq` with temporary files and validation.
|
||||
|
||||
**Key Functions:**
|
||||
- `patch_json(file, jq_expr)` - Apply jq expression to file
|
||||
- `set_json_string()`, `set_json_number()`, `set_json_bool()` - Type-safe setters
|
||||
- `json_path_exists()`, `get_json_value()` - Query helpers
|
||||
- `validate_json()` - JSON validation
|
||||
|
||||
### `config.sh` - Configuration Management
|
||||
Handles TOML configuration files for Cosmos SDK nodes using `crudini` or `sed` fallbacks.
|
||||
|
||||
**Key Functions:**
|
||||
- `configure_node()` - Complete node configuration with ports and settings
|
||||
- `enable_rpc()`, `enable_rest()`, `enable_grpc()` - Enable services
|
||||
- `set_consensus_timeouts()`, `set_min_gas_prices()` - Parameter setters
|
||||
- `set_toml_value()` - Generic TOML value setter
|
||||
|
||||
### `keys.sh` - Key Management
|
||||
Provides functions for importing, managing, and using cryptographic keys.
|
||||
|
||||
**Key Functions:**
|
||||
- `import_mnemonic()` - Import key from mnemonic phrase
|
||||
- `ensure_key()`, `get_key_address()` - Key validation and retrieval
|
||||
- `fund_key()`, `delegate_to_validator()` - Account operations
|
||||
- `wait_for_sync()` - Node synchronization waiting
|
||||
|
||||
### `tx.sh` - Transaction Operations
|
||||
Handles blockchain transactions with error handling and retry logic.
|
||||
|
||||
**Key Functions:**
|
||||
- `submit_tx()` - Submit transaction with gas and error handling
|
||||
- `query_chain()` - Query blockchain state
|
||||
- `submit_proposal()`, `vote_proposal()` - Governance operations
|
||||
- `stake_tokens()`, `get_balance()` - Staking operations
|
||||
|
||||
### `genesis.sh` - Genesis File Operations
|
||||
Specialized functions for genesis file creation and modification.
|
||||
|
||||
**Key Functions:**
|
||||
- `generate_vrf_key()` - Generate VRF keypair for network
|
||||
- `update_genesis_params()` - Apply Sonr-specific genesis parameters
|
||||
- `add_constitution()` - Add constitution to governance
|
||||
- `validate_genesis()` - Genesis file validation
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Environment Setup
|
||||
```bash
|
||||
#!/bin/bash
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/lib/env.sh"
|
||||
source "${SCRIPT_DIR}/lib/config.sh"
|
||||
|
||||
init_env
|
||||
ensure_chain_dir
|
||||
configure_node "$CHAIN_DIR" --rpc-port 26657 --rest-port 1317
|
||||
```
|
||||
|
||||
### Key Management
|
||||
```bash
|
||||
#!/bin/bash
|
||||
source "scripts/lib/keys.sh"
|
||||
import_mnemonic "mykey" "word1 word2 word3..." "eth_secp256k1"
|
||||
fund_key "mykey" "1000000usnr"
|
||||
```
|
||||
|
||||
### Genesis Creation
|
||||
```bash
|
||||
#!/bin/bash
|
||||
source "scripts/lib/genesis.sh"
|
||||
update_genesis_params
|
||||
add_constitution
|
||||
generate_vrf_key "$CHAIN_DIR"
|
||||
```
|
||||
|
||||
## Integration Guidelines
|
||||
|
||||
1. **Always source required libraries** at the top of scripts
|
||||
2. **Call `init_env()`** early to set up logging and validation
|
||||
3. **Use consistent error handling** with the provided logging functions
|
||||
4. **Validate inputs** using `ensure_*` functions before operations
|
||||
5. **Handle Docker vs local execution** in wrapper functions
|
||||
6. **Use descriptive log messages** for user feedback
|
||||
|
||||
## Error Handling
|
||||
|
||||
All functions use consistent error handling:
|
||||
- Functions return 0 on success, 1 on failure
|
||||
- Use `log_error()` for user-visible errors
|
||||
- Use `log_warn()` for non-fatal issues
|
||||
- Cleanup temporary files automatically via `trap`
|
||||
|
||||
## Docker Compatibility
|
||||
|
||||
All functions support both local and Docker execution modes:
|
||||
- Use `is_docker()` to detect environment
|
||||
- Use `run_binary()` wrapper for command execution
|
||||
- Mount volumes correctly for Docker containers
|
||||
- Handle TTY and interactive mode appropriately
|
||||
@@ -0,0 +1,341 @@
|
||||
#!/bin/bash
|
||||
|
||||
# scripts/lib/config.sh - Configuration file utilities for TOML and other formats
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source environment and JSON helpers
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/env.sh"
|
||||
source "${SCRIPT_DIR}/jq_patch.sh"
|
||||
|
||||
# Check if crudini is available for TOML operations
|
||||
CRUDINI_AVAILABLE=false
|
||||
if command -v crudini >/dev/null 2>&1; then
|
||||
CRUDINI_AVAILABLE=true
|
||||
fi
|
||||
|
||||
# Set TOML value using crudini if available, fallback to sed
|
||||
# Usage: set_toml_value <file> <section> <key> <value>
|
||||
set_toml_value() {
|
||||
local file="$1"
|
||||
local section="$2"
|
||||
local key="$3"
|
||||
local value="$4"
|
||||
|
||||
ensure_file "$file"
|
||||
|
||||
if [[ "$CRUDINI_AVAILABLE" == "true" ]]; then
|
||||
if crudini --set "$file" "$section" "$key" "$value" 2>/dev/null; then
|
||||
log_success "Set $section.$key = $value in $file"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback to sed for simple cases
|
||||
log_warn "Using sed fallback for TOML update (install crudini for better support)"
|
||||
|
||||
# Escape special characters in value
|
||||
local escaped_value
|
||||
escaped_value=$(printf '%s\n' "$value" | sed 's/[[\.*^$()+?{|]/\\&/g')
|
||||
|
||||
# Try to find and replace the line
|
||||
if sed -i "s|^\s*$key\s*=.*|$key = \"$escaped_value\"|g" "$file"; then
|
||||
log_success "Set $key = $value in $file (using sed)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_error "Failed to set $section.$key in $file"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Enable RPC server
|
||||
# Usage: enable_rpc <config_file> [port]
|
||||
enable_rpc() {
|
||||
local config_file="$1"
|
||||
local port="${2:-26657}"
|
||||
|
||||
ensure_file "$config_file"
|
||||
|
||||
log_info "Enabling RPC server on port $port"
|
||||
|
||||
# Update laddr
|
||||
set_toml_value "$config_file" "" "laddr" "tcp://0.0.0.0:$port"
|
||||
|
||||
# Enable CORS
|
||||
set_toml_value "$config_file" "" "cors_allowed_origins" '["*"]'
|
||||
}
|
||||
|
||||
# Enable REST API server
|
||||
# Usage: enable_rest <app_config_file> [port]
|
||||
enable_rest() {
|
||||
local app_config_file="$1"
|
||||
local port="${2:-1317}"
|
||||
|
||||
ensure_file "$app_config_file"
|
||||
|
||||
log_info "Enabling REST API server on port $port"
|
||||
|
||||
# Update address
|
||||
set_toml_value "$app_config_file" "api" "address" "tcp://0.0.0.0:$port"
|
||||
|
||||
# Enable API
|
||||
set_toml_value "$app_config_file" "api" "enable" "true"
|
||||
|
||||
# Enable unsafe CORS
|
||||
set_toml_value "$app_config_file" "api" "enabled-unsafe-cors" "true"
|
||||
}
|
||||
|
||||
# Enable gRPC server
|
||||
# Usage: enable_grpc <app_config_file> [port]
|
||||
enable_grpc() {
|
||||
local app_config_file="$1"
|
||||
local port="${2:-9090}"
|
||||
|
||||
ensure_file "$app_config_file"
|
||||
|
||||
log_info "Enabling gRPC server on port $port"
|
||||
|
||||
# Update address
|
||||
set_toml_value "$app_config_file" "grpc" "address" "0.0.0.0:$port"
|
||||
}
|
||||
|
||||
# Enable gRPC-Web server
|
||||
# Usage: enable_grpc_web <app_config_file> [port]
|
||||
enable_grpc_web() {
|
||||
local app_config_file="$1"
|
||||
local port="${2:-9091}"
|
||||
|
||||
ensure_file "$app_config_file"
|
||||
|
||||
log_info "Enabling gRPC-Web server on port $port"
|
||||
|
||||
# Update address
|
||||
set_toml_value "$app_config_file" "grpc-web" "address" "0.0.0.0:$port"
|
||||
}
|
||||
|
||||
# Enable JSON-RPC
|
||||
# Usage: enable_json_rpc <app_config_file> [port] [ws_port]
|
||||
enable_json_rpc() {
|
||||
local app_config_file="$1"
|
||||
local port="${2:-8545}"
|
||||
local ws_port="${3:-8546}"
|
||||
|
||||
ensure_file "$app_config_file"
|
||||
|
||||
log_info "Enabling JSON-RPC on port $port (WebSocket: $ws_port)"
|
||||
|
||||
# Enable JSON-RPC
|
||||
set_toml_value "$app_config_file" "json-rpc" "enable" "true"
|
||||
|
||||
# Set address
|
||||
set_toml_value "$app_config_file" "json-rpc" "address" "0.0.0.0:$port"
|
||||
|
||||
# Set WebSocket address
|
||||
set_toml_value "$app_config_file" "json-rpc" "ws-address" "0.0.0.0:$ws_port"
|
||||
|
||||
# Enable APIs
|
||||
set_toml_value "$app_config_file" "json-rpc" "api" "eth,txpool,personal,net,debug,web3"
|
||||
}
|
||||
|
||||
# Enable Rosetta API
|
||||
# Usage: enable_rosetta <app_config_file> [port]
|
||||
enable_rosetta() {
|
||||
local app_config_file="$1"
|
||||
local port="${2:-8080}"
|
||||
|
||||
ensure_file "$app_config_file"
|
||||
|
||||
log_info "Enabling Rosetta API on port $port"
|
||||
|
||||
# Update address
|
||||
set_toml_value "$app_config_file" "rosetta" "address" "0.0.0.0:$port"
|
||||
}
|
||||
|
||||
# Set consensus timeouts
|
||||
# Usage: set_consensus_timeouts <config_file> [propose] [prevote] [precommit] [commit]
|
||||
set_consensus_timeouts() {
|
||||
local config_file="$1"
|
||||
local timeout_propose="${2:-5s}"
|
||||
local timeout_prevote="${3:-1s}"
|
||||
local timeout_precommit="${4:-1s}"
|
||||
local timeout_commit="${5:-5s}"
|
||||
|
||||
ensure_file "$config_file"
|
||||
|
||||
log_info "Setting consensus timeouts: propose=$timeout_propose, prevote=$timeout_prevote, precommit=$timeout_precommit, commit=$timeout_commit"
|
||||
|
||||
set_toml_value "$config_file" "consensus" "timeout_propose" "$timeout_propose"
|
||||
set_toml_value "$config_file" "consensus" "timeout_prevote" "$timeout_prevote"
|
||||
set_toml_value "$config_file" "consensus" "timeout_precommit" "$timeout_precommit"
|
||||
set_toml_value "$config_file" "consensus" "timeout_commit" "$timeout_commit"
|
||||
}
|
||||
|
||||
# Set pruning strategy
|
||||
# Usage: set_pruning <app_config_file> [strategy]
|
||||
set_pruning() {
|
||||
local app_config_file="$1"
|
||||
local strategy="${2:-default}"
|
||||
|
||||
ensure_file "$app_config_file"
|
||||
|
||||
log_info "Setting pruning strategy to $strategy"
|
||||
|
||||
set_toml_value "$app_config_file" "pruning" "strategy" "$strategy"
|
||||
}
|
||||
|
||||
# Set minimum gas prices
|
||||
# Usage: set_min_gas_prices <app_config_file> [price]
|
||||
set_min_gas_prices() {
|
||||
local app_config_file="$1"
|
||||
local price="${2:-0${DENOM}}"
|
||||
|
||||
ensure_file "$app_config_file"
|
||||
|
||||
log_info "Setting minimum gas prices to $price"
|
||||
|
||||
set_toml_value "$app_config_file" "minimum-gas-prices" "minimum-gas-prices" "$price"
|
||||
}
|
||||
|
||||
# Enable metrics
|
||||
# Usage: enable_metrics <config_file> [retention_time]
|
||||
enable_metrics() {
|
||||
local config_file="$1"
|
||||
local retention_time="${2:-3600}"
|
||||
|
||||
ensure_file "$config_file"
|
||||
|
||||
log_info "Enabling metrics with retention time ${retention_time}s"
|
||||
|
||||
# Enable prometheus in config.toml
|
||||
set_toml_value "$config_file" "instrumentation" "prometheus" "true"
|
||||
|
||||
# Set retention time in app.toml
|
||||
if [[ -f "$config_file" ]]; then
|
||||
set_toml_value "$config_file" "telemetry" "prometheus-retention-time" "$retention_time"
|
||||
fi
|
||||
}
|
||||
|
||||
# Set keyring backend
|
||||
# Usage: set_keyring_backend <client_config_file> [backend]
|
||||
set_keyring_backend() {
|
||||
local client_config_file="$1"
|
||||
local backend="${2:-test}"
|
||||
|
||||
ensure_file "$client_config_file"
|
||||
|
||||
log_info "Setting keyring backend to $backend"
|
||||
|
||||
set_toml_value "$client_config_file" "keyring-backend" "keyring-backend" "$backend"
|
||||
}
|
||||
|
||||
# Set chain ID in client config
|
||||
# Usage: set_client_chain_id <client_config_file> [chain_id]
|
||||
set_client_chain_id() {
|
||||
local client_config_file="$1"
|
||||
local chain_id="${2:-$CHAIN_ID}"
|
||||
|
||||
ensure_file "$client_config_file"
|
||||
|
||||
log_info "Setting client chain ID to $chain_id"
|
||||
|
||||
set_toml_value "$client_config_file" "chain-id" "chain-id" "$chain_id"
|
||||
}
|
||||
|
||||
# Set client output format
|
||||
# Usage: set_client_output <client_config_file> [format]
|
||||
set_client_output() {
|
||||
local client_config_file="$1"
|
||||
local format="${2:-json}"
|
||||
|
||||
ensure_file "$client_config_file"
|
||||
|
||||
log_info "Setting client output format to $format"
|
||||
|
||||
set_toml_value "$client_config_file" "output" "output" "$format"
|
||||
}
|
||||
|
||||
# Configure full node settings
|
||||
# Usage: configure_node <config_dir> [options...]
|
||||
configure_node() {
|
||||
local config_dir="$1"
|
||||
shift
|
||||
|
||||
ensure_chain_dir
|
||||
|
||||
local config_toml="$config_dir/config/config.toml"
|
||||
local app_toml="$config_dir/config/app.toml"
|
||||
local client_toml="$config_dir/config/client.toml"
|
||||
|
||||
log_info "Configuring node in $config_dir"
|
||||
|
||||
# Apply configurations based on arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--rpc-port)
|
||||
enable_rpc "$config_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--rest-port)
|
||||
enable_rest "$app_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--grpc-port)
|
||||
enable_grpc "$app_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--grpc-web-port)
|
||||
enable_grpc_web "$app_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--json-rpc-port)
|
||||
enable_json_rpc "$app_toml" "$2" "$3"
|
||||
shift 3
|
||||
;;
|
||||
--rosetta-port)
|
||||
enable_rosetta "$app_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--consensus-timeouts)
|
||||
set_consensus_timeouts "$config_toml" "$2" "$3" "$4" "$5"
|
||||
shift 5
|
||||
;;
|
||||
--pruning)
|
||||
set_pruning "$app_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--min-gas-prices)
|
||||
set_min_gas_prices "$app_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--metrics)
|
||||
enable_metrics "$config_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--keyring-backend)
|
||||
set_keyring_backend "$client_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--chain-id)
|
||||
set_client_chain_id "$client_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
--output-format)
|
||||
set_client_output "$client_toml" "$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown configuration option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log_success "Node configuration completed"
|
||||
}
|
||||
|
||||
# Export functions
|
||||
export -f set_toml_value enable_rpc enable_rest enable_grpc enable_grpc_web
|
||||
export -f enable_json_rpc enable_rosetta set_consensus_timeouts set_pruning
|
||||
export -f set_min_gas_prices enable_metrics set_keyring_backend set_client_chain_id
|
||||
export -f set_client_output configure_node
|
||||
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
|
||||
# scripts/lib/env.sh - Environment defaults and utility functions for Sonr scripts
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Default environment variables for Sonr
|
||||
export DENOM="${DENOM:=usnr}"
|
||||
export CHAIN_BIN="${CHAIN_BIN:=snrd}"
|
||||
export CHAIN_DIR="${CHAIN_DIR:=$HOME/.sonr}"
|
||||
export CHAIN_ID="${CHAIN_ID:=sonrtest_1-1}"
|
||||
export KEYRING_BACKEND="${KEYRING_BACKEND:=test}"
|
||||
export NODE_URL="${NODE_URL:=http://0.0.0.0:26657}"
|
||||
export GAS="${GAS:=auto}"
|
||||
export GAS_ADJUSTMENT="${GAS_ADJUSTMENT:=1.5}"
|
||||
|
||||
# Colors for logging
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $*"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $*"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $*"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $*"
|
||||
}
|
||||
|
||||
# Utility functions
|
||||
require_cmd() {
|
||||
local cmd="$1"
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
log_error "Required command '$cmd' not found. Please install it."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
join_path() {
|
||||
local base="$1"
|
||||
local path="$2"
|
||||
echo "$base/$path" | sed 's#//#/#g'
|
||||
}
|
||||
|
||||
# Check if running in Docker
|
||||
is_docker() {
|
||||
[[ -f /.dockerenv ]] || [[ -n "${DOCKER_CONTAINER:-}" ]]
|
||||
}
|
||||
|
||||
# Get absolute path
|
||||
abs_path() {
|
||||
local path="$1"
|
||||
if [[ -d "$path" ]]; then
|
||||
cd "$path" && pwd
|
||||
else
|
||||
cd "$(dirname "$path")" && echo "$(pwd)/$(basename "$path")"
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate chain directory exists
|
||||
ensure_chain_dir() {
|
||||
if [[ ! -d "$CHAIN_DIR" ]]; then
|
||||
log_error "Chain directory does not exist: $CHAIN_DIR"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate binary exists
|
||||
ensure_binary() {
|
||||
if ! command -v "$CHAIN_BIN" >/dev/null 2>&1; then
|
||||
log_error "Binary '$CHAIN_BIN' not found in PATH"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if file exists
|
||||
ensure_file() {
|
||||
local file="$1"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
log_error "Required file not found: $file"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Retry function for operations that might fail
|
||||
retry() {
|
||||
local max_attempts="$1"
|
||||
local delay="$2"
|
||||
local cmd="$3"
|
||||
shift 3
|
||||
|
||||
local attempt=1
|
||||
while [[ $attempt -le $max_attempts ]]; do
|
||||
log_info "Attempt $attempt/$max_attempts: $cmd $*"
|
||||
if "$cmd" "$@"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $attempt -lt $max_attempts ]]; then
|
||||
log_warn "Command failed, retrying in ${delay}s..."
|
||||
sleep "$delay"
|
||||
fi
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
log_error "Command failed after $max_attempts attempts: $cmd $*"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Initialize environment
|
||||
init_env() {
|
||||
require_cmd jq
|
||||
ensure_binary
|
||||
|
||||
# Set up cleanup trap
|
||||
trap cleanup EXIT
|
||||
|
||||
log_info "Environment initialized for Sonr chain: $CHAIN_ID"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# Remove temporary files if they exist
|
||||
rm -f /tmp/genesis.json /tmp/config.toml /tmp/app.toml /tmp/client.toml
|
||||
}
|
||||
|
||||
# Export functions for use in other scripts
|
||||
export -f log_info log_warn log_error log_success
|
||||
export -f require_cmd join_path is_docker abs_path
|
||||
export -f ensure_chain_dir ensure_binary ensure_file
|
||||
export -f retry init_env cleanup
|
||||
@@ -0,0 +1,210 @@
|
||||
#!/bin/bash
|
||||
|
||||
# scripts/lib/genesis.sh - Genesis file utilities for Sonr scripts
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source environment and JSON helpers
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/env.sh"
|
||||
source "${SCRIPT_DIR}/jq_patch.sh"
|
||||
|
||||
# Generate VRF keypair for the network
|
||||
# Usage: generate_vrf_key [chain_dir]
|
||||
generate_vrf_key() {
|
||||
local chain_dir="${1:-$CHAIN_DIR}"
|
||||
|
||||
ensure_chain_dir
|
||||
|
||||
local genesis_file="$chain_dir/config/genesis.json"
|
||||
ensure_file "$genesis_file"
|
||||
|
||||
local chain_id
|
||||
chain_id=$(get_json_value "$genesis_file" '.chain_id')
|
||||
|
||||
if [[ -z "$chain_id" || "$chain_id" == "null" ]]; then
|
||||
log_error "Failed to extract chain-id from genesis file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Generating VRF keypair for network: $chain_id"
|
||||
|
||||
# Create deterministic entropy from chain-id using SHA256
|
||||
local entropy_seed
|
||||
entropy_seed=$(echo -n "$chain_id" | sha256sum | cut -d' ' -f1)
|
||||
|
||||
# Generate 64 bytes of deterministic randomness
|
||||
local seed_part1="$entropy_seed"
|
||||
local seed_part2
|
||||
seed_part2=$(echo -n "$entropy_seed" | sha256sum | cut -d' ' -f1)
|
||||
|
||||
# Combine to create 64 bytes of hex data
|
||||
local vrf_key_hex="${seed_part1}${seed_part2}"
|
||||
|
||||
# Ensure we have exactly 128 hex characters (64 bytes)
|
||||
if [[ ${#vrf_key_hex} -ne 128 ]]; then
|
||||
log_error "Generated VRF key has incorrect size: ${#vrf_key_hex}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Path to store VRF secret key
|
||||
local vrf_key_path="$chain_dir/vrf_secret.key"
|
||||
|
||||
# Ensure directory exists
|
||||
mkdir -p "$chain_dir"
|
||||
|
||||
# Convert hex to binary and write to file
|
||||
echo -n "$vrf_key_hex" | xxd -r -p > "$vrf_key_path"
|
||||
|
||||
# Set restrictive permissions (owner read/write only)
|
||||
chmod 0600 "$vrf_key_path"
|
||||
|
||||
# Validate file was created with correct size (64 bytes)
|
||||
local file_size
|
||||
file_size=$(wc -c < "$vrf_key_path")
|
||||
|
||||
if [[ $file_size -ne 64 ]]; then
|
||||
log_error "VRF key file has incorrect size: ${file_size} bytes"
|
||||
rm -f "$vrf_key_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "VRF keypair generated for network: $chain_id"
|
||||
log_success "VRF secret key stored securely: $vrf_key_path"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Update genesis with Sonr-specific parameters
|
||||
# Usage: update_genesis_params [genesis_file]
|
||||
update_genesis_params() {
|
||||
local genesis_file="${1:-$CHAIN_DIR/config/genesis.json}"
|
||||
|
||||
ensure_file "$genesis_file"
|
||||
|
||||
log_info "Updating genesis parameters for Sonr"
|
||||
|
||||
# Update stake denomination
|
||||
set_json_string "$genesis_file" 'app_state.staking.params.bond_denom' "$DENOM"
|
||||
|
||||
# Update mint denomination
|
||||
set_json_string "$genesis_file" 'app_state.mint.params.mint_denom' "$DENOM"
|
||||
|
||||
# Update crisis fee
|
||||
set_json_object "$genesis_file" 'app_state.crisis.constant_fee' "{\"denom\":\"$DENOM\",\"amount\":\"1000\"}"
|
||||
|
||||
# Update minimum commission rate
|
||||
set_json_string "$genesis_file" 'app_state.staking.params.min_commission_rate' "0.050000000000000000"
|
||||
|
||||
# Update block max gas
|
||||
set_json_string "$genesis_file" 'consensus.params.block.max_gas' "100000000000"
|
||||
|
||||
# Update unbonding time
|
||||
set_json_string "$genesis_file" 'app_state.staking.params.unbonding_time' "300s"
|
||||
|
||||
# Update downtime jail duration
|
||||
set_json_string "$genesis_file" 'app_state.slashing.params.downtime_jail_duration' "60s"
|
||||
|
||||
# Update governance parameters for SDK v0.47+
|
||||
if json_path_exists "$genesis_file" '.app_state.gov.params'; then
|
||||
set_json_string "$genesis_file" 'app_state.gov.params.max_deposit_period' "30s"
|
||||
set_json_string "$genesis_file" 'app_state.gov.params.min_deposit[0].amount' "10"
|
||||
set_json_string "$genesis_file" 'app_state.gov.params.voting_period' "30s"
|
||||
set_json_string "$genesis_file" 'app_state.gov.params.quorum' "0.000000000000000000"
|
||||
set_json_string "$genesis_file" 'app_state.gov.params.threshold' "0.000000000000000000"
|
||||
set_json_string "$genesis_file" 'app_state.gov.params.veto_threshold' "0.000000000000000000"
|
||||
fi
|
||||
|
||||
# Update EVM parameters if present
|
||||
if json_path_exists "$genesis_file" '.app_state.evm'; then
|
||||
set_json_string "$genesis_file" 'app_state.evm.params.evm_denom' "$DENOM"
|
||||
set_json_object "$genesis_file" 'app_state.evm.params.active_static_precompiles' '["0x0000000000000000000000000000000000000100","0x0000000000000000000000000000000000000400","0x0000000000000000000000000000000000000800","0x0000000000000000000000000000000000000801","0x0000000000000000000000000000000000000802","0x0000000000000000000000000000000000000803","0x0000000000000000000000000000000000000804","0x0000000000000000000000000000000000000805"]'
|
||||
fi
|
||||
|
||||
# Update ERC20 parameters if present
|
||||
if json_path_exists "$genesis_file" '.app_state.erc20'; then
|
||||
set_json_object "$genesis_file" 'app_state.erc20.params.native_precompiles' '["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"]'
|
||||
set_json_object "$genesis_file" 'app_state.erc20.token_pairs' '[{"contract_owner":1,"erc20_address":"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE","denom":"'"$DENOM"'","enabled":true}]'
|
||||
fi
|
||||
|
||||
# Update feemarket parameters if present
|
||||
if json_path_exists "$genesis_file" '.app_state.feemarket'; then
|
||||
set_json_bool "$genesis_file" 'app_state.feemarket.params.no_base_fee' true
|
||||
set_json_string "$genesis_file" 'app_state.feemarket.params.base_fee' "0.000000000000000000"
|
||||
fi
|
||||
|
||||
# Update tokenfactory parameters if present
|
||||
if json_path_exists "$genesis_file" '.app_state.tokenfactory'; then
|
||||
set_json_object "$genesis_file" 'app_state.tokenfactory.params.denom_creation_fee' '[]'
|
||||
set_json_number "$genesis_file" 'app_state.tokenfactory.params.denom_creation_gas_consume' 100000
|
||||
fi
|
||||
|
||||
# Update ABCI parameters if present
|
||||
if json_path_exists "$genesis_file" '.consensus.params.abci'; then
|
||||
set_json_string "$genesis_file" 'consensus.params.abci.vote_extensions_enable_height' "1"
|
||||
fi
|
||||
|
||||
log_success "Genesis parameters updated for Sonr"
|
||||
}
|
||||
|
||||
# Add constitution to governance if CONSTITUTION.md exists
|
||||
# Usage: add_constitution [genesis_file] [constitution_file]
|
||||
add_constitution() {
|
||||
local genesis_file="${1:-$CHAIN_DIR/config/genesis.json}"
|
||||
local constitution_file="${2:-CONSTITUTION.md}"
|
||||
|
||||
ensure_file "$genesis_file"
|
||||
|
||||
# Look for CONSTITUTION.md in the git root directory
|
||||
local script_dir
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
local git_root
|
||||
git_root="$(cd "${script_dir}/.." && pwd)"
|
||||
local constitution_path="$git_root/$constitution_file"
|
||||
|
||||
if [[ ! -f "$constitution_path" ]]; then
|
||||
log_warn "Constitution file not found: $constitution_path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Adding constitution from: $constitution_path"
|
||||
|
||||
local constitution_content
|
||||
constitution_content=$(cat "$constitution_path" | jq -Rs .)
|
||||
|
||||
set_json_object "$genesis_file" 'app_state.gov.constitution' "$constitution_content"
|
||||
|
||||
log_success "Constitution added to governance"
|
||||
}
|
||||
|
||||
# Validate genesis file
|
||||
# Usage: validate_genesis [genesis_file]
|
||||
validate_genesis() {
|
||||
local genesis_file="${1:-$CHAIN_DIR/config/genesis.json}"
|
||||
|
||||
ensure_file "$genesis_file"
|
||||
|
||||
log_info "Validating genesis file..."
|
||||
|
||||
# Validate JSON
|
||||
validate_json "$genesis_file"
|
||||
|
||||
# Check required fields
|
||||
local chain_id
|
||||
chain_id=$(get_json_value "$genesis_file" '.chain_id')
|
||||
if [[ -z "$chain_id" || "$chain_id" == "null" ]]; then
|
||||
log_error "Genesis file missing chain_id"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check app_state exists
|
||||
if ! json_path_exists "$genesis_file" '.app_state'; then
|
||||
log_error "Genesis file missing app_state"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Genesis file validation passed"
|
||||
}
|
||||
|
||||
# Export functions
|
||||
export -f generate_vrf_key update_genesis_params add_constitution validate_genesis
|
||||
@@ -0,0 +1,154 @@
|
||||
#!/bin/bash
|
||||
|
||||
# scripts/lib/jq_patch.sh - JSON patching utilities for genesis and config files
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source environment helpers
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/env.sh"
|
||||
|
||||
# Patch JSON file with jq expression and save to temp file then move back
|
||||
# Usage: patch_json <file> <jq_expression>
|
||||
patch_json() {
|
||||
local file="$1"
|
||||
local jq_expr="$2"
|
||||
|
||||
ensure_file "$file"
|
||||
|
||||
log_info "Patching $file with: $jq_expr"
|
||||
|
||||
# Create temp file in same directory to avoid cross-filesystem issues
|
||||
local temp_file
|
||||
temp_file="$(dirname "$file")/.tmp.$(basename "$file").$$"
|
||||
|
||||
if ! jq -r "$jq_expr" "$file" > "$temp_file" 2>/dev/null; then
|
||||
rm -f "$temp_file"
|
||||
log_error "Failed to patch JSON file: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
mv "$temp_file" "$file"
|
||||
log_success "Successfully patched $file"
|
||||
}
|
||||
|
||||
# Ensure array contains specific value
|
||||
# Usage: ensure_array_contains <file> <path> <value>
|
||||
ensure_array_contains() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
local value="$3"
|
||||
|
||||
ensure_file "$file"
|
||||
|
||||
local current_value
|
||||
current_value=$(jq -r "$path // []" "$file")
|
||||
|
||||
# Check if value already exists
|
||||
if [[ "$current_value" == *"\"$value\""* ]]; then
|
||||
log_info "Value '$value' already exists in $path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Add value to array
|
||||
local jq_expr
|
||||
jq_expr=".${path} += [\"$value\"]"
|
||||
|
||||
patch_json "$file" "$jq_expr"
|
||||
}
|
||||
|
||||
# Set string value at path
|
||||
# Usage: set_json_string <file> <path> <value>
|
||||
set_json_string() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
local value="$3"
|
||||
|
||||
local jq_expr
|
||||
jq_expr=".${path} = \"$value\""
|
||||
|
||||
patch_json "$file" "$jq_expr"
|
||||
}
|
||||
|
||||
# Set numeric value at path
|
||||
# Usage: set_json_number <file> <path> <value>
|
||||
set_json_number() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
local value="$3"
|
||||
|
||||
local jq_expr
|
||||
jq_expr=".${path} = $value"
|
||||
|
||||
patch_json "$file" "$jq_expr"
|
||||
}
|
||||
|
||||
# Set boolean value at path
|
||||
# Usage: set_json_bool <file> <path> <value>
|
||||
set_json_bool() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
local value="$3"
|
||||
|
||||
local jq_expr
|
||||
jq_expr=".${path} = $value"
|
||||
|
||||
patch_json "$file" "$jq_expr"
|
||||
}
|
||||
|
||||
# Set object value at path
|
||||
# Usage: set_json_object <file> <path> <json_object>
|
||||
set_json_object() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
local json_object="$3"
|
||||
|
||||
local jq_expr
|
||||
jq_expr=".${path} = $json_object"
|
||||
|
||||
patch_json "$file" "$jq_expr"
|
||||
}
|
||||
|
||||
# Check if path exists and is not null
|
||||
# Usage: json_path_exists <file> <path>
|
||||
json_path_exists() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
|
||||
ensure_file "$file"
|
||||
|
||||
local result
|
||||
result=$(jq -r "$path // empty" "$file" 2>/dev/null)
|
||||
|
||||
[[ -n "$result" && "$result" != "null" ]]
|
||||
}
|
||||
|
||||
# Get value at path
|
||||
# Usage: get_json_value <file> <path>
|
||||
get_json_value() {
|
||||
local file="$1"
|
||||
local path="$2"
|
||||
|
||||
ensure_file "$file"
|
||||
|
||||
jq -r "$path" "$file"
|
||||
}
|
||||
|
||||
# Validate JSON file
|
||||
# Usage: validate_json <file>
|
||||
validate_json() {
|
||||
local file="$1"
|
||||
|
||||
ensure_file "$file"
|
||||
|
||||
if ! jq empty "$file" >/dev/null 2>&1; then
|
||||
log_error "Invalid JSON in file: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "JSON validation passed for $file"
|
||||
}
|
||||
|
||||
# Export functions
|
||||
export -f patch_json ensure_array_contains set_json_string set_json_number
|
||||
export -f set_json_bool set_json_object json_path_exists get_json_value validate_json
|
||||
@@ -0,0 +1,211 @@
|
||||
#!/bin/bash
|
||||
|
||||
# scripts/lib/keys.sh - Key management utilities for Sonr scripts
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source environment helpers
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/env.sh"
|
||||
|
||||
# Import mnemonic and add key to keyring
|
||||
# Usage: import_mnemonic <key_name> <mnemonic> [algo]
|
||||
import_mnemonic() {
|
||||
local key_name="$1"
|
||||
local mnemonic="$2"
|
||||
local algo="${3:-eth_secp256k1}"
|
||||
|
||||
log_info "Importing key '$key_name'"
|
||||
|
||||
if [[ -z "$mnemonic" ]]; then
|
||||
log_error "Mnemonic cannot be empty"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Use Docker wrapper if needed
|
||||
if is_docker; then
|
||||
echo "$mnemonic" | $CHAIN_BIN keys add "$key_name" \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--algo "$algo" \
|
||||
--recover
|
||||
else
|
||||
echo "$mnemonic" | $CHAIN_BIN keys add "$key_name" \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--algo "$algo" \
|
||||
--home "$CHAIN_DIR" \
|
||||
--recover
|
||||
fi
|
||||
|
||||
log_success "Key '$key_name' imported successfully"
|
||||
}
|
||||
|
||||
# Ensure key exists in keyring
|
||||
# Usage: ensure_key <key_name>
|
||||
ensure_key() {
|
||||
local key_name="$1"
|
||||
|
||||
if ! $CHAIN_BIN keys show "$key_name" \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--home "$CHAIN_DIR" \
|
||||
--output json >/dev/null 2>&1; then
|
||||
|
||||
log_error "Key '$key_name' not found in keyring"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Key '$key_name' exists in keyring"
|
||||
}
|
||||
|
||||
# Get key address
|
||||
# Usage: get_key_address <key_name>
|
||||
get_key_address() {
|
||||
local key_name="$1"
|
||||
|
||||
ensure_key "$key_name"
|
||||
|
||||
local address
|
||||
address=$($CHAIN_BIN keys show "$key_name" \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--home "$CHAIN_DIR" \
|
||||
--output json | jq -r '.address')
|
||||
|
||||
if [[ -z "$address" || "$address" == "null" ]]; then
|
||||
log_error "Failed to get address for key '$key_name'"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$address"
|
||||
}
|
||||
|
||||
# Fund key with tokens from genesis
|
||||
# Usage: fund_key <key_name> <amount>
|
||||
fund_key() {
|
||||
local key_name="$1"
|
||||
local amount="$2"
|
||||
|
||||
ensure_key "$key_name"
|
||||
|
||||
local address
|
||||
address=$(get_key_address "$key_name")
|
||||
|
||||
log_info "Funding key '$key_name' ($address) with $amount"
|
||||
|
||||
if is_docker; then
|
||||
$CHAIN_BIN genesis add-genesis-account "$address" "$amount" \
|
||||
--keyring-backend "$KEYRING_BACKEND"
|
||||
else
|
||||
$CHAIN_BIN genesis add-genesis-account "$address" "$amount" \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--home "$CHAIN_DIR"
|
||||
fi
|
||||
|
||||
log_success "Funded key '$key_name' with $amount"
|
||||
}
|
||||
|
||||
# Delegate tokens to validator
|
||||
# Usage: delegate_to_validator <delegator_key> <validator_address> <amount>
|
||||
delegate_to_validator() {
|
||||
local delegator_key="$1"
|
||||
local validator_address="$2"
|
||||
local amount="$3"
|
||||
|
||||
ensure_key "$delegator_key"
|
||||
|
||||
log_info "Delegating $amount from '$delegator_key' to validator $validator_address"
|
||||
|
||||
$CHAIN_BIN tx staking delegate "$validator_address" "$amount" \
|
||||
--from "$delegator_key" \
|
||||
--chain-id "$CHAIN_ID" \
|
||||
--node "$NODE_URL" \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--gas "$GAS" \
|
||||
--gas-adjustment "$GAS_ADJUSTMENT" \
|
||||
--yes
|
||||
|
||||
log_success "Delegation completed"
|
||||
}
|
||||
|
||||
# Create validator with key
|
||||
# Usage: create_validator <key_name> <moniker> <amount> [options...]
|
||||
create_validator() {
|
||||
local key_name="$1"
|
||||
local moniker="$2"
|
||||
local amount="$3"
|
||||
shift 3
|
||||
|
||||
ensure_key "$key_name"
|
||||
|
||||
local validator_address
|
||||
validator_address=$(get_key_address "$key_name")
|
||||
|
||||
log_info "Creating validator '$moniker' with key '$key_name'"
|
||||
|
||||
# Build validator JSON
|
||||
local validator_json
|
||||
validator_json=$(cat <<EOF
|
||||
{
|
||||
"pubkey": "$($CHAIN_BIN tendermint show-validator)",
|
||||
"amount": "$amount",
|
||||
"moniker": "$moniker",
|
||||
"commission-rate": "0.1",
|
||||
"commission-max-rate": "0.2",
|
||||
"commission-max-change-rate": "0.01",
|
||||
"min-self-delegation": "1000000"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "$validator_json" > /tmp/validator.json
|
||||
|
||||
$CHAIN_BIN tx staking create-validator /tmp/validator.json \
|
||||
--from "$key_name" \
|
||||
--chain-id "$CHAIN_ID" \
|
||||
--node "$NODE_URL" \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--gas "$GAS" \
|
||||
--gas-adjustment "$GAS_ADJUSTMENT" \
|
||||
--yes
|
||||
|
||||
rm -f /tmp/validator.json
|
||||
|
||||
log_success "Validator '$moniker' created successfully"
|
||||
}
|
||||
|
||||
# Wait for node to sync
|
||||
# Usage: wait_for_sync [max_tries]
|
||||
wait_for_sync() {
|
||||
local max_tries="${1:-10}"
|
||||
|
||||
log_info "Waiting for node to sync..."
|
||||
|
||||
local tries=0
|
||||
while [[ $tries -lt $max_tries ]]; do
|
||||
if $CHAIN_BIN status \
|
||||
--node "$NODE_URL" \
|
||||
--output json 2>/dev/null | jq -e '.SyncInfo.catching_up == false' >/dev/null 2>&1; then
|
||||
|
||||
log_success "Node is synced"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Still syncing... ($((tries + 1))/$max_tries)"
|
||||
sleep 30
|
||||
((tries++))
|
||||
done
|
||||
|
||||
log_error "Node failed to sync after $max_tries attempts"
|
||||
return 1
|
||||
}
|
||||
|
||||
# List keys in keyring
|
||||
# Usage: list_keys
|
||||
list_keys() {
|
||||
$CHAIN_BIN keys list \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--home "$CHAIN_DIR" \
|
||||
--output json | jq
|
||||
}
|
||||
|
||||
# Export functions
|
||||
export -f import_mnemonic ensure_key get_key_address fund_key
|
||||
export -f delegate_to_validator create_validator wait_for_sync list_keys
|
||||
@@ -0,0 +1,228 @@
|
||||
#!/bin/bash
|
||||
|
||||
# scripts/lib/tx.sh - Transaction utilities for Sonr scripts
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source environment and key helpers
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/env.sh"
|
||||
source "${SCRIPT_DIR}/keys.sh"
|
||||
|
||||
# Submit transaction with automatic gas and error handling
|
||||
# Usage: submit_tx <from_key> <command...>
|
||||
submit_tx() {
|
||||
local from_key="$1"
|
||||
shift
|
||||
|
||||
ensure_key "$from_key"
|
||||
|
||||
log_info "Submitting transaction from '$from_key': $*"
|
||||
|
||||
local tx_result
|
||||
tx_result=$($CHAIN_BIN tx "$@" \
|
||||
--from "$from_key" \
|
||||
--chain-id "$CHAIN_ID" \
|
||||
--node "$NODE_URL" \
|
||||
--keyring-backend "$KEYRING_BACKEND" \
|
||||
--gas "$GAS" \
|
||||
--gas-adjustment "$GAS_ADJUSTMENT" \
|
||||
--output json \
|
||||
--yes 2>&1)
|
||||
|
||||
# Check for transaction hash
|
||||
local tx_hash
|
||||
tx_hash=$(echo "$tx_result" | jq -r '.txhash // empty')
|
||||
|
||||
if [[ -n "$tx_hash" && "$tx_hash" != "null" ]]; then
|
||||
log_success "Transaction submitted: $tx_hash"
|
||||
|
||||
# Wait for confirmation
|
||||
sleep 5
|
||||
|
||||
# Query transaction result
|
||||
local tx_query
|
||||
tx_query=$($CHAIN_BIN query tx "$tx_hash" \
|
||||
--node "$NODE_URL" \
|
||||
--output json 2>/dev/null)
|
||||
|
||||
local tx_code
|
||||
tx_code=$(echo "$tx_query" | jq -r '.code // 0')
|
||||
|
||||
if [[ "$tx_code" == "0" ]]; then
|
||||
log_success "Transaction confirmed successfully"
|
||||
echo "$tx_hash"
|
||||
return 0
|
||||
else
|
||||
local tx_log
|
||||
tx_log=$(echo "$tx_query" | jq -r '.raw_log // "Unknown error"')
|
||||
log_error "Transaction failed (code $tx_code): $tx_log"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_error "Transaction submission failed: $tx_result"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Query with error handling
|
||||
# Usage: query_chain <command...>
|
||||
query_chain() {
|
||||
log_info "Querying chain: $*"
|
||||
|
||||
local result
|
||||
result=$($CHAIN_BIN query "$@" \
|
||||
--node "$NODE_URL" \
|
||||
--output json 2>/dev/null)
|
||||
|
||||
if [[ -z "$result" || "$result" == "null" ]]; then
|
||||
log_error "Query failed or returned null"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
# Get validator address (first available)
|
||||
# Usage: get_validator_address
|
||||
get_validator_address() {
|
||||
log_info "Getting validator address..."
|
||||
|
||||
local validators
|
||||
validators=$(query_chain staking validators)
|
||||
|
||||
local validator_address
|
||||
validator_address=$(echo "$validators" | jq -r '.validators[0].operator_address // empty')
|
||||
|
||||
if [[ -z "$validator_address" || "$validator_address" == "null" ]]; then
|
||||
log_error "No validators found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Using validator: $validator_address"
|
||||
echo "$validator_address"
|
||||
}
|
||||
|
||||
# Submit governance proposal
|
||||
# Usage: submit_proposal <from_key> <proposal_file> [proposal_type]
|
||||
submit_proposal() {
|
||||
local from_key="$1"
|
||||
local proposal_file="$2"
|
||||
local proposal_type="${3:-}"
|
||||
|
||||
ensure_file "$proposal_file"
|
||||
ensure_key "$from_key"
|
||||
|
||||
log_info "Submitting governance proposal from '$from_key'"
|
||||
|
||||
local submit_cmd="submit-proposal"
|
||||
if [[ -n "$proposal_type" ]]; then
|
||||
submit_cmd="$submit_cmd $proposal_type"
|
||||
fi
|
||||
|
||||
# Check if legacy proposal command is needed
|
||||
if ! $CHAIN_BIN tx gov submit-proposal --help 2>/dev/null | grep -q "submit-proposal"; then
|
||||
submit_cmd="submit-legacy-proposal"
|
||||
fi
|
||||
|
||||
submit_tx "$from_key" gov "$submit_cmd" "$proposal_file"
|
||||
}
|
||||
|
||||
# Vote on governance proposal
|
||||
# Usage: vote_proposal <from_key> <proposal_id> <vote_option>
|
||||
vote_proposal() {
|
||||
local from_key="$1"
|
||||
local proposal_id="$2"
|
||||
local vote_option="${3:-yes}"
|
||||
|
||||
ensure_key "$from_key"
|
||||
|
||||
log_info "Voting '$vote_option' on proposal $proposal_id from '$from_key'"
|
||||
|
||||
submit_tx "$from_key" gov vote "$proposal_id" "$vote_option"
|
||||
}
|
||||
|
||||
# Wait for proposal to pass
|
||||
# Usage: wait_for_proposal <proposal_id> [max_tries] [interval]
|
||||
wait_for_proposal() {
|
||||
local proposal_id="$1"
|
||||
local max_tries="${2:-3}"
|
||||
local interval="${3:-30}"
|
||||
|
||||
log_info "Waiting for proposal $proposal_id to pass..."
|
||||
|
||||
local tries=0
|
||||
while [[ $tries -lt $max_tries ]]; do
|
||||
local status
|
||||
status=$(query_chain gov proposal "$proposal_id" | jq -r '.status // "unknown"')
|
||||
|
||||
case "$status" in
|
||||
"PROPOSAL_STATUS_PASSED")
|
||||
log_success "Proposal $proposal_id has passed"
|
||||
return 0
|
||||
;;
|
||||
"PROPOSAL_STATUS_REJECTED")
|
||||
log_error "Proposal $proposal_id was rejected"
|
||||
return 1
|
||||
;;
|
||||
"PROPOSAL_STATUS_FAILED")
|
||||
log_error "Proposal $proposal_id failed"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
log_info "Proposal status: $status ($((tries + 1))/$max_tries)"
|
||||
sleep "$interval"
|
||||
((tries++))
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log_error "Proposal $proposal_id did not pass after $max_tries attempts"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Stake tokens to validator
|
||||
# Usage: stake_tokens <from_key> <validator_address> <amount>
|
||||
stake_tokens() {
|
||||
local from_key="$1"
|
||||
local validator_address="$2"
|
||||
local amount="$3"
|
||||
|
||||
ensure_key "$from_key"
|
||||
|
||||
log_info "Staking $amount from '$from_key' to $validator_address"
|
||||
|
||||
submit_tx "$from_key" staking delegate "$validator_address" "$amount"
|
||||
}
|
||||
|
||||
# Query account balance
|
||||
# Usage: get_balance <address> [denom]
|
||||
get_balance() {
|
||||
local address="$1"
|
||||
local denom="${2:-$DENOM}"
|
||||
|
||||
log_info "Getting balance for $address (denom: $denom)"
|
||||
|
||||
local balance
|
||||
balance=$(query_chain bank balances "$address" | jq -r ".balances[] | select(.denom == \"$denom\") | .amount // \"0\"")
|
||||
|
||||
echo "$balance"
|
||||
}
|
||||
|
||||
# Send tokens
|
||||
# Usage: send_tokens <from_key> <to_address> <amount>
|
||||
send_tokens() {
|
||||
local from_key="$1"
|
||||
local to_address="$2"
|
||||
local amount="$3"
|
||||
|
||||
ensure_key "$from_key"
|
||||
|
||||
log_info "Sending $amount from '$from_key' to $to_address"
|
||||
|
||||
submit_tx "$from_key" bank send "$from_key" "$to_address" "$amount"
|
||||
}
|
||||
|
||||
# Export functions
|
||||
export -f submit_tx query_chain get_validator_address submit_proposal
|
||||
export -f vote_proposal wait_for_proposal stake_tokens get_balance send_tokens
|
||||
Reference in New Issue
Block a user