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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Soroban smart contracts for the ACTA identity and Verifiable Credential infrastructure on Stellar.

**Latest release:** [vc-vault v0.3.0](https://github.com/ACTA-Team/contracts-acta/releases/tag/vc-vault-v0.3.0)
**Latest release:** [vc-vault v0.4.0](https://github.com/ACTA-Team/contracts-acta/releases/tag/vc-vault-v0.4.0)

---

Expand Down Expand Up @@ -30,7 +30,7 @@ Per-holder vault for Verifiable Credentials on Stellar. Manages VC storage, issu

| Category | Functions |
|---|---|
| Admin | `nominate_admin`, `accept_contract_admin`, `upgrade`, `version`, `fee_*` |
| Admin | `nominate_admin`, `accept_contract_admin`, `version` |
| Vault | `create_vault`, `create_sponsored_vault`, `set_vault_admin`, `authorize_issuer`, `authorize_issuers`, `revoke_issuer`, `revoke_vault`, `list_authorized_issuers`, `list_denied_issuers`, `authorized_issuer_count`, `denied_issuer_count` |
| Credentials | `issue`, `batch_issue`, `issue_linked`, `revoke`, `verify_vc`, `get_vc`, `list_vc_ids`, `vc_count`, `push` |

Expand Down
2 changes: 1 addition & 1 deletion contracts/did-stellar-registry/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "did-stellar-registry"
version = "0.1.0"
version = "0.2.0"
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion contracts/vc-vault/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "vc-vault-contract"
version = "0.3.0"
version = "0.4.0"
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
Expand Down
1 change: 0 additions & 1 deletion contracts/vc-vault/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ The contract admin is set atomically at deploy time via Soroban's `__constructor
|---|---|---|
| `nominate_admin(new_admin)` | `contract_admin` | Propose a new contract admin. Must be accepted by the nominee. |
| `accept_contract_admin()` | pending nominee | Complete the two-step admin transfer. Fails with `NoPendingAdmin` if no nomination exists. |
| `upgrade(new_wasm_hash)` | `contract_admin` | Replace the contract WASM. Irreversible per-invocation. |
| `version()` | none | Returns the crate version string (from `CARGO_PKG_VERSION`). |

### Fee configuration
Expand Down
9 changes: 1 addition & 8 deletions contracts/vc-vault/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::storage;
use crate::validator::*;
use crate::vault;
use soroban_sdk::{
contract, contractimpl, contractmeta, panic_with_error, symbol_short, Address, BytesN, Env,
contract, contractimpl, contractmeta, panic_with_error, symbol_short, Address, Env,
IntoVal, String, Vec,
};

Expand Down Expand Up @@ -63,13 +63,6 @@ impl VcVaultTrait for VcVaultContract {
events::admin_transferred(&e, &old_admin, &pending);
}

fn upgrade(e: Env, new_wasm_hash: BytesN<32>) {
require_contract_admin(&e);
storage::extend_instance_ttl(&e);
events::contract_upgraded(&e, &new_wasm_hash);
e.deployer().update_current_contract_wasm(new_wasm_hash);
}

fn version(e: Env) -> String {
String::from_str(&e, VERSION)
}
Expand Down
14 changes: 1 addition & 13 deletions contracts/vc-vault/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Contract events. Published on key state transitions for on-chain observability.

use soroban_sdk::{contractevent, Address, BytesN, Env, String};
use soroban_sdk::{contractevent, Address, Env, String};

// --- Vault lifecycle ---

Expand Down Expand Up @@ -75,11 +75,6 @@ pub struct AdminTransferred {
pub new_admin: Address,
}

#[contractevent]
pub struct ContractUpgraded {
pub new_wasm_hash: BytesN<32>,
}

// --- Publishers ---

pub fn vault_created(e: &Env, owner: &Address, did_uri: &String) {
Expand Down Expand Up @@ -169,10 +164,3 @@ pub fn admin_transferred(e: &Env, old_admin: &Address, new_admin: &Address) {
}
.publish(e);
}

pub fn contract_upgraded(e: &Env, new_wasm_hash: &BytesN<32>) {
ContractUpgraded {
new_wasm_hash: new_wasm_hash.clone(),
}
.publish(e);
}
3 changes: 1 addition & 2 deletions contracts/vc-vault/src/interface.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Public contract interface. All exported functions are defined here.

use soroban_sdk::{Address, BytesN, Env, String, Vec};
use soroban_sdk::{Address, Env, String, Vec};

use crate::types::{VCStatus, VerifiableCredential};

Expand All @@ -9,7 +9,6 @@ pub trait VcVaultTrait {
// --- Admin ---
fn nominate_admin(e: Env, new_admin: Address);
fn accept_contract_admin(e: Env);
fn upgrade(e: Env, new_wasm_hash: BytesN<32>);
fn version(e: Env) -> String;

// --- Vault management ---
Expand Down
30 changes: 28 additions & 2 deletions docs/deployments/testnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,37 @@ RPC: `https://soroban-testnet.stellar.org:443`
| Version | Contract ID | Date | Notes |
|---|---|---|---|
| 0.1.0 | `CB7ATU7SF5QUKJMSULJDJVWJZVDXC23HTZX6NFUDTSFPVT6MA575NNZJ` | 2026-05-06 | Tranche 1 initial deploy |
| 0.2.0 | `CBUNQ3GX3ZQ4MF64H7JCYZMXLGOS47VPIQQS7NCR6V3KX6YP7O72L5QF` | 2026-06-22 | Allow key reuse across verification relationships; reject duplicate service `id_suffix`; admin-role spec; clippy. WASM hash `6835c23806075288284c89e133b271a3ac9c61977fbe49121f92c5431f29a0e7`. |

## vc-vault
## vc-vault-factory

| Version | Contract ID | Date | Notes |
|---|---|---|---|
| 0.1.0 | `CC3SQ7UTAQQDQF6PUQMQIGK3BMPB22OKMHE5Y5XELEX3JFAKC72SQOAM` | 2026-05-06 | Tranche 1 initial deploy |
| 0.1.0 | `CDRFQRIP4FA3WMPWCSAM3XEY6EM6EGKRYZRSCSVZ5NHCF6AGEVR2XEPQ` | 2026-06-22 | First release. Deploys single-tenant vaults (deterministic address from `keccak(salt‖XDR(owner))`), centralizes fee config, `is_vault` registry. Constructed with vault template WASM hash `2bd0323a98acb8469606808368da6c79824f2dd8391494b94ddbeb3d22c1a957`. WASM hash `f94a77905d87f9a195ea837414b4995c7d3d66bed0e287481710246bc1d5bdcd`. |

### Fee configuration (factory `CDRFQRIP…`, set 2026-06-22)

| Field | Value |
|---|---|
| enabled | `true` |
| token | `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC` (native XLM SAC) |
| dest | `GAP7AUGY2Q2NKIHJ2XMZAGVHD7KDF2UAUEPXE5HRXL7BOXRWDHXHG6IY` |
| standard | `50000000` stroops = **5 XLM ≈ 1 USD** @ ~$0.20/XLM |

Charged per credential issued; the issuer pays. The amount is fixed on-chain (does
not track the USD price). Adjust with `set_fee_standard` / `set_fee_custom`, or
`set_fee_enabled false` to disable.

## vc-vault

Since v0.4.0 the vault is **single-tenant** and is no longer deployed standalone —
individual vaults are instantiated by `vc-vault-factory.deploy(...)`. The release
publishes the vault as an installed WASM **template**; the factory is constructed
with its hash.

| Version | Contract ID / WASM hash | Date | Notes |
|---|---|---|---|
| 0.1.0 | `CC3SQ7UTAQQDQF6PUQMQIGK3BMPB22OKMHE5Y5XELEX3JFAKC72SQOAM` | 2026-05-06 | Tranche 1 initial deploy (multi-tenant). |
| 0.2.0 | `CBXC6LXBY5FGEG46VZ4AJ2AH2EJBINBA7BMILIEO4EJYI6ZTY7K7J5D5` | 2026-05-07 | SOW D2: O(1) index + pagination + migrate + batch_issue. WASM hash `c8da61dd3dd46b2810a743d50a388c09a00f0b7e8e2df7ceb5a71c8ce5dc4dd8`. |
| 0.3.0 | `CATL4IDH7XXPDC2UHSEX2GP45PPBVDFSKUDTKCSQICDOJVDLYNKISXFH` | 2026-05-14 | Refactoring: constructor, types rename, push_vc extraction, input caps, event emissions, O(1) issuer storage, tombstone TTL fix. WASM hash `775a141520de56fb4b1ebeb55d63e49fadf03f467ea8444cddb2caed2756ca8c`. |
| 0.4.0 | WASM hash `2bd0323a98acb8469606808368da6c79824f2dd8391494b94ddbeb3d22c1a957` (template; deployed via factory) | 2026-06-22 | Single-tenant rearchitecture + open issuance (deny-by-exception) + fees moved to factory; `upgrade` entrypoint removed (immutable). Installed as a template; instances created by `vc-vault-factory` `CDRFQRIP4FA3WMPWCSAM3XEY6EM6EGKRYZRSCSVZ5NHCF6AGEVR2XEPQ`. |
18 changes: 16 additions & 2 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,30 @@ build_did_registry() {
echo "Built: target/wasm32v1-none/release/did_stellar_registry.optimized.wasm"
}

# vc-vault-factory already declares cdylib so a standard cargo build suffices.
build_factory() {
cargo build \
-p vc-vault-factory-contract \
--target wasm32v1-none \
--release
stellar contract optimize \
--wasm target/wasm32v1-none/release/vc_vault_factory_contract.wasm
echo "Built: target/wasm32v1-none/release/vc_vault_factory_contract.optimized.wasm"
}

case "$PACKAGE" in
vc-vault)
build_vc_vault ;;
did-stellar-registry)
build_did_registry ;;
vc-vault-factory)
build_factory ;;
all)
build_vc_vault
build_did_registry ;;
build_did_registry
build_factory ;;
*)
echo "Unknown package: $PACKAGE" >&2
echo "Usage: $0 [vc-vault|did-stellar-registry|all]" >&2
echo "Usage: $0 [vc-vault|did-stellar-registry|vc-vault-factory|all]" >&2
exit 1 ;;
esac
109 changes: 65 additions & 44 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,74 +6,95 @@ set -eu
# Usage:
# ./scripts/deploy.sh <package> <network> <source-account>
#
# package: vc-vault | did-stellar-registry
# package: did-stellar-registry | vc-vault | vc-vault-factory
# network: testnet | mainnet
# source-account: stellar keys alias (e.g. acta_deployer)
#
# Deployment model (since vc-vault v0.4.0):
# - did-stellar-registry: deployed standalone (constructor: --admin).
# - vc-vault: single-tenant TEMPLATE — not deployed standalone. Its WASM is
# installed (uploaded) and the resulting hash is handed to the factory.
# Individual vaults are created by calling vc-vault-factory.deploy(...).
# - vc-vault-factory: deployed with the vault template hash + a contract admin.
#
# Examples:
# ./scripts/deploy.sh did-stellar-registry testnet acta_deployer
# ./scripts/deploy.sh vc-vault testnet acta_deployer
# ./scripts/deploy.sh vc-vault testnet acta_deployer # install template
# ./scripts/deploy.sh vc-vault-factory testnet acta_deployer # installs vault + deploys factory
#
# Env overrides:
# DID_ADMIN / FACTORY_ADMIN — admin address (defaults to the source address)
# VAULT_HASH — reuse a pre-installed vc-vault template hash
#
# Prerequisites:
# - stellar-cli installed and configured
# - Network already added:
# stellar config network add testnet \
# --rpc-url https://soroban-testnet.stellar.org:443 \
# --network-passphrase "Test SDF Network ; September 2015"
# - Source account key generated:
# stellar keys generate acta_deployer --network <network>
# - WASM built:
# ./scripts/build.sh <package>
# Prerequisites: stellar-cli configured, network added, source key funded, and
# ./scripts/build.sh <package> run first.

PACKAGE=${1:-}
NETWORK=${2:-}
SOURCE=${3:-}

if [ -z "$PACKAGE" ] || [ -z "$NETWORK" ] || [ -z "$SOURCE" ]; then
echo "Usage: $0 <package> <network> <source-account>" >&2
echo " package: vc-vault | did-stellar-registry" >&2
echo " package: did-stellar-registry | vc-vault | vc-vault-factory" >&2
echo " network: testnet | mainnet" >&2
exit 1
fi

REL="target/wasm32v1-none/release"
VAULT_WASM="$REL/vc_vault_contract.optimized.wasm"
FACTORY_WASM="$REL/vc_vault_factory_contract.optimized.wasm"
DID_WASM="$REL/did_stellar_registry.optimized.wasm"

require_wasm() {
if [ ! -f "$1" ]; then
echo "WASM not found: $1" >&2
echo "Run: ./scripts/build.sh $2" >&2
exit 1
fi
}

case "$PACKAGE" in
vc-vault)
WASM="target/wasm32v1-none/release/vc_vault_contract.optimized.wasm"
ADMIN=${VC_ADMIN:-$(stellar keys address "$SOURCE")}
CONSTRUCTOR_ARGS="-- --contract_admin $ADMIN"
;;
did-stellar-registry)
WASM="target/wasm32v1-none/release/did_stellar_registry.optimized.wasm"
# Requires an admin address at construction.
# Defaults to the deployer address; override by setting DID_ADMIN env var.
require_wasm "$DID_WASM" did-stellar-registry
ADMIN=${DID_ADMIN:-$(stellar keys address "$SOURCE")}
CONSTRUCTOR_ARGS="-- --admin $ADMIN"
echo "Deploying did-stellar-registry to $NETWORK (admin $ADMIN)..."
CONTRACT_ID=$(stellar contract deploy \
--wasm "$DID_WASM" --source "$SOURCE" --network "$NETWORK" \
-- --admin "$ADMIN")
echo ""
echo "Contract ID: $CONTRACT_ID"
;;

vc-vault)
require_wasm "$VAULT_WASM" vc-vault
echo "Installing vc-vault template WASM on $NETWORK..."
HASH=$(stellar contract upload \
--wasm "$VAULT_WASM" --source "$SOURCE" --network "$NETWORK")
echo ""
echo "vc-vault template WASM hash: $HASH"
echo "Pass this to the factory (VAULT_HASH=$HASH ./scripts/deploy.sh vc-vault-factory ...)."
;;

vc-vault-factory)
require_wasm "$VAULT_WASM" vc-vault
require_wasm "$FACTORY_WASM" vc-vault-factory
ADMIN=${FACTORY_ADMIN:-$(stellar keys address "$SOURCE")}
VAULT_HASH=${VAULT_HASH:-$(stellar contract upload \
--wasm "$VAULT_WASM" --source "$SOURCE" --network "$NETWORK")}
echo "Deploying vc-vault-factory to $NETWORK (admin $ADMIN, vault_hash $VAULT_HASH)..."
CONTRACT_ID=$(stellar contract deploy \
--wasm "$FACTORY_WASM" --source "$SOURCE" --network "$NETWORK" \
-- --vault_init_meta "{\"vault_hash\":\"$VAULT_HASH\",\"contract_admin\":\"$ADMIN\"}")
echo ""
echo "Factory Contract ID: $CONTRACT_ID"
echo "Vault template hash: $VAULT_HASH"
;;

*)
echo "Unknown package: $PACKAGE" >&2
echo " package: vc-vault | did-stellar-registry" >&2
echo " package: did-stellar-registry | vc-vault | vc-vault-factory" >&2
exit 1 ;;
esac

if [ ! -f "$WASM" ]; then
echo "WASM not found: $WASM" >&2
echo "Run: ./scripts/build.sh $PACKAGE" >&2
exit 1
fi

echo "Deploying $PACKAGE to $NETWORK..."
echo " WASM: $WASM"
echo " Source: $SOURCE"

CONTRACT_ID=$(stellar contract deploy \
--wasm "$WASM" \
--source "$SOURCE" \
--network "$NETWORK" \
$CONSTRUCTOR_ARGS)

echo ""
echo "Contract ID: $CONTRACT_ID"
echo ""
echo "Add this entry to docs/deployments/$NETWORK.md:"
echo ""
echo "| $PACKAGE | $(date +%Y-%m-%d) | \`$CONTRACT_ID\` | $NETWORK |"
echo "Record the result in docs/deployments/$NETWORK.md"
Loading