From c87dea61c1c2217aa41211e13b8b97f2351d5284 Mon Sep 17 00:00:00 2001 From: Martinsbab Date: Thu, 30 Jul 2026 08:58:30 +0000 Subject: [PATCH] docs: improve RBAC deployment script documentation and doc comments --- deployments/deploy-wrapper-testnet.md | 118 +++++++++++++++++++++++-- deployments/deploy-wrapper-testnet.ps1 | 55 ++++++++++-- deployments/deploy-wrapper-testnet.sh | 52 ++++++++++- 3 files changed, 210 insertions(+), 15 deletions(-) diff --git a/deployments/deploy-wrapper-testnet.md b/deployments/deploy-wrapper-testnet.md index 35d3bc5c..f6649e65 100644 --- a/deployments/deploy-wrapper-testnet.md +++ b/deployments/deploy-wrapper-testnet.md @@ -1,6 +1,21 @@ # Deploy Wrapper Contract to Stellar Testnet -This guide documents the steps to build and deploy the `bc-forge-wrapper` contract to the Stellar Soroban Testnet. +This guide documents the steps to build and deploy the `bc-forge-wrapper` contract to the Stellar Soroban Testnet, including Role-Based Access Control (RBAC) initialization. + +## RBAC Overview + +The wrapper contract embeds the admin (RBAC) module from `contracts/admin`. When you initialize the contract with an admin address, the RBAC system is set up with the following roles: + +| Role | Privilege | +|------|-----------| +| `Admin` | Full administrative control; implicitly satisfies **all** role guards | +| `Minter` | Permission to mint new tokens and set maximum supply | +| `SuperAdmin` | Permission to upgrade contract WASM | +| `Pauser` | Permission to pause and unpause the contract | + +> **The `Admin` role is a superset:** any role check — `require_minter`, `require_super_admin`, `require_pauser` — passes for the configured admin address. Explicit `Minter`, `SuperAdmin`, and `Pauser` grants are only needed for _other_ addresses. + +For full details, see [`docs/ACCESS_CONTROL.md`](../docs/ACCESS_CONTROL.md). ## Prerequisites @@ -60,9 +75,13 @@ CCK7E4VJ3Y7Z5XK5QZ5XK5QZ5XK5QZ5XK5QZ5XK5QZ5XK5QZ5XK5QZ5X > **Note:** Save this contract ID — you will need it for initialization and invocation. -## Step 4: Initialize the Wrapper Contract +## Step 4: Initialize the Wrapper Contract (RBAC Init) + +Initializing the contract performs **RBAC initialization**: the admin address is stored and automatically granted the `Admin` role. Because `Admin` is a superset role, this single address gains full access to all protected operations. + +### 4a. Deploy (or use an existing) underlying token contract -The wrapper needs to be pointed at an underlying SEP-41 token contract. First deploy (or use an existing) token contract: +The wrapper needs to be pointed at an underlying SEP-41 token contract: ```bash # Deploy a token contract to wrap @@ -73,7 +92,7 @@ soroban contract deploy \ --network-passphrase "Test SDF Network ; September 2015" ``` -Save the token contract ID and initialize it (if new): +### 4b. Initialize the token contract ```bash soroban contract invoke \ @@ -89,7 +108,9 @@ soroban contract invoke \ --symbol "wTKN" ``` -Now initialize the wrapper: +This sets the admin address in the token contract's RBAC storage via `init_storage`. + +### 4c. Initialize the wrapper (sets RBAC admin + underlying token) ```bash soroban contract invoke \ @@ -106,6 +127,33 @@ soroban contract invoke \ --symbol "wTKN" ``` +On success, the RBAC storage is initialized: the admin address is stored and the `Admin` role is granted. The wrapper is now ready for use. + +### 4d. (Optional) Grant additional roles + +To grant specific roles to other addresses (e.g., a delegation wallet), use `grant_role`: + +```bash +soroban contract invoke \ + --id \ + --source bc-forge-admin \ + --rpc-url https://soroban-testnet.stellar.org \ + --network-passphrase "Test SDF Network ; September 2015" \ + -- \ + grant_role \ + --caller $(soroban keys address bc-forge-admin) \ + --role Minter \ + --address +``` + +| Parameter | Description | +|-----------|-------------| +| `caller` | The admin or super-admin address making the grant | +| `role` | One of `Minter`, `SuperAdmin`, `Pauser` (Admin is set via `set_admin`) | +| `address` | The address receiving the role | + +> **Note:** The `Admin` role cannot be granted via `grant_role` — use `set_admin` instead. Admin is a superset: any address holding `Admin` passes all role checks. + ## Step 5: Verify Deployment ### Check Contract Version @@ -153,6 +201,23 @@ soroban contract invoke \ Expected output: `0` +### Verify RBAC Roles (Optional) + +Check that the admin address holds a specific role: + +```bash +soroban contract invoke \ + --id \ + --rpc-url https://soroban-testnet.stellar.org \ + --network-passphrase "Test SDF Network ; September 2015" \ + -- \ + has_role \ + --role Admin \ + --address $(soroban keys address bc-forge-admin) +``` + +Expected output: `true` + ## Step 6: Test Basic Invocation — Wrap/Unwrap Flow ### Mint Underlying Tokens to a User @@ -236,10 +301,51 @@ soroban contract invoke \ ## Troubleshooting -- **`HostError: Error(Contract, #2)`**: Contract not initialized. Call `initialize` first. +### General Errors + +- **`HostError: Error(Contract, #2)`**: Contract not initialized. Call `initialize` first (see Step 4). - **`HostError: Error(Contract, #3)`**: Invalid amount (≤ 0). Check your amount values. - **`HostError: Error(Contract, #4)`**: Insufficient balance. The caller does not have enough wrapped tokens. - **`HostError: Error(Contract, #5)`**: Insufficient allowance. The wrapper has not been approved to spend enough underlying tokens. - **`HostError: Error(Contract, #6)`**: Contract is paused. Call `unpause` first. - **`HostError: Error(Contract, #7)`**: Reentrant call detected (should not happen in normal usage). - **`HostError: Error(Contract, #8)`**: Underlying token call failed. + +### RBAC Errors + +| Error | Code | Meaning | +|-------|------|---------| +| `RoleNotGranted` | `#1` | *(Unused — kept for ABI stability. Prefer `RoleNotHeld`.)* | +| `RoleNotHeld` | `#2` | An address does not hold the required role. Verify via `has_role`. | +| `UnauthorizedRole` | `#3` | `require_role_guard` failed: the caller is not authorized. | +| `InvalidAddress` | `#4` | A zero-address sentinel was passed where a valid address is required. | +| `InvalidRole` | `#5` | An unrecognized role value was provided. | +| `AlreadyInitialized` | `#6` | Contract has already been initialized — a second `init_storage` call is rejected. | + +### RBAC Verification Commands + +Check if an address holds a specific role: + +```bash +soroban contract invoke \ + --id \ + --rpc-url https://soroban-testnet.stellar.org \ + --network-passphrase "Test SDF Network ; September 2015" \ + -- \ + has_role \ + --role Minter \ + --address +``` + +View the configured admin address (the role parameter is accepted but the same singular admin address is always returned): + +```bash +soroban contract invoke \ + --id \ + --rpc-url https://soroban-testnet.stellar.org \ + --network-passphrase "Test SDF Network ; September 2015" \ + -- \ + get_role_admin \ + --role Admin +``` + diff --git a/deployments/deploy-wrapper-testnet.ps1 b/deployments/deploy-wrapper-testnet.ps1 index ff6f5b0f..87dd2426 100644 --- a/deployments/deploy-wrapper-testnet.ps1 +++ b/deployments/deploy-wrapper-testnet.ps1 @@ -1,6 +1,23 @@ #!/usr/bin/env pwsh -# Deploy bc-forge-wrapper contract to Stellar Testnet -# Usage: ./deploy-wrapper-testnet.ps1 -AdminSeed "" +# Deploy bc-forge-wrapper contract to Stellar Testnet and initialize RBAC storage. +# +# The wrapper contract embeds the admin (RBAC) module. Deploying and then +# initializing the contract creates the admin role and grants the caller the +# `Admin` role, which implicitly satisfies all role guards (Minter, SuperAdmin, +# Pauser). Post-deployment, additional roles can be granted via `grant_role`. +# +# .PARAMETER AdminSeed +# Secret key (seed) of the Stellar account that will become the RBAC admin. +# .PARAMETER RpcUrl +# Soroban RPC endpoint (default: Soroban Testnet). +# .PARAMETER NetworkPassphrase +# Network passphrase (default: Testnet passphrase). +# +# Usage: .\deploy-wrapper-testnet.ps1 -AdminSeed "" +# +# See also: +# deploy-wrapper-testnet.md – Full step-by-step guide with RBAC init details +# docs/ACCESS_CONTROL.md – RBAC role hierarchy and protected operations param( [Parameter(Mandatory = $true)] @@ -12,7 +29,9 @@ param( $ErrorActionPreference = "Stop" -# Step 1: Build WASM +# --------------------------------------------------------------------------- +# Step 1 — Build WASM +# --------------------------------------------------------------------------- Write-Host "=== Building WASM ===" -ForegroundColor Cyan cargo build --target wasm32-unknown-unknown --release -p bc-forge-wrapper if ($LASTEXITCODE -ne 0) { throw "WASM build failed" } @@ -20,7 +39,9 @@ if ($LASTEXITCODE -ne 0) { throw "WASM build failed" } $WasmPath = "target/wasm32-unknown-unknown/release/bc_forge_wrapper.wasm" Write-Host "WASM built: $((Get-Item $WasmPath).Length) bytes" -ForegroundColor Green -# Step 2: Deploy wrapper contract +# --------------------------------------------------------------------------- +# Step 2 — Deploy contract (no RBAC init yet; deploy first, then initialize) +# --------------------------------------------------------------------------- Write-Host "=== Deploying Wrapper Contract ===" -ForegroundColor Cyan $WrapperId = & soroban contract deploy ` --wasm $WasmPath ` @@ -32,7 +53,9 @@ if ($LASTEXITCODE -ne 0) { throw "Wrapper contract deploy failed" } Write-Host "Wrapper Contract ID: $WrapperId" -ForegroundColor Green -# Step 3: Verify deployment +# --------------------------------------------------------------------------- +# Step 3 — Verify deployment (read-only calls; does NOT initialize RBAC) +# --------------------------------------------------------------------------- Write-Host "=== Verifying Deployment ===" -ForegroundColor Cyan $Version = & soroban contract invoke ` --id $WrapperId ` @@ -68,13 +91,31 @@ $Supply = & soroban contract invoke ` supply Write-Host "Initial supply: $Supply" -ForegroundColor Green -# Output results +# --------------------------------------------------------------------------- +# Step 4 — RBAC Initialization (post-deployment) +# +# After deployment the caller MUST invoke `initialize` on the wrapper +# contract. This call sets the RBAC admin and the underlying token address. +# The admin account implicitly holds every role (Admin, Minter, SuperAdmin, +# Pauser) — no separate grant_role call is needed for the admin. +# +# Additional roles can be granted to other addresses via grant_role. +# --------------------------------------------------------------------------- +# NOTE: This script deploys and verifies only. RBAC initialization +# (initialize) is performed separately via the commands documented in +# deploy-wrapper-testnet.md (Step 4). + +Write-Host "" -ForegroundColor Yellow Write-Host "=== Deployment Summary ===" -ForegroundColor Cyan Write-Host "Wrapper Contract ID: $WrapperId" -ForegroundColor Yellow Write-Host "RPC URL: $RpcUrl" -ForegroundColor Yellow Write-Host "Network: $NetworkPassphrase" -ForegroundColor Yellow +Write-Host "" -ForegroundColor Yellow +Write-Host "IMPORTANT: This contract is NOT yet initialized." -ForegroundColor Red +Write-Host "Run the initialization commands from deploy-wrapper-testnet.md (Step 4)" -ForegroundColor Red +Write-Host "to configure the RBAC admin and underlying token before using the contract." -ForegroundColor Red -# Save to file +# Save summary for downstream initialization scripts $summary = @{ wrapperContractId = $WrapperId rpcUrl = $RpcUrl diff --git a/deployments/deploy-wrapper-testnet.sh b/deployments/deploy-wrapper-testnet.sh index 2797ceb6..ae3b62ca 100644 --- a/deployments/deploy-wrapper-testnet.sh +++ b/deployments/deploy-wrapper-testnet.sh @@ -1,7 +1,26 @@ #!/usr/bin/env bash -# Deploy bc-forge-wrapper contract to Stellar Testnet +# Deploy bc-forge-wrapper contract to Stellar Testnet and initialize RBAC storage. +# +# The wrapper contract embeds the admin (RBAC) module. Deploying and then +# initializing the contract creates the admin role and grants the caller the +# `Admin` role, which implicitly satisfies all role guards (Minter, SuperAdmin, +# Pauser). Post-deployment, additional roles can be granted via `grant_role`. +# +# Required environment / argument: +# ADMIN_SEED – Secret key (seed) of the Stellar account that will become +# the RBAC admin. Pass as the first positional argument or +# export as the ADMIN_SEED environment variable. +# +# Optional environment: +# RPC_URL – Soroban RPC endpoint (default: Soroban Testnet) +# NETWORK_PASSPHRASE – Network passphrase (default: Testnet passphrase) +# # Usage: ./deploy-wrapper-testnet.sh # or: export ADMIN_SEED= && ./deploy-wrapper-testnet.sh +# +# See also: +# deploy-wrapper-testnet.md – Full step-by-step guide with RBAC init details +# docs/ACCESS_CONTROL.md – RBAC role hierarchy and protected operations set -euo pipefail @@ -19,12 +38,18 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" cd "$PROJECT_DIR" +# --------------------------------------------------------------------------- +# Step 1 — Build WASM +# --------------------------------------------------------------------------- echo "=== Building WASM ===" cargo build --target wasm32-unknown-unknown --release -p bc-forge-wrapper WASM_PATH="target/wasm32-unknown-unknown/release/bc_forge_wrapper.wasm" echo "WASM built: $(stat -c%s "$WASM_PATH") bytes" +# --------------------------------------------------------------------------- +# Step 2 — Deploy contract (no RBAC init yet; deploy first, then initialize) +# --------------------------------------------------------------------------- echo "=== Deploying Wrapper Contract ===" WRAPPER_ID=$(soroban contract deploy \ --wasm "$WASM_PATH" \ @@ -35,6 +60,9 @@ WRAPPER_ID=$(soroban contract deploy \ ) echo "Wrapper Contract ID: $WRAPPER_ID" +# --------------------------------------------------------------------------- +# Step 3 — Verify deployment (reads the contract but does NOT initialize it) +# --------------------------------------------------------------------------- echo "=== Verifying Deployment ===" echo "Version:" soroban contract invoke \ @@ -70,13 +98,33 @@ soroban contract invoke \ -- \ supply +# --------------------------------------------------------------------------- +# Step 4 — RBAC Initialization (post-deployment) +# +# After deployment the caller MUST invoke `initialize` on the wrapper +# contract. This call sets the RBAC admin and the underlying token address. +# The admin account implicitly holds every role (Admin, Minter, SuperAdmin, +# Pauser) — no separate grant_role call is needed for the admin. +# +# Additional roles can be granted to other addresses via grant_role: +# soroban contract invoke --id "$WRAPPER_ID" --source-account "$ADMIN_SEED" ... -- \ +# grant_role --caller "$ADMIN" --role Minter --address
+# --------------------------------------------------------------------------- +# NOTE: This script deploys and verifies only. RBAC initialization +# (initialize) must be performed separately as documented in +# deploy-wrapper-testnet.md (Step 4). + echo "" echo "=== Deployment Summary ===" echo "Wrapper Contract ID: $WRAPPER_ID" echo "RPC URL: $RPC_URL" echo "Network: $NETWORK_PASSPHRASE" +echo "" +echo "IMPORTANT: This contract is NOT yet initialized." +echo "Run the initialization commands from deploy-wrapper-testnet.md (Step 4)" +echo "to configure the RBAC admin and underlying token before using the contract." -# Save summary +# Save summary for downstream initialization scripts cat > "$SCRIPT_DIR/wrapper-deployment.json" <