Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ docs/refactor/
skills-lock.json
docs/did-spec/did-stellar-v0.1 copy.md
docs/architecture/
.env
.env.*
.claude/
2 changes: 1 addition & 1 deletion docs/deployments/testnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ RPC: `https://soroban-testnet.stellar.org:443`
|---|---|
| enabled | `true` |
| token | `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC` (native XLM SAC) |
| dest | `GAP7AUGY2Q2NKIHJ2XMZAGVHD7KDF2UAUEPXE5HRXL7BOXRWDHXHG6IY` |
| dest | _(deployer wallet)_ |
| standard | `50000000` stroops = **5 XLM ≈ 1 USD** @ ~$0.20/XLM |

Charged per credential issued; the issuer pays. The amount is fixed on-chain (does
Expand Down
314 changes: 314 additions & 0 deletions scripts/deploy-mainnet.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
#!/bin/sh
set -eu

# Usage:
# ./scripts/deploy-mainnet.sh [--dry-run] # full deploy
# ./scripts/deploy-mainnet.sh derive-key # extract S… from a seed phrase
#
# Required env (loaded from .env): DEPLOYER_SECRET (S… key), DEPLOYER_ADDRESS (G…).
# Optional env overrides: DID_ADMIN FACTORY_ADMIN FEE_TOKEN FEE_DEST FEE_STANDARD FEE_ENABLED.

NETWORK_NAME=mainnet
PASSPHRASE="Public Global Stellar Network ; September 2015"
RPC_URL="${RPC_URL:-https://mainnet.sorobanrpc.com}"
HORIZON_URL="https://horizon.stellar.org"
USDC_ISSUER="GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
TMP_IDENTITY="_acta_deployer"

Comment on lines +16 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Fix temporary identity lifecycle before handling secrets.

--dry-run still imports/removes a signing identity, fixed aliases can delete a pre-existing local key, and _acta_derive can remain if derivation exits early. Use per-run aliases, track created identities, and skip key import in dry-run.

Suggested fix
-TMP_IDENTITY="_acta_deployer"
+TMP_IDENTITY="_acta_deployer_$$"
+DEPLOY_IDENTITY_CREATED=0
+DERIVE_IDENTITY=""

 cleanup() {
-    stellar keys rm "$TMP_IDENTITY" --force >/dev/null 2>&1 || true
+    if [ "${DEPLOY_IDENTITY_CREATED:-0}" -eq 1 ]; then
+        stellar keys rm "$TMP_IDENTITY" --force >/dev/null 2>&1 || true
+    fi
+    if [ -n "${DERIVE_IDENTITY:-}" ]; then
+        stellar keys rm "$DERIVE_IDENTITY" --force >/dev/null 2>&1 || true
+    fi
 }

 import_and_verify_identity() {
     step "Importing + verifying deployer identity"
+    if [ "$DRY_RUN" -eq 1 ]; then
+        say "  [dry-run] would import and verify deployer identity"
+        return 0
+    fi
     : "${DEPLOYER_SECRET:?DEPLOYER_SECRET is not set in .env}"
-    stellar keys rm "$TMP_IDENTITY" --force >/dev/null 2>&1 || true
     printf '%s\n' "$DEPLOYER_SECRET" | stellar keys add "$TMP_IDENTITY" --secret-key >/dev/null
+    DEPLOY_IDENTITY_CREATED=1
     derived="$(stellar keys public-key "$TMP_IDENTITY")"

 derive_key() {
     : "${DEPLOYER_ADDRESS:?set DEPLOYER_ADDRESS to the deployer G… address in .env}"
-    tmp="_acta_derive"
-    stellar keys rm "$tmp" --force >/dev/null 2>&1 || true
+    tmp="_acta_derive_$$"
+    DERIVE_IDENTITY="$tmp"
     say "Paste your Freighter 12/24-word recovery phrase when prompted."
     stellar keys add "$tmp" --seed-phrase >/dev/null
@@
-        stellar keys rm "$tmp" --force >/dev/null 2>&1 || true
+        stellar keys rm "$tmp" --force >/dev/null 2>&1 || true
+        DERIVE_IDENTITY=""
         exit 1
@@
     stellar keys secret "$tmp" --hd-path "$found"
-    stellar keys rm "$tmp" --force >/dev/null 2>&1 || true
+    stellar keys rm "$tmp" --force >/dev/null 2>&1 || true
+    DERIVE_IDENTITY=""

Also applies to: 133-143, 261-285, 291-298

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy-mainnet.sh` around lines 16 - 17, The temporary identity
handling in deploy-mainnet.sh is unsafe: the fixed TMP_IDENTITY alias and the
_acta_derive flow can overwrite or leave behind existing keys, and dry-run still
imports/removes a signing identity. Update the identity lifecycle around the
secret-handling logic to use per-run aliases, track which identities were
actually created, and only import/remove keys when not in dry-run. Make the
changes in the TMP_IDENTITY-related setup and the _acta_derive / cleanup paths
so early exits still leave no stray identities and pre-existing local keys are
never deleted.

if [ -f .env ]; then
set -a
. ./.env
set +a
fi

# Network-level constants.
FEE_TOKEN="${FEE_TOKEN:-CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75}"
FEE_STANDARD="${FEE_STANDARD:-10000000}"
FEE_ENABLED="${FEE_ENABLED:-true}"
INCLUSION_FEE="${INCLUSION_FEE:-1000000}"

# Identity-dependent params.
DEPLOYER_ADDRESS="${DEPLOYER_ADDRESS:-}"
DID_ADMIN="${DID_ADMIN:-}"
FACTORY_ADMIN="${FACTORY_ADMIN:-}"
FEE_DEST="${FEE_DEST:-}"

# Resume support: pre-set any of these (e.g. in .env) to skip an already-done
# step after a partial run, so a re-run doesn't redeploy/repay for it.
DID_ID="${DID_ID:-}"
VAULT_HASH="${VAULT_HASH:-}"
FACTORY_ID="${FACTORY_ID:-}"

DRY_RUN=0
SUBCOMMAND=""

usage() {
cat >&2 <<'EOF'
Usage:
./scripts/deploy-mainnet.sh [--dry-run] Deploy all three contracts to mainnet
./scripts/deploy-mainnet.sh derive-key Derive a single-account S… from a seed phrase
./scripts/deploy-mainnet.sh -h|--help Show this help
EOF
}

# Parse args: optional subcommand + flags.
for arg in "$@"; do
case "$arg" in
derive-key) SUBCOMMAND="derive-key" ;;
--dry-run) DRY_RUN=1 ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown argument: $arg" >&2; usage; exit 1 ;;
esac
done
Comment on lines +55 to +62

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Reject derive-key --dry-run explicitly.

--dry-run is silently ignored for derive-key, so a user can still be prompted for a recovery phrase and print a secret after requesting a dry run.

Suggested fix
 for arg in "$@"; do
     case "$arg" in
         derive-key) SUBCOMMAND="derive-key" ;;
         --dry-run)  DRY_RUN=1 ;;
         -h|--help)  usage; exit 0 ;;
         *) echo "Unknown argument: $arg" >&2; usage; exit 1 ;;
     esac
 done
+
+if [ "$SUBCOMMAND" = "derive-key" ] && [ "$DRY_RUN" -eq 1 ]; then
+    echo "--dry-run is only supported for full deploy, not derive-key" >&2
+    usage
+    exit 1
+fi

Also applies to: 310-311

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy-mainnet.sh` around lines 55 - 62, The argument parsing in the
deploy-mainnet.sh loop currently lets derive-key proceed even when --dry-run is
set, which can still trigger recovery phrase prompts and secret output. Update
the SUBCOMMAND/DRY_RUN handling in the top-level argument parser and the
derive-key path so that derive-key explicitly rejects --dry-run (for example by
erroring out before any key derivation flow starts), keeping the safeguard in
the derive-key command logic rather than silently ignoring it.


say() { printf '%s\n' "$*" >&2; }
step() { printf '\n==> %s\n' "$*" >&2; }

# Run a side-effecting command; under --dry-run just print it.
run() {
if [ "$DRY_RUN" -eq 1 ]; then
printf ' [dry-run] %s\n' "$*" >&2
return 0
fi
"$@"
}

# Run a command that produces a value on stdout (e.g. a contract id).
# Under --dry-run, print the command and echo the placeholder instead.
capture() {
placeholder="$1"; shift
if [ "$DRY_RUN" -eq 1 ]; then
printf ' [dry-run] %s\n' "$*" >&2
printf '%s' "$placeholder"
return 0
fi
"$@"
}

# Require the deployer address from the env and default the identity-derived
# params to it. Kept out of top-level so --help works without the env set.
resolve_identity() {
: "${DEPLOYER_ADDRESS:?set DEPLOYER_ADDRESS to the deployer G… address in .env}"
DID_ADMIN="${DID_ADMIN:-$DEPLOYER_ADDRESS}"
FACTORY_ADMIN="${FACTORY_ADMIN:-$DEPLOYER_ADDRESS}"
FEE_DEST="${FEE_DEST:-$DEPLOYER_ADDRESS}"
}

print_params() {
step "Resolved mainnet parameters"
say " network : $NETWORK_NAME ($RPC_URL)"
say " deployer address : $DEPLOYER_ADDRESS"
say " DID admin : $DID_ADMIN"
say " factory admin : $FACTORY_ADMIN"
say " fee token (USDC) : $FEE_TOKEN"
say " fee dest : $FEE_DEST"
say " fee standard : $FEE_STANDARD (= $((FEE_STANDARD / 10000000)) USDC)"
say " fee enabled : $FEE_ENABLED"
}

bootstrap_network() {
step "Configuring '$NETWORK_NAME' network ($RPC_URL)"
run stellar network add "$NETWORK_NAME" \
--rpc-url "$RPC_URL" \
--network-passphrase "$PASSPHRASE"
say " network '$NETWORK_NAME' -> $RPC_URL"
}
Comment on lines +109 to +115

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Stellar CLI behavior before relying on resume flow.
command -v stellar >/dev/null || {
  echo "stellar CLI is not installed in this environment"
  exit 0
}
stellar network add --help

Repository: ACTA-Team/contracts-acta

Length of output: 211


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== deploy script excerpt ==\n'
sed -n '90,135p' scripts/deploy-mainnet.sh

printf '\n== repo references for stellar network add / resume / idempot ==\n'
rg -n "stellar network add|network remove|network update|resume|idempot" scripts . -g '!**/.git/**' || true

printf '\n== files mentioning deploy-mainnet ==\n'
rg -n "deploy-mainnet\.sh|bootstrap_network" . -g '!**/.git/**' || true

Repository: ACTA-Team/contracts-acta

Length of output: 3655


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== deploy script flow around bootstrap ==\n'
sed -n '240,330p' scripts/deploy-mainnet.sh

printf '\n== resume references ==\n'
rg -n "resume|resumable|rerun|re-run|recovery|idempotent|already configured|skip if already present" scripts docs -g '!**/.git/**' || true

printf '\n== stellar network commands in repo ==\n'
rg -n "stellar network (add|rm|remove|set|list|ls)" . -g '!**/.git/**' || true

Repository: ACTA-Team/contracts-acta

Length of output: 4104


🌐 Web query:

Stellar CLI "network add" existing network already exists overwrite update idempotent

💡 Result:

The Stellar CLI network add command does not natively support an "overwrite" or "update" flag to modify an existing network configuration [1][2]. When attempting to add a network with a name that already exists in your configuration, the command will typically fail because the entry already exists [1][2]. To update or modify an existing network, you must first remove the current configuration and then add it again with the new parameters [2][3]: 1. Remove the existing network: stellar network rm 2. Add the network again with the desired settings: stellar network add --rpc-url <RPC_URL> --network-passphrase <NETWORK_PASSPHRASE> Unlike some other Stellar CLI commands (such as those for contract aliases or identity management) that feature explicit --overwrite options [4][5], the network add command is not designed to be idempotent in this manner [1][2]. If you need to manage network configurations programmatically, it is recommended to ensure your workflow performs these steps sequentially or manually verifies the existence of the network before attempting the addition.

Citations:


Make stellar network add resume-safe stellar network add fails when the network already exists, so a resumed deploy can abort before any resumable steps run. Remove the existing entry first or skip the add when it’s already configured.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy-mainnet.sh` around lines 109 - 115, The bootstrap_network
helper currently calls stellar network add unconditionally, which can fail on a
resumed deploy if the network was already configured. Update bootstrap_network
to be resume-safe by checking whether the network is already present before
calling stellar network add, or by removing any existing entry first and then
re-adding it. Use the existing bootstrap_network function and
NETWORK_NAME/RPC_URL/PASSPHRASE values to keep the behavior consistent while
avoiding an abort before resumable steps run.


confirm_gate() {
if [ "$DRY_RUN" -eq 1 ]; then
say ""
say " [dry-run] skipping confirmation gate"
return 0
fi
say ""
say "This will deploy to MAINNET and spend real XLM from $DEPLOYER_ADDRESS."
printf 'Type exactly "DEPLOY MAINNET" to continue: ' >&2
read -r reply
if [ "$reply" != "DEPLOY MAINNET" ]; then
say "Aborted (got: '$reply')."
exit 1
fi
}

# Remove the temporary signing identity on any exit path.
cleanup() {
stellar keys rm "$TMP_IDENTITY" --force >/dev/null 2>&1 || true
}

import_and_verify_identity() {
step "Importing + verifying deployer identity"
: "${DEPLOYER_SECRET:?DEPLOYER_SECRET is not set in .env}"
stellar keys rm "$TMP_IDENTITY" --force >/dev/null 2>&1 || true
printf '%s\n' "$DEPLOYER_SECRET" | stellar keys add "$TMP_IDENTITY" --secret-key >/dev/null
derived="$(stellar keys public-key "$TMP_IDENTITY")"
if [ "$derived" != "$DEPLOYER_ADDRESS" ]; then
say " ABORT: secret resolves to $derived but DEPLOYER_ADDRESS is $DEPLOYER_ADDRESS"
exit 1
fi
say " verified: $derived"
}

check_funding() {
step "Checking deployer is funded (XLM for tx fees)"
if [ "$DRY_RUN" -eq 1 ]; then
say " [dry-run] would GET $HORIZON_URL/accounts/$DEPLOYER_ADDRESS"
return 0
fi
code="$(curl -s -o /dev/null -w '%{http_code}' "$HORIZON_URL/accounts/$DEPLOYER_ADDRESS")"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add timeouts and explicit curl failure handling.

A stalled Horizon call can hang the deploy indefinitely, and transport failures currently exit via set -e without the script’s abort message.

Suggested fix
-    code="$(curl -s -o /dev/null -w '%{http_code}' "$HORIZON_URL/accounts/$DEPLOYER_ADDRESS")"
+    code="$(curl -sS --connect-timeout 10 --max-time 30 \
+        -o /dev/null -w '%{http_code}' "$HORIZON_URL/accounts/$DEPLOYER_ADDRESS")" || {
+        say "  ABORT: unable to reach Horizon for $DEPLOYER_ADDRESS"
+        exit 1
+    }
@@
-    body="$(curl -s "$HORIZON_URL/accounts/$FEE_DEST")"
+    body="$(curl -fsS --connect-timeout 10 --max-time 30 "$HORIZON_URL/accounts/$FEE_DEST")" || {
+        say "  ABORT: unable to fetch Horizon account for fee dest $FEE_DEST"
+        exit 1
+    }

Also applies to: 221-221

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy-mainnet.sh` at line 157, The Horizon account lookup curl call
can hang indefinitely and transport failures currently bypass the script’s own
abort handling. Update the curl invocation that assigns code using HORIZON_URL
and DEPLOYER_ADDRESS to use explicit timeout options and to fail in a way the
script can detect, then handle that failure in the surrounding deploy flow so it
emits the script’s abort message before exiting. Apply the same fix to the other
matching curl usage at the referenced second location in deploy-mainnet.sh.

if [ "$code" = "200" ]; then
say " funded (account exists on mainnet)"
elif [ "$code" = "404" ]; then
say " ABORT: $DEPLOYER_ADDRESS is not funded on mainnet (Horizon 404)."
say " Fund it with XLM before deploying."
exit 1
else
say " ABORT: unexpected Horizon status $code for $DEPLOYER_ADDRESS"
exit 1
fi
}

build_all() {
step "Building optimized WASMs"
run sh scripts/build.sh
}

deploy_did() {
if [ -n "$DID_ID" ]; then
step "Skipping did-stellar-registry (using existing $DID_ID)"
return 0
fi
step "Deploying did-stellar-registry (admin $DID_ADMIN)"
DID_ID="$(capture '<DID_ID>' stellar contract deploy \
--wasm target/wasm32v1-none/release/did_stellar_registry.optimized.wasm \
--source "$TMP_IDENTITY" --network "$NETWORK_NAME" --inclusion-fee "$INCLUSION_FEE" \
-- --admin "$DID_ADMIN")"
say " DID contract id: $DID_ID"
}

upload_vault() {
if [ -n "$VAULT_HASH" ]; then
step "Skipping vc-vault upload (using existing hash $VAULT_HASH)"
return 0
fi
step "Uploading vc-vault template WASM"
VAULT_HASH="$(capture '<VAULT_HASH>' stellar contract upload \
--wasm target/wasm32v1-none/release/vc_vault_contract.optimized.wasm \
--source "$TMP_IDENTITY" --network "$NETWORK_NAME" --inclusion-fee "$INCLUSION_FEE")"
say " vault template hash: $VAULT_HASH"
}

deploy_factory() {
if [ -n "$FACTORY_ID" ]; then
step "Skipping vc-vault-factory (using existing $FACTORY_ID)"
return 0
fi
step "Deploying vc-vault-factory (admin $FACTORY_ADMIN)"
FACTORY_ID="$(capture '<FACTORY_ID>' stellar contract deploy \
--wasm target/wasm32v1-none/release/vc_vault_factory_contract.optimized.wasm \
--source "$TMP_IDENTITY" --network "$NETWORK_NAME" --inclusion-fee "$INCLUSION_FEE" \
-- --vault_init_meta "{\"vault_hash\":\"$VAULT_HASH\",\"contract_admin\":\"$FACTORY_ADMIN\"}")"
say " factory contract id: $FACTORY_ID"
}

# Sets EFFECTIVE_FEE_ENABLED based on the dest's USDC trustline.
check_trustline() {
step "Checking fee dest has a USDC trustline"
EFFECTIVE_FEE_ENABLED="$FEE_ENABLED"
if [ "$DRY_RUN" -eq 1 ]; then
say " [dry-run] would check USDC trustline for $FEE_DEST"
return 0
fi
body="$(curl -s "$HORIZON_URL/accounts/$FEE_DEST")"
if printf '%s' "$body" | grep -q "$USDC_ISSUER"; then
say " USDC trustline present on $FEE_DEST"
Comment on lines +221 to +223

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Match the trustline by both asset code and issuer

Grepping the account JSON for $USDC_ISSUER can match unrelated fields or other assets from the same issuer, so fees may be enabled without a USDC trustline. Check asset_code == "USDC" and asset_issuer == $USDC_ISSUER on the same balances[] entry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy-mainnet.sh` around lines 221 - 223, The trustline check in the
deploy-mainnet script is too broad because it greps the account JSON for only
the issuer string, which can match unrelated fields or other assets. Update the
logic around the curl/printf/grep block in the deploy flow to inspect the same
balances[] entry for both asset_code equal to USDC and asset_issuer equal to
USDC_ISSUER before allowing fees to be enabled. Use the existing symbols
HORIZON_URL, FEE_DEST, and USDC_ISSUER to locate the check and make the match
specific to the USDC trustline.

else
say " WARNING: $FEE_DEST has no USDC trustline."
say " Without it the first issue() fails when transferring the fee."
printf 'Enable fees anyway? Type "yes" to enable, anything else defers (config written, disabled): ' >&2
read -r ans
if [ "$ans" != "yes" ]; then
EFFECTIVE_FEE_ENABLED="false"
say " Deferring: fees will be configured but left disabled."
fi
fi
}

configure_fees() {
step "Configuring factory fee (USDC)"
run stellar contract invoke \
--id "$FACTORY_ID" --source "$TMP_IDENTITY" --network "$NETWORK_NAME" --inclusion-fee "$INCLUSION_FEE" \
-- set_fee_config --token "$FEE_TOKEN" --dest "$FEE_DEST" --standard "$FEE_STANDARD"
say " fee_config set: token=$FEE_TOKEN dest=$FEE_DEST standard=$FEE_STANDARD"
run stellar contract invoke \
--id "$FACTORY_ID" --source "$TMP_IDENTITY" --network "$NETWORK_NAME" --inclusion-fee "$INCLUSION_FEE" \
-- set_fee_enabled --enabled "$EFFECTIVE_FEE_ENABLED"
say " fee_enabled set: $EFFECTIVE_FEE_ENABLED"
}

print_summary() {
step "Deploy summary — record these in docs/deployments/mainnet.md"
say " did-stellar-registry : $DID_ID"
say " vc-vault template : $VAULT_HASH"
say " vc-vault-factory : $FACTORY_ID"
say " fee token / dest : $FEE_TOKEN / $FEE_DEST"
say " fee standard / state : $FEE_STANDARD / $EFFECTIVE_FEE_ENABLED"
if [ "$DRY_RUN" -eq 1 ]; then
say ""
say " (dry-run — nothing was deployed; values above are placeholders)"
fi
}

derive_key() {
: "${DEPLOYER_ADDRESS:?set DEPLOYER_ADDRESS to the deployer G… address in .env}"
tmp="_acta_derive"
stellar keys rm "$tmp" --force >/dev/null 2>&1 || true
say "Paste your Freighter 12/24-word recovery phrase when prompted."
stellar keys add "$tmp" --seed-phrase >/dev/null
found=""
i=0
while [ "$i" -le 30 ]; do
addr="$(stellar keys public-key "$tmp" --hd-path "$i" 2>/dev/null)"
if [ "$addr" = "$DEPLOYER_ADDRESS" ]; then
found="$i"
break
fi
i=$((i + 1))
done
if [ -z "$found" ]; then
say "No hd-path 0..30 matched $DEPLOYER_ADDRESS."
say "That account may have been imported separately (not from this phrase)."
stellar keys rm "$tmp" --force >/dev/null 2>&1 || true
exit 1
fi
say "Matched $DEPLOYER_ADDRESS at hd-path $found. Secret key below:"
stellar keys secret "$tmp" --hd-path "$found"
stellar keys rm "$tmp" --force >/dev/null 2>&1 || true
say ""
say "Copy it into the env, then clear your scrollback:"
say " read -rs DEPLOYER_SECRET && export DEPLOYER_SECRET"
}

trap cleanup EXIT INT TERM

main() {
resolve_identity
print_params
bootstrap_network
confirm_gate
import_and_verify_identity
check_funding
build_all
deploy_did
upload_vault
deploy_factory
check_trustline
configure_fees
print_summary
}

# Dispatch.
if [ "$SUBCOMMAND" = "derive-key" ]; then
derive_key
exit 0
fi
main
Loading