Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 112 additions & 6 deletions deployments/deploy-wrapper-testnet.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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 \
Expand All @@ -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 \
Expand All @@ -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 <WRAPPER_CONTRACT_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 <OTHER_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
Expand Down Expand Up @@ -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 <WRAPPER_CONTRACT_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
Expand Down Expand Up @@ -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 <WRAPPER_CONTRACT_ID> \
--rpc-url https://soroban-testnet.stellar.org \
--network-passphrase "Test SDF Network ; September 2015" \
-- \
has_role \
--role Minter \
--address <TARGET_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 <WRAPPER_CONTRACT_ID> \
--rpc-url https://soroban-testnet.stellar.org \
--network-passphrase "Test SDF Network ; September 2015" \
-- \
get_role_admin \
--role Admin
```

55 changes: 48 additions & 7 deletions deployments/deploy-wrapper-testnet.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
#!/usr/bin/env pwsh
# Deploy bc-forge-wrapper contract to Stellar Testnet
# Usage: ./deploy-wrapper-testnet.ps1 -AdminSeed "<SECRET_KEY>"
# 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 "<SECRET_KEY>"
#
# 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)]
Expand All @@ -12,15 +29,19 @@ 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" }

$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 `
Expand All @@ -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 `
Expand Down Expand Up @@ -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
Expand Down
52 changes: 50 additions & 2 deletions deployments/deploy-wrapper-testnet.sh
Original file line number Diff line number Diff line change
@@ -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 <ADMIN_SECRET_KEY>
# or: export ADMIN_SEED=<secret> && ./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

Expand All @@ -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" \
Expand All @@ -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 \
Expand Down Expand Up @@ -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 <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" <<EOF
{
"wrapperContractId": "$WRAPPER_ID",
Expand Down