Skip to content

feat: vault factory POC — single-tenant vc-vault + vc-vault-factory#52

Open
aguilar1x wants to merge 11 commits into
devfrom
feat/vault-factory-poc
Open

feat: vault factory POC — single-tenant vc-vault + vc-vault-factory#52
aguilar1x wants to merge 11 commits into
devfrom
feat/vault-factory-poc

Conversation

@aguilar1x
Copy link
Copy Markdown
Contributor

@aguilar1x aguilar1x commented May 17, 2026

Summary

  • Refactors vc-vault from multi-tenant to single-tenant: each holder gets their own deployed contract instance, owner: Address removed from all public functions, owner stored at construction
  • Adds vc-vault-factory contract: deploys vault instances deterministically via deploy_v2, registers them, and exposes is_vault for cross-contract verification
  • Adds deploy_sponsored: anyone can deploy a vault on behalf of an owner (deployer signs/pays, vault belongs to owner from creation)
  • Adds push / receive_push: production-ready cross-vault VC transfer — source vault calls destination's receive_push, which verifies the source is a legitimate factory vault before accepting the credential

Changes

contracts/vc-vault

  • New constructor: (vault_owner, contract_admin, did_uri, factory_address)
  • Removed: create_vault, create_sponsored_vault, sponsored-vault whitelist functions
  • Added: push(vc_id, dest_vault), receive_push(source_vault, vc_id, vc_data, issuer_did)
  • Storage: new VaultOwner and VaultFactory persistent keys; all keys flattened (owner no longer part of key)

contracts/vc-vault-factory

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

Warning

Rate limit exceeded

@aguilar1x has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 42 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34918867-5955-45b9-87b2-3635da0519f6

📥 Commits

Reviewing files that changed from the base of the PR and between e30b412 and fe78dab.

📒 Files selected for processing (23)
  • Cargo.toml
  • contracts/vc-vault-factory/Cargo.toml
  • contracts/vc-vault-factory/README.md
  • contracts/vc-vault-factory/src/contract.rs
  • contracts/vc-vault-factory/src/events.rs
  • contracts/vc-vault-factory/src/lib.rs
  • contracts/vc-vault-factory/src/storage.rs
  • contracts/vc-vault-factory/src/test.rs
  • contracts/vc-vault/src/contract.rs
  • contracts/vc-vault/src/error.rs
  • contracts/vc-vault/src/events.rs
  • contracts/vc-vault/src/interface.rs
  • contracts/vc-vault/src/storage/credential.rs
  • contracts/vc-vault/src/storage/issuer.rs
  • contracts/vc-vault/src/storage/mod.rs
  • contracts/vc-vault/src/storage/sponsor.rs
  • contracts/vc-vault/src/storage/ttl.rs
  • contracts/vc-vault/src/storage/vault.rs
  • contracts/vc-vault/src/test.rs
  • contracts/vc-vault/src/validator.rs
  • contracts/vc-vault/src/vault/credential.rs
  • contracts/vc-vault/src/vault/issuer.rs
  • contracts/vc-vault/src/vault/mod.rs
📝 Walkthrough

Walkthrough

This PR introduces a new vault factory contract for deterministic, single-tenant vault deployment and refactors the existing vc-vault contract from a multi-owner/multi-vault model to a single-vault-per-instance architecture. Vault ownership and operations are now fixed at contract initialization; issuance, revocation, and issuer management no longer require per-call owner parameters. Sponsored vaults and linked VCs have been removed.

Changes

New Vault Factory

Layer / File(s) Summary
Factory contract and storage
Cargo.toml, contracts/vc-vault-factory/Cargo.toml, contracts/vc-vault-factory/src/contract.rs, contracts/vc-vault-factory/src/events.rs, contracts/vc-vault-factory/src/storage.rs, contracts/vc-vault-factory/src/lib.rs
VaultFactory contract with deploy/deploy_sponsored/is_vault entrypoints; deterministic salt derivation via keccak256 hash of (user_salt, owner); persistent deployment tracking; factory initialization stores VaultInitMeta (vault hash and admin); event publishing for normal and sponsored deployments.
Factory integration tests
contracts/vc-vault-factory/src/test.rs
Vault registry validation, deterministic address derivation across owners/salts, event emission assertions, and end-to-end vault operations (issuer authorization, VC issuance, verification) for both normal and sponsored deployments.

VC Vault Single-Instance Refactoring

Layer / File(s) Summary
Storage schema redesign
contracts/vc-vault/src/storage/mod.rs
VcVaultDataKey variants simplified: remove leading Address from vault/issuer/VC keys (e.g., VaultAdmin/VaultDid/VaultRevoked become address-less; VaultIssuerIndex(Address, u32) → VaultIssuerIndex(u32); VaultVC(Address, String) → VaultVC(String)); sponsor storage variants removed.
Vault and credential storage APIs
contracts/vc-vault/src/storage/vault.rs, contracts/vc-vault/src/storage/credential.rs
Vault metadata (owner, admin, DID, revoked) now uses fixed keys without owner parameter; VC payload/index/parent/status storage refactored to key by vc_id only, dropping (owner, ...) tuples; index position mappings maintain O(1) append/remove semantics.
Issuer index storage APIs
contracts/vc-vault/src/storage/issuer.rs
Authorized and denied issuer indices converted to vault-global keys (VaultIssuerIndex(u32), VaultIssuerPosition(Address) instead of (Address, u32) and (Address, Address)); append_denied_issuer_to_index becomes no-op if issuer already present.
TTL extension and authentication
contracts/vc-vault/src/storage/ttl.rs, contracts/vc-vault/src/validator.rs
extend_vault_ttl/extend_vc_ttl/extend_vc_status_ttl signatures simplified to vault-scoped parameters; require_vault_initialized/require_vault_admin/require_vault_active/require_issuer_authorized drop owner argument and call vault-global storage helpers.
Events, errors, and interface
contracts/vc-vault/src/events.rs, contracts/vc-vault/src/error.rs, contracts/vc-vault/src/interface.rs
Event payloads simplified (VaultRevoked becomes empty; VaultAdminChanged/IssuerAuthorized/IssuerRevoked/VCIssued/VCRevoked remove owner); event publishers updated accordingly; error variants VaultAlreadyExists and NotAuthorizedSponsor removed; VcVaultTrait signatures updated to remove owner parameter from all entrypoints.
Credential and issuer operations
contracts/vc-vault/src/vault/credential.rs, contracts/vc-vault/src/vault/issuer.rs, contracts/vc-vault/src/vault/mod.rs
store_vc removes owner parameter, uses vc_id-only index; revoke_vc uses vault-scoped status keying; authorize_issuer/authorize_issuers/revoke_issuer drop owner and call vault-global issuer storage; push_vc removed from exports.
Contract constructor and entrypoints
contracts/vc-vault/src/contract.rs
Constructor signature changed to (vault_owner, contract_admin, did_uri); all entrypoints (set_vault_admin, authorize_issuers, issue, batch_issue, revoke, list_vc_ids, vc_count, get_vc, verify_vc, revoke_vault, list_authorized/denied_issuers, authorized/denied_issuer_count) updated to remove owner parameter; linked-VC and sponsored-vault entrypoints removed.

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly Related PRs

  • ACTA-Team/contracts-acta#33: Introduced batch_issue entrypoint; this PR refactors batch_issue signature and storage model to vault-scoped keying.
  • ACTA-Team/contracts-acta#45: Modularized vc-vault storage into separate files; this PR refactors those same storage modules to change key structure and drop owner dimensions.
  • ACTA-Team/contracts-acta#5: Added linked-VC support with parent tracking; this PR removes linked-VC entrypoints and refactors VCParent storage to vault-scoped keying.

Poem

🐰 A factory conjures vaults with salt and hash,
Each one deployed with owner set in flash!
No more the owner passed from call to call—
The vault remembers all, and holds it all. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: introducing a vault factory POC with single-tenant vault architecture, which is the primary objective of the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/vault-factory-poc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@aguilar1x aguilar1x self-assigned this May 17, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
contracts/vc-vault/src/storage/issuer.rs (1)

198-206: 💤 Low value

Consider adding a limit check for denied issuers.

append_denied_issuer_to_index lacks the MAX_ISSUERS_LIST check that append_issuer_to_index has at line 84. While the denied list is typically smaller, this allows unbounded storage growth if many issuers are denied over time.

Suggested fix
 pub fn append_denied_issuer_to_index(e: &Env, issuer: &Address) {
     if denied_issuer_index_contains(e, issuer) {
         return;
     }
     let count = read_denied_issuer_count(e);
+    if count >= MAX_ISSUERS_LIST {
+        panic_with_error!(e, ContractError::IssuerListTooLong);
+    }
     write_denied_issuer_at(e, count, issuer);
     write_denied_issuer_position(e, issuer, count);
     write_denied_issuer_count(e, count + 1);
 }
🤖 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 `@contracts/vc-vault/src/storage/issuer.rs` around lines 198 - 206,
append_denied_issuer_to_index allows unbounded growth; add the same
MAX_ISSUERS_LIST check used by append_issuer_to_index: read the current count
with read_denied_issuer_count(e) and if count >= MAX_ISSUERS_LIST, bail out
(return or revert as the contract pattern requires) before writing; keep the
existing denied_issuer_index_contains check and only perform
write_denied_issuer_at, write_denied_issuer_position, and
write_denied_issuer_count when the count is below MAX_ISSUERS_LIST.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@contracts/vc-vault-factory/src/test.rs`:
- Around line 163-180: The test function
test_deploy_and_deploy_sponsored_same_owner_same_salt_same_address is
inconsistent with its assertions: it declares the intent "same owner + same salt
+ same address" but creates owner2 and asserts the two derived addresses differ;
either restore the intended same-owner scenario by using the original owner for
the deploy_sponsored call (replace owner2 with owner and change addr_sponsored =
client.deploy_sponsored(&deployer, &owner, &did_uri, &salt) and
assert_eq!(addr_normal, addr_sponsored)), or rename the test and its comment to
reflect "different owner -> different address" and keep owner2 and assert_ne;
update the test name and any inline comment accordingly and ensure the
assertions match client.deploy and client.deploy_sponsored behavior.

---

Nitpick comments:
In `@contracts/vc-vault/src/storage/issuer.rs`:
- Around line 198-206: append_denied_issuer_to_index allows unbounded growth;
add the same MAX_ISSUERS_LIST check used by append_issuer_to_index: read the
current count with read_denied_issuer_count(e) and if count >= MAX_ISSUERS_LIST,
bail out (return or revert as the contract pattern requires) before writing;
keep the existing denied_issuer_index_contains check and only perform
write_denied_issuer_at, write_denied_issuer_position, and
write_denied_issuer_count when the count is below MAX_ISSUERS_LIST.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 838de906-b5f4-4047-ae12-335df3aaba17

📥 Commits

Reviewing files that changed from the base of the PR and between 0eb1600 and e30b412.

📒 Files selected for processing (22)
  • Cargo.toml
  • contracts/vc-vault-factory/Cargo.toml
  • contracts/vc-vault-factory/src/contract.rs
  • contracts/vc-vault-factory/src/events.rs
  • contracts/vc-vault-factory/src/lib.rs
  • contracts/vc-vault-factory/src/storage.rs
  • contracts/vc-vault-factory/src/test.rs
  • contracts/vc-vault/src/contract.rs
  • contracts/vc-vault/src/error.rs
  • contracts/vc-vault/src/events.rs
  • contracts/vc-vault/src/interface.rs
  • contracts/vc-vault/src/storage/credential.rs
  • contracts/vc-vault/src/storage/issuer.rs
  • contracts/vc-vault/src/storage/mod.rs
  • contracts/vc-vault/src/storage/sponsor.rs
  • contracts/vc-vault/src/storage/ttl.rs
  • contracts/vc-vault/src/storage/vault.rs
  • contracts/vc-vault/src/test.rs
  • contracts/vc-vault/src/validator.rs
  • contracts/vc-vault/src/vault/credential.rs
  • contracts/vc-vault/src/vault/issuer.rs
  • contracts/vc-vault/src/vault/mod.rs
💤 Files with no reviewable changes (2)
  • contracts/vc-vault/src/storage/sponsor.rs
  • contracts/vc-vault/src/error.rs

Comment thread contracts/vc-vault-factory/src/test.rs
aguilar1x added 11 commits May 16, 2026 21:13
Each vault is now its own contract, so no key needs to be scoped by
owner address. VaultOwner is written once at construction and read
from instance storage. sponsor.rs removed — sponsored vault deferred.
All require_* helpers now read vault state from instance storage
instead of taking owner: &Address. require_vault_admin reads
VaultAdmin directly; require_vault_active reads VaultRevoked.
store_vc, store_vc_with_fee, revoke_vc, authorize_issuer,
authorize_issuers, revoke_issuer and is_authorized no longer take
owner: &Address. push_vc removed — cross-contract push is deferred.
… entrypoints

__constructor(vault_owner, contract_admin, did_uri) replaces create_vault.
All public functions drop owner: Address. Removed: create_vault,
create_sponsored_vault, sponsor management, push, issue_linked,
get_vc_parent. Dead error codes VaultAlreadyExists and
NotAuthorizedSponsor removed.
Constructor registers via env.register(VcVaultContract, (owner, admin, did_uri)).
All client calls drop the owner first arg. Removed tests for create_vault,
sponsored vault, push, issue_linked and get_vc_parent. 93 tests passing.
Factory deploys single-tenant vc-vault instances via deploy_v2.
Salt = keccak256(user_salt || owner_bytes) prevents frontrunning and
makes vault addresses deterministic per (owner, salt) pair.

deploy(owner, did_uri, salt) — owner signs their own vault.
deploy_sponsored(deployer, owner, did_uri, salt) — any third party
  signs and pays; vault ownership goes to owner from creation.

is_vault(address) queries the persistent registry of deployed vaults.
10 tests passing including full integration and sponsored flow.
Adds VaultFactory persistent key to store the factory that deployed each
vault, enabling cross-vault source verification in receive_push.
push() moves a VC from the source vault to a destination vault via a
cross-contract call. receive_push() verifies the source is a legitimate
factory-deployed vault before accepting the credential and sets itself
as the new issuance authority.
@aguilar1x aguilar1x force-pushed the feat/vault-factory-poc branch from c3c58f4 to fe78dab Compare May 17, 2026 03:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: vc-vault-factory POC — single-tenant vaults deployed per holder

1 participant