Contracts

Contracts Reference

The public interface of ACTA's Soroban contracts, for teams integrating at the contract level (Rust, CLI, or custom tooling) without going through the ACTA API. Source lives in the contracts-acta repository; error codes are on and the did:stellar registry interface is documented in the .

Overview

ContractVersionRole
vc-vault0.4.0Single-tenant credential vault, one per owner, instantiated by the factory from a template WASM
vc-vault-factory0.1.0One per network; deploys vaults deterministically and quotes the issuance fee
did-stellar-registry0.2.0Issuer DID records (see )

vc-vault

Each vault is constructed by the factory with (vault_owner, contract_admin, did_uri, factory_address). The vault admin starts as the owner.

Vault roles

RoleCan call
contract_adminnominate_admin; a nominee calls accept_contract_admin (no upgrade or fee powers)
Vault admin (owner by default)set_vault_admin, deny_issuer, allow_issuer, revoke_vault, push
vault_ownerset_vault_did, revoke
Any non-denied issuerissue, batch_issue

Vault functions

FunctionAuthNotes
version() -> StringNoneReturns the crate version, e.g. "0.4.0"
vault_owner() -> AddressNone
vault_did() -> Option<String>None
set_vault_did(did_uri)Vault ownerdid_uri up to 256 bytes
set_vault_admin(new_admin)Vault admin
deny_issuer(issuer_addr)Vault adminNo-op if already denied; list capped at 1,000
allow_issuer(issuer_addr)Vault adminNo-op if absent
revoke_vault()Vault adminIrreversible; blocks all writes
issue(vc_id, vc_data, vault_contract, issuer_addr, issuer_did) -> StringIssuerCharges the factory-quoted fee from the issuer; duplicate vc_id fails
batch_issue(issuer_addr, vault_contract, issuer_did, vcs) -> Vec<String>Issuervcs = list of (vc_id, vc_data), 1 to 5 entries; single fee transfer of unit x n
revoke(vc_id, date)Vault ownerCredential must be valid; status becomes Revoked(date)
push(vc_id, dest_vault)Vault adminMoves a valid credential to another factory-deployed vault with the same owner, then deletes it locally
receive_push(source_vault, source_owner, vc_id, vc_data, issuer_did)Source vaultValidates the source via factory is_vault and the same-owner rule
list_vc_ids(offset, limit) -> Vec<String>Nonelimit up to 200
vc_count() -> u32None
get_vc(vc_id) -> Option<VerifiableCredential>NoneReturns { id, data, issuance_contract, issuer_did }; data is the encrypted payload
verify_vc(vc_id) -> VCStatusNoneValid, Invalid, or Revoked(date). Status only: it does not prove who issued; check issuer_did for that
list_denied_issuers(offset, limit) -> Vec<Address>Nonelimit up to 200
denied_issuer_count() -> u32None
nominate_admin(new_admin) / accept_contract_admin()Admin / nomineeTwo-step contract-admin handover

Vault limits

LimitValue
vc_idmax 64 bytes
vc_datamax 10,000 bytes
did_uri / issuer_didmax 256 bytes
datemax 64 bytes
Batch sizemax 5 credentials
List page sizemax 200
Denied issuersmax 1,000

Vault events

ContractInitialized, VaultCreated, AdminNominated, AdminTransferred, VaultAdminChanged, VaultDidChanged, IssuerDenied, IssuerAllowed, VaultRevoked, VCIssued (per credential, also emitted on batch and on receive_push), VCRevoked, VCPushed.

vc-vault-factory

Constructed once per network with VaultInitMeta { vault_hash, contract_admin }. The template hash has no setter: shipping new vault code means deploying a new factory.

Factory functions

FunctionAuthNotes
deploy(owner, did_uri, user_salt) -> AddressOwnerDeploys the owner's vault; emits VaultDeployed
deploy_sponsored(deployer, owner, did_uri, user_salt) -> AddressDeployerSponsor pays and signs; vault belongs to owner; emits SponsoredVaultDeployed
is_vault(vault_address) -> boolNoneTrue iff deployed by this factory
quote_fee(issuer) -> FeeQuoteNone{ enabled, amount, token, dest }; per-issuer custom fee (with optional expiry) wins over the standard fee
set_fee_config(token, dest, standard)Admin
set_fee_enabled(enabled)AdminEnabling requires token + dest + standard configured
set_fee_standard(amount) / set_min_fee(amount)AdminAmounts validated non-negative, above the min fee, and at most 10^18
set_fee_custom(issuer, amount, expires_at?) / remove_fee_custom(issuer)AdminExpiry must be in the future
nominate_admin / accept_admin / get_adminAdmin / nominee / noneTwo-step handover

Deterministic addresses

The vault address is derived before deployment, with no lookup table:

text
deploy_salt = keccak256(user_salt (32 bytes) || XDR(ScAddress(owner)))

The owner is encoded as canonical ScAddress XDR (not the G... text form). The same (owner, user_salt) always yields the same address; the default salt is 32 zero bytes (one canonical vault per owner). Mixing the owner into the salt prevents cross-owner collisions and front-running.

Deployed contracts

Mainnet (passphrase Public Global Stellar Network ; September 2015)

ContractID / hash
vc-vault-factoryCCWNZ6UMUXCDOVP2TWOPVLI4KP4VY4YF7VKPN6XLYVHNFAT24NDB33CX
vc-vault template WASM2bd0323a98acb8469606808368da6c79824f2dd8391494b94ddbeb3d22c1a957
Fee configEnabled; 1 USDC per credential (USDC SAC CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75)

Testnet (passphrase Test SDF Network ; September 2015)

ContractID / hash
vc-vault-factoryCDRFQRIP4FA3WMPWCSAM3XEY6EM6EGKRYZRSCSVZ5NHCF6AGEVR2XEPQ
vc-vault template WASM2bd0323a98acb8469606808368da6c79824f2dd8391494b94ddbeb3d22c1a957
Fee configEnabled; 5 XLM per credential (native XLM SAC)

The vc-vault contract has no standalone contract ID on either network: it exists only as per-owner instances deployed by the factory.

Immutability & storage

  • No upgrade entrypoints: neither the vault (since 0.4.0) nor the factory can swap their code.
  • Vault credential data and indexes live in persistent Soroban storage; instance storage holds admin state. TTLs are extended on both reads and writes (threshold ~30 days, bump ~180 days at 5-second ledgers).
  • Contracts are built for the wasm32v1-none target and optimized with the Stellar CLI.