Skip to content

Conversation

CoveMB
Copy link
Contributor

@CoveMB CoveMB commented Sep 25, 2025

Fixes #675

Add ability for the Contract model to store metadata
Use this metadata to store security contact

@CoveMB CoveMB requested review from a team as code owners September 25, 2025 21:08
Copy link
Contributor

coderabbitai bot commented Sep 25, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Replaces the security contact tag with a generalized contract metadata system across Stellar core, printing, and info-setting. Updates interfaces, builder APIs, and rendering to emit contractmeta!(key="...", val="...") entries. Tests and snapshots are adjusted accordingly. UI tooltip text is updated. Adds a patch changeset for @openzeppelin/wizard-stellar.

Changes

Cohort / File(s) Summary
Contract metadata model and API
packages/core/stellar/src/contract.ts, packages/core/stellar/src/set-info.ts, packages/core/stellar/src/print.ts
Adds Contract.metadata: Map<string,string>; removes securityContact. Introduces ContractBuilder.addContractMetadata({...}
Tests and snapshots
packages/core/stellar/src/contract.test.ts, packages/core/stellar/src/contract.test.ts.md, packages/core/stellar/src/fungible.compile.test.ts
Tests switch from addSecurityTag to addContractMetadata, including single and multiple metadata cases. Snapshots updated to include no_std, use soroban_sdk::contractmeta, and contractmeta!(...) entries. Adds a compilation test variant including security contact metadata.
UI copy
packages/ui/src/stellar/InfoSection.svelte
Updates HelpTooltip text for Security Contact to describe adding a contact point to generated contract comments.
Release metadata
.changeset/silent-bags-repair.md
Adds a patch bump for @openzeppelin/wizard-stellar noting “Add contract metadata on contract model.”

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as UI (Wizard)
  participant Core as setInfo(…)
  participant Builder as ContractBuilder
  participant Printer as printContract

  User->>UI: Enter security contact (email/URL)
  UI->>Core: setInfo(builder, { securityContact, license })
  Core->>Builder: addContractMetadata({ key: "contact", value: securityContact })
  Core->>Builder: license = license (optional)

  note over Builder: Builder.metadata updated (Map<key,value>)

  UI->>Printer: printContract(contract)
  Printer->>Printer: printMetadata(contract.metadata)
  Printer-->>UI: contract source with contractmeta!(key="contact", val="...")

  note right of Printer: No securityContact tag path\nMetadata drives emission
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • ericglau
  • ernestognw

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title succinctly captures the primary change of migrating the security contact into the contract metadata for Stellar without extraneous details or file references. It directly relates to the main modifications and clearly communicates the intent to teammates reviewing the history.
Linked Issues Check ✅ Passed The changes implement storing the security contact in the contract metadata using the soroban_sdk contractmeta macro, remove the old comment‐only approach, and ensure that metadata is emitted in the compiled contract with an appropriate key, thereby satisfying the objectives of issue #675.
Out of Scope Changes Check ✅ Passed All modifications relate to migrating the security contact to metadata, updating related tests, code generation, and UI tooltip, with the version bump changeset being standard release housekeeping rather than an unrelated feature.
Description Check ✅ Passed The pull request description directly references fixing issue #675 and outlines the addition of metadata storage for the Contract model as well as using that metadata for the security contact, which aligns with the changes in the code.

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.

Copy link
Contributor

@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: 2

🧹 Nitpick comments (2)
packages/core/stellar/src/set-info.ts (1)

12-16: Consider a more explicit metadata key and trim input

Key 'contact' is generic. 'security-contact' reads clearer and avoids collisions; trimming prevents empty/whitespace-only entries.

Apply this diff:

-export function setInfo(c: ContractBuilder, { securityContact, license }: Info): void {
-  if (securityContact) c.addContractMetadata({ key: 'contact', value: securityContact });
+export function setInfo(c: ContractBuilder, { securityContact, license }: Info): void {
+  const sc = securityContact?.trim();
+  if (sc) c.addContractMetadata({ key: 'security-contact', value: sc });

If you adopt this, update tests/snapshots and any UI copy that shows the key.

packages/core/stellar/src/contract.ts (1)

270-274: Harden addContractMetadata against empty keys/values

Guard against empty keys and normalize values to reduce surprises.

Apply this diff:

-  addContractMetadata(metadata: { key: string; value: string }[] | { key: string; value: string }) {
-    (Array.isArray(metadata) ? metadata : [metadata]).forEach(({ key, value }) => this.metadata.set(key, value));
-
-    if (this.metadata.size > 0) this.addUseClause('soroban_sdk', 'contractmeta');
-  }
+  addContractMetadata(metadata: { key: string; value: string }[] | { key: string; value: string }) {
+    const items = Array.isArray(metadata) ? metadata : [metadata];
+    for (const { key, value } of items) {
+      const k = key?.trim();
+      if (!k) continue;
+      this.metadata.set(k, (value ?? '').toString());
+    }
+    if (this.metadata.size > 0) this.addUseClause('soroban_sdk', 'contractmeta');
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a90806 and 0091f40.

⛔ Files ignored due to path filters (1)
  • packages/core/stellar/src/contract.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • .changeset/silent-bags-repair.md (1 hunks)
  • packages/core/stellar/src/contract.test.ts (1 hunks)
  • packages/core/stellar/src/contract.test.ts.md (1 hunks)
  • packages/core/stellar/src/contract.ts (3 hunks)
  • packages/core/stellar/src/fungible.compile.test.ts (1 hunks)
  • packages/core/stellar/src/print.ts (2 hunks)
  • packages/core/stellar/src/set-info.ts (1 hunks)
  • packages/ui/src/stellar/InfoSection.svelte (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-15T22:49:25.653Z
Learnt from: ernestognw
PR: OpenZeppelin/contracts-wizard#609
File: .changeset/sour-hats-grow.md:2-6
Timestamp: 2025-08-15T22:49:25.653Z
Learning: In OpenZeppelin contracts-wizard, breaking changes that have concrete migration paths (like dependency migrations from Community Contracts to OpenZeppelin Contracts) can be handled as minor version bumps instead of major bumps, per maintainer ernestognw's versioning policy.

Applied to files:

  • .changeset/silent-bags-repair.md
🧬 Code graph analysis (4)
packages/core/stellar/src/contract.test.ts (2)
packages/core/stellar/src/contract.ts (1)
  • ContractBuilder (74-275)
packages/core/stellar/src/print.ts (1)
  • printContract (20-40)
packages/core/stellar/src/fungible.compile.test.ts (2)
packages/core/stellar/src/utils/compile-test.ts (1)
  • runRustCompilationTest (40-69)
packages/core/stellar/src/fungible.ts (1)
  • buildFungible (57-88)
packages/core/stellar/src/set-info.ts (1)
packages/core/stellar/src/contract.ts (1)
  • ContractBuilder (74-275)
packages/core/stellar/src/print.ts (1)
packages/core/stellar/src/contract.ts (1)
  • Contract (3-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build (solidity, default)
  • GitHub Check: build (stellar, compile)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (10)
.changeset/silent-bags-repair.md (1)

1-5: Changeset LGTM

Patch bump and summary look appropriate for the scope.

packages/core/stellar/src/fungible.compile.test.ts (1)

293-313: Good coverage for metadata path

Test adds a realistic opts.info.securityContact scenario for compilation.

packages/core/stellar/src/print.ts (1)

31-31: Correct placement of metadata emission

Printing metadata immediately after use clauses is correct (macro is imported before use).

packages/core/stellar/src/contract.test.ts (2)

92-96: LGTM

Single metadata case covered.


98-106: LGTM

Multi-metadata and composition with docs covered.

Optionally add a snapshot for escaping to guard against regressions introduced by special chars in metadata. Example test to add:

test('contract metadata escaping', t => {
  const Foo = new ContractBuilder('Foo');
  Foo.addContractMetadata([
    { key: 'a"b', value: 'c\\d' },
    { key: 'line', value: 'x\ny' },
  ]);
  t.snapshot(printContract(Foo));
});
packages/core/stellar/src/contract.test.ts.md (3)

158-173: Snapshot correctness

Macro import precedes contractmeta! and formatting looks consistent.


174-190: Snapshot correctness

Multiple metadata entries emitted in declared order; import present.


191-209: Snapshot correctness with documentation

Docs and metadata combine cleanly; ordering is sensible.

packages/core/stellar/src/contract.ts (2)

4-4: Interface extension LGTM

Contract.metadata added with appropriate type.


77-77: Builder initialization LGTM

Map initialized eagerly; fine.

Comment on lines +370 to 372
function printMetadata(contract: Contract) {
return Array.from(contract.metadata.entries()).map(([key, value]) => `contractmeta!(key="${key}", val="${value}");`);
}
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 25, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Escape metadata strings to avoid invalid Rust when values contain quotes/backslashes/newlines

Unescaped user-supplied key/value can break codegen (e.g., value with a double quote).

Apply this diff:

-function printMetadata(contract: Contract) {
-  return Array.from(contract.metadata.entries()).map(([key, value]) => `contractmeta!(key="${key}", val="${value}");`);
-}
+function printMetadata(contract: Contract) {
+  const esc = (s: string) =>
+    s
+      .replace(/\\/g, '\\\\')
+      .replace(/"/g, '\\"')
+      .replace(/\n/g, '\\n')
+      .replace(/\r/g, '\\r')
+      .replace(/\t/g, '\\t');
+  return Array.from(contract.metadata.entries()).map(
+    ([key, value]) => `contractmeta!(key="${esc(key)}", val="${esc(value)}");`,
+  );
+}

Consider adding a unit test that includes quotes/newlines to lock this in (see suggestion on test file).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function printMetadata(contract: Contract) {
return Array.from(contract.metadata.entries()).map(([key, value]) => `contractmeta!(key="${key}", val="${value}");`);
}
function printMetadata(contract: Contract) {
const esc = (s: string) =>
s
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t');
return Array.from(contract.metadata.entries()).map(
([key, value]) => `contractmeta!(key="${esc(key)}", val="${esc(value)}");`,
);
}
🤖 Prompt for AI Agents
In packages/core/stellar/src/print.ts around lines 370 to 372, the metadata
key/value are emitted raw and not escaped, which can produce invalid Rust code
when values contain quotes, backslashes, newlines, etc.; update printMetadata to
escape metadata strings for Rust string literals (at minimum replace backslash
with \\ and double quote with \", convert newlines to \n and tabs to \t, and
handle carriage returns) before interpolating into the contractmeta!(...)
invocation so generated Rust remains valid; add or update a unit test that
passes keys/values containing quotes, backslashes and newlines to ensure
escaping is correct and future changes don't regress.

Copy link
Member

Choose a reason for hiding this comment

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

@CoveMB We should sanitize the value when adding metadata, since that is user-provided. We can use toByteArray from packages/core/stellar/src/utils/convert-strings.ts, as that is what we use for other string literals.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

use soroban_sdk::contractmeta;␊
contractmeta!(key="contact", val="security@example.com");␊

Choose a reason for hiding this comment

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

I've noticed in the EVM communities it is common to have a regular contact and a security contact. Would it be beneficial to do that in this ecosystem as well? We don't have to add the general contact now, but it might be a good idea to differentiate the security contact now so as to make space for including a general contact in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted, that make sense, security_contact then?

if (securityContact) {
c.addSecurityTag(securityContact);
}
export function setInfo(c: ContractBuilder, { securityContact, license }: Info): void {
Copy link
Member

Choose a reason for hiding this comment

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

We should add at least 1 snapshot test that covers going through this setInfo function. Currently, the snapshot tests in contract.test.ts only test addContractMetadata directly, while the compilation test in fungible.compile.test.ts does not include a snapshot.

Comment on lines +370 to 372
function printMetadata(contract: Contract) {
return Array.from(contract.metadata.entries()).map(([key, value]) => `contractmeta!(key="${key}", val="${value}");`);
}
Copy link
Member

Choose a reason for hiding this comment

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

@CoveMB We should sanitize the value when adding metadata, since that is user-provided. We can use toByteArray from packages/core/stellar/src/utils/convert-strings.ts, as that is what we use for other string literals.

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.

Stellar: Store security contract as contract meta
3 participants