-
-
- Browser
- Storage Limit
-
-
-
-
- Chrome/Edge
- 60% of total disk space
-
-
- Firefox
- 50% of free disk space
-
-
- Safari
- Starts at 1GB, can request more
-
-
- Mobile Browsers
- Varies by device
-
-
-
-
-## Security Considerations
-
-
- - Databases are isolated by account address
- - No private keys or sensitive cryptographic material are stored
- - Only UCAN tokens and metadata are persisted
- - Always use HTTPS in production
- - Consider encrypting sensitive data before storage
-
-
-## Troubleshooting
-
-### Storage Not Persisting
-
-1. Verify you're running on HTTPS
-2. Check that IndexedDB is enabled in browser settings
-3. Confirm available storage quota
-4. Explicitly request persistent storage
-
-```typescript
-try {
- await vault.initialize('/plugin.wasm', accountAddress);
-} catch (error) {
- if (error.code === 'VAULT_NOT_INITIALIZED') {
- // Handle initialization error
- }
-}
-```
-
-## Migration Guide
-
-To migrate from non-persistent to persistent storage:
-
-```typescript
-// Before (non-persistent)
-const vault = createVaultClient();
-await vault.initialize();
-
-// After (with persistence)
-const vault = createVaultClient({
- enablePersistence: true,
-});
-await vault.initialize('/plugin.wasm', accountAddress);
-```
-
-
- **No other code changes are required!** All existing methods work the same way.
-
\ No newline at end of file
diff --git a/docs/quickstart/browser.mdx b/docs/quickstart/browser.mdx
deleted file mode 100644
index d55677bec..000000000
--- a/docs/quickstart/browser.mdx
+++ /dev/null
@@ -1,191 +0,0 @@
----
-title: Getting Started with Browser/ESM
-description: A quick start guide for using Sonr in web browsers with WebAuthn integration
-sidebarTitle: Browser Quickstart
-icon: "globe"
----
-
-This guide will walk you through creating a simple web application that interacts with the Sonr network directly from the browser. We will use the Sonr JavaScript SDK to create a new user identity, claim a Vault, and send a transaction.
-
-## Prerequisites
-
-- A local Sonr network running. See the [Validator Setup Guide](/quickstart/validators) for instructions
-- A modern web browser with WebAuthn support (Chrome, Firefox, Safari, Edge)
-
-## 1. Project Setup
-
-
-
-### Create an HTML File
-
-Create a new `index.html` file and add the following basic structure:
-
-```html
-
-
-
- Sonr Browser Quickstart
-
-
-
-
Sonr Browser Quickstart
-
-
-
-
-```
-
-
-
-
-### Create a JavaScript File
-
-Create a new `app.js` file in the same directory. This is where we will write our application logic.
-
-
-
-
-### Install the Sonr SDK
-
-For this quickstart, we will use the Sonr SDK from a CDN. Add the following script tag to the `` of your `index.html` file:
-
-```html
-
-```
-
-
-
-
-## 2. Creating an Identity
-
-Now, let's add the logic to create a new user identity and claim a Vault.
-
-
-
-### Add Event Listener
-
-In `app.js`, add an event listener to the "Create Identity" button:
-
-```javascript
-document
- .getElementById("create-identity")
- .addEventListener("click", async () => {
- const output = document.getElementById("output");
- output.innerHTML = "Creating identity...";
-
- try {
- // Code to create identity will go here
- } catch (error) {
- output.innerHTML = `Error: ${error.message}`;
- }
- });
-```
-
-
-
-
-### Implement Identity Creation
-
-Inside the event listener, use the `WebAuthn.createCredential` method to create a new WebAuthn credential and the `Sonr.claimVault` method to claim a new Vault on the network.
-
-```javascript
-// Inside the try block
-const credential = await WebAuthn.createCredential({
- rp: { name: "Sonr Quickstart" },
- user: {
- id: new Uint8Array(16), // Should be a unique user ID
- name: "user@example.com",
- displayName: "Test User",
- },
-});
-
-output.innerHTML = `Credential created: ${credential.id}`;
-
-const sonr = new Sonr({ httpUrl: "http://localhost:1317" });
-const vault = await sonr.claimVault(credential);
-
-output.innerHTML = `Vault claimed! DID: ${vault.did}`;
-```
-
-
- In a real application, the user ID should be a unique and stable identifier
- for the user, not a random value.
-
-
-
-
-## 3. Running the Application
-
-To run the application, you need a simple web server. You can use the `http-server` package for this.
-
-```bash
-# Install http-server
-npm install -g http-server
-
-# Start the server
-http-server
-```
-
-Now, open your browser and navigate to `http://localhost:8080`. When you click the "Create Identity" button, your browser will prompt you to create a new passkey using your device's biometrics or a security key.
-
-## 4. Sending a Transaction
-
-Once you have a Vault, you can use it to send transactions.
-
-
-
-### Add a Send Button
-
-Add a new button to your `index.html` file:
-
-```html
-
-```
-
-
-
-
-### Implement Transaction Sending
-
-In `app.js`, add an event listener to the new button. This will use the `sonr.send` method to send a transaction.
-
-```javascript
-let vaultInstance;
-
-// After claiming the vault...
-vaultInstance = vault;
-document.getElementById("send-transaction").disabled = false;
-
-document
- .getElementById("send-transaction")
- .addEventListener("click", async () => {
- const output = document.getElementById("output");
- output.innerHTML = "Sending transaction...";
-
- try {
- const result = await vaultInstance.send({
- to: "snr1..._recipient_address_...",
- amount: "1000000usnr", // 1 SNR
- });
-
- output.innerHTML = `Transaction successful! TxHash: ${result.txhash}`;
- } catch (error) {
- output.innerHTML = `Error: ${error.message}`;
- }
- });
-```
-
-
-
-
-## Next Steps
-
-Congratulations! You have successfully created a web application that interacts with the Sonr network. From here, you can explore more advanced features:
-
-- **Service Registration**: Register your application as a trusted service on the network
-- **UCAN Authorization**: Request and manage user permissions for your application
-- **Cross-Chain Operations**: Interact with other blockchains through IBC
diff --git a/docs/quickstart/golang.mdx b/docs/quickstart/golang.mdx
deleted file mode 100644
index 98159669d..000000000
--- a/docs/quickstart/golang.mdx
+++ /dev/null
@@ -1,521 +0,0 @@
----
-title: Getting Started with Golang
-description: A quick start guide for building applications with Sonr using the Go Client SDK
-sidebarTitle: Golang Quickstart
-icon: "golang"
----
-
-This guide provides a walkthrough for setting up a Go project to interact with the Sonr network using the official Go Client SDK. You will learn how to configure the client, manage keys, query the blockchain, send transactions, and use advanced features like WebAuthn gasless transactions.
-
-## Prerequisites
-
-- A local Sonr network running. See the [Validator Setup Guide](/quickstart/validators) for instructions
-- Go version 1.24 or higher
-
-## 1. Project Setup
-
-
-
-### Initialize a Go Module
-
-Create a new directory for your project and initialize a Go module:
-
-```bash
-mkdir sonr-go-quickstart
-cd sonr-go-quickstart
-go mod init github.com/your-username/sonr-go-quickstart
-```
-
-
-
-
-### Add the Sonr Client SDK Dependency
-
-Add the Sonr Client SDK to your project's dependencies:
-
-```bash
-go get github.com/sonr-io/sonr/client
-```
-
-
-
-
-## 2. Client Configuration and Setup
-
-Let's set up the Sonr client with proper configuration and key management.
-
-
-
-### Create the Main File
-
-Create a new file named `main.go`.
-
-
-
-
-### Initialize the Client
-
-Add the following code to `main.go` to initialize the Sonr client:
-
-```go
-package main
-
-import (
- "context"
- "fmt"
- "log"
-
- "google.golang.org/grpc"
- "google.golang.org/grpc/credentials/insecure"
-
- "github.com/sonr-io/sonr/client/config"
- "github.com/sonr-io/sonr/client/keys"
- "github.com/sonr-io/sonr/client/sonr"
- "github.com/sonr-io/sonr/client/tx"
-)
-
-func main() {
- // Use local network configuration
- cfg := config.LocalNetwork()
-
- // Establish gRPC connection
- conn, err := grpc.Dial(
- cfg.GRPC,
- grpc.WithTransportCredentials(insecure.NewCredentials()),
- )
- if err != nil {
- log.Fatal("Failed to connect:", err)
- }
- defer conn.Close()
-
- // Create the Sonr client
- client, err := sonr.NewClient(&cfg, conn)
- if err != nil {
- log.Fatal("Failed to create client:", err)
- }
-
- fmt.Println("Sonr client initialized successfully!")
- fmt.Printf("Connected to: %s\n", cfg.ChainID)
-}
-```
-
-
-
-
-### Create a Keyring Manager
-
-Add key management to handle wallet operations:
-
-```go
-// Initialize keyring manager (using test backend for development)
-keyringManager, err := keys.NewKeyringManager(
- "test", // backend: test, file, os
- ".sonr-keys", // directory for keys
- cfg.ChainID, // chain ID
-)
-if err != nil {
- log.Fatal("Failed to create keyring:", err)
-}
-
-// Create a new wallet
-walletIdentity, mnemonic, err := keyringManager.CreateWallet(
- context.Background(),
- "my-wallet", // wallet name
- "", // passphrase (empty for test backend)
-)
-if err != nil {
- log.Fatal("Failed to create wallet:", err)
-}
-
-fmt.Printf("Wallet created!\n")
-fmt.Printf("Address: %s\n", walletIdentity.Address)
-fmt.Printf("DID: %s\n", walletIdentity.DID)
-fmt.Printf("Mnemonic: %s\n", mnemonic)
-```
-
-
- **Important**: In production, use secure keyring backends like "os" or "file" with proper passphrase protection. Never expose mnemonics in your code.
-
-
-
-
-## 3. Querying the Blockchain
-
-Now, let's query the blockchain using the unified query client.
-
-
-
-### Create a Query Client
-
-Add the query client to your application:
-
-```go
-import (
- "github.com/sonr-io/sonr/client/query"
-)
-
-// Create query client
-queryClient, err := query.NewQueryClient(conn, &cfg)
-if err != nil {
- log.Fatal("Failed to create query client:", err)
-}
-```
-
-
-
-
-### Query Account Balance
-
-Query an account's balance:
-
-```go
-// Query account balance
-address := walletIdentity.Address // or any other address
-balance, err := queryClient.Balance(
- context.Background(),
- address,
- cfg.StakingDenom, // "usnr"
-)
-if err != nil {
- log.Printf("Failed to query balance: %v", err)
-} else {
- fmt.Printf("Balance for %s: %s %s\n",
- address,
- balance.Balance.Amount.String(),
- balance.Balance.Denom,
- )
-}
-
-// Query all balances for an account
-allBalances, err := queryClient.AllBalances(
- context.Background(),
- address,
- nil, // pagination
-)
-if err != nil {
- log.Printf("Failed to query all balances: %v", err)
-} else {
- fmt.Printf("All balances: %v\n", allBalances.Balances)
-}
-```
-
-
-
-
-### Query Module-Specific Data
-
-Query DID documents and other module data:
-
-```go
-import (
- "github.com/sonr-io/sonr/client/modules/did"
-)
-
-// Create DID module client
-didClient := did.NewDIDClient()
-
-// Query DID document (if exists)
-didID := "did:sonr:example123"
-didDoc, err := queryClient.GetDID(context.Background(), didID)
-if err != nil {
- log.Printf("DID not found: %v", err)
-} else {
- fmt.Printf("DID Document: %+v\n", didDoc)
-}
-
-// List all DIDs with pagination
-didList, err := queryClient.ListDIDs(
- context.Background(),
- &query.ListOptions{
- Limit: 10,
- Offset: 0,
- },
-)
-if err != nil {
- log.Printf("Failed to list DIDs: %v", err)
-} else {
- fmt.Printf("Found %d DIDs\n", len(didList.DIDs))
-}
-```
-
-
-
-
-## 4. Building and Broadcasting Transactions
-
-Let's build and broadcast transactions using the transaction builder.
-
-
-
-### Create a Transaction Builder
-
-Initialize the transaction builder with gas estimation:
-
-```go
-// Create transaction builder
-txBuilder, err := tx.NewTxBuilder(&cfg, conn)
-if err != nil {
- log.Fatal("Failed to create tx builder:", err)
-}
-
-// Create broadcaster
-broadcaster, err := tx.NewBroadcaster(&cfg, conn)
-if err != nil {
- log.Fatal("Failed to create broadcaster:", err)
-}
-
-// Create gas estimator
-gasEstimator := tx.NewGasEstimator(conn, &cfg)
-```
-
-
-
-
-### Send Tokens Between Accounts
-
-Build and broadcast a bank send transaction:
-
-```go
-import (
- sdk "github.com/cosmos/cosmos-sdk/types"
- banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
-)
-
-// Create a second wallet to receive funds
-receiver, _, err := keyringManager.CreateWallet(
- context.Background(),
- "receiver-wallet",
- "",
-)
-if err != nil {
- log.Fatal("Failed to create receiver wallet:", err)
-}
-
-// Create send message
-amount := sdk.NewCoins(sdk.NewInt64Coin("usnr", 1000000)) // 1 SNR
-sendMsg := &banktypes.MsgSend{
- FromAddress: walletIdentity.Address,
- ToAddress: receiver.Address,
- Amount: amount,
-}
-
-// Build transaction
-txBuilder = txBuilder.
- AddMessage(sendMsg).
- WithMemo("Test transaction").
- WithGasLimit(200000)
-
-// Estimate gas
-gasEstimate, err := gasEstimator.EstimateGas(
- context.Background(),
- []sdk.Msg{sendMsg},
-)
-if err != nil {
- log.Printf("Gas estimation failed: %v", err)
-} else {
- fmt.Printf("Estimated gas: %d\n", gasEstimate.GasLimit)
- txBuilder = txBuilder.WithGasLimit(gasEstimate.GasLimit)
-}
-
-// Calculate and set fee
-fee := gasEstimator.CalculateFee(
- gasEstimate.GasLimit,
- cfg.GasPrice,
- cfg.StakingDenom,
-)
-txBuilder = txBuilder.WithFee(fee)
-
-// Sign the transaction
-signedTx, err := txBuilder.Sign(context.Background(), keyringManager)
-if err != nil {
- log.Fatal("Failed to sign transaction:", err)
-}
-
-// Broadcast the transaction
-result, err := broadcaster.BroadcastTx(context.Background(), signedTx)
-if err != nil {
- log.Fatal("Failed to broadcast transaction:", err)
-}
-
-fmt.Printf("Transaction successful!\n")
-fmt.Printf("TxHash: %s\n", result.TxHash)
-fmt.Printf("Height: %d\n", result.Height)
-fmt.Printf("Gas Used: %d\n", result.GasUsed)
-```
-
-
-
-
-## 5. WebAuthn Gasless Transactions (Advanced)
-
-Sonr supports gasless WebAuthn registration, allowing users to onboard without holding tokens.
-
-
-
-### Initialize WebAuthn Client
-
-Set up the WebAuthn client for gasless operations:
-
-```go
-import (
- "github.com/sonr-io/sonr/client/auth"
-)
-
-// Create WebAuthn client
-webauthnClient := auth.NewWebAuthnClient(
- keyringManager,
- "localhost", // Relying Party ID
- "Sonr Local", // Relying Party Name
-)
-
-// Create gasless transaction manager
-gaslessManager := auth.NewGaslessTransactionManager(
- txBuilder,
- broadcaster,
- &cfg,
-)
-
-// Create WebAuthn gasless client
-gaslessClient := auth.NewWebAuthnGaslessClient(
- webauthnClient,
- gaslessManager,
- &cfg,
-)
-```
-
-
-
-
-### Initiate Gasless Registration
-
-Start a gasless WebAuthn registration:
-
-```go
-// Begin gasless registration
-registrationResult, err := gaslessClient.RegisterGasless(
- context.Background(),
- "alice", // username
- "Alice Smith", // display name
-)
-if err != nil {
- log.Fatal("Failed to initiate registration:", err)
-}
-
-fmt.Printf("Registration initiated!\n")
-fmt.Printf("Gasless eligible: %v\n", registrationResult.GaslessEligible)
-fmt.Printf("Estimated gas: %d\n", registrationResult.EstimatedGas)
-
-// The challenge would be sent to a browser for completion
-// In a real application, you'd handle the browser response
-```
-
-
-
-
-### Check Gasless Eligibility
-
-Verify if a transaction is eligible for gasless processing:
-
-```go
-// Check if messages are eligible for gasless
-msgs := []sdk.Msg{
- &didtypes.MsgRegisterWebAuthnCredential{
- Controller: "sonr1...",
- Username: "alice",
- },
-}
-
-isEligible := gaslessManager.IsEligibleForGasless(msgs)
-fmt.Printf("Transaction eligible for gasless: %v\n", isEligible)
-
-// Estimate gas for gasless transaction
-gasNeeded := gaslessManager.EstimateGaslessGas(
- "/did.v1.MsgRegisterWebAuthnCredential",
-)
-fmt.Printf("Gas needed for gasless WebAuthn: %d\n", gasNeeded)
-```
-
-
-
-
-## 6. Working with DID Module
-
-Create and manage decentralized identities:
-
-
-
-### Create a DID Document
-
-```go
-import (
- didtypes "github.com/sonr-io/sonr/x/did/types"
-)
-
-// Create DID document
-didDoc := &didtypes.DidDocument{
- Id: "did:sonr:" + walletIdentity.Address,
- Controller: walletIdentity.Address,
- VerificationMethod: []*didtypes.VerificationMethod{
- {
- Id: "did:sonr:" + walletIdentity.Address + "#key-1",
- VerificationMethodKind: didtypes.VerificationMethodKind_Ed25519VerificationKey2020,
- Controller: "did:sonr:" + walletIdentity.Address,
- PublicKeyMultibase: "z6MkhaXgBZD...", // Your public key
- },
- },
- Service: []*didtypes.Service{
- {
- Id: "did:sonr:" + walletIdentity.Address + "#dwn",
- ServiceKind: didtypes.ServiceKind_DecentralizedWebNode,
- SingleEndpoint: "https://dwn.example.com",
- },
- },
-}
-
-// Create the message
-createDIDMsg, err := didClient.CreateDID(
- walletIdentity.Address,
- didDoc,
-)
-if err != nil {
- log.Fatal("Failed to create DID message:", err)
-}
-
-// Add to transaction and broadcast
-txBuilder = txBuilder.
- ClearMessages().
- AddMessage(createDIDMsg).
- WithMemo("Create DID")
-
-// Sign and broadcast as shown earlier
-```
-
-
-
-
-## Next Steps
-
-This quickstart has covered the fundamentals of the Sonr Go Client SDK:
-
-- **Client Configuration**: Setting up connections and network configurations
-- **Key Management**: Creating and managing wallets with the keyring
-- **Querying**: Reading blockchain state and module data
-- **Transactions**: Building, signing, and broadcasting transactions
-- **Gas Estimation**: Calculating optimal gas limits and fees
-- **WebAuthn**: Gasless onboarding with WebAuthn credentials
-- **DID Module**: Creating decentralized identities
-
-### Advanced Topics to Explore:
-
-- **DWN Module**: Manage decentralized web nodes and data records
-- **Service Module**: Register and verify services with domain verification
-- **UCAN Integration**: Implement capability-based authorization
-- **Multi-signature**: Create and manage multi-sig accounts
-- **IBC Transfers**: Cross-chain token transfers
-- **Custom Modules**: Interact with your own custom modules
-
-### Useful Resources:
-
-- [Client SDK API Reference](https://pkg.go.dev/github.com/sonr-io/sonr/client)
-- [Cosmos SDK Documentation](https://docs.cosmos.network)
-- [Sonr GitHub Repository](https://github.com/sonr-io/sonr)
diff --git a/docs/quickstart/react.mdx b/docs/quickstart/react.mdx
deleted file mode 100644
index af9fab23c..000000000
--- a/docs/quickstart/react.mdx
+++ /dev/null
@@ -1,190 +0,0 @@
----
-title: Getting Started with ReactJS
-description: A quick start guide for building applications with Sonr using JavaScript and TypeScript
-sidebarTitle: React Quickstart
-icon: "react"
----
-
-This guide will show you how to set up a Node.js project with TypeScript and use the Sonr SDK to interact with the Sonr network.
-
-## Prerequisites
-
-- A local Sonr network running. See the [Validator Setup Guide](/quickstart/validators) for instructions
-- Node.js version 16 or higher
-- npm or yarn
-
-## 1. Project Setup
-
-
-
-### Initialize a New Project
-
-Create a new directory for your project and initialize it with npm:
-
-```bash
-mkdir sonr-ts-quickstart
-cd sonr-ts-quickstart
-npm init -y
-```
-
-
-
-
-### Install Dependencies
-
-Install the Sonr SDK and TypeScript:
-
-```bash
-npm install @sonr/sdk typescript ts-node @types/node
-```
-
-
-
-
-### Configure TypeScript
-
-Create a `tsconfig.json` file in your project root with the following configuration:
-
-```json
-{
- "compilerOptions": {
- "target": "es2020",
- "module": "commonjs",
- "esModuleInterop": true,
- "forceConsistentCasingInFileNames": true,
- "strict": true,
- "skipLibCheck": true
- }
-}
-```
-
-
-
-
-## 2. Creating a Wallet
-
-Now, let's create a new TypeScript file and add the logic to create a new Sonr wallet.
-
-
-
-### Create the Main File
-
-Create a new file named `index.ts`.
-
-
-
-
-### Implement Wallet Creation
-
-Add the following code to `index.ts` to create a new wallet and log its address and mnemonic:
-
-```typescript
-import { Sonr } from "@sonr/sdk";
-
-async function main() {
- console.log("Creating a new Sonr wallet...");
-
- const sonr = new Sonr({ httpUrl: "http://localhost:1317" });
- const wallet = await sonr.createWallet();
-
- console.log(`Wallet created!`);
- console.log(`Address: ${wallet.address}`);
- console.log(`Mnemonic: ${wallet.mnemonic}`);
-}
-
-main().catch(console.error);
-```
-
-
- **Important**: In a real application, you must store the mnemonic securely.
- Never expose it in client-side code.
-
-
-
-
-### Run the Script
-
-Execute the script using `ts-node`:
-
-```bash
-npx ts-node index.ts
-```
-
-You should see the new wallet's address and mnemonic printed to the console.
-
-
-
-
-## 3. Querying the Blockchain
-
-Let's query the blockchain to get the balance of our new wallet.
-
-
-
-### Get Account Balance
-
-Modify your `index.ts` file to query the account balance after creating the wallet. You will need to fund this account from the localnet faucet for it to have a balance.
-
-```typescript
-// ... after creating the wallet
-
-console.log("Querying account balance...");
-
-// The localnet validator has funds, so we'll use its address for the query
-const validatorAddress = "snr1..._validator_address_..."; // Replace with the actual validator address from your localnet
-const balance = await sonr.getAccountBalance(validatorAddress);
-
-console.log(`Balance for ${validatorAddress}:`, balance);
-```
-
-
-
-
-### Run the Script Again
-
-Run the script to see the account balance:
-
-```bash
-npx ts-node index.ts
-```
-
-
-
-
-## 4. Sending a Transaction
-
-Finally, let's send a transaction from one account to another.
-
-
-
-### Implement Transaction Sending
-
-For this step, you will need two wallets. You can create a second one using the same `createWallet` method. Ensure both wallets have funds from the localnet faucet.
-
-```typescript
-// ... inside your main function
-
-const wallet1 = await sonr.createWallet(); // Fund this from the faucet
-const wallet2 = await sonr.createWallet();
-
-console.log(`Sending 1 SNR from ${wallet1.address} to ${wallet2.address}`);
-
-const result = await wallet1.send({
- to: wallet2.address,
- amount: "1000000usnr", // 1 SNR
-});
-
-console.log(`Transaction successful! TxHash: ${result.txhash}`);
-```
-
-
-
-
-## Next Steps
-
-This quickstart has shown you the basics of interacting with the Sonr network using our TypeScript SDK. You can now explore more advanced topics:
-
-- **Service Registration**: Register your application as a trusted service
-- **UCAN Authorization**: Implement capability-based permissions
-- **Smart Contract Interaction**: Call and query smart contracts on the Sonr network
-
diff --git a/docs/reference/architecture/decentralized-web-node.mdx b/docs/reference/architecture/decentralized-web-node.mdx
deleted file mode 100644
index 571c1a061..000000000
--- a/docs/reference/architecture/decentralized-web-node.mdx
+++ /dev/null
@@ -1,155 +0,0 @@
----
-title: "DWN Plugin Architecture"
-description: "Deep dive into the Decentralized Web Node (DWN) plugin system and integration"
-sidebarTitle: "WASM Architecture"
-icon: "puzzle"
----
-
-# DWN Plugin Architecture
-
-The Sonr project implements a flexible and secure plugin system for Decentralized Web Nodes (DWN), enabling modular and extensible functionality.
-
-## Overview
-
-The plugin architecture is designed to:
-
-- Support dynamic loading of WebAssembly (WASM) plugins
-- Provide a standardized interface for plugin interactions
-- Enable secure, isolated execution of plugins
-
-## Core Components
-
-### Plugin Manager
-
-The `PluginManager` manages plugin lifecycle and interactions:
-
-```go
-type PluginManager struct {
- plugins map[string]Plugin
- actors map[string]Actor
-}
-
-type Plugin interface {
- Initialize(config map[string]any) error
- Execute(method string, payload []byte) ([]byte, error)
- Close() error
-}
-```
-
-### Plugin Configuration
-
-Plugins are configured through a structured configuration:
-
-```go
-type PluginConfig struct {
- ID string // Unique plugin identifier
- Type string // Plugin type (e.g., "motor", "crypto")
- Path string // WASM module path
- Environment map[string]any // Plugin-specific environment variables
-}
-```
-
-## Loading and Initializing Plugins
-
-### Basic Plugin Loading
-
-```go
-func (pm *PluginManager) LoadPlugin(config PluginConfig) error {
- // Load WASM module
- module, err := extism.Load(config.Path)
- if err != nil {
- return err
- }
-
- // Initialize plugin
- plugin := &WASMPlugin{
- module: module,
- config: config,
- }
-
- // Store in plugin registry
- pm.plugins[config.ID] = plugin
-}
-```
-
-### Actor-Based Plugin Management
-
-```go
-func (pm *PluginManager) CreateActor(pluginID string) (*Actor, error) {
- plugin, exists := pm.plugins[pluginID]
- if !exists {
- return nil, errors.New("plugin not found")
- }
-
- actor := NewActor(plugin)
- pm.actors[actor.ID] = actor
-
- return actor, nil
-}
-```
-
-## Plugin Execution Workflow
-
-1. Plugin is loaded from WASM module
-2. Configuration is applied
-3. Plugin is initialized
-4. Specific methods can be invoked through a standardized interface
-
-### Example Plugin Execution
-
-```go
-func ExecutePluginMethod(pluginID, method string, payload []byte) ([]byte, error) {
- plugin := pluginManager.plugins[pluginID]
- return plugin.Execute(method, payload)
-}
-```
-
-## Configuration and Environment
-
-### Plugin Environment Variables
-
-```json
-{
- "motor_plugin": {
- "enclave_config": { ... },
- "chain_id": "sonr-testnet-1",
- "log_level": "debug"
- }
-}
-```
-
-## Error Handling and Logging
-
-```go
-type PluginError struct {
- Code string
- Message string
- Details map[string]any
-}
-```
-
-## Security Considerations
-
-- WASM plugins run in an isolated sandbox
-- Limited access to system resources
-- Runtime restrictions prevent malicious behavior
-- Cryptographic verification of plugin modules
-
-## Plugin Types
-
-1. **Crypto Plugins**: Cryptographic operations
-2. **Motor Plugins**: MPC and token management
-3. **DID Plugins**: Decentralized Identity operations
-4. **Custom Plugins**: Application-specific extensions
-
-## Best Practices
-
-- Keep plugins small and focused
-- Use standardized interfaces
-- Implement comprehensive error handling
-- Validate all plugin inputs
-- Monitor plugin performance
-
-## Advanced Configuration
-
-For advanced plugin configuration and deployment, refer to the [PDK Configuration Guide](/blockchain/modules/dwn/configuration).
diff --git a/docs/reference/architecture/wallet-abstraction.mdx b/docs/reference/architecture/wallet-abstraction.mdx
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docs/reference/libraries/go-client.mdx b/docs/reference/libraries/go-client.mdx
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docs/reference/libraries/go-crypto.mdx b/docs/reference/libraries/go-crypto.mdx
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docs/reference/libraries/go-ui.mdx b/docs/reference/libraries/go-ui.mdx
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docs/reference/packages/cli.mdx b/docs/reference/packages/cli.mdx
deleted file mode 100644
index ae2d115cc..000000000
--- a/docs/reference/packages/cli.mdx
+++ /dev/null
@@ -1,173 +0,0 @@
----
-title: "@sonr.io/ui"
-sidebarTitle: "@sonr.io/ui"
-description: "A package for Sonr's centralized shadcn/ui component library"
-icon: "palette"
----
-
-# @sonr.io/ui
-
-## Overview
-
-The `@sonr.io/ui` package serves as our centralized shadcn/ui component library, providing a consistent and accessible design system across Sonr's ecosystem. Leveraging the power of shadcn/ui, we've created a fully customizable and type-safe component library with a primary color of `#17c2ff`.
-
-
- Our UI package is built to provide maximum flexibility while maintaining
- strict design consistency.
-
-
-## Component Migration Strategy
-
-### Before: Custom Button Component
-
-Previously, our Button component relied on manual variant classes and custom implementations:
-
-```tsx
-// Old Implementation
-const Button = ({ variant, className, ...props }) => {
- const variantClasses = {
- primary: "bg-blue-500 text-white",
- secondary: "bg-gray-200 text-black",
- // Multiple manual variant definitions
- };
-
- return (
-
- );
-};
-```
-
-### After: Shadcn Button Implementation
-
-Our new implementation uses `cva` (class-variance-authority) and the `cn` utility for robust variant management:
-
-```tsx
-// New Implementation
-import { buttonVariants } from "@sonr.io/ui/components/ui/button";
-import { cn } from "@sonr.io/ui/lib/utils";
-
-const Button = ({
- variant = "default",
- size = "default",
- className,
- ...props
-}) => {
- return (
-
- );
-};
-```
-
-
- Key Improvements: - Type-safe variant management - Enhanced accessibility -
- Consistent theming - Reduced bundle size
-
-
-## Theme Configuration
-
-### Color System
-
-Our primary theme color is `#17c2ff`, defined in HSL format for maximum flexibility:
-
-```css
-:root {
- --primary-h: 200; /* Hue */
- --primary-s: 100%; /* Saturation */
- --primary-l: 59%; /* Lightness */
- --primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
-}
-```
-
-### CSS Variables Structure
-
-```css
-:root {
- --background: 0 0% 100%;
- --foreground: 240 10% 3.9%;
- --primary: 200 100% 59%;
- --primary-foreground: 210 40% 98%;
- --secondary: 220 14.3% 95.9%;
- --secondary-foreground: 220.9 39.3% 11%;
- /* Additional theme variables */
-}
-```
-
-## Usage Guide
-
-### Importing Components
-
-Import components directly from the `@sonr.io/ui` package:
-
-```tsx
-import { Button } from "@sonr.io/ui/components/ui/button";
-import { Input } from "@sonr.io/ui/components/ui/input";
-```
-
-### Adding New Components
-
-Use the shadcn CLI within the `packages/ui` directory:
-
-```bash
-# Navigate to packages/ui
-cd packages/ui
-
-# Add a new component
-npx shadcn-ui@latest add button
-```
-
-
- Always add components from the `packages/ui` directory to maintain our
- centralized component management.
-
-
-## Architecture Patterns
-
-### Monorepo Structure
-
-```
-sonr/
-├── packages/
-│ └── ui/
-│ ├── components/
-│ │ └── ui/
-│ │ ├── button.tsx
-│ │ ├── input.tsx
-│ │ └── ...
-│ ├── lib/
-│ │ └── utils.ts
-│ └── styles/
-│ └── globals.css
-```
-
-### Key Principles
-
-- **Single Source of Truth**: All UI components live in `packages/ui`
-- **No Local `components.json`**: Centralized configuration
-- **Turbo-powered Build Pipeline**: Efficient component compilation
-
-## Global Styles Integration
-
-In your application's main entry point:
-
-```tsx
-import "@sonr.io/ui/styles/globals.css";
-
-function MyApp({ Component, pageProps }) {
- return ;
-}
-```
-
-## Best Practices
-
-1. Always use the centralized components from `@sonr.io/ui`
-2. Prefer `cn()` utility for dynamic className composition
-3. Leverage TypeScript for type-safe component usage
-4. Use CSS variables for theming consistency
-
-
- Remember: Our UI library is designed to be both flexible and consistent. When
- in doubt, refer to the components in `@sonr.io/ui`.
-
diff --git a/docs/reference/packages/es.mdx b/docs/reference/packages/es.mdx
deleted file mode 100644
index ae2d115cc..000000000
--- a/docs/reference/packages/es.mdx
+++ /dev/null
@@ -1,173 +0,0 @@
----
-title: "@sonr.io/ui"
-sidebarTitle: "@sonr.io/ui"
-description: "A package for Sonr's centralized shadcn/ui component library"
-icon: "palette"
----
-
-# @sonr.io/ui
-
-## Overview
-
-The `@sonr.io/ui` package serves as our centralized shadcn/ui component library, providing a consistent and accessible design system across Sonr's ecosystem. Leveraging the power of shadcn/ui, we've created a fully customizable and type-safe component library with a primary color of `#17c2ff`.
-
-
- Our UI package is built to provide maximum flexibility while maintaining
- strict design consistency.
-
-
-## Component Migration Strategy
-
-### Before: Custom Button Component
-
-Previously, our Button component relied on manual variant classes and custom implementations:
-
-```tsx
-// Old Implementation
-const Button = ({ variant, className, ...props }) => {
- const variantClasses = {
- primary: "bg-blue-500 text-white",
- secondary: "bg-gray-200 text-black",
- // Multiple manual variant definitions
- };
-
- return (
-
- );
-};
-```
-
-### After: Shadcn Button Implementation
-
-Our new implementation uses `cva` (class-variance-authority) and the `cn` utility for robust variant management:
-
-```tsx
-// New Implementation
-import { buttonVariants } from "@sonr.io/ui/components/ui/button";
-import { cn } from "@sonr.io/ui/lib/utils";
-
-const Button = ({
- variant = "default",
- size = "default",
- className,
- ...props
-}) => {
- return (
-
- );
-};
-```
-
-
- Key Improvements: - Type-safe variant management - Enhanced accessibility -
- Consistent theming - Reduced bundle size
-
-
-## Theme Configuration
-
-### Color System
-
-Our primary theme color is `#17c2ff`, defined in HSL format for maximum flexibility:
-
-```css
-:root {
- --primary-h: 200; /* Hue */
- --primary-s: 100%; /* Saturation */
- --primary-l: 59%; /* Lightness */
- --primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
-}
-```
-
-### CSS Variables Structure
-
-```css
-:root {
- --background: 0 0% 100%;
- --foreground: 240 10% 3.9%;
- --primary: 200 100% 59%;
- --primary-foreground: 210 40% 98%;
- --secondary: 220 14.3% 95.9%;
- --secondary-foreground: 220.9 39.3% 11%;
- /* Additional theme variables */
-}
-```
-
-## Usage Guide
-
-### Importing Components
-
-Import components directly from the `@sonr.io/ui` package:
-
-```tsx
-import { Button } from "@sonr.io/ui/components/ui/button";
-import { Input } from "@sonr.io/ui/components/ui/input";
-```
-
-### Adding New Components
-
-Use the shadcn CLI within the `packages/ui` directory:
-
-```bash
-# Navigate to packages/ui
-cd packages/ui
-
-# Add a new component
-npx shadcn-ui@latest add button
-```
-
-
- Always add components from the `packages/ui` directory to maintain our
- centralized component management.
-
-
-## Architecture Patterns
-
-### Monorepo Structure
-
-```
-sonr/
-├── packages/
-│ └── ui/
-│ ├── components/
-│ │ └── ui/
-│ │ ├── button.tsx
-│ │ ├── input.tsx
-│ │ └── ...
-│ ├── lib/
-│ │ └── utils.ts
-│ └── styles/
-│ └── globals.css
-```
-
-### Key Principles
-
-- **Single Source of Truth**: All UI components live in `packages/ui`
-- **No Local `components.json`**: Centralized configuration
-- **Turbo-powered Build Pipeline**: Efficient component compilation
-
-## Global Styles Integration
-
-In your application's main entry point:
-
-```tsx
-import "@sonr.io/ui/styles/globals.css";
-
-function MyApp({ Component, pageProps }) {
- return ;
-}
-```
-
-## Best Practices
-
-1. Always use the centralized components from `@sonr.io/ui`
-2. Prefer `cn()` utility for dynamic className composition
-3. Leverage TypeScript for type-safe component usage
-4. Use CSS variables for theming consistency
-
-
- Remember: Our UI library is designed to be both flexible and consistent. When
- in doubt, refer to the components in `@sonr.io/ui`.
-
diff --git a/docs/reference/packages/pkl.mdx b/docs/reference/packages/pkl.mdx
deleted file mode 100644
index ae2d115cc..000000000
--- a/docs/reference/packages/pkl.mdx
+++ /dev/null
@@ -1,173 +0,0 @@
----
-title: "@sonr.io/ui"
-sidebarTitle: "@sonr.io/ui"
-description: "A package for Sonr's centralized shadcn/ui component library"
-icon: "palette"
----
-
-# @sonr.io/ui
-
-## Overview
-
-The `@sonr.io/ui` package serves as our centralized shadcn/ui component library, providing a consistent and accessible design system across Sonr's ecosystem. Leveraging the power of shadcn/ui, we've created a fully customizable and type-safe component library with a primary color of `#17c2ff`.
-
-
- Our UI package is built to provide maximum flexibility while maintaining
- strict design consistency.
-
-
-## Component Migration Strategy
-
-### Before: Custom Button Component
-
-Previously, our Button component relied on manual variant classes and custom implementations:
-
-```tsx
-// Old Implementation
-const Button = ({ variant, className, ...props }) => {
- const variantClasses = {
- primary: "bg-blue-500 text-white",
- secondary: "bg-gray-200 text-black",
- // Multiple manual variant definitions
- };
-
- return (
-
- );
-};
-```
-
-### After: Shadcn Button Implementation
-
-Our new implementation uses `cva` (class-variance-authority) and the `cn` utility for robust variant management:
-
-```tsx
-// New Implementation
-import { buttonVariants } from "@sonr.io/ui/components/ui/button";
-import { cn } from "@sonr.io/ui/lib/utils";
-
-const Button = ({
- variant = "default",
- size = "default",
- className,
- ...props
-}) => {
- return (
-
- );
-};
-```
-
-
- Key Improvements: - Type-safe variant management - Enhanced accessibility -
- Consistent theming - Reduced bundle size
-
-
-## Theme Configuration
-
-### Color System
-
-Our primary theme color is `#17c2ff`, defined in HSL format for maximum flexibility:
-
-```css
-:root {
- --primary-h: 200; /* Hue */
- --primary-s: 100%; /* Saturation */
- --primary-l: 59%; /* Lightness */
- --primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
-}
-```
-
-### CSS Variables Structure
-
-```css
-:root {
- --background: 0 0% 100%;
- --foreground: 240 10% 3.9%;
- --primary: 200 100% 59%;
- --primary-foreground: 210 40% 98%;
- --secondary: 220 14.3% 95.9%;
- --secondary-foreground: 220.9 39.3% 11%;
- /* Additional theme variables */
-}
-```
-
-## Usage Guide
-
-### Importing Components
-
-Import components directly from the `@sonr.io/ui` package:
-
-```tsx
-import { Button } from "@sonr.io/ui/components/ui/button";
-import { Input } from "@sonr.io/ui/components/ui/input";
-```
-
-### Adding New Components
-
-Use the shadcn CLI within the `packages/ui` directory:
-
-```bash
-# Navigate to packages/ui
-cd packages/ui
-
-# Add a new component
-npx shadcn-ui@latest add button
-```
-
-
- Always add components from the `packages/ui` directory to maintain our
- centralized component management.
-
-
-## Architecture Patterns
-
-### Monorepo Structure
-
-```
-sonr/
-├── packages/
-│ └── ui/
-│ ├── components/
-│ │ └── ui/
-│ │ ├── button.tsx
-│ │ ├── input.tsx
-│ │ └── ...
-│ ├── lib/
-│ │ └── utils.ts
-│ └── styles/
-│ └── globals.css
-```
-
-### Key Principles
-
-- **Single Source of Truth**: All UI components live in `packages/ui`
-- **No Local `components.json`**: Centralized configuration
-- **Turbo-powered Build Pipeline**: Efficient component compilation
-
-## Global Styles Integration
-
-In your application's main entry point:
-
-```tsx
-import "@sonr.io/ui/styles/globals.css";
-
-function MyApp({ Component, pageProps }) {
- return ;
-}
-```
-
-## Best Practices
-
-1. Always use the centralized components from `@sonr.io/ui`
-2. Prefer `cn()` utility for dynamic className composition
-3. Leverage TypeScript for type-safe component usage
-4. Use CSS variables for theming consistency
-
-
- Remember: Our UI library is designed to be both flexible and consistent. When
- in doubt, refer to the components in `@sonr.io/ui`.
-
diff --git a/docs/reference/packages/sdk.mdx b/docs/reference/packages/sdk.mdx
deleted file mode 100644
index ae2d115cc..000000000
--- a/docs/reference/packages/sdk.mdx
+++ /dev/null
@@ -1,173 +0,0 @@
----
-title: "@sonr.io/ui"
-sidebarTitle: "@sonr.io/ui"
-description: "A package for Sonr's centralized shadcn/ui component library"
-icon: "palette"
----
-
-# @sonr.io/ui
-
-## Overview
-
-The `@sonr.io/ui` package serves as our centralized shadcn/ui component library, providing a consistent and accessible design system across Sonr's ecosystem. Leveraging the power of shadcn/ui, we've created a fully customizable and type-safe component library with a primary color of `#17c2ff`.
-
-
- Our UI package is built to provide maximum flexibility while maintaining
- strict design consistency.
-
-
-## Component Migration Strategy
-
-### Before: Custom Button Component
-
-Previously, our Button component relied on manual variant classes and custom implementations:
-
-```tsx
-// Old Implementation
-const Button = ({ variant, className, ...props }) => {
- const variantClasses = {
- primary: "bg-blue-500 text-white",
- secondary: "bg-gray-200 text-black",
- // Multiple manual variant definitions
- };
-
- return (
-
- );
-};
-```
-
-### After: Shadcn Button Implementation
-
-Our new implementation uses `cva` (class-variance-authority) and the `cn` utility for robust variant management:
-
-```tsx
-// New Implementation
-import { buttonVariants } from "@sonr.io/ui/components/ui/button";
-import { cn } from "@sonr.io/ui/lib/utils";
-
-const Button = ({
- variant = "default",
- size = "default",
- className,
- ...props
-}) => {
- return (
-
- );
-};
-```
-
-
- Key Improvements: - Type-safe variant management - Enhanced accessibility -
- Consistent theming - Reduced bundle size
-
-
-## Theme Configuration
-
-### Color System
-
-Our primary theme color is `#17c2ff`, defined in HSL format for maximum flexibility:
-
-```css
-:root {
- --primary-h: 200; /* Hue */
- --primary-s: 100%; /* Saturation */
- --primary-l: 59%; /* Lightness */
- --primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
-}
-```
-
-### CSS Variables Structure
-
-```css
-:root {
- --background: 0 0% 100%;
- --foreground: 240 10% 3.9%;
- --primary: 200 100% 59%;
- --primary-foreground: 210 40% 98%;
- --secondary: 220 14.3% 95.9%;
- --secondary-foreground: 220.9 39.3% 11%;
- /* Additional theme variables */
-}
-```
-
-## Usage Guide
-
-### Importing Components
-
-Import components directly from the `@sonr.io/ui` package:
-
-```tsx
-import { Button } from "@sonr.io/ui/components/ui/button";
-import { Input } from "@sonr.io/ui/components/ui/input";
-```
-
-### Adding New Components
-
-Use the shadcn CLI within the `packages/ui` directory:
-
-```bash
-# Navigate to packages/ui
-cd packages/ui
-
-# Add a new component
-npx shadcn-ui@latest add button
-```
-
-
- Always add components from the `packages/ui` directory to maintain our
- centralized component management.
-
-
-## Architecture Patterns
-
-### Monorepo Structure
-
-```
-sonr/
-├── packages/
-│ └── ui/
-│ ├── components/
-│ │ └── ui/
-│ │ ├── button.tsx
-│ │ ├── input.tsx
-│ │ └── ...
-│ ├── lib/
-│ │ └── utils.ts
-│ └── styles/
-│ └── globals.css
-```
-
-### Key Principles
-
-- **Single Source of Truth**: All UI components live in `packages/ui`
-- **No Local `components.json`**: Centralized configuration
-- **Turbo-powered Build Pipeline**: Efficient component compilation
-
-## Global Styles Integration
-
-In your application's main entry point:
-
-```tsx
-import "@sonr.io/ui/styles/globals.css";
-
-function MyApp({ Component, pageProps }) {
- return ;
-}
-```
-
-## Best Practices
-
-1. Always use the centralized components from `@sonr.io/ui`
-2. Prefer `cn()` utility for dynamic className composition
-3. Leverage TypeScript for type-safe component usage
-4. Use CSS variables for theming consistency
-
-
- Remember: Our UI library is designed to be both flexible and consistent. When
- in doubt, refer to the components in `@sonr.io/ui`.
-
diff --git a/docs/reference/packages/ui.mdx b/docs/reference/packages/ui.mdx
deleted file mode 100644
index ae2d115cc..000000000
--- a/docs/reference/packages/ui.mdx
+++ /dev/null
@@ -1,173 +0,0 @@
----
-title: "@sonr.io/ui"
-sidebarTitle: "@sonr.io/ui"
-description: "A package for Sonr's centralized shadcn/ui component library"
-icon: "palette"
----
-
-# @sonr.io/ui
-
-## Overview
-
-The `@sonr.io/ui` package serves as our centralized shadcn/ui component library, providing a consistent and accessible design system across Sonr's ecosystem. Leveraging the power of shadcn/ui, we've created a fully customizable and type-safe component library with a primary color of `#17c2ff`.
-
-
- Our UI package is built to provide maximum flexibility while maintaining
- strict design consistency.
-
-
-## Component Migration Strategy
-
-### Before: Custom Button Component
-
-Previously, our Button component relied on manual variant classes and custom implementations:
-
-```tsx
-// Old Implementation
-const Button = ({ variant, className, ...props }) => {
- const variantClasses = {
- primary: "bg-blue-500 text-white",
- secondary: "bg-gray-200 text-black",
- // Multiple manual variant definitions
- };
-
- return (
-
- );
-};
-```
-
-### After: Shadcn Button Implementation
-
-Our new implementation uses `cva` (class-variance-authority) and the `cn` utility for robust variant management:
-
-```tsx
-// New Implementation
-import { buttonVariants } from "@sonr.io/ui/components/ui/button";
-import { cn } from "@sonr.io/ui/lib/utils";
-
-const Button = ({
- variant = "default",
- size = "default",
- className,
- ...props
-}) => {
- return (
-
- );
-};
-```
-
-
- Key Improvements: - Type-safe variant management - Enhanced accessibility -
- Consistent theming - Reduced bundle size
-
-
-## Theme Configuration
-
-### Color System
-
-Our primary theme color is `#17c2ff`, defined in HSL format for maximum flexibility:
-
-```css
-:root {
- --primary-h: 200; /* Hue */
- --primary-s: 100%; /* Saturation */
- --primary-l: 59%; /* Lightness */
- --primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
-}
-```
-
-### CSS Variables Structure
-
-```css
-:root {
- --background: 0 0% 100%;
- --foreground: 240 10% 3.9%;
- --primary: 200 100% 59%;
- --primary-foreground: 210 40% 98%;
- --secondary: 220 14.3% 95.9%;
- --secondary-foreground: 220.9 39.3% 11%;
- /* Additional theme variables */
-}
-```
-
-## Usage Guide
-
-### Importing Components
-
-Import components directly from the `@sonr.io/ui` package:
-
-```tsx
-import { Button } from "@sonr.io/ui/components/ui/button";
-import { Input } from "@sonr.io/ui/components/ui/input";
-```
-
-### Adding New Components
-
-Use the shadcn CLI within the `packages/ui` directory:
-
-```bash
-# Navigate to packages/ui
-cd packages/ui
-
-# Add a new component
-npx shadcn-ui@latest add button
-```
-
-
- Always add components from the `packages/ui` directory to maintain our
- centralized component management.
-
-
-## Architecture Patterns
-
-### Monorepo Structure
-
-```
-sonr/
-├── packages/
-│ └── ui/
-│ ├── components/
-│ │ └── ui/
-│ │ ├── button.tsx
-│ │ ├── input.tsx
-│ │ └── ...
-│ ├── lib/
-│ │ └── utils.ts
-│ └── styles/
-│ └── globals.css
-```
-
-### Key Principles
-
-- **Single Source of Truth**: All UI components live in `packages/ui`
-- **No Local `components.json`**: Centralized configuration
-- **Turbo-powered Build Pipeline**: Efficient component compilation
-
-## Global Styles Integration
-
-In your application's main entry point:
-
-```tsx
-import "@sonr.io/ui/styles/globals.css";
-
-function MyApp({ Component, pageProps }) {
- return ;
-}
-```
-
-## Best Practices
-
-1. Always use the centralized components from `@sonr.io/ui`
-2. Prefer `cn()` utility for dynamic className composition
-3. Leverage TypeScript for type-safe component usage
-4. Use CSS variables for theming consistency
-
-
- Remember: Our UI library is designed to be both flexible and consistent. When
- in doubt, refer to the components in `@sonr.io/ui`.
-
diff --git a/networks/localnet/.env.example b/networks/localnet/.env.example
new file mode 100644
index 000000000..659d2fca1
--- /dev/null
+++ b/networks/localnet/.env.example
@@ -0,0 +1,17 @@
+CHAIN_ID=sonrtest_1-1
+DENOM=usnr
+KEYRING=test
+KEYALGO=eth_secp256k1
+BLOCK_TIME=5s
+VOTING_PERIOD=30s
+EXPEDITED_VOTING_PERIOD=15s
+MAX_GAS=100000000
+MIN_COMMISSION_RATE=0.050000000000000000
+NARUTO_MNEMONIC="decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry"
+SENKU_MNEMONIC="wealth flavor believe regret funny network recall kiss grape useless pepper cram hint member few certain unveil rather brick bargain curious require crowd raise"
+YAEGER_MNEMONIC="quality vacuum heart guard buzz spike sight swarm shove special gym robust assume sudden deposit grid alcohol choice devote leader tilt noodle tide penalty"
+FAUCET_MNEMONIC="notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius"
+BASE_ALLOCATION=100000000000000000000000000usnr
+STAKE_AMOUNT=30000000000000000000000usnr
+FAUCET_ALLOCATION=250000000000000000000000000000usnr
+DOCKER_IMAGE=onsonr/snrd:latest
diff --git a/networks/localnet/docker-compose.yml b/networks/localnet/docker-compose.yml
new file mode 100644
index 000000000..d6358339c
--- /dev/null
+++ b/networks/localnet/docker-compose.yml
@@ -0,0 +1,106 @@
+name: sonr-core
+
+services:
+ ipfs:
+ container_name: ipfs
+ image: ipfs/kubo:release
+ ports:
+ - "127.0.0.1:5001:5001"
+ - "127.0.0.1:8080:8080"
+ - "127.0.0.1:4001:4001"
+ volumes:
+ - ${HOME}/.ipfs:/data/ipfs
+ networks:
+ - sonr-network
+
+ cluster:
+ container_name: cluster
+ image: ipfs/ipfs-cluster:latest
+ depends_on:
+ - ipfs
+ environment:
+ CLUSTER_PEERNAME: cluster
+ CLUSTER_SECRET: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+ CLUSTER_IPFSHTTP_NODEMULTIADDRESS: /dns4/ipfs/tcp/5001
+ CLUSTER_CRDT_TRUSTEDPEERS: "*"
+ CLUSTER_MONITORPINGINTERVAL: 2s
+ ports:
+ - "127.0.0.1:9094:9094"
+ - "127.0.0.1:9095:9095"
+ volumes:
+ - ${HOME}/.ipfs-cluster:/data/ipfs-cluster
+ networks:
+ - sonr-network
+
+ caddy:
+ container_name: caddy
+ image: caddy:2-alpine
+ restart: unless-stopped
+ depends_on:
+ snrd:
+ condition: service_healthy
+ ports:
+ - "80:80"
+ - "443:443"
+ - "443:443/udp"
+ volumes:
+ - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
+ - caddy-data:/data
+ - caddy-config:/config
+ environment:
+ CADDY_DOMAIN: ${CADDY_DOMAIN:-localhost}
+ networks:
+ - sonr-network
+
+ snrd:
+ container_name: snrd
+ image: onsonr/snrd:latest
+ build:
+ context: .
+ dockerfile: cmd/snrd/Dockerfile
+ restart: unless-stopped
+ depends_on:
+ - ipfs
+ command: ["/usr/bin/devnet"]
+ environment:
+ CHAIN_ID: ${CHAIN_ID:-sonrtest_1-1}
+ MONIKER: ${MONIKER:-sonr-dev-node}
+ DENOM: ${DENOM:-usnr}
+ BLOCK_TIME: ${BLOCK_TIME:-1s}
+ CLEAN: ${CLEAN:-true}
+ KEYRING: ${KEYRING:-test}
+ HOME_DIR: /home/snrd/.snrd
+ KEY: acc0
+ KEY2: acc1
+ KEYALGO: eth_secp256k1
+ LOG_LEVEL: ${LOG_LEVEL:-debug}
+ TRACE: ${TRACE:-true}
+ SKIP_INSTALL: "true"
+ ports:
+ - "26657:26657"
+ - "1317:1317"
+ - "9090:9090"
+ - "9091:9091"
+ - "8545:8545"
+ - "8546:8546"
+ - "26656:26656"
+ - "6060:6060"
+ volumes:
+ - ${HOME}/.sonr:/root/.sonr
+ healthcheck:
+ test: ["CMD", "snrd", "status", "--home", "/home/snrd/.snrd"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 60s
+ networks:
+ - sonr-network
+
+networks:
+ sonr-network:
+ driver: bridge
+ name: sonr-network
+
+volumes:
+ caddy-config:
+ caddy-data:
diff --git a/networks/testnet/.env.example b/networks/testnet/.env.example
new file mode 100644
index 000000000..659d2fca1
--- /dev/null
+++ b/networks/testnet/.env.example
@@ -0,0 +1,17 @@
+CHAIN_ID=sonrtest_1-1
+DENOM=usnr
+KEYRING=test
+KEYALGO=eth_secp256k1
+BLOCK_TIME=5s
+VOTING_PERIOD=30s
+EXPEDITED_VOTING_PERIOD=15s
+MAX_GAS=100000000
+MIN_COMMISSION_RATE=0.050000000000000000
+NARUTO_MNEMONIC="decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry"
+SENKU_MNEMONIC="wealth flavor believe regret funny network recall kiss grape useless pepper cram hint member few certain unveil rather brick bargain curious require crowd raise"
+YAEGER_MNEMONIC="quality vacuum heart guard buzz spike sight swarm shove special gym robust assume sudden deposit grid alcohol choice devote leader tilt noodle tide penalty"
+FAUCET_MNEMONIC="notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius"
+BASE_ALLOCATION=100000000000000000000000000usnr
+STAKE_AMOUNT=30000000000000000000000usnr
+FAUCET_ALLOCATION=250000000000000000000000000000usnr
+DOCKER_IMAGE=onsonr/snrd:latest
diff --git a/networks/testnet/DOCKER.md b/networks/testnet/DOCKER.md
new file mode 100644
index 000000000..e64325d20
--- /dev/null
+++ b/networks/testnet/DOCKER.md
@@ -0,0 +1,591 @@
+# Docker Containers
+
+This document describes all Docker containers used in the Sonr testnet architecture.
+
+## Overview
+
+The testnet consists of 7 containers organized in a validator-sentry architecture:
+- **3 Validators** (private, isolated networks)
+- **3 Sentry Nodes** (public-facing, Cloudflare tunnel integration)
+- **1 IPFS Node** (distributed storage)
+
+All containers use the `unless-stopped` restart policy for resilience.
+
+---
+
+## Container Architecture
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ Cloudflare Tunnel (DockFlare) │
+└────────────────────┬────────────────────────────────────┘
+ │
+ ┌────────────────┴────────────────────┐
+ │ net-public + cloudflare-net │
+ │ │
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────┐
+ │ │ sentry- │ │ sentry- │ │ sentry- │ │ IPFS │
+ │ │ alice │◄─► bob │◄─► carol │ │ │
+ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────┘
+ └───────┼─────────────┼─────────────┼─────────────────┘
+ │ │ │
+ │ private │ private │ private
+ │ │ │
+ ┌───────▼──────┐ ┌────▼──────┐ ┌────▼──────┐
+ │ val-alice │ │ val-bob │ │ val-carol │
+ │ (net-alice) │ │ (net-bob) │ │(net-carol)│
+ └──────────────┘ └───────────┘ └───────────┘
+```
+
+---
+
+## Validator Containers
+
+Validators are the core consensus nodes that produce blocks and maintain blockchain state. They are isolated on private networks for security.
+
+### val-alice
+
+**Purpose:** Alice's validator node
+**Image:** `onsonr/snrd:latest`
+**Network:** `net-alice` (private)
+**Volume:** `./val-alice:/root/.sonr`
+
+**Configuration:**
+- **Chain ID:** `sonrtest_1-1`
+- **Moniker:** `val-alice`
+- **Pruning:** Disabled (`--pruning=nothing`)
+- **Min Gas Prices:** `0usnr` (free for testnet)
+
+**Security Features:**
+- Runs on isolated private network
+- Only connects to its own sentry (`sentry-alice`)
+- No direct public access
+- Protected by sentry's `private_peer_ids` configuration
+
+**Data Directory:**
+```
+./val-alice/
+├── config/
+│ ├── genesis.json # Genesis state
+│ ├── config.toml # Tendermint config
+│ ├── app.toml # Application config
+│ └── priv_validator_key.json # Validator signing key (CRITICAL!)
+└── data/ # Blockchain data
+```
+
+---
+
+### val-bob
+
+**Purpose:** Bob's validator node
+**Image:** `onsonr/snrd:latest`
+**Network:** `net-bob` (private)
+**Volume:** `./val-bob:/root/.sonr`
+
+**Configuration:**
+- **Chain ID:** `sonrtest_1-1`
+- **Moniker:** `val-bob`
+- **Pruning:** Disabled
+- **Min Gas Prices:** `0usnr`
+
+**Security:** Same as val-alice, isolated on `net-bob`
+
+---
+
+### val-carol
+
+**Purpose:** Carol's validator node
+**Image:** `onsonr/snrd:latest`
+**Network:** `net-carol` (private)
+**Volume:** `./val-carol:/root/.sonr`
+
+**Configuration:**
+- **Chain ID:** `sonrtest_1-1`
+- **Moniker:** `val-carol`
+- **Pruning:** Disabled
+- **Min Gas Prices:** `0usnr`
+
+**Security:** Same as val-alice, isolated on `net-carol`
+
+---
+
+## Sentry Containers
+
+Sentry nodes act as a protective layer between validators and the public internet. They handle all public RPC, REST, gRPC, and EVM requests while shielding validator identities.
+
+### sentry-alice
+
+**Purpose:** Alice's public-facing sentry node
+**Image:** `onsonr/snrd:latest`
+**Networks:**
+- `net-alice` (connects to val-alice)
+- `net-public` (connects to other sentries)
+- `cloudflare-net` (for DockFlare tunnels)
+
+**Volume:** `./sentry-alice:/root/.sonr`
+**Depends On:** `val-alice`
+
+**Configuration:**
+- **Chain ID:** `sonrtest_1-1`
+- **Moniker:** `sentry-alice`
+- **Pruning:** Disabled
+- **Min Gas Prices:** `0usnr`
+
+**Exposed Services:**
+- **RPC:** Port 26657 (Tendermint RPC)
+- **REST:** Port 1317 (Cosmos REST API)
+- **gRPC:** Port 9090 (gRPC endpoint)
+- **EVM JSON-RPC:** Port 8545 (Ethereum-compatible)
+- **EVM WebSocket:** Port 8546 (WebSocket subscriptions)
+
+**JSON-RPC APIs Enabled:**
+- `eth` - Ethereum JSON-RPC
+- `txpool` - Transaction pool inspection
+- `personal` - Account management
+- `net` - Network info
+- `debug` - Debugging APIs
+- `web3` - Web3 utilities
+
+**Cloudflare Tunnel Endpoints:**
+
+| Endpoint | Hostname | Internal Service | Description |
+|----------|----------|------------------|-------------|
+| RPC | `alice-rpc.sonr.land` | `http://sentry-alice:26657` | Tendermint RPC for queries and transactions |
+| REST | `alice-rest.sonr.land` | `http://sentry-alice:1317` | Cosmos REST API (LCD) |
+| gRPC | `alice-grpc.sonr.land` | `http://sentry-alice:9090` | gRPC for efficient binary communication |
+| EVM | `alice-evm.sonr.land` | `http://sentry-alice:8545` | EVM JSON-RPC (MetaMask compatible) |
+
+**Peer Connections:**
+- **Persistent Peer:** `val-alice` (private connection)
+- **Seeds:** `sentry-bob`, `sentry-carol` (public P2P)
+- **Private Peer IDs:** Marks `val-alice` as private to hide from network
+
+**Security Features:**
+- Shields validator from direct exposure
+- Handles all public-facing traffic
+- Rate limiting and DDoS protection via Cloudflare
+- TLS encryption via Cloudflare tunnels
+
+---
+
+### sentry-bob
+
+**Purpose:** Bob's public-facing sentry node
+**Image:** `onsonr/snrd:latest`
+**Networks:** `net-bob`, `net-public`, `cloudflare-net`
+**Volume:** `./sentry-bob:/root/.sonr`
+**Depends On:** `val-bob`
+
+**Configuration:** Same as sentry-alice, with moniker `sentry-bob`
+
+**Cloudflare Tunnel Endpoints:**
+- `bob-rpc.sonr.land` → RPC (26657)
+- `bob-rest.sonr.land` → REST (1317)
+- `bob-grpc.sonr.land` → gRPC (9090)
+- `bob-evm.sonr.land` → EVM JSON-RPC (8545)
+
+**Peer Connections:**
+- **Persistent Peer:** `val-bob`
+- **Seeds:** `sentry-alice`, `sentry-carol`
+
+---
+
+### sentry-carol
+
+**Purpose:** Carol's public-facing sentry node
+**Image:** `onsonr/snrd:latest`
+**Networks:** `net-carol`, `net-public`, `cloudflare-net`
+**Volume:** `./sentry-carol:/root/.sonr`
+**Depends On:** `val-carol`
+
+**Configuration:** Same as sentry-alice, with moniker `sentry-carol`
+
+**Cloudflare Tunnel Endpoints:**
+- `carol-rpc.sonr.land` → RPC (26657)
+- `carol-rest.sonr.land` → REST (1317)
+- `carol-grpc.sonr.land` → gRPC (9090)
+- `carol-evm.sonr.land` → EVM JSON-RPC (8545)
+
+**Peer Connections:**
+- **Persistent Peer:** `val-carol`
+- **Seeds:** `sentry-alice`, `sentry-bob`
+
+---
+
+## IPFS Container
+
+### ipfsctl
+
+**Purpose:** Distributed file storage and content addressing
+**Image:** `ipfs/kubo:latest` (official IPFS implementation)
+**Networks:**
+- `net-public` (connects to sentry nodes)
+- `cloudflare-net` (for DockFlare tunnels)
+
+**Volume:** `ipfs-data:/data/ipfs` (named volume)
+
+**Configuration:**
+- **IPFS Profile:** `server` (optimized for server deployment)
+
+**Exposed Services:**
+- **API:** Port 5001 (IPFS HTTP API)
+- **Gateway:** Port 8080 (IPFS Gateway for content retrieval)
+- **Swarm:** Port 4001 (P2P networking)
+
+**Cloudflare Tunnel Endpoints:**
+
+| Endpoint | Hostname | Internal Service | Description |
+|----------|----------|------------------|-------------|
+| API | `ipfs-api.sonr.land` | `http://ipfsctl:5001` | IPFS API for adding/pinning content |
+| Gateway | `ipfs-gateway.sonr.land` | `http://ipfsctl:8080` | HTTP gateway for retrieving content |
+
+**Features:**
+- **Content-Addressed Storage:** Files identified by cryptographic hash (CID)
+- **Distributed Network:** Connects to global IPFS network
+- **Persistent Storage:** Data stored in named volume
+- **HTTP API:** RESTful API for programmatic access
+- **Gateway Access:** Retrieve content via HTTP using CID
+
+**Common Operations:**
+
+```bash
+# Add file to IPFS
+curl -X POST -F file=@myfile.txt https://ipfs-api.sonr.land/api/v0/add
+
+# Retrieve file by CID
+curl https://ipfs-gateway.sonr.land/ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG/readme
+
+# Check version
+curl -X POST https://ipfs-api.sonr.land/api/v0/version | jq
+```
+
+---
+
+## Network Topology
+
+### Private Networks
+
+Each validator has its own isolated network:
+
+| Network | Purpose | Containers |
+|---------|---------|------------|
+| `net-alice` | Alice validator isolation | `val-alice`, `sentry-alice` |
+| `net-bob` | Bob validator isolation | `val-bob`, `sentry-bob` |
+| `net-carol` | Carol validator isolation | `val-carol`, `sentry-carol` |
+
+**Security Benefits:**
+- Validators cannot directly communicate with each other
+- Prevents Byzantine attacks at network layer
+- Forces all communication through sentries
+
+### Public Network
+
+| Network | Purpose | Containers |
+|---------|---------|------------|
+| `net-public` | Sentry interconnection | `sentry-alice`, `sentry-bob`, `sentry-carol`, `ipfsctl` |
+
+**Purpose:**
+- Sentries discover and connect to each other
+- IPFS accessible to all sentries
+- Public P2P gossip network
+
+### Cloudflare Network
+
+| Network | Purpose | Containers |
+|---------|---------|------------|
+| `cloudflare-net` | DockFlare tunnel access | `sentry-alice`, `sentry-bob`, `sentry-carol`, `ipfsctl` |
+
+**Type:** External (must be created before starting stack)
+**Purpose:** Allows DockFlare to discover and tunnel traffic to containers
+
+**Setup:**
+```bash
+docker network create cloudflare-net
+```
+
+---
+
+## Volume Management
+
+### Bind Mounts (Validators & Sentries)
+
+```
+./val-alice:/root/.sonr
+./val-bob:/root/.sonr
+./val-carol:/root/.sonr
+./sentry-alice:/root/.sonr
+./sentry-bob:/root/.sonr
+./sentry-carol:/root/.sonr
+```
+
+**Advantages:**
+- Direct file system access from host
+- Easy backup and inspection
+- No permission issues (files owned by host user)
+
+**Backup:**
+```bash
+tar -czf testnet-backup.tar.gz val-* sentry-*
+```
+
+### Named Volume (IPFS)
+
+```
+ipfs-data:/data/ipfs
+```
+
+**Advantages:**
+- Managed by Docker
+- Better performance on some systems
+- Automatic cleanup with `docker compose down -v`
+
+**Backup:**
+```bash
+docker run --rm -v ipfs-data:/data -v $(pwd):/backup alpine tar czf /backup/ipfs-backup.tar.gz /data
+```
+
+---
+
+## Container Management
+
+### Start All Containers
+```bash
+make start
+# or
+docker compose up -d
+```
+
+### Stop All Containers
+```bash
+make stop
+# or
+docker compose down
+```
+
+### View Container Logs
+```bash
+# All containers
+docker compose logs -f
+
+# Specific container
+docker compose logs -f sentry-alice
+
+# Last 100 lines
+docker compose logs --tail=100 val-alice
+```
+
+### Execute Commands in Containers
+```bash
+# Via bootstrap script
+devbox run exec sentry-alice status
+
+# Via docker compose
+docker compose exec sentry-alice snrd status --home /root/.sonr
+
+# Via docker
+docker exec -it sentry-alice snrd keys list --keyring-backend test
+```
+
+### Inspect Container Configuration
+```bash
+# View environment variables
+docker inspect sentry-alice -f '{{json .Config.Env}}' | jq
+
+# View networks
+docker inspect sentry-alice -f '{{json .NetworkSettings.Networks}}' | jq
+
+# View mounts
+docker inspect sentry-alice -f '{{json .Mounts}}' | jq
+```
+
+### Resource Usage
+```bash
+# Real-time stats
+docker stats
+
+# Container resource limits (if set)
+docker inspect sentry-alice -f '{{json .HostConfig.Memory}}' | jq
+```
+
+---
+
+## Health Checks
+
+### Container Status
+```bash
+docker ps --filter "name=val-" --filter "name=sentry-" --filter "name=ipfs"
+```
+
+### Node Sync Status
+```bash
+# Check if nodes are syncing
+for container in sentry-alice sentry-bob sentry-carol; do
+ echo "=== $container ==="
+ docker exec $container sh -c 'curl -s http://localhost:26657/status | jq -r ".result.sync_info.catching_up"'
+done
+```
+
+### Block Height
+```bash
+# Current block height
+docker exec sentry-alice sh -c 'curl -s http://localhost:26657/status | jq -r ".result.sync_info.latest_block_height"'
+```
+
+### IPFS Health
+```bash
+# Check IPFS daemon
+curl -X POST https://ipfs-api.sonr.land/api/v0/version | jq
+
+# Check connected peers
+curl -X POST https://ipfs-api.sonr.land/api/v0/swarm/peers | jq
+```
+
+---
+
+## Security Considerations
+
+### Validator Security
+1. **Never expose validators directly** - Always use sentries
+2. **Backup `priv_validator_key.json`** - Cannot be recovered if lost
+3. **Monitor validator uptime** - Downtime results in slashing
+4. **Use KMS in production** - Hardware security modules for key management
+
+### Sentry Security
+1. **Rate limiting via Cloudflare** - Protects against DDoS
+2. **TLS encryption** - All traffic encrypted via Cloudflare tunnels
+3. **No exposed ports** - All access via tunnels only
+4. **Regular updates** - Keep `onsonr/snrd` image updated
+
+### IPFS Security
+1. **Content verification** - All content verified by CID
+2. **No private data** - IPFS is a public network
+3. **Pin important content** - Prevent garbage collection
+4. **Monitor storage** - IPFS can grow large over time
+
+---
+
+## Troubleshooting
+
+### Container Won't Start
+```bash
+# Check logs
+docker compose logs
+
+# Check resource usage
+docker stats
+
+# Verify networks exist
+docker network ls | grep -E "net-|cloudflare"
+
+# Recreate container
+docker compose up -d --force-recreate
+```
+
+### Permission Issues
+```bash
+# Fix ownership (if needed)
+sudo chown -R $USER:$USER val-* sentry-*
+
+# Check bind mount permissions
+ls -la val-alice/
+```
+
+### Network Connectivity Issues
+```bash
+# Test connectivity between containers
+docker exec sentry-alice ping -c 3 val-alice
+docker exec sentry-alice ping -c 3 sentry-bob
+
+# Check network configuration
+docker network inspect net-public
+```
+
+### IPFS Issues
+```bash
+# Check IPFS daemon status
+docker compose logs ipfsctl
+
+# Restart IPFS
+docker compose restart ipfsctl
+
+# Clear IPFS cache (WARNING: destructive)
+docker compose down
+docker volume rm testnet_ipfs-data
+docker compose up -d
+```
+
+---
+
+## Performance Tuning
+
+### Resource Limits (Optional)
+
+Add to `docker-compose.yml`:
+
+```yaml
+services:
+ val-alice:
+ deploy:
+ resources:
+ limits:
+ cpus: '2.0'
+ memory: 4G
+ reservations:
+ cpus: '1.0'
+ memory: 2G
+```
+
+### Logging Configuration
+
+Prevent log bloat:
+
+```yaml
+services:
+ val-alice:
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
+ max-file: "3"
+```
+
+---
+
+## Maintenance
+
+### Update Containers
+```bash
+# Pull latest images
+docker compose pull
+
+# Restart with new images
+docker compose up -d
+```
+
+### Clean Up
+```bash
+# Remove stopped containers
+docker compose down
+
+# Remove all data (WARNING: destructive)
+docker compose down -v
+
+# Clean Docker system
+docker system prune -a
+```
+
+### Backup Procedure
+```bash
+# Stop containers
+make stop
+
+# Backup data
+tar -czf testnet-backup-$(date +%Y%m%d).tar.gz val-* sentry-* .env
+
+# Backup IPFS volume
+docker run --rm -v testnet_ipfs-data:/data -v $(pwd):/backup \
+ alpine tar czf /backup/ipfs-backup-$(date +%Y%m%d).tar.gz /data
+
+# Restart containers
+make start
+```
diff --git a/networks/testnet/ENVIRONMENT.md b/networks/testnet/ENVIRONMENT.md
new file mode 100644
index 000000000..890bea070
--- /dev/null
+++ b/networks/testnet/ENVIRONMENT.md
@@ -0,0 +1,247 @@
+# Environment Variables
+
+This document describes all environment variables that can be configured for the Sonr testnet.
+
+## Configuration File
+
+All variables are defined in `.env` file in the repository root. Copy `.env.example` to `.env` and customize:
+
+```bash
+cp .env.example .env
+```
+
+---
+
+## Global Configuration
+
+These variables are used during initialization and apply to all services.
+
+### Chain Configuration
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `CHAIN_ID` | `sonrtest_1-1` | Blockchain chain identifier |
+| `DENOM` | `usnr` | Base denomination for the native token |
+| `KEYRING` | `test` | Keyring backend (test, file, os) |
+| `KEYALGO` | `eth_secp256k1` | Key algorithm for validator keys |
+
+### Network Parameters
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `BLOCK_TIME` | `5s` | Target block time |
+| `VOTING_PERIOD` | `30s` | Governance proposal voting period |
+| `EXPEDITED_VOTING_PERIOD` | `15s` | Expedited proposal voting period |
+| `MAX_GAS` | `100000000` | Maximum gas per block |
+| `MIN_COMMISSION_RATE` | `0.050000000000000000` | Minimum validator commission rate (5%) |
+
+### Validator Mnemonics
+
+**⚠️ WARNING**: Change these for production! Default mnemonics are public.
+
+| Variable | Description |
+|----------|-------------|
+| `ALICE_MNEMONIC` | 24-word mnemonic for Alice validator |
+| `BOB_MNEMONIC` | 24-word mnemonic for Bob validator |
+| `CAROL_MNEMONIC` | 24-word mnemonic for Carol validator |
+| `FAUCET_MNEMONIC` | 24-word mnemonic for faucet account |
+
+### Initial Balances
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `BASE_ALLOCATION` | `100000000000000000000000000usnr` | Base allocation per validator (100M SNR) |
+| `STAKE_AMOUNT` | `30000000000000000000000usnr` | Initial stake per validator (30M SNR) |
+| `FAUCET_ALLOCATION` | `250000000000000000000000000000usnr` | Faucet account balance (250M SNR) |
+
+### Docker Configuration
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `DOCKER_IMAGE` | `onsonr/snrd:latest` | Docker image for validator and sentry nodes |
+
+---
+
+## Container-Specific Variables
+
+These variables are set in `docker-compose.yml` for each container.
+
+### Validators (val-alice, val-bob, val-carol)
+
+All validators share the same environment variables but with different values:
+
+| Variable | Value | Description |
+|----------|-------|-------------|
+| `CHAIN_ID` | `sonrtest_1-1` | Chain identifier (same for all) |
+| `MONIKER` | `val-{name}` | Validator node name (val-alice, val-bob, val-carol) |
+
+**Command-line flags:**
+- `--home /root/.sonr` - Node home directory
+- `--pruning=nothing` - Disable state pruning
+- `--minimum-gas-prices=0usnr` - Minimum gas price (0 for testnet)
+- `--chain-id=sonrtest_1-1` - Chain identifier
+
+**Networks:**
+- Private network: `net-{name}` (net-alice, net-bob, net-carol)
+
+**Volumes:**
+- `./val-{name}:/root/.sonr` - Bind mount for node data
+
+---
+
+### Sentries (sentry-alice, sentry-bob, sentry-carol)
+
+All sentries share the same environment variables but with different values:
+
+| Variable | Value | Description |
+|----------|-------|-------------|
+| `CHAIN_ID` | `sonrtest_1-1` | Chain identifier (same for all) |
+| `MONIKER` | `sentry-{name}` | Sentry node name (sentry-alice, sentry-bob, sentry-carol) |
+
+**Command-line flags:**
+- `--home /root/.sonr` - Node home directory
+- `--pruning=nothing` - Disable state pruning
+- `--minimum-gas-prices=0usnr` - Minimum gas price (0 for testnet)
+- `--rpc.laddr=tcp://0.0.0.0:26657` - RPC listen address
+- `--json-rpc.api=eth,txpool,personal,net,debug,web3` - Enabled JSON-RPC APIs
+- `--json-rpc.address=0.0.0.0:8545` - EVM JSON-RPC address
+- `--json-rpc.ws-address=0.0.0.0:8546` - EVM WebSocket address
+- `--chain-id=sonrtest_1-1` - Chain identifier
+
+**Networks:**
+- Private network: `net-{name}` (connects to validator)
+- `net-public` (connects to other sentries)
+- `cloudflare-net` (for DockFlare tunnels)
+
+**Volumes:**
+- `./sentry-{name}:/root/.sonr` - Bind mount for node data
+
+**DockFlare Labels (indexed):**
+
+Each sentry exposes 4 endpoints via Cloudflare tunnels:
+
+| Index | Hostname Pattern | Service | Description |
+|-------|------------------|---------|-------------|
+| `0` | `{name}-rpc.sonr.land` | `http://sentry-{name}:26657` | Tendermint RPC |
+| `1` | `{name}-rest.sonr.land` | `http://sentry-{name}:1317` | Cosmos REST API |
+| `2` | `{name}-grpc.sonr.land` | `http://sentry-{name}:9090` | gRPC endpoint |
+| `3` | `{name}-evm.sonr.land` | `http://sentry-{name}:8545` | EVM JSON-RPC |
+
+**Example for sentry-alice:**
+```yaml
+labels:
+ - "dockflare.enable=true"
+ - "dockflare.0.hostname=alice-rpc.sonr.land"
+ - "dockflare.0.service=http://sentry-alice:26657"
+ - "dockflare.1.hostname=alice-rest.sonr.land"
+ - "dockflare.1.service=http://sentry-alice:1317"
+ - "dockflare.2.hostname=alice-grpc.sonr.land"
+ - "dockflare.2.service=http://sentry-alice:9090"
+ - "dockflare.3.hostname=alice-evm.sonr.land"
+ - "dockflare.3.service=http://sentry-alice:8545"
+```
+
+---
+
+### IPFS (ipfsctl)
+
+| Variable | Value | Description |
+|----------|-------|-------------|
+| `IPFS_PROFILE` | `server` | IPFS configuration profile for server mode |
+
+**Networks:**
+- `net-public` (connects to sentries)
+- `cloudflare-net` (for DockFlare tunnels)
+
+**Volumes:**
+- `ipfs-data:/data/ipfs` - Named volume for IPFS data
+
+**DockFlare Labels:**
+
+| Index | Hostname | Service | Description |
+|-------|----------|---------|-------------|
+| `0` | `ipfs-api.sonr.land` | `http://ipfsctl:5001` | IPFS API endpoint |
+| `1` | `ipfs-gateway.sonr.land` | `http://ipfsctl:8080` | IPFS Gateway |
+
+---
+
+## DockFlare Configuration
+
+DockFlare requires the following environment variables (not in `.env`, set when running DockFlare container):
+
+| Variable | Required | Description |
+|----------|----------|-------------|
+| `CLOUDFLARE_API_TOKEN` | ✅ Yes | Cloudflare API token with Tunnel:Edit, DNS:Edit, Zone:Read permissions |
+| `CLOUDFLARE_ACCOUNT_ID` | ⚠️ Recommended | Cloudflare account ID (found in dashboard) |
+| `CLOUDFLARE_ZONE_ID` | ⚠️ Recommended | Cloudflare zone ID for domain (found in dashboard) |
+
+**Example DockFlare Setup:**
+```bash
+docker run -d \
+ --name dockflare \
+ --restart unless-stopped \
+ --network cloudflare-net \
+ -v /var/run/docker.sock:/var/run/docker.sock \
+ -e CLOUDFLARE_API_TOKEN=your_token_here \
+ -e CLOUDFLARE_ACCOUNT_ID=your_account_id \
+ -e CLOUDFLARE_ZONE_ID=your_zone_id \
+ ghcr.io/sonr-io/dockflare:latest
+```
+
+---
+
+## Runtime Overrides
+
+You can override specific values at runtime by modifying `docker-compose.yml` or passing environment variables:
+
+### Override Docker Image
+```bash
+export DOCKER_IMAGE=onsonr/snrd:v1.2.3
+make start
+```
+
+### Override Chain ID
+Edit `docker-compose.yml` and change all instances of:
+```yaml
+environment:
+ - CHAIN_ID=your-custom-chain-id
+```
+
+And update command flags:
+```yaml
+command: >
+ snrd start
+ ...
+ --chain-id=your-custom-chain-id
+```
+
+---
+
+## Security Best Practices
+
+1. **Never commit `.env` to version control** - It's already in `.gitignore`
+2. **Generate new mnemonics for production**:
+ ```bash
+ snrd keys add test --keyring-backend test --output json | jq -r .mnemonic
+ ```
+3. **Use KMS for production validator keys** (e.g., tmkms)
+4. **Rotate Cloudflare API tokens** regularly
+5. **Use strong passwords** if switching from `test` keyring to `file` or `os`
+6. **Backup mnemonics securely** - They cannot be recovered if lost
+
+---
+
+## Verification
+
+After configuration, verify your setup:
+
+```bash
+# Check .env is loaded
+make status
+
+# Test endpoints
+make test
+
+# View container environment
+docker inspect sentry-alice -f '{{json .Config.Env}}' | jq
+```
diff --git a/networks/testnet/Makefile b/networks/testnet/Makefile
new file mode 100644
index 000000000..6e2a730f5
--- /dev/null
+++ b/networks/testnet/Makefile
@@ -0,0 +1,66 @@
+# Sonr Testnet Makefile
+.PHONY: help all init start stop restart clean status logs test setup
+
+# Default target
+.DEFAULT_GOAL := help
+
+# Help target
+help:
+ @echo "Sonr Testnet Management (via Devbox)"
+ @echo ""
+ @echo "Setup:"
+ @echo " make all - Check/install devbox"
+ @echo ""
+ @echo "Testnet Operations:"
+ @echo " make setup - Create .env from template"
+ @echo " make init - Initialize validators and sentries"
+ @echo " make start - Start testnet"
+ @echo " make stop - Stop testnet"
+ @echo " make restart - Restart testnet"
+ @echo " make clean - Clean all data"
+ @echo " make status - Show status"
+ @echo " make logs - View logs"
+ @echo ""
+ @echo "Testing:"
+ @echo " make test - Run basic tests"
+
+# Check/install devbox
+all:
+ @command -v devbox >/dev/null 2>&1 || (echo "Installing devbox..." && curl -fsSL https://get.jetpack.io/devbox | bash)
+ @echo "✅ devbox is ready"
+
+# Setup .env file
+setup:
+ @devbox run setup
+
+# Initialize validators
+init:
+ @devbox run init
+
+# Start testnet
+start:
+ @devbox run start
+
+# Stop testnet
+stop:
+ @devbox run stop
+
+# Restart testnet
+restart:
+ @devbox run restart
+
+# Clean all data
+clean:
+ @devbox run clean
+
+# Show status
+status:
+ @devbox run status
+
+# View logs
+logs:
+ @devbox run logs
+
+# Run tests
+test:
+ @devbox run test
\ No newline at end of file
diff --git a/networks/testnet/README.md b/networks/testnet/README.md
new file mode 100644
index 000000000..32bcd282e
--- /dev/null
+++ b/networks/testnet/README.md
@@ -0,0 +1,407 @@
+# Sonr Testnet
+
+Production-ready 3-validator testnet with validator-sentry architecture and Cloudflare tunnel integration via DockFlare.
+
+## Overview
+
+This testnet provides:
+- **3 Validators** with isolated private networks
+- **3 Sentry Nodes** for public access and DDoS protection
+- **IPFS Node** for distributed storage
+- **Cloudflare Tunnels** for secure, zero-configuration public endpoints
+- **14 Public Endpoints** via `*.sonr.land` domains
+
+## Quick Start
+
+### Prerequisites
+
+1. **Docker & Docker Compose**
+ ```bash
+ docker --version
+ docker compose version
+ ```
+
+2. **Devbox** (provides snrd binary via Nix)
+ ```bash
+ # Auto-install via Makefile
+ make all
+
+ # Or install manually
+ curl -fsSL https://get.jetpack.io/devbox | bash
+ ```
+
+3. **DockFlare** (for Cloudflare tunnels)
+ ```bash
+ docker network create cloudflare-net
+ docker run -d \
+ --name dockflare \
+ --restart unless-stopped \
+ --network cloudflare-net \
+ -v /var/run/docker.sock:/var/run/docker.sock \
+ -e CLOUDFLARE_API_TOKEN=your_token_here \
+ -e CLOUDFLARE_ACCOUNT_ID=your_account_id \
+ -e CLOUDFLARE_ZONE_ID=your_zone_id \
+ ghcr.io/sonr-io/dockflare:latest
+ ```
+
+ > Get credentials from: https://dash.cloudflare.com
+
+### Setup
+
+```bash
+# 1. Clone repository
+git clone https://github.com/sonr-io/testnet
+cd testnet
+
+# 2. Install devbox (if not already installed)
+make all
+
+# 3. Create environment configuration
+make setup
+# Optional: Edit .env to customize
+
+# 4. Initialize testnet
+make init
+
+# 5. Start testnet
+make start
+
+# 6. Verify endpoints
+make test
+```
+
+**That's it!** Your testnet is running with Cloudflare tunnels.
+
+---
+
+## Commands
+
+All testnet operations are managed via `make`:
+
+```bash
+make all # Check/install devbox
+make setup # Create .env from template
+make init # Initialize validators and sentries
+make start # Start testnet
+make stop # Stop testnet
+make restart # Restart testnet
+make clean # Clean all data (WARNING: destructive)
+make status # Show status and endpoints
+make logs # View logs
+make test # Run basic tests
+make help # Show available commands
+```
+
+> **Note:** All commands use devbox under the hood, which provides the `snrd` binary via Nix.
+
+---
+
+## Public Endpoints
+
+All services are accessible via Cloudflare tunnels (no port conflicts):
+
+### Sentry Endpoints
+
+**Alice:**
+- RPC: `https://alice-rpc.sonr.land`
+- REST: `https://alice-rest.sonr.land`
+- gRPC: `https://alice-grpc.sonr.land`
+- EVM: `https://alice-evm.sonr.land`
+
+**Bob:**
+- RPC: `https://bob-rpc.sonr.land`
+- REST: `https://bob-rest.sonr.land`
+- gRPC: `https://bob-grpc.sonr.land`
+- EVM: `https://bob-evm.sonr.land`
+
+**Carol:**
+- RPC: `https://carol-rpc.sonr.land`
+- REST: `https://carol-rest.sonr.land`
+- gRPC: `https://carol-grpc.sonr.land`
+- EVM: `https://carol-evm.sonr.land`
+
+### IPFS Endpoints
+
+- API: `https://ipfs-api.sonr.land`
+- Gateway: `https://ipfs-gateway.sonr.land`
+
+### Example Usage
+
+```bash
+# Query blockchain status
+curl https://alice-rpc.sonr.land/status | jq
+
+# Query account balance
+curl https://alice-rest.sonr.land/cosmos/bank/v1beta1/balances/idx16wx7ye3ce060tjvmmpu8lm0ak5xr7gm2vjyh4k | jq
+
+# Send transaction
+snrd tx bank send alice idx1... 1000000usnr \
+ --node https://alice-rpc.sonr.land \
+ --chain-id sonrtest_1-1 \
+ --keyring-backend test \
+ --yes
+
+# IPFS operations
+curl -X POST https://ipfs-api.sonr.land/api/v0/version | jq
+curl https://ipfs-gateway.sonr.land/ipfs/
+```
+
+---
+
+## Architecture
+
+```
+ Cloudflare Tunnel (DockFlare)
+ │
+ ▼
+ ┌──────────────────────────────────────────────┐
+ │ net-public + cloudflare-net │
+ │ │
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
+ │ │sentry-alice │◄─►sentry-bob │◄─►sentry-carol │ │ IPFS │
+ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────────┘
+ └─────────┼──────────────────┼──────────────────┼──────────────────────┘
+ │ │ │
+ │ Private │ Private │ Private
+ │ │ │
+ ┌─────────▼────────┐ ┌──────▼────────┐ ┌─────▼─────────┐
+ │ val-alice │ │ val-bob │ │ val-carol │
+ │ (net-alice) │ │ (net-bob) │ │ (net-carol) │
+ └──────────────────┘ └───────────────┘ └───────────────┘
+```
+
+### Key Features
+
+- **Validator Isolation**: Each validator on private network
+- **Sentry Protection**: Public traffic filtered through sentries
+- **No Port Conflicts**: All services via Cloudflare tunnels
+- **Auto-Discovery**: DockFlare automatically creates tunnels
+- **TLS Encryption**: All traffic encrypted via Cloudflare
+
+---
+
+## Genesis Accounts
+
+| Name | Address | Balance | Purpose |
+|------|---------|---------|---------|
+| Alice | `idx140fehngcrxvhdt84x729p3f0qmkmea8n570lrg` | 100M SNR | Validator |
+| Bob | `idx1r6yue0vuyj9m7xw78npspt9drq2tmtvgcrf7sr` | 100M SNR | Validator |
+| Carol | `idx1pe9mc2q72u94sn2gg52ramrt26x5efw6kslflg` | 100M SNR | Validator |
+| Faucet | `idx16wx7ye3ce060tjvmmpu8lm0ak5xr7gm2vjyh4k` | 250M SNR | Faucet |
+
+**Total Genesis Supply:** 550M SNR (300M staked + 250M faucet)
+
+---
+
+## Configuration
+
+Configuration is managed via `.env` file:
+
+```bash
+cp .env.example .env
+```
+
+Quick setup:
+```bash
+make setup # Creates .env from .env.example
+```
+
+**Key Variables:**
+- `CHAIN_ID=sonrtest_1-1` - Chain identifier
+- `DENOM=usnr` - Native token denomination
+- `BLOCK_TIME=5s` - Target block time
+- `VOTING_PERIOD=30s` - Governance voting period
+- `ALICE_MNEMONIC`, `BOB_MNEMONIC`, `CAROL_MNEMONIC` - Validator keys
+- `FAUCET_MNEMONIC` - Faucet account key
+- `DOCKER_IMAGE=onsonr/snrd:latest` - Container image
+
+**⚠️ WARNING:** Default mnemonics in `.env.example` are public. Generate new ones for production!
+
+For complete configuration details, see [docs/Environment.md](docs/Environment.md)
+
+---
+
+## Documentation
+
+### Detailed Guides
+
+- **[Docker.md](docs/Docker.md)** - Complete container documentation
+ - All 7 containers explained
+ - Network topology and security
+ - Volume management
+ - Health checks and troubleshooting
+ - Performance tuning
+ - Backup procedures
+
+- **[Cloudflare.md](docs/Cloudflare.md)** - DockFlare integration guide
+ - Complete domain mapping (14 endpoints)
+ - DockFlare setup and configuration
+ - DNS management
+ - Testing all endpoint types
+ - Access policies
+ - Troubleshooting tunnels
+
+- **[Environment.md](docs/Environment.md)** - Environment variable reference
+ - Global configuration variables
+ - Container-specific variables
+ - DockFlare environment variables
+ - Runtime overrides
+ - Security best practices
+
+- **[Architecture.md](docs/Architecture.md)** - Architecture deep dive
+- **[CLAUDE.md](CLAUDE.md)** - Quick reference for AI assistants
+
+---
+
+## Troubleshooting
+
+### Initialization Takes 1-2 Minutes
+
+This is normal. The script initializes 6 nodes, creates genesis, and configures peer connections.
+
+**Expected output:**
+- 📋 Initializing validators
+- 🛡️ Initializing sentries
+- 💰 Adding genesis accounts
+- 🔗 Setting up peer connections
+- ✅ Completion with validator addresses
+
+**If init fails:**
+```bash
+make clean
+make init
+```
+
+### Validators Not Syncing
+
+```bash
+# Check logs
+docker compose logs val-alice
+
+# Verify peer connections
+docker exec val-alice snrd tendermint show-node-id --home /root/.sonr
+```
+
+### Cloudflare Tunnels Not Working
+
+Check DockFlare logs:
+```bash
+docker logs dockflare
+```
+
+Common issues:
+- Missing `cloudflare-net` network
+- Invalid API token
+- Container not on `cloudflare-net`
+
+See [docs/Cloudflare.md#troubleshooting](docs/Cloudflare.md#troubleshooting) for detailed help.
+
+### IPFS Port Conflict
+
+If IPFS fails with "port 8080 already allocated":
+1. Stop the conflicting service
+2. Or modify `docker-compose.yml` to use different port
+
+---
+
+## Production Deployment
+
+**Before deploying to production:**
+
+1. ✅ **Generate new mnemonics** - Never use defaults
+ ```bash
+ snrd keys add test --keyring-backend test --output json | jq -r .mnemonic
+ ```
+
+2. ✅ **Use KMS** for validator key management (e.g., tmkms)
+
+3. ✅ **Configure firewall rules** to restrict validator access
+
+4. ✅ **Enable monitoring** (Prometheus, Grafana)
+
+5. ✅ **Set up backups** of validator keys and state
+ ```bash
+ tar -czf backup.tar.gz val-* sentry-* .env
+ ```
+
+6. ✅ **Rotate Cloudflare API tokens** regularly (every 90 days)
+
+7. ✅ **Enable rate limiting** in Cloudflare dashboard
+
+8. ✅ **Use persistent volumes** instead of bind mounts (optional)
+
+---
+
+## Development
+
+### View Logs
+
+```bash
+# All services
+make logs
+
+# Specific service
+docker compose logs -f sentry-alice
+
+# Search logs
+docker compose logs val-alice | grep -i error
+```
+
+### Execute Commands
+
+```bash
+# Via docker compose (recommended)
+docker compose exec sentry-alice snrd query bank total
+
+# Direct docker exec
+docker exec -it sentry-alice snrd keys list --keyring-backend test
+```
+
+### Clean Restart
+
+```bash
+make clean # Remove all data (destructive!)
+make init # Reinitialize
+make start # Start fresh
+```
+
+---
+
+## Testing
+
+```bash
+# Run basic tests
+make test
+```
+
+### Manual Testing
+
+```bash
+# RPC
+curl https://alice-rpc.sonr.land/status | jq
+
+# REST
+curl https://alice-rest.sonr.land/cosmos/base/tendermint/v1beta1/node_info | jq
+
+# EVM JSON-RPC
+curl -X POST https://alice-evm.sonr.land \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' | jq
+
+# IPFS
+curl -X POST https://ipfs-api.sonr.land/api/v0/version | jq
+```
+
+---
+
+## Support
+
+- **Documentation**: See [docs/](docs/) directory
+- **Issues**: https://github.com/sonr-io/testnet/issues
+- **Discord**: https://discord.gg/sonr
+
+---
+
+## License
+
+Apache 2.0
diff --git a/networks/testnet/docker-compose.yml b/networks/testnet/docker-compose.yml
new file mode 100644
index 000000000..5a48d56be
--- /dev/null
+++ b/networks/testnet/docker-compose.yml
@@ -0,0 +1,200 @@
+name: sonr-testnet
+networks:
+ net-naruto:
+ net-senku:
+ net-yaeger:
+ net-public:
+ dokploy-network:
+ external: true
+
+services:
+ val-naruto:
+ image: onsonr/snrd:latest
+ container_name: val-naruto
+ command: sh -c "mkdir -p /root/.sonr && /usr/bin/testnet-setup.sh && snrd start --home /root/.sonr --pruning=nothing --minimum-gas-prices=0usnr --chain-id=sonrtest_1-1"
+ environment:
+ - CHAIN_ID=sonrtest_1-1
+ - MONIKER=val-naruto
+ - NODE_TYPE=validator
+ - VALIDATOR_NAME=val-naruto
+ - WAIT_FOR_SYNC=false
+ volumes:
+ - ../val-naruto:/root/.sonr
+ networks:
+ - net-naruto
+ restart: unless-stopped
+
+ sentry-naruto:
+ image: onsonr/snrd:latest
+ container_name: sentry-naruto
+ command: sh -c "/usr/bin/testnet-setup.sh && snrd start --home /root/.sonr --pruning=nothing --minimum-gas-prices=0usnr --rpc.laddr=tcp://0.0.0.0:26657 --json-rpc.api=eth,txpool,personal,net,debug,web3 --json-rpc.address=0.0.0.0:8545 --json-rpc.ws-address=0.0.0.0:8546 --chain-id=sonrtest_1-1"
+ environment:
+ - CHAIN_ID=sonrtest_1-1
+ - MONIKER=sentry-naruto
+ - NODE_TYPE=sentry
+ - VALIDATOR_NAME=sentry-naruto
+ - WAIT_FOR_SYNC=true
+ volumes:
+ - ../sentry-naruto:/root/.sonr
+ networks:
+ - net-naruto
+ - net-public
+ - dokploy-network
+ labels:
+ - traefik.enable=true
+ - traefik.http.routers.naruto-rpc.rule=Host(`${naruto_rpc_domain}`)
+ - traefik.http.routers.naruto-rpc.entrypoints=websecure
+ - traefik.http.routers.naruto-rpc.tls.certResolver=letsencrypt
+ - traefik.http.services.naruto-rpc.loadbalancer.server.port=26657
+ - traefik.http.routers.naruto-rest.rule=Host(`${naruto_rest_domain}`)
+ - traefik.http.routers.naruto-rest.entrypoints=websecure
+ - traefik.http.routers.naruto-rest.tls.certResolver=letsencrypt
+ - traefik.http.services.naruto-rest.loadbalancer.server.port=1317
+ - traefik.http.routers.naruto-grpc.rule=Host(`${naruto_grpc_domain}`)
+ - traefik.http.routers.naruto-grpc.entrypoints=websecure
+ - traefik.http.routers.naruto-grpc.tls.certResolver=letsencrypt
+ - traefik.http.services.naruto-grpc.loadbalancer.server.port=9090
+ - traefik.http.routers.naruto-evm.rule=Host(`${naruto_evm_domain}`)
+ - traefik.http.routers.naruto-evm.entrypoints=websecure
+ - traefik.http.routers.naruto-evm.tls.certResolver=letsencrypt
+ - traefik.http.services.naruto-evm.loadbalancer.server.port=8545
+ depends_on:
+ - val-naruto
+ restart: unless-stopped
+
+ # Senku's Validator
+ val-senku:
+ image: onsonr/snrd:latest
+ container_name: val-senku
+ command: sh -c "/usr/bin/testnet-setup.sh && snrd start --home /root/.sonr --pruning=nothing --minimum-gas-prices=0usnr --chain-id=sonrtest_1-1"
+ environment:
+ - CHAIN_ID=sonrtest_1-1
+ - MONIKER=val-senku
+ - NODE_TYPE=validator
+ - VALIDATOR_NAME=val-senku
+ - WAIT_FOR_SYNC=false
+ volumes:
+ - ../val-senku:/root/.sonr
+ networks:
+ - net-senku
+ restart: unless-stopped
+
+ # Senku's Sentry
+ sentry-senku:
+ image: onsonr/snrd:latest
+ container_name: sentry-senku
+ command: sh -c "/usr/bin/testnet-setup.sh && snrd start --home /root/.sonr --pruning=nothing --minimum-gas-prices=0usnr --rpc.laddr=tcp://0.0.0.0:26657 --json-rpc.api=eth,txpool,personal,net,debug,web3 --json-rpc.address=0.0.0.0:8545 --json-rpc.ws-address=0.0.0.0:8546 --chain-id=sonrtest_1-1"
+ environment:
+ - CHAIN_ID=sonrtest_1-1
+ - MONIKER=sentry-senku
+ - NODE_TYPE=sentry
+ - VALIDATOR_NAME=sentry-senku
+ - WAIT_FOR_SYNC=true
+ volumes:
+ - ../sentry-senku:/root/.sonr
+ networks:
+ - net-senku
+ - net-public
+ - dokploy-network
+ labels:
+ - traefik.enable=true
+ - traefik.http.routers.senku-rpc.rule=Host(`${senku_rpc_domain}`)
+ - traefik.http.routers.senku-rpc.entrypoints=websecure
+ - traefik.http.routers.senku-rpc.tls.certResolver=letsencrypt
+ - traefik.http.services.senku-rpc.loadbalancer.server.port=26657
+ - traefik.http.routers.senku-rest.rule=Host(`${senku_rest_domain}`)
+ - traefik.http.routers.senku-rest.entrypoints=websecure
+ - traefik.http.routers.senku-rest.tls.certResolver=letsencrypt
+ - traefik.http.services.senku-rest.loadbalancer.server.port=1317
+ - traefik.http.routers.senku-grpc.rule=Host(`${senku_grpc_domain}`)
+ - traefik.http.routers.senku-grpc.entrypoints=websecure
+ - traefik.http.routers.senku-grpc.tls.certResolver=letsencrypt
+ - traefik.http.services.senku-grpc.loadbalancer.server.port=9090
+ - traefik.http.routers.senku-evm.rule=Host(`${senku_evm_domain}`)
+ - traefik.http.routers.senku-evm.entrypoints=websecure
+ - traefik.http.routers.senku-evm.tls.certResolver=letsencrypt
+ - traefik.http.services.senku-evm.loadbalancer.server.port=8545
+ depends_on:
+ - val-senku
+ restart: unless-stopped
+
+ # Yaeger's Validator
+ val-yaeger:
+ image: onsonr/snrd:latest
+ container_name: val-yaeger
+ command: sh -c "/usr/bin/testnet-setup.sh && snrd start --home /root/.sonr --pruning=nothing --minimum-gas-prices=0usnr --chain-id=sonrtest_1-1"
+ environment:
+ - CHAIN_ID=sonrtest_1-1
+ - MONIKER=val-yaeger
+ - NODE_TYPE=validator
+ - VALIDATOR_NAME=val-yaeger
+ - WAIT_FOR_SYNC=false
+ volumes:
+ - ../val-yaeger:/root/.sonr
+ networks:
+ - net-yaeger
+ restart: unless-stopped
+
+ # Yaeger's Sentry (Public Node)
+ sentry-yaeger:
+ image: onsonr/snrd:latest
+ container_name: sentry-yaeger
+ command: sh -c "/usr/bin/testnet-setup.sh && snrd start --home /root/.sonr --pruning=nothing --minimum-gas-prices=0usnr --rpc.laddr=tcp://0.0.0.0:26657 --json-rpc.api=eth,txpool,personal,net,debug,web3 --json-rpc.address=0.0.0.0:8545 --json-rpc.ws-address=0.0.0.0:8546 --chain-id=sonrtest_1-1"
+ environment:
+ - CHAIN_ID=sonrtest_1-1
+ - MONIKER=sentry-yaeger
+ - NODE_TYPE=sentry
+ - VALIDATOR_NAME=sentry-yaeger
+ - WAIT_FOR_SYNC=true
+ volumes:
+ - ../sentry-yaeger:/root/.sonr
+ networks:
+ - net-yaeger
+ - net-public
+ - dokploy-network
+ labels:
+ - traefik.enable=true
+ - traefik.http.routers.yaeger-rpc.rule=Host(`${yaeger_rpc_domain}`)
+ - traefik.http.routers.yaeger-rpc.entrypoints=websecure
+ - traefik.http.routers.yaeger-rpc.tls.certResolver=letsencrypt
+ - traefik.http.services.yaeger-rpc.loadbalancer.server.port=26657
+ - traefik.http.routers.yaeger-rest.rule=Host(`${yaeger_rest_domain}`)
+ - traefik.http.routers.yaeger-rest.entrypoints=websecure
+ - traefik.http.routers.yaeger-rest.tls.certResolver=letsencrypt
+ - traefik.http.services.yaeger-rest.loadbalancer.server.port=1317
+ - traefik.http.routers.yaeger-grpc.rule=Host(`${yaeger_grpc_domain}`)
+ - traefik.http.routers.yaeger-grpc.entrypoints=websecure
+ - traefik.http.routers.yaeger-grpc.tls.certResolver=letsencrypt
+ - traefik.http.services.yaeger-grpc.loadbalancer.server.port=9090
+ - traefik.http.routers.yaeger-evm.rule=Host(`${yaeger_evm_domain}`)
+ - traefik.http.routers.yaeger-evm.entrypoints=websecure
+ - traefik.http.routers.yaeger-evm.tls.certResolver=letsencrypt
+ - traefik.http.services.yaeger-evm.loadbalancer.server.port=8545
+ depends_on:
+ - val-yaeger
+ restart: unless-stopped
+
+ ipfsctl:
+ image: ipfs/kubo:latest
+ container_name: ipfsctl
+ environment:
+ - IPFS_PROFILE=server
+ volumes:
+ - ipfs-data:/data/ipfs
+ networks:
+ - net-public
+ - dokploy-network
+ labels:
+ - traefik.enable=true
+ - traefik.http.routers.ipfs-api.rule=Host(`${ipfs_api_domain}`)
+ - traefik.http.routers.ipfs-api.entrypoints=websecure
+ - traefik.http.routers.ipfs-api.tls.certResolver=letsencrypt
+ - traefik.http.services.ipfs-api.loadbalancer.server.port=5001
+ - traefik.http.routers.ipfs-gateway.rule=Host(`${ipfs_gateway_domain}`)
+ - traefik.http.routers.ipfs-gateway.entrypoints=websecure
+ - traefik.http.routers.ipfs-gateway.tls.certResolver=letsencrypt
+ - traefik.http.services.ipfs-gateway.loadbalancer.server.port=8080
+ restart: unless-stopped
+
+volumes:
+ ipfs-data:
diff --git a/networks/testnet/template.toml b/networks/testnet/template.toml
new file mode 100644
index 000000000..2bcd4a577
--- /dev/null
+++ b/networks/testnet/template.toml
@@ -0,0 +1,107 @@
+[variables]
+# Naruto Sentry Domains
+naruto_rpc_domain = "${domain}"
+naruto_rest_domain = "${domain}"
+naruto_grpc_domain = "${domain}"
+naruto_evm_domain = "${domain}"
+
+# Senku Sentry Domains
+senku_rpc_domain = "${domain}"
+senku_rest_domain = "${domain}"
+senku_grpc_domain = "${domain}"
+senku_evm_domain = "${domain}"
+
+# Yaeger Sentry Domains
+yaeger_rpc_domain = "${domain}"
+yaeger_rest_domain = "${domain}"
+yaeger_grpc_domain = "${domain}"
+yaeger_evm_domain = "${domain}"
+
+# IPFS Domains
+ipfs_api_domain = "${domain}"
+ipfs_gateway_domain = "${domain}"
+
+[config]
+# Naruto Sentry RPC
+[[config.domains]]
+serviceName = "sentry-naruto"
+port = 26657
+host = "${naruto_rpc_domain}"
+
+# Naruto Sentry REST
+[[config.domains]]
+serviceName = "sentry-naruto"
+port = 1317
+host = "${naruto_rest_domain}"
+
+# Naruto Sentry gRPC
+[[config.domains]]
+serviceName = "sentry-naruto"
+port = 9090
+host = "${naruto_grpc_domain}"
+
+# Naruto Sentry EVM
+[[config.domains]]
+serviceName = "sentry-naruto"
+port = 8545
+host = "${naruto_evm_domain}"
+
+# Senku Sentry RPC
+[[config.domains]]
+serviceName = "sentry-senku"
+port = 26657
+host = "${senku_rpc_domain}"
+
+# Senku Sentry REST
+[[config.domains]]
+serviceName = "sentry-senku"
+port = 1317
+host = "${senku_rest_domain}"
+
+# Senku Sentry gRPC
+[[config.domains]]
+serviceName = "sentry-senku"
+port = 9090
+host = "${senku_grpc_domain}"
+
+# Senku Sentry EVM
+[[config.domains]]
+serviceName = "sentry-senku"
+port = 8545
+host = "${senku_evm_domain}"
+
+# Yaeger Sentry RPC
+[[config.domains]]
+serviceName = "sentry-yaeger"
+port = 26657
+host = "${yaeger_rpc_domain}"
+
+# Yaeger Sentry REST
+[[config.domains]]
+serviceName = "sentry-yaeger"
+port = 1317
+host = "${yaeger_rest_domain}"
+
+# Yaeger Sentry gRPC
+[[config.domains]]
+serviceName = "sentry-yaeger"
+port = 9090
+host = "${yaeger_grpc_domain}"
+
+# Yaeger Sentry EVM
+[[config.domains]]
+serviceName = "sentry-yaeger"
+port = 8545
+host = "${yaeger_evm_domain}"
+
+# IPFS API
+[[config.domains]]
+serviceName = "ipfsctl"
+port = 5001
+host = "${ipfs_api_domain}"
+
+# IPFS Gateway
+[[config.domains]]
+serviceName = "ipfsctl"
+port = 8080
+host = "${ipfs_gateway_domain}"
\ No newline at end of file
diff --git a/plugin.json b/plugin.json
index 29e4e20e9..ef0b33f47 100644
--- a/plugin.json
+++ b/plugin.json
@@ -1,5 +1,5 @@
{
- "name": "snrd",
+ "name": "sonr",
"version": "0.1.0",
"description": "Plugin for Sonr PostgreSQL Docker image with pgsodium support and auto-start service",
"packages": ["docker@latest"],
@@ -19,7 +19,7 @@
"{{ .Virtenv }}/data": "",
"{{ .Virtenv }}/logs": "",
"{{ .Virtenv }}/process-compose.yaml": "etc/process-compose.yaml",
- "{{ .DevboxDir }}/init.sh": "etc/init.sh"
+ "{{ .DevboxDir }}/init.sh": "scripts/test_node.sh"
},
"shell": {
"init_hook": ["chmod +x {{ .DevboxDir }}/init.sh", "{{ .DevboxDir }}/init.sh"],
diff --git a/scripts/build-chain.sh b/scripts/build-chain.sh
new file mode 100755
index 000000000..7b3996cc0
--- /dev/null
+++ b/scripts/build-chain.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+mkdir -p /tmp/chains $UPGRADE_DIR
+
+echo "Fetching code from tag"
+mkdir -p /tmp/chains/$CHAIN_NAME
+cd /tmp/chains/$CHAIN_NAME
+
+if [[ $CODE_TAG =~ ^[0-9a-fA-F]{40}$ ]]; then
+ echo "Trying to fetch code from commit hash"
+ curl -LO $CODE_REPO/archive/$CODE_TAG.zip
+ unzip $CODE_TAG.zip
+ code_dir=${CODE_REPO##*/}-${CODE_TAG}
+elif [[ $CODE_TAG = v* ]]; then
+ echo "Trying to fetch code from tag with 'v' prefix"
+ curl -LO $CODE_REPO/archive/refs/tags/$CODE_TAG.zip
+ unzip $CODE_TAG.zip
+ code_dir=${CODE_REPO##*/}-${CODE_TAG#"v"}
+else
+ echo "Trying to fetch code from tag or branch"
+ if curl -fsLO $CODE_REPO/archive/refs/tags/$CODE_TAG.zip; then
+ unzip $CODE_TAG.zip
+ code_dir=${CODE_REPO##*/}-$CODE_TAG
+ elif curl -fsLO $CODE_REPO/archive/refs/heads/$CODE_TAG.zip; then
+ unzip $(echo $CODE_TAG | rev | cut -d "/" -f 1 | rev).zip
+ code_dir=${CODE_REPO##*/}-${CODE_TAG/\//-}
+ else
+ echo "Tag or branch '$CODE_TAG' not found"
+ exit 1
+ fi
+fi
+
+echo "Fetch wasmvm if needed"
+cd /tmp/chains/$CHAIN_NAME/$code_dir
+WASM_VERSION=$(cat go.mod | grep -oe "github.com/CosmWasm/wasmvm v[0-9.]*" | cut -d ' ' -f 2)
+if [[ WASM_VERSION != "" ]]; then
+ mkdir -p /tmp/chains/libwasmvm_muslc
+ cd /tmp/chains/libwasmvm_muslc
+ curl -LO https://github.com/CosmWasm/wasmvm/releases/download/$WASM_VERSION/libwasmvm_muslc.x86_64.a
+ cp libwasmvm_muslc.x86_64.a /lib/libwasmvm_muslc.a
+fi
+
+echo "Build chain binary"
+cd /tmp/chains/$CHAIN_NAME/$code_dir
+CGO_ENABLED=1 BUILD_TAGS="muslc linkstatic" LINK_STATICALLY=true LEDGER_ENABLED=false make install
+
+echo "Copy created binary to the upgrade directories"
+if [[ $UPGRADE_NAME == "genesis" ]]; then
+ mkdir -p $UPGRADE_DIR/genesis/bin
+ cp $GOBIN/$CHAIN_BIN $UPGRADE_DIR/genesis/bin
+else
+ mkdir -p $UPGRADE_DIR/upgrades/$UPGRADE_NAME/bin
+ cp $GOBIN/$CHAIN_BIN $UPGRADE_DIR/upgrades/$UPGRADE_NAME/bin
+fi
+
+echo "Cleanup"
+rm -rf /tmp/chains/$CHAIN_NAME
diff --git a/scripts/chain-rpc-ready.sh b/scripts/chain-rpc-ready.sh
new file mode 100755
index 000000000..ffeba85f5
--- /dev/null
+++ b/scripts/chain-rpc-ready.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# chain-rpc-ready.sh - Check if a CometBFT or Tendermint RPC service is ready
+# Usage: chain-rpc-ready.sh [RPC_URL]
+
+set -euo pipefail
+
+RPC_URL=${1:-"http://localhost:26657"}
+
+echo 1>&2 "Checking if $RPC_URL is ready..."
+
+# Check if the RPC URL is reachable,
+json=$(curl -s --connect-timeout 2 "$RPC_URL/status")
+
+# and the bootstrap block state has been validated,
+if [ "$(echo "$json" | jq -r '.result.sync_info | (.earliest_block_height < .latest_block_height)')" != true ]; then
+ echo 1>&2 "$RPC_URL is not ready: bootstrap block state has not been validated"
+ exit 1
+fi
+
+# and the node is not catching up.
+if [ "$(echo "$json" | jq -r .result.sync_info.catching_up)" != false ]; then
+ echo 1>&2 "$RPC_URL is not ready: node is catching up"
+ exit 1
+fi
+
+echo "$json" | jq -r .result
+exit 0
diff --git a/scripts/create-genesis.sh b/scripts/create-genesis.sh
index e9b09c1a7..1e18ae4ef 100755
--- a/scripts/create-genesis.sh
+++ b/scripts/create-genesis.sh
@@ -1,146 +1,114 @@
#!/bin/bash
-set -eux
+# scripts/create-genesis.sh - Create genesis file for Sonr network
-# generate_vrf_key generates a VRF keypair and stores it securely
-generate_vrf_key() {
- local home_dir="$1"
+set -euo pipefail
- if [[ -z "${home_dir}" ]]; then
- echo "Error: HOME_DIR parameter is required" >&2
- return 1
- fi
+# Source helper libraries
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "${SCRIPT_DIR}/lib/env.sh"
+source "${SCRIPT_DIR}/lib/keys.sh"
+source "${SCRIPT_DIR}/lib/genesis.sh"
- local genesis_file="${home_dir}/config/genesis.json"
-
- if [[ ! -f "${genesis_file}" ]]; then
- echo "Error: Genesis file not found at ${genesis_file}" >&2
- return 1
- fi
-
- local chain_id
- chain_id=$(jq -r '.chain_id' "${genesis_file}" 2>/dev/null)
-
- if [[ -z "${chain_id}" || "${chain_id}" == "null" ]]; then
- echo "Error: Failed to extract chain-id from genesis file" >&2
- return 1
- fi
-
- echo "Generating VRF keypair for network: ${chain_id}"
-
- local entropy_seed
- entropy_seed=$(echo -n "${chain_id}" | sha256sum | cut -d' ' -f1)
-
- local seed_part1="${entropy_seed}"
- local seed_part2
- seed_part2=$(echo -n "${entropy_seed}" | sha256sum | cut -d' ' -f1)
-
- local vrf_key_hex="${seed_part1}${seed_part2}"
-
- if [[ ${#vrf_key_hex} -ne 128 ]]; then
- echo "Error: Generated VRF key has incorrect size: ${#vrf_key_hex}" >&2
- return 1
- fi
-
- local vrf_key_path="${home_dir}/vrf_secret.key"
- mkdir -p "${home_dir}"
-
- echo -n "${vrf_key_hex}" | xxd -r -p > "${vrf_key_path}"
- chmod 0600 "${vrf_key_path}"
-
- local file_size
- file_size=$(wc -c < "${vrf_key_path}")
-
- if [[ ${file_size} -ne 64 ]]; then
- echo "Error: VRF key file has incorrect size: ${file_size} bytes" >&2
- rm -f "${vrf_key_path}"
- return 1
- fi
-
- echo "✓ VRF keypair generated for network: ${chain_id}"
- echo "✓ VRF secret key stored securely: ${vrf_key_path}"
-
- return 0
-}
-
-DENOM="${DENOM:=usnr}"
-# Match init-testnet.sh allocation: 100000000000000000000000000snr = 100000000000000000000000000000000usnr
-COINS="${COINS:=100000000000000000000000000000000$DENOM,100000000000000000000000000snr}"
-CHAIN_ID="${CHAIN_ID:=sonrtest_1-1}"
-CHAIN_BIN="${CHAIN_BIN:=snrd}"
-CHAIN_DIR="${CHAIN_DIR:=$HOME/.sonr}"
-KEYS_CONFIG="${KEYS_CONFIG:=configs/keys.json}"
+# Initialize environment
+init_env
+# Set defaults for genesis creation
FAUCET_ENABLED="${FAUCET_ENABLED:=true}"
NUM_VALIDATORS="${NUM_VALIDATORS:=1}"
NUM_RELAYERS="${NUM_RELAYERS:=0}"
-# check if the binary has genesis subcommand or not, if not, set CHAIN_GENESIS_CMD to empty
-CHAIN_GENESIS_CMD=$($CHAIN_BIN 2>&1 | grep -q "genesis-related subcommands" && echo "genesis" || echo "")
+# Match init-testnet.sh allocation: 100000000000000000000000000snr = 100000000000000000000000000000000usnr
+BASE_COINS="${BASE_COINS:=100000000000000000000000000000000${DENOM},100000000000000000000000000snr}"
+VALIDATOR_AMOUNT="1000000000000000000000000000${DENOM}"
-jq -r ".genesis[0].mnemonic" "$KEYS_CONFIG" | $CHAIN_BIN init "$CHAIN_ID" --chain-id "$CHAIN_ID" --default-denom "$DENOM" --recover
-
-# Add genesis keys to the keyring and self delegate initial coins
-echo "Adding key...." $(jq -r ".genesis[0].name" "$KEYS_CONFIG")
-jq -r ".genesis[0].mnemonic" "$KEYS_CONFIG" | $CHAIN_BIN keys add $(jq -r ".genesis[0].name" "$KEYS_CONFIG") --recover --keyring-backend="test"
-$CHAIN_BIN "$CHAIN_GENESIS_CMD" add-genesis-account $($CHAIN_BIN keys show -a $(jq -r .genesis[0].name "$KEYS_CONFIG") --keyring-backend="test") "$COINS" --keyring-backend="test"
-
-# Add faucet key to the keyring and self delegate initial coins
-echo "Adding key...." $(jq -r ".faucet[0].name" "$KEYS_CONFIG")
-jq -r ".faucet[0].mnemonic" "$KEYS_CONFIG" | $CHAIN_BIN keys add $(jq -r ".faucet[0].name" "$KEYS_CONFIG") --recover --keyring-backend="test"
-$CHAIN_BIN "$CHAIN_GENESIS_CMD" add-genesis-account $($CHAIN_BIN keys show -a $(jq -r .faucet[0].name "$KEYS_CONFIG") --keyring-backend="test") "$COINS" --keyring-backend="test"
-
-# Add test keys to the keyring and self delegate initial coins
-echo "Adding key...." $(jq -r ".keys[0].name" "$KEYS_CONFIG")
-jq -r ".keys[0].mnemonic" "$KEYS_CONFIG" | $CHAIN_BIN keys add $(jq -r ".keys[0].name" "$KEYS_CONFIG") --recover --keyring-backend="test"
-$CHAIN_BIN "$CHAIN_GENESIS_CMD" add-genesis-account $($CHAIN_BIN keys show -a $(jq -r .keys[0].name "$KEYS_CONFIG") --keyring-backend="test") "$COINS" --keyring-backend="test"
-
-if [[ $FAUCET_ENABLED == "false" && $NUM_RELAYERS -gt "-1" ]]; then
- ## Add relayers keys and delegate tokens
- for i in $(seq 0 "$NUM_RELAYERS"); do
- # Add relayer key and delegate tokens
- RELAYER_KEY_NAME="$(jq -r ".relayers[$i].name" "$KEYS_CONFIG")"
- echo "Adding relayer key.... $RELAYER_KEY_NAME"
- jq -r ".relayers[$i].mnemonic" "$KEYS_CONFIG" | $CHAIN_BIN keys add "$RELAYER_KEY_NAME" --recover --keyring-backend="test"
- $CHAIN_BIN "$CHAIN_GENESIS_CMD" add-genesis-account $($CHAIN_BIN keys show -a "$RELAYER_KEY_NAME" --keyring-backend="test") "$COINS" --keyring-backend="test"
- # Add relayer-cli key and delegate tokens
- RELAYER_CLI_KEY_NAME="$(jq -r ".relayers_cli[$i].name" "$KEYS_CONFIG")"
- echo "Adding relayer-cli key.... $RELAYER_CLI_KEY_NAME"
- jq -r ".relayers_cli[$i].mnemonic" "$KEYS_CONFIG" | $CHAIN_BIN keys add "$RELAYER_CLI_KEY_NAME" --recover --keyring-backend="test"
- $CHAIN_BIN "$CHAIN_GENESIS_CMD" add-genesis-account $($CHAIN_BIN keys show -a "$RELAYER_CLI_KEY_NAME" --keyring-backend="test") "$COINS" --keyring-backend="test"
- done
+# Check if binary has genesis subcommand
+CHAIN_GENESIS_CMD=""
+if $CHAIN_BIN 2>&1 | grep -q "genesis-related subcommands"; then
+ CHAIN_GENESIS_CMD="genesis"
fi
-## if faucet not enabled then add validator and relayer with index as keys and into gentx
-if [[ $FAUCET_ENABLED == "false" && $NUM_VALIDATORS -gt "1" ]]; then
- ## Add validators key and delegate tokens
- for i in $(seq 0 "$NUM_VALIDATORS"); do
- VAL_KEY_NAME="$(jq -r '.validators[0].name' "$KEYS_CONFIG")-$i"
- echo "Adding validator key.... $VAL_KEY_NAME"
- jq -r ".validators[0].mnemonic" "$KEYS_CONFIG" | $CHAIN_BIN keys add "$VAL_KEY_NAME" --index "$i" --recover --keyring-backend="test"
- $CHAIN_BIN "$CHAIN_GENESIS_CMD" add-genesis-account $($CHAIN_BIN keys show -a "$VAL_KEY_NAME" --keyring-backend="test") "$COINS" --keyring-backend="test"
- done
+log_info "Creating genesis for Sonr network: $CHAIN_ID"
+
+# Initialize chain
+local genesis_mnemonic
+genesis_mnemonic=$(jq -r ".genesis[0].mnemonic" "$KEYS_CONFIG")
+echo "$genesis_mnemonic" | $CHAIN_BIN init "$CHAIN_ID" --chain-id "$CHAIN_ID" --default-denom "$DENOM" --recover
+
+# Add genesis accounts
+log_info "Adding genesis accounts..."
+
+# Genesis key
+local genesis_key_name
+genesis_key_name=$(jq -r ".genesis[0].name" "$KEYS_CONFIG")
+import_mnemonic "$genesis_key_name" "$genesis_mnemonic"
+fund_key "$genesis_key_name" "$BASE_COINS"
+
+# Faucet key
+local faucet_key_name
+faucet_key_name=$(jq -r ".faucet[0].name" "$KEYS_CONFIG")
+local faucet_mnemonic
+faucet_mnemonic=$(jq -r ".faucet[0].mnemonic" "$KEYS_CONFIG")
+import_mnemonic "$faucet_key_name" "$faucet_mnemonic"
+fund_key "$faucet_key_name" "$BASE_COINS"
+
+# Test key
+local test_key_name
+test_key_name=$(jq -r ".keys[0].name" "$KEYS_CONFIG")
+local test_mnemonic
+test_mnemonic=$(jq -r ".keys[0].mnemonic" "$KEYS_CONFIG")
+import_mnemonic "$test_key_name" "$test_mnemonic"
+fund_key "$test_key_name" "$BASE_COINS"
+
+# Add relayer keys if faucet is disabled
+if [[ "$FAUCET_ENABLED" == "false" && "$NUM_RELAYERS" -gt 0 ]]; then
+ for i in $(seq 0 "$NUM_RELAYERS"); do
+ local relayer_key_name
+ relayer_key_name=$(jq -r ".relayers[$i].name" "$KEYS_CONFIG")
+ local relayer_mnemonic
+ relayer_mnemonic=$(jq -r ".relayers[$i].mnemonic" "$KEYS_CONFIG")
+ import_mnemonic "$relayer_key_name" "$relayer_mnemonic"
+ fund_key "$relayer_key_name" "$BASE_COINS"
+
+ local relayer_cli_key_name
+ relayer_cli_key_name=$(jq -r ".relayers_cli[$i].name" "$KEYS_CONFIG")
+ local relayer_cli_mnemonic
+ relayer_cli_mnemonic=$(jq -r ".relayers_cli[$i].mnemonic" "$KEYS_CONFIG")
+ import_mnemonic "$relayer_cli_key_name" "$relayer_cli_mnemonic"
+ fund_key "$relayer_cli_key_name" "$BASE_COINS"
+ done
fi
-echo "Creating gentx..."
-COIN=$(echo "$COINS" | cut -d ',' -f1)
-# Use full validator amount to meet minimum delegation requirement (274890886240)
-# Match the working init-testnet.sh: 1000000000000000000000snr = 1000000000000000000000000000usnr
-VALIDATOR_AMOUNT="1000000000000000000000000000$DENOM"
-$CHAIN_BIN "$CHAIN_GENESIS_CMD" gentx $(jq -r ".genesis[0].name" "$KEYS_CONFIG") "$VALIDATOR_AMOUNT" --keyring-backend="test" --chain-id "$CHAIN_ID" --gas-prices="0$DENOM"
+# Add additional validator keys if needed
+if [[ "$FAUCET_ENABLED" == "false" && "$NUM_VALIDATORS" -gt 1 ]]; then
+ for i in $(seq 1 "$NUM_VALIDATORS"); do
+ local val_key_name="${genesis_key_name}-${i}"
+ local val_mnemonic
+ val_mnemonic=$(jq -r ".validators[0].mnemonic" "$KEYS_CONFIG")
+ import_mnemonic "$val_key_name" "$val_mnemonic"
+ fund_key "$val_key_name" "$BASE_COINS"
+ done
+fi
-echo "Output of gentx"
-cat "$CHAIN_DIR"/config/gentx/*.json | jq
+# Create genesis transaction
+log_info "Creating genesis transaction..."
+$CHAIN_BIN "$CHAIN_GENESIS_CMD" gentx "$genesis_key_name" "$VALIDATOR_AMOUNT" \
+ --keyring-backend "$KEYRING_BACKEND" \
+ --chain-id "$CHAIN_ID" \
+ --gas-prices "0${DENOM}"
-echo "Running collect-gentxs"
+log_info "Genesis transaction output:"
+cat "$CHAIN_DIR/config/gentx"/*.json | jq
+
+# Collect genesis transactions
+log_info "Collecting genesis transactions..."
$CHAIN_BIN "$CHAIN_GENESIS_CMD" collect-gentxs
-ls "$CHAIN_DIR"/config
-
# Generate VRF keypair
-echo ""
-echo "Generating VRF keypair..."
-if ! generate_vrf_key "${CHAIN_DIR}"; then
- echo "Warning: VRF key generation failed, but continuing..."
- echo "Note: Multi-validator encryption features may not work without VRF keys"
+log_info "Generating VRF keypair..."
+if ! generate_vrf_key "$CHAIN_DIR"; then
+ log_warn "VRF key generation failed, but continuing..."
+ log_warn "Note: Multi-validator encryption features may not work without VRF keys"
fi
+
+log_success "Genesis creation completed"
diff --git a/scripts/create-ics.sh b/scripts/create-ics.sh
new file mode 100755
index 000000000..f0d64ef91
--- /dev/null
+++ b/scripts/create-ics.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+# scripts/create-ics.sh - Create ICS proposal for Sonr network
+
+set -euo pipefail
+
+# Source helper libraries
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "${SCRIPT_DIR}/lib/env.sh"
+source "${SCRIPT_DIR}/lib/keys.sh"
+source "${SCRIPT_DIR}/lib/tx.sh"
+
+# Initialize environment
+init_env
+
+# Set defaults for ICS setup
+KEY_NAME="${KEY_NAME:-ics-setup}"
+STAKE_AMOUNT="10000000${DENOM}"
+MAX_RETRIES="${MAX_RETRIES:-3}"
+RETRY_INTERVAL="${RETRY_INTERVAL:-30}"
+
+# Validate required parameters
+if [[ -z "${PROPOSAL_FILE:-}" ]]; then
+ log_error "PROPOSAL_FILE environment variable is required"
+ exit 1
+fi
+
+ensure_file "$PROPOSAL_FILE"
+
+log_info "Setting up ICS proposal with key '$KEY_NAME'"
+
+# Import key from keys config if available
+if [[ -f "${KEYS_CONFIG:-}" ]]; then
+ local key_mnemonic
+ key_mnemonic=$(jq -r ".keys[0].mnemonic" "$KEYS_CONFIG" 2>/dev/null || echo "")
+
+ if [[ -n "$key_mnemonic" && "$key_mnemonic" != "null" ]]; then
+ import_mnemonic "$KEY_NAME" "$key_mnemonic"
+ else
+ log_warn "No valid mnemonic found in $KEYS_CONFIG, using existing key or create one manually"
+ fi
+else
+ log_warn "KEYS_CONFIG not set, ensure key '$KEY_NAME' exists"
+fi
+
+# Ensure key exists
+ensure_key "$KEY_NAME"
+
+# Get validator address and stake tokens
+local validator_address
+validator_address=$(get_validator_address)
+
+stake_tokens "$KEY_NAME" "$validator_address" "$STAKE_AMOUNT"
+
+# Determine proposal command (legacy vs new)
+local submit_cmd="submit-proposal"
+if $CHAIN_BIN tx gov --help 2>/dev/null | grep -q "submit-legacy-proposal"; then
+ submit_cmd="submit-legacy-proposal"
+fi
+
+log_info "Using proposal command: $submit_cmd"
+
+# Submit proposal
+local tx_hash
+tx_hash=$(submit_proposal "$KEY_NAME" "$PROPOSAL_FILE" "consumer-addition")
+
+# Extract proposal ID from transaction
+local proposal_id
+proposal_id=$(query_chain tx "$tx_hash" | jq -r '.logs[0].events[] | select(.type=="submit_proposal").attributes[] | select(.key=="proposal_id").value // empty')
+
+if [[ -z "$proposal_id" || "$proposal_id" == "null" ]]; then
+ log_error "Failed to extract proposal ID from transaction"
+ exit 1
+fi
+
+log_info "Proposal ID: $proposal_id"
+
+# Vote on proposal
+vote_proposal "$KEY_NAME" "$proposal_id" "yes"
+
+# Wait for proposal to pass
+wait_for_proposal "$proposal_id" "$MAX_RETRIES" "$RETRY_INTERVAL"
+
+log_success "ICS proposal setup completed successfully"
diff --git a/scripts/create-validator.sh b/scripts/create-validator.sh
new file mode 100755
index 000000000..ca86d4f0c
--- /dev/null
+++ b/scripts/create-validator.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+# scripts/create-validator.sh - Create a validator for Sonr network
+
+set -euo pipefail
+
+# Source helper libraries
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "${SCRIPT_DIR}/lib/env.sh"
+source "${SCRIPT_DIR}/lib/keys.sh"
+source "${SCRIPT_DIR}/lib/tx.sh"
+
+# Initialize environment
+init_env
+
+# Ensure chain directory exists
+ensure_chain_dir
+
+# Set defaults for validator creation
+VAL_NAME="${VAL_NAME:-$CHAIN_ID-validator}"
+VALIDATOR_AMOUNT="5000000000${DENOM}"
+
+log_info "Creating validator '$VAL_NAME' for Sonr network"
+
+# Wait for node to sync
+wait_for_sync 10
+
+# Get Cosmos SDK version to determine validator creation format
+set +e
+cosmos_sdk_version=$($CHAIN_BIN version --long | sed -n 's/cosmos_sdk_version: \(.*\)/\1/p')
+set -e
+
+log_info "Cosmos SDK version: $cosmos_sdk_version"
+
+# Create validator based on SDK version
+if [[ "$cosmos_sdk_version" > "v0.50.0" ]]; then
+ log_info "Using Cosmos SDK v0.50+ validator creation format"
+
+ # Create validator JSON for v0.50+
+ local validator_json
+ validator_json=$(cat < /tmp/validator.json
+
+ # Submit validator creation transaction
+ submit_tx "$VAL_NAME" staking create-validator /tmp/validator.json
+
+ rm -f /tmp/validator.json
+else
+ log_info "Using legacy validator creation format"
+
+ # Check if min-self-delegation parameter is supported
+ local args=""
+ if $CHAIN_BIN tx staking create-validator --help 2>/dev/null | grep -q "min-self-delegation"; then
+ args="--min-self-delegation=1000000"
+ fi
+
+ # Submit validator creation transaction with legacy format
+ submit_tx "$VAL_NAME" staking create-validator \
+ --pubkey="$($CHAIN_BIN tendermint show-validator $NODE_ARGS)" \
+ --moniker "$VAL_NAME" \
+ --amount "$VALIDATOR_AMOUNT" \
+ --commission-rate="0.10" \
+ --commission-max-rate="0.20" \
+ --commission-max-change-rate="0.01" \
+ $args
+fi
+
+log_success "Validator '$VAL_NAME' created successfully"
diff --git a/scripts/ibc-connection.sh b/scripts/ibc-connection.sh
new file mode 100755
index 000000000..da16e35a8
--- /dev/null
+++ b/scripts/ibc-connection.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+REGISTRY_URL="$1"
+CHAIN_1="$2"
+CHAIN_2="$3"
+
+set -eux
+
+function connection_id() {
+ CONNECTION_ID=$(curl -s $REGISTRY_URL/ibc/$CHAIN_1/$CHAIN_2 | jq -r ".chain_1.connection_id")
+ echo $CONNECTION_ID
+}
+
+echo "Try to get connection id, if failed, wait for 2 seconds and try again"
+max_tries=20
+while [[ max_tries -gt 0 ]]
+do
+ id=$(connection_id)
+ if [[ -n "$id" ]]; then
+ echo "Found connection id: $id"
+ exit 0
+ fi
+ echo "Failed to get connection id. Sleeping for 2 secs. Tries left $max_tries"
+ ((max_tries--))
+ sleep 10
+done
diff --git a/scripts/lib/README.md b/scripts/lib/README.md
new file mode 100644
index 000000000..754006037
--- /dev/null
+++ b/scripts/lib/README.md
@@ -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
\ No newline at end of file
diff --git a/scripts/lib/config.sh b/scripts/lib/config.sh
new file mode 100644
index 000000000..d6b8f73cd
--- /dev/null
+++ b/scripts/lib/config.sh
@@ -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
+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 [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 [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 [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 [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 [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 [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 [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 [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 [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 [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 [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 [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 [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 [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
\ No newline at end of file
diff --git a/scripts/lib/env.sh b/scripts/lib/env.sh
new file mode 100644
index 000000000..d2aac8417
--- /dev/null
+++ b/scripts/lib/env.sh
@@ -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
\ No newline at end of file
diff --git a/scripts/lib/genesis.sh b/scripts/lib/genesis.sh
new file mode 100644
index 000000000..86ce85f06
--- /dev/null
+++ b/scripts/lib/genesis.sh
@@ -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
\ No newline at end of file
diff --git a/scripts/lib/jq_patch.sh b/scripts/lib/jq_patch.sh
new file mode 100644
index 000000000..0de952568
--- /dev/null
+++ b/scripts/lib/jq_patch.sh
@@ -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
+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
+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
+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
+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
+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
+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
+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
+get_json_value() {
+ local file="$1"
+ local path="$2"
+
+ ensure_file "$file"
+
+ jq -r "$path" "$file"
+}
+
+# Validate JSON file
+# Usage: validate_json
+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
\ No newline at end of file
diff --git a/scripts/lib/keys.sh b/scripts/lib/keys.sh
new file mode 100644
index 000000000..9100c13bf
--- /dev/null
+++ b/scripts/lib/keys.sh
@@ -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 [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
+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
+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
+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
+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 [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 < /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
\ No newline at end of file
diff --git a/scripts/lib/tx.sh b/scripts/lib/tx.sh
new file mode 100644
index 000000000..3d6af0d2d
--- /dev/null
+++ b/scripts/lib/tx.sh
@@ -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
+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
+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 [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
+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 [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
+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 [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
+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
\ No newline at end of file
diff --git a/scripts/test_node.sh b/scripts/test_node.sh
index 5bcc9e513..d3f8bcee9 100755
--- a/scripts/test_node.sh
+++ b/scripts/test_node.sh
@@ -5,156 +5,76 @@
# CHAIN_ID="localchain_9000-1" HOME_DIR="~/.sonr" BLOCK_TIME="1000ms" CLEAN=true sh scripts/test_node.sh
# CHAIN_ID="localchain_9000-2" HOME_DIR="~/.sonr" CLEAN=true RPC=36657 REST=2317 PROFF=6061 P2P=36656 GRPC=8090 GRPC_WEB=8091 ROSETTA=8081 BLOCK_TIME="500ms" sh scripts/test_node.sh
-set -eu
+set -euo pipefail
-export KEY="acc0"
-export KEY2="acc1"
+# Source helper libraries
+# Get the directory of this script reliably
+SCRIPT_DIR="/usr/local/lib/sonr-scripts"
+source "${SCRIPT_DIR}/env.sh"
+source "${SCRIPT_DIR}/config.sh"
+source "${SCRIPT_DIR}/keys.sh"
+source "${SCRIPT_DIR}/genesis.sh"
-export CHAIN_ID=${CHAIN_ID:-"sonrtest_1-1"}
-export MONIKER="localvalidator"
-export KEYALGO="eth_secp256k1"
-export KEYRING=${KEYRING:-"test"}
-export HOME_DIR=$(eval echo "${HOME_DIR:-"~/.sonr"}")
-export BINARY=${BINARY:-snrd}
-export DENOM=${DENOM:-usnr}
+# Initialize environment
+init_env
-export CLEAN=${CLEAN:-"false"}
-export RPC=${RPC:-"26657"}
-export REST=${REST:-"1317"}
-export PROFF=${PROFF:-"6060"}
-export P2P=${P2P:-"26656"}
-export GRPC=${GRPC:-"9090"}
-export GRPC_WEB=${GRPC_WEB:-"9091"}
-export ROSETTA=${ROSETTA:-"8080"}
-export JSON_RPC=${JSON_RPC:-"8545"}
-export JSON_RPC_WS=${JSON_RPC_WS:-"8546"}
-export BLOCK_TIME=${BLOCK_TIME:-"5s"}
+# Set defaults for test node
+export KEY="${KEY:-acc0}"
+export KEY2="${KEY2:-acc1}"
+export MONIKER="${MONIKER:-localvalidator}"
+export KEYALGO="${KEYALGO:-eth_secp256k1}"
-# Configurable Mnemomics
-export SONR_MNEMONIC_1=${SONR_MNEMONIC_1:-"decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry"}
-export SONR_MNEMONIC_2=${SONR_MNEMONIC_2:-"wealth flavor believe regret funny network recall kiss grape useless pepper cram hint member few certain unveil rather brick bargain curious require crowd raise"}
+# Configurable ports
+export RPC="${RPC:-26657}"
+export REST="${REST:-1317}"
+export PROFF="${PROFF:-6060}"
+export P2P="${P2P:-26656}"
+export GRPC="${GRPC:-9090}"
+export GRPC_WEB="${GRPC_WEB:-9091}"
+export ROSETTA="${ROSETTA:-8080}"
+export JSON_RPC="${JSON_RPC:-8545}"
+export JSON_RPC_WS="${JSON_RPC_WS:-8546}"
+export BLOCK_TIME="${BLOCK_TIME:-5s}"
+
+# Configurable mnemonics
+export SONR_MNEMONIC_1="${SONR_MNEMONIC_1:-decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry}"
+export SONR_MNEMONIC_2="${SONR_MNEMONIC_2:-wealth flavor believe regret funny network recall kiss grape useless pepper cram hint member few certain unveil rather brick bargain curious require crowd raise}"
+
+# Docker and installation options
+export FORCE_DOCKER="${FORCE_DOCKER:-false}"
+export SKIP_INSTALL="${SKIP_INSTALL:-false}"
+export CLEAN="${CLEAN:-false}"
+export DOCKER_DETACHED="${DOCKER_DETACHED:-false}"
# Check if binary exists, if not use Docker (or force Docker if requested)
-export FORCE_DOCKER=${FORCE_DOCKER:-"false"}
-export SKIP_INSTALL=${SKIP_INSTALL:-"false"}
USE_DOCKER=false
-if [[ "${FORCE_DOCKER}" == "true" ]] || [[ -z $(which "${BINARY}") ]]; then
+if [[ "${FORCE_DOCKER}" == "true" ]] || ! command -v "$CHAIN_BIN" >/dev/null 2>&1; then
# Check if Docker is available and use it
if command -v docker >/dev/null 2>&1; then
if [[ "${FORCE_DOCKER}" == "true" ]]; then
- echo "Force Docker mode enabled, using Docker image onsonr/snrd:latest..."
+ log_info "Force Docker mode enabled, using Docker image onsonr/snrd:latest"
else
- echo "Binary ${BINARY} not found locally, checking for Docker image onsonr/snrd:latest..."
+ log_info "Binary $CHAIN_BIN not found locally, checking for Docker image onsonr/snrd:latest"
fi
if docker image inspect onsonr/snrd:latest >/dev/null 2>&1; then
- echo "Using Docker image onsonr/snrd:latest"
+ log_info "Using Docker image onsonr/snrd:latest"
USE_DOCKER=true
else
- echo "Docker image onsonr/snrd:latest not found. Pulling image..."
+ log_info "Docker image onsonr/snrd:latest not found. Pulling image..."
docker pull onsonr/snrd:latest || {
- echo "Failed to pull onsonr/snrd:latest. Please ensure Docker is running and you have internet access."
+ log_error "Failed to pull onsonr/snrd:latest. Please ensure Docker is running and you have internet access."
exit 1
}
USE_DOCKER=true
fi
else
- echo "Binary ${BINARY} not found. Please either:"
- echo " 1. Install ${BINARY} with 'make install'"
- echo " 2. Install Docker to use the containerized version"
+ log_error "Binary $CHAIN_BIN not found. Please either:"
+ log_error " 1. Install $CHAIN_BIN with 'make install'"
+ log_error " 2. Install Docker to use the containerized version"
exit 1
fi
fi
-# Final check if not using Docker
-if [[ "${USE_DOCKER}" == "false" ]]; then
- command -v "${BINARY}" >/dev/null 2>&1 || {
- echo >&2 "${BINARY} command not found. Ensure this is setup / properly installed in your GOPATH (make install)."
- exit 1
- }
-fi
-command -v jq >/dev/null 2>&1 || {
- echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/"
- exit 1
-}
-
-# generate_vrf_key generates a VRF keypair and stores it securely
-# Mirrors the Go implementation in app/commands/enhance_init.go
-generate_vrf_key() {
- local home_dir="$1"
- local use_docker="${2:-false}"
-
- # Validate parameters
- if [[ -z "${home_dir}" ]]; then
- echo "Error: HOME_DIR parameter is required" >&2
- return 1
- fi
-
- # Path to genesis file
- local genesis_file="${home_dir}/config/genesis.json"
-
- # Check if genesis file exists
- if [[ ! -f "${genesis_file}" ]]; then
- echo "Error: Genesis file not found at ${genesis_file}" >&2
- return 1
- fi
-
- # Extract chain-id from genesis file
- local chain_id
- chain_id=$(jq -r '.chain_id' "${genesis_file}" 2>/dev/null)
-
- if [[ -z "${chain_id}" || "${chain_id}" == "null" ]]; then
- echo "Error: Failed to extract chain-id from genesis file" >&2
- return 1
- fi
-
- echo "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
- echo "Error: Generated VRF key has incorrect size: ${#vrf_key_hex}" >&2
- return 1
- fi
-
- # Path to store VRF secret key
- local vrf_key_path="${home_dir}/vrf_secret.key"
-
- # Ensure directory exists
- mkdir -p "${home_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
- echo "Error: VRF key file has incorrect size: ${file_size} bytes" >&2
- rm -f "${vrf_key_path}"
- return 1
- fi
-
- echo "✓ VRF keypair generated for network: ${chain_id}"
- echo "✓ VRF secret key stored securely: ${vrf_key_path}"
-
- return 0
-}
-
# Create wrapper function for binary execution
run_binary() {
if [[ "${USE_DOCKER}" == "true" ]]; then
@@ -172,10 +92,11 @@ run_binary() {
onsonr/snrd:latest \
snrd --home /root/.sonr "$@"
else
- ${BINARY} "$@"
+ ${CHAIN_BIN} "$@"
fi
}
+# Set client configuration
set_config() {
run_binary config set client chain-id "${CHAIN_ID}"
run_binary config set client keyring-backend "${KEYRING}"
@@ -185,100 +106,49 @@ set_config
from_scratch() {
# Fresh install on current branch (skip if using Docker or SKIP_INSTALL is true)
if [[ "${USE_DOCKER}" == "false" ]] && [[ "${SKIP_INSTALL}" == "false" ]]; then
+ log_info "Installing $CHAIN_BIN..."
make install
fi
- # remove existing daemon files.
+ # Remove existing daemon files
if [[ ${#HOME_DIR} -le 2 ]]; then
- echo "HOME_DIR must be more than 2 characters long"
- return
+ log_error "HOME_DIR must be more than 2 characters long"
+ return 1
fi
- rm -rf "${HOME_DIR}" && echo "Removed ${HOME_DIR}"
+ rm -rf "${HOME_DIR}"
+ log_info "Removed existing chain directory: ${HOME_DIR}"
- # reset values if not set already after whipe
+ # Reset configuration
set_config
- add_key() {
- key=$1
- mnemonic=$2
- if [[ "${USE_DOCKER}" == "true" ]]; then
- # For Docker, we need to pass the mnemonic differently
- mkdir -p "${HOME_DIR}"
- echo "${mnemonic}" | docker run --rm -i \
- -v "${HOME_DIR}:/root/.sonr" \
- --network host \
- onsonr/snrd:latest \
- snrd --home /root/.sonr keys add "${key}" --keyring-backend "${KEYRING}" --algo "${KEYALGO}" --recover
- else
- echo "${mnemonic}" | ${BINARY} keys add "${key}" --keyring-backend "${KEYRING}" --algo "${KEYALGO}" --home "${HOME_DIR}" --recover
- fi
- }
-
- # idx140fehngcrxvhdt84x729p3f0qmkmea8n570lrg
- add_key "${KEY}" "${SONR_MNEMONIC_1}"
-
- # idx1r6yue0vuyj9m7xw78npspt9drq2tmtvgcrf7sr
- add_key "${KEY2}" "${SONR_MNEMONIC_2}"
+ # Add test keys
+ log_info "Adding test keys..."
+ import_mnemonic "${KEY}" "${SONR_MNEMONIC_1}" "$KEYALGO"
+ import_mnemonic "${KEY2}" "${SONR_MNEMONIC_2}" "$KEYALGO"
+ # Initialize chain
+ log_info "Initializing chain with moniker: $MONIKER"
if [[ "${USE_DOCKER}" == "true" ]]; then
- # For Docker init, we need to handle it specially
docker run --rm \
-v "${HOME_DIR}:/root/.sonr" \
--network host \
onsonr/snrd:latest \
snrd --home /root/.sonr init "${MONIKER}" --chain-id "${CHAIN_ID}" --default-denom "${DENOM}"
else
- ${BINARY} init "${MONIKER}" --chain-id "${CHAIN_ID}" --default-denom "${DENOM}" --home "${HOME_DIR}"
+ ${CHAIN_BIN} init "${MONIKER}" --chain-id "${CHAIN_ID}" --default-denom "${DENOM}" --home "${HOME_DIR}"
fi
- update_test_genesis() {
- cat "${HOME_DIR}"/config/genesis.json | jq "$1" >"${HOME_DIR}"/config/tmp_genesis.json && mv "${HOME_DIR}"/config/tmp_genesis.json "${HOME_DIR}"/config/genesis.json
- }
+ # Update genesis parameters
+ log_info "Updating genesis parameters..."
+ update_genesis_params
- # === CORE MODULES ===
+ # Add constitution if available
+ add_constitution
- # Block
- update_test_genesis '.consensus_params["block"]["max_gas"]="100000000"'
+ # Set up genesis accounts and transactions
+ local BASE_GENESIS_ALLOCATIONS="100000000000000000000000000${DENOM},100000000test"
- # Gov
- update_test_genesis $(printf '.app_state["gov"]["params"]["min_deposit"]=[{"denom":"%s","amount":"1000000"}]' "${DENOM}")
- update_test_genesis '.app_state["gov"]["params"]["voting_period"]="30s"'
- update_test_genesis '.app_state["gov"]["params"]["expedited_voting_period"]="15s"'
-
- # Add CONSTITUTION.md to governance if it exists
- if [ -f "CONSTITUTION.md" ]; then
- CONSTITUTION_CONTENT=$(cat CONSTITUTION.md | jq -Rs .)
- update_test_genesis ".app_state[\"gov\"][\"constitution\"]=$CONSTITUTION_CONTENT"
- fi
-
- update_test_genesis $(printf '.app_state["evm"]["params"]["evm_denom"]="%s"' "${DENOM}")
- update_test_genesis '.app_state["evm"]["params"]["active_static_precompiles"]=["0x0000000000000000000000000000000000000100","0x0000000000000000000000000000000000000400","0x0000000000000000000000000000000000000800","0x0000000000000000000000000000000000000801","0x0000000000000000000000000000000000000802","0x0000000000000000000000000000000000000803","0x0000000000000000000000000000000000000804","0x0000000000000000000000000000000000000805"]'
- update_test_genesis '.app_state["erc20"]["params"]["native_precompiles"]=["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"]' # https://eips.ethereum.org/EIPS/eip-7528
- update_test_genesis $(printf '.app_state["erc20"]["token_pairs"]=[{contract_owner:1,erc20_address:"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",denom:"%s",enabled:true}]' "${DENOM}")
- update_test_genesis '.app_state["feemarket"]["params"]["no_base_fee"]=true'
- update_test_genesis '.app_state["feemarket"]["params"]["base_fee"]="0.000000000000000000"'
-
- # staking
- update_test_genesis $(printf '.app_state["staking"]["params"]["bond_denom"]="%s"' "${DENOM}")
- update_test_genesis '.app_state["staking"]["params"]["min_commission_rate"]="0.050000000000000000"'
-
- # mint
- update_test_genesis $(printf '.app_state["mint"]["params"]["mint_denom"]="%s"' "${DENOM}")
-
- # crisis
- update_test_genesis $(printf '.app_state["crisis"]["constant_fee"]={"denom":"%s","amount":"1000"}' "${DENOM}")
-
- ## abci
- update_test_genesis '.consensus["params"]["abci"]["vote_extensions_enable_height"]="1"'
-
- # === CUSTOM MODULES ===
- # tokenfactory
- update_test_genesis '.app_state["tokenfactory"]["params"]["denom_creation_fee"]=[]'
- update_test_genesis '.app_state["tokenfactory"]["params"]["denom_creation_gas_consume"]=100000'
-
- BASE_GENESIS_ALLOCATIONS="100000000000000000000000000${DENOM},100000000test"
-
- # Allocate genesis accounts
+ log_info "Adding genesis accounts..."
if [[ "${USE_DOCKER}" == "true" ]]; then
docker run --rm \
-v "${HOME_DIR}:/root/.sonr" \
@@ -307,85 +177,78 @@ from_scratch() {
onsonr/snrd:latest \
snrd --home /root/.sonr genesis validate-genesis
else
- ${BINARY} genesis add-genesis-account "${KEY}" "${BASE_GENESIS_ALLOCATIONS}" --keyring-backend "${KEYRING}" --home "${HOME_DIR}" --append
- ${BINARY} genesis add-genesis-account "${KEY2}" "${BASE_GENESIS_ALLOCATIONS}" --keyring-backend "${KEYRING}" --home "${HOME_DIR}" --append
+ ${CHAIN_BIN} genesis add-genesis-account "${KEY}" "${BASE_GENESIS_ALLOCATIONS}" --keyring-backend "${KEYRING}" --home "${HOME_DIR}" --append
+ ${CHAIN_BIN} genesis add-genesis-account "${KEY2}" "${BASE_GENESIS_ALLOCATIONS}" --keyring-backend "${KEYRING}" --home "${HOME_DIR}" --append
# Sign genesis transaction
- ${BINARY} genesis gentx "${KEY}" 1000000000000000000000"${DENOM}" --gas-prices 0"${DENOM}" --keyring-backend "${KEYRING}" --chain-id "${CHAIN_ID}" --home "${HOME_DIR}"
- ${BINARY} genesis collect-gentxs --home "${HOME_DIR}"
- ${BINARY} genesis validate-genesis --home "${HOME_DIR}"
- fi
- err=$?
- if [[ ${err} -ne 0 ]]; then
- echo "Failed to validate genesis"
- return
+ ${CHAIN_BIN} genesis gentx "${KEY}" 1000000000000000000000"${DENOM}" --gas-prices 0"${DENOM}" --keyring-backend "${KEYRING}" --chain-id "${CHAIN_ID}" --home "${HOME_DIR}"
+ ${CHAIN_BIN} genesis collect-gentxs --home "${HOME_DIR}"
+ ${CHAIN_BIN} genesis validate-genesis --home "${HOME_DIR}"
fi
+
+ log_success "Genesis setup completed"
}
-# check if CLEAN is not set to false
+# Check if CLEAN is not set to false
if [[ ${CLEAN} != "false" ]]; then
- echo "Starting from a clean state"
+ log_info "Starting from a clean state"
from_scratch
# Generate VRF keypair (must be done after genesis file is created)
- echo ""
- echo "Generating VRF keypair..."
- if ! generate_vrf_key "${HOME_DIR}" "${USE_DOCKER}"; then
- echo "Warning: VRF key generation failed, but continuing..."
- echo "Note: Multi-validator encryption features may not work without VRF keys"
+ log_info "Generating VRF keypair..."
+ if ! generate_vrf_key "${HOME_DIR}"; then
+ log_warn "VRF key generation failed, but continuing..."
+ log_warn "Note: Multi-validator encryption features may not work without VRF keys"
fi
fi
-echo "Starting node..."
+log_info "Configuring node ports and settings..."
-# Opens the RPC endpoint to outside connections
-sed -i -e 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:'"${RPC}"'"/g' "${HOME_DIR}"/config/config.toml
-sed -i -e 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/g' "${HOME_DIR}"/config/config.toml
+# Configure node with all the specified ports and settings
+configure_node "$HOME_DIR" \
+ --rpc-port "$RPC" \
+ --rest-port "$REST" \
+ --grpc-port "$GRPC" \
+ --grpc-web-port "$GRPC_WEB" \
+ --json-rpc-port "$JSON_RPC" \
+ --rosetta-port "$ROSETTA" \
+ --min-gas-prices "0${DENOM}" \
+ --pruning nothing
-# REST endpoint
-sed -i -e 's/address = "tcp:\/\/localhost:1317"/address = "tcp:\/\/0.0.0.0:'"${REST}"'"/g' "${HOME_DIR}"/config/app.toml
-sed -i -e 's/enable = false/enable = true/g' "${HOME_DIR}"/config/app.toml
-sed -i -e 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/g' "${HOME_DIR}"/config/app.toml
+# Set consensus timeouts
+set_consensus_timeouts "$HOME_DIR/config/config.toml" "5s" "1s" "1s" "$BLOCK_TIME"
-# peer exchange
-sed -i -e 's/pprof_laddr = "localhost:6060"/pprof_laddr = "localhost:'"${PROFF}"'"/g' "${HOME_DIR}"/config/config.toml
-sed -i -e 's/laddr = "tcp:\/\/0.0.0.0:26656"/laddr = "tcp:\/\/0.0.0.0:'"${P2P}"'"/g' "${HOME_DIR}"/config/config.toml
+# Enable CORS for RPC
+set_toml_value "$HOME_DIR/config/config.toml" "" "cors_allowed_origins" '["*"]'
-# GRPC
-sed -i -e 's/address = "localhost:9090"/address = "0.0.0.0:'"${GRPC}"'"/g' "${HOME_DIR}"/config/app.toml
-sed -i -e 's/address = "localhost:9091"/address = "0.0.0.0:'"${GRPC_WEB}"'"/g' "${HOME_DIR}"/config/app.toml
+# Set pprof address
+set_toml_value "$HOME_DIR/config/config.toml" "" "pprof_laddr" "localhost:${PROFF}"
-# Rosetta Api
-sed -i -e 's/address = ":8080"/address = "0.0.0.0:'"${ROSETTA}"'"/g' "${HOME_DIR}"/config/app.toml
+# Set P2P address
+set_toml_value "$HOME_DIR/config/config.toml" "p2p" "laddr" "tcp://0.0.0.0:${P2P}"
-# JSON-RPC
-sed -i -e '/\[json-rpc\]/,/^\[/ s/enable = false/enable = true/' "${HOME_DIR}"/config/app.toml
-sed -i -e '/\[json-rpc\]/,/^\[/ s/address = "127.0.0.1:8545"/address = "0.0.0.0:'"${JSON_RPC}"'"/' "${HOME_DIR}"/config/app.toml
-sed -i -e '/\[json-rpc\]/,/^\[/ s/ws-address = "127.0.0.1:8546"/ws-address = "0.0.0.0:'"${JSON_RPC_WS}"'"/' "${HOME_DIR}"/config/app.toml
-
-# Faster blocks
-sed -i -e 's/timeout_commit = "5s"/timeout_commit = "'"${BLOCK_TIME}"'"/g' "${HOME_DIR}"/config/config.toml
+log_info "Starting node..."
# Start the node (with or without Docker)
if [[ "${USE_DOCKER}" == "true" ]]; then
- echo "Starting node using Docker..."
+ log_info "Starting node using Docker..."
# Check for detached mode via environment variable or prompt
DETACHED_MODE=""
if [[ "${DOCKER_DETACHED}" == "true" ]]; then
DETACHED_MODE="-d"
- echo "Running in detached mode. Use 'docker logs -f sonr-testnode' to view logs."
- echo "Stop with: docker stop sonr-testnode"
+ log_info "Running in detached mode. Use 'docker logs -f sonr-testnode' to view logs."
+ log_info "Stop with: docker stop sonr-testnode"
elif [ -t 0 ]; then
- echo ""
- echo "Would you like to run the node in detached mode (background)? [y/N]"
+ log_info ""
+ log_info "Would you like to run the node in detached mode (background)? [y/N]"
read -r -n 1 DETACH_RESPONSE
- echo ""
+ log_info ""
if [[ "$DETACH_RESPONSE" =~ ^[Yy]$ ]]; then
DETACHED_MODE="-d"
- echo "Running in detached mode. Use 'docker logs -f sonr-testnode' to view logs."
- echo "Stop with: docker stop sonr-testnode"
+ log_info "Running in detached mode. Use 'docker logs -f sonr-testnode' to view logs."
+ log_info "Stop with: docker stop sonr-testnode"
else
- echo "Running in foreground mode. Use Ctrl+C to stop."
+ log_info "Running in foreground mode. Use Ctrl+C to stop."
fi
fi
@@ -404,15 +267,18 @@ if [[ "${USE_DOCKER}" == "true" ]]; then
# If running detached, show status
if [ -n "$DETACHED_MODE" ]; then
- echo ""
- echo "✅ Node started in background"
- echo ""
- echo "Useful commands:"
- echo " View logs: docker logs -f sonr-testnode"
- echo " Stop node: docker stop sonr-testnode"
- echo " Node status: curl http://localhost:${RPC}/status | jq '.result.sync_info'"
- echo ""
+ log_info ""
+ log_success "Node started in background"
+ log_info ""
+ log_info "Useful commands:"
+ log_info " View logs: docker logs -f sonr-testnode"
+ log_info " Stop node: docker stop sonr-testnode"
+ log_info " Node status: curl http://localhost:${RPC}/status | jq '.result.sync_info'"
+ log_info ""
fi
else
- ${BINARY} start --pruning=nothing --minimum-gas-prices=0"${DENOM}" --rpc.laddr="tcp://0.0.0.0:${RPC}" --home "${HOME_DIR}" --json-rpc.api=eth,txpool,personal,net,debug,web3 --json-rpc.address="0.0.0.0:${JSON_RPC}" --json-rpc.ws-address="0.0.0.0:${JSON_RPC_WS}" --chain-id="${CHAIN_ID}"
+ log_info "Starting node locally..."
+ ${CHAIN_BIN} start --pruning=nothing --minimum-gas-prices=0"${DENOM}" --rpc.laddr="tcp://0.0.0.0:${RPC}" --home "${HOME_DIR}" --json-rpc.api=eth,txpool,personal,net,debug,web3 --json-rpc.address="0.0.0.0:${JSON_RPC}" --json-rpc.ws-address="0.0.0.0:${JSON_RPC_WS}" --chain-id="${CHAIN_ID}"
fi
+
+log_success "Node startup completed"
diff --git a/scripts/testnet-setup.sh b/scripts/testnet-setup.sh
new file mode 100755
index 000000000..d47eb0b31
--- /dev/null
+++ b/scripts/testnet-setup.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+# scripts/testnet-setup.sh - Initialize Sonr testnet nodes for Docker deployment
+
+set -euo pipefail
+
+# Source helper libraries
+SCRIPT_DIR="/usr/local/lib/sonr-scripts"
+source "${SCRIPT_DIR}/env.sh"
+source "${SCRIPT_DIR}/config.sh"
+source "${SCRIPT_DIR}/keys.sh"
+source "${SCRIPT_DIR}/genesis.sh"
+
+# Initialize environment
+init_env
+
+# Set defaults for testnet
+NODE_TYPE="${NODE_TYPE:-validator}" # validator, sentry
+MONIKER="${MONIKER:-validator}"
+VALIDATOR_NAME="${VALIDATOR_NAME:-$MONIKER}"
+CHAIN_ID="${CHAIN_ID:-sonrtest_1-1}"
+HOME_DIR="${HOME_DIR:-/root/.sonr}"
+
+log_info "Setting up Sonr testnet node: $NODE_TYPE ($VALIDATOR_NAME)"
+
+# Ensure chain directory exists
+ensure_chain_dir
+
+# Update genesis parameters
+log_info "Updating genesis parameters..."
+update_genesis_params
+
+# Add constitution if available
+add_constitution
+
+# Configure node
+log_info "Configuring node..."
+configure_node "$CHAIN_DIR" \
+ --rpc-port 26657 \
+ --rest-port 1317 \
+ --grpc-port 9090 \
+ --grpc-web-port 9091 \
+ --json-rpc-port 8545 8546 \
+ --rosetta-port 8080 \
+ --min-gas-prices "0${DENOM}" \
+ --pruning nothing \
+ --keyring-backend "$KEYRING_BACKEND" \
+ --chain-id "$CHAIN_ID" \
+ --output-format json
+
+# Set consensus timeouts for faster blocks
+set_consensus_timeouts "$CHAIN_DIR/config/config.toml" "5s" "1s" "1s" "1s"
+
+# Enable CORS for RPC
+set_toml_value "$CHAIN_DIR/config/config.toml" "" "cors_allowed_origins" '["*"]'
+
+# Set pprof address
+set_toml_value "$CHAIN_DIR/config/config.toml" "" "pprof_laddr" "localhost:6060"
+
+# Set P2P address
+set_toml_value "$CHAIN_DIR/config/config.toml" "p2p" "laddr" "tcp://0.0.0.0:26656"
+
+# Generate VRF keypair
+log_info "Generating VRF keypair..."
+if ! generate_vrf_key "$CHAIN_DIR"; then
+ log_warn "VRF key generation failed, but continuing..."
+ log_warn "Note: Multi-validator encryption features may not work without VRF keys"
+fi
+
+# Create validator if this is a validator node
+if [[ "$NODE_TYPE" == "validator" ]]; then
+ log_info "Creating validator..."
+
+ # Wait for node to sync (in case it's connecting to other nodes)
+ if [[ "${WAIT_FOR_SYNC:-false}" == "true" ]]; then
+ wait_for_sync 10
+ fi
+
+ # Create validator (this would need proper key setup)
+ log_info "Validator creation requires manual key setup and gentx creation"
+ log_info "Run: snrd genesis gentx --chain-id $CHAIN_ID"
+fi
+
+log_success "Testnet setup completed for $NODE_TYPE node: $VALIDATOR_NAME"
\ No newline at end of file
diff --git a/scripts/transfer-tokens.sh b/scripts/transfer-tokens.sh
new file mode 100755
index 000000000..c91a9fed5
--- /dev/null
+++ b/scripts/transfer-tokens.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+ADDRESS="$1"
+DENOM="$2"
+FAUCET_URL="$3"
+FAUCET_ENABLED="$4"
+
+set -eux
+
+function transfer_token() {
+ status_code=$(curl --header "Content-Type: application/json" \
+ --request POST --write-out %{http_code} --silent --output /dev/null \
+ --data '{"denom":"'"$DENOM"'","address":"'"$ADDRESS"'"}' \
+ $FAUCET_URL)
+ echo $status_code
+}
+
+if [[ $FAUCET_ENABLED == "false" ]];
+then
+ echo "Faucet not enabled... skipping transfer token from faucet"
+ exit 0
+fi
+
+echo "Try to send tokens, if failed, wait for 5 seconds and try again"
+max_tries=5
+while [[ max_tries -gt 0 ]]
+do
+ status_code=$(transfer_token)
+ if [[ "$status_code" -eq 200 ]]; then
+ echo "Successfully sent tokens"
+ exit 0
+ fi
+ echo "Failed to send tokens. Sleeping for 2 secs. Tries left $max_tries"
+ ((max_tries--))
+ sleep 2
+done
diff --git a/scripts/update-config.sh b/scripts/update-config.sh
new file mode 100755
index 000000000..4a4bfb50e
--- /dev/null
+++ b/scripts/update-config.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# scripts/update-config.sh - Update configuration files for Sonr node
+
+set -euo pipefail
+
+# Source helper libraries
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "${SCRIPT_DIR}/lib/env.sh"
+source "${SCRIPT_DIR}/lib/config.sh"
+
+# Initialize environment
+init_env
+
+# Ensure chain directory exists
+ensure_chain_dir
+
+log_info "Updating configuration files for Sonr node"
+
+# Configure node with standard settings
+configure_node "$CHAIN_DIR" \
+ --rpc-port 26657 \
+ --rest-port 1317 \
+ --grpc-port 9090 \
+ --grpc-web-port 9091 \
+ --json-rpc-port 8545 8546 \
+ --rosetta-port 8080 \
+ --min-gas-prices "0${DENOM}" \
+ --pruning default \
+ --keyring-backend "$KEYRING_BACKEND" \
+ --chain-id "$CHAIN_ID" \
+ --output-format json
+
+# Enable metrics if requested
+if [[ "${METRICS:-false}" == "true" ]]; then
+ enable_metrics "$CHAIN_DIR/config/config.toml" 3600
+fi
+
+# Set consensus timeouts if provided
+if [[ -n "${TIMEOUT_PROPOSE:-}" ]]; then
+ set_consensus_timeouts "$CHAIN_DIR/config/config.toml" \
+ "$TIMEOUT_PROPOSE" \
+ "${TIMEOUT_PREVOTE:-1s}" \
+ "${TIMEOUT_PRECOMMIT:-1s}" \
+ "${TIMEOUT_COMMIT:-5s}"
+fi
+
+log_success "Configuration update completed"
diff --git a/scripts/update-genesis.sh b/scripts/update-genesis.sh
index 8b88d3cd9..1c933f537 100755
--- a/scripts/update-genesis.sh
+++ b/scripts/update-genesis.sh
@@ -1,130 +1,32 @@
#!/bin/bash
-DENOM="${DENOM:=usnr}"
-CHAIN_BIN="${CHAIN_BIN:=snrd}"
-CHAIN_DIR="${CHAIN_DIR:=$HOME/.sonr}"
+# scripts/update-genesis.sh - Update genesis.json with Sonr-specific parameters
-set -eux
+set -euo pipefail
-ls "$CHAIN_DIR"/config
-
-echo "Update genesis.json file with updated local params"
-sed -i -e "s/\"stake\"/\"$DENOM\"/g" "$CHAIN_DIR"/config/genesis.json
-sed -i "s/\"time_iota_ms\": \".*\"/\"time_iota_ms\": \"$TIME_IOTA_MS\"/" "$CHAIN_DIR"/config/genesis.json
-
-echo "Update max gas param"
-jq -r '.consensus.params.block.max_gas |= "100000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
-mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-
-echo "Update staking unbonding time and slashing jail time"
-jq -r '.app_state.staking.params.unbonding_time |= "300s"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
-mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-jq -r '.app_state.slashing.params.downtime_jail_duration |= "60s"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
-mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-
-# overrides for older sdk versions, before 0.47
-function gov_overrides_sdk_v46() {
- jq -r '.app_state.gov.deposit_params.max_deposit_period |= "30s"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.deposit_params.min_deposit[0].amount |= "10"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.voting_params.voting_period |= "30s"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.tally_params.quorum |= "0.000000000000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.tally_params.threshold |= "0.000000000000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.tally_params.veto_threshold |= "0.000000000000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-}
-
-# overrides for newer sdk versions, post 0.47
-function gov_overrides_sdk_v47() {
- jq -r '.app_state.gov.params.max_deposit_period |= "30s"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.params.min_deposit[0].amount |= "10"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.params.voting_period |= "30s"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.params.quorum |= "0.000000000000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.params.threshold |= "0.000000000000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.gov.params.veto_threshold |= "0.000000000000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-}
-
-# EVM and feemarket configuration
-if [ "$(jq -r '.app_state.evm' "$CHAIN_DIR"/config/genesis.json)" != "null" ]; then
- jq -r ".app_state.evm.params.evm_denom |= \"$DENOM\"" "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.evm.params.active_static_precompiles |= ["0x0000000000000000000000000000000000000100","0x0000000000000000000000000000000000000400","0x0000000000000000000000000000000000000800","0x0000000000000000000000000000000000000801","0x0000000000000000000000000000000000000802","0x0000000000000000000000000000000000000803","0x0000000000000000000000000000000000000804","0x0000000000000000000000000000000000000805"]' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
-
-if [ "$(jq -r '.app_state.erc20' "$CHAIN_DIR"/config/genesis.json)" != "null" ]; then
- jq -r '.app_state.erc20.params.native_precompiles |= ["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"]' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r ".app_state.erc20.token_pairs |= [{\"contract_owner\":1,\"erc20_address\":\"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\",\"denom\":\"$DENOM\",\"enabled\":true}]" "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
-
-if [ "$(jq -r '.app_state.feemarket.params' "$CHAIN_DIR"/config/genesis.json)" != "null" ]; then
- jq -r '.app_state.feemarket.params.no_base_fee |= true' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.feemarket.params.base_fee |= "0.000000000000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
-
-# Staking and mint configuration
-if [ "$(jq -r '.app_state.staking' "$CHAIN_DIR"/config/genesis.json)" != "null" ]; then
- jq -r ".app_state.staking.params.bond_denom |= \"$DENOM\"" "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.staking.params.min_commission_rate |= "0.050000000000000000"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
-
-if [ "$(jq -r '.app_state.mint' "$CHAIN_DIR"/config/genesis.json)" != "null" ]; then
- jq -r ".app_state.mint.params.mint_denom |= \"$DENOM\"" "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
-
-if [ "$(jq -r '.app_state.crisis' "$CHAIN_DIR"/config/genesis.json)" != "null" ]; then
- jq -r ".app_state.crisis.constant_fee |= {\"denom\":\"$DENOM\",\"amount\":\"1000\"}" "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
-
-# Token factory configuration
-if [ "$(jq -r '.app_state.tokenfactory' "$CHAIN_DIR"/config/genesis.json)" != "null" ]; then
- jq -r '.app_state.tokenfactory.params.denom_creation_fee |= []' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
- jq -r '.app_state.tokenfactory.params.denom_creation_gas_consume |= 100000' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
-
-# ABCI configuration
-if [ "$(jq -r '.consensus.params.abci' "$CHAIN_DIR"/config/genesis.json)" != "null" ]; then
- jq -r '.consensus.params.abci.vote_extensions_enable_height |= "1"' "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
-
-# Add CONSTITUTION.md to governance if it exists
-# Look for CONSTITUTION.md in the git root directory (parent of scripts directory)
+# Source helper libraries
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-GIT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
-CONSTITUTION_FILE="${GIT_ROOT}/CONSTITUTION.md"
+source "${SCRIPT_DIR}/lib/env.sh"
+source "${SCRIPT_DIR}/lib/genesis.sh"
-if [ -f "$CONSTITUTION_FILE" ]; then
- echo "Adding CONSTITUTION.md to governance module from: $CONSTITUTION_FILE"
- CONSTITUTION_CONTENT=$(cat "$CONSTITUTION_FILE" | jq -Rs .)
- jq -r ".app_state.gov.constitution = $CONSTITUTION_CONTENT" "$CHAIN_DIR"/config/genesis.json >/tmp/genesis.json
- mv /tmp/genesis.json "$CHAIN_DIR"/config/genesis.json
-fi
+# Initialize environment
+init_env
-if [ "$(jq -r '.app_state.gov.params' "$CHAIN_DIR"/config/genesis.json)" == "null" ]; then
- gov_overrides_sdk_v46
-else
- gov_overrides_sdk_v47
-fi
+# Ensure chain directory exists
+ensure_chain_dir
+log_info "Updating genesis.json file with Sonr parameters"
+
+# Update genesis parameters using helper functions
+update_genesis_params
+
+# Add constitution if available
+add_constitution
+
+# Validate genesis file
+validate_genesis
+
+# Show node ID
$CHAIN_BIN tendermint show-node-id
+
+log_success "Genesis update completed"