Skip to content
Open
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
27ebf04
Before running with prettier
CoveMB Feb 21, 2025
7c7828e
After running with prettier
CoveMB Feb 21, 2025
ce5fcd3
Add consistent-type-imports rule
CoveMB Feb 21, 2025
3cd0b59
Add lint step in ci action
CoveMB Feb 21, 2025
31f0c78
resolve prettier conflict
CoveMB Feb 21, 2025
365421b
After running with prettier
CoveMB Feb 21, 2025
cedaeaa
resolve prettier conflict
CoveMB Feb 21, 2025
98bd8af
Add lint step in ci action
CoveMB Feb 21, 2025
a9098d0
resolve prettier conflict
CoveMB Feb 21, 2025
6e9df26
resolve prettier conflict
CoveMB Feb 21, 2025
574a739
Remove .vscode directory from Git tracking
CoveMB Feb 21, 2025
c0e9002
move linter action in it's own job
CoveMB Feb 21, 2025
86c65dc
add lint note in readme
CoveMB Feb 21, 2025
a1111d3
Update .github/workflows/test.yml
CoveMB Feb 21, 2025
abbd5a4
Merge remote-tracking branch 'upstream/master'
CoveMB Feb 21, 2025
beffa34
Merge branch 'master' into master
ericglau Feb 21, 2025
d6bec2a
lint script files
CoveMB Feb 21, 2025
315b775
Merge branch 'master' of github.com:CoveMB/contracts-wizard
CoveMB Feb 21, 2025
6ed6e4f
Merge remote-tracking branch 'upstream/master'
CoveMB Feb 24, 2025
ea90cd1
Merge remote-tracking branch 'upstream/master'
CoveMB Feb 28, 2025
abf687a
Merge remote-tracking branch 'upstream/master'
CoveMB Mar 4, 2025
426b62d
Merge remote-tracking branch 'upstream/master'
CoveMB Mar 20, 2025
ea25cc1
Merge remote-tracking branch 'upstream/master'
CoveMB Mar 20, 2025
0911f87
Merge remote-tracking branch 'upstream/master'
CoveMB Mar 25, 2025
4914083
Merge remote-tracking branch 'upstream/master'
CoveMB Mar 27, 2025
5ce527f
Merge remote-tracking branch 'upstream/master'
CoveMB Apr 4, 2025
03a32fc
Merge remote-tracking branch 'upstream/master'
CoveMB Apr 7, 2025
b3c0347
Merge remote-tracking branch 'upstream/master'
CoveMB Apr 12, 2025
0a52a65
Merge remote-tracking branch 'upstream/master'
CoveMB May 13, 2025
9e74342
Merge remote-tracking branch 'upstream/master'
CoveMB May 19, 2025
d727f51
Merge remote-tracking branch 'upstream/master'
CoveMB May 21, 2025
60fb18f
Merge remote-tracking branch 'upstream/master'
CoveMB May 26, 2025
cbb7631
Merge remote-tracking branch 'upstream/master'
CoveMB May 27, 2025
0f1267f
Merge remote-tracking branch 'upstream/master'
CoveMB Jun 5, 2025
acc5e6e
Merge remote-tracking branch 'upstream/master'
CoveMB Jun 16, 2025
e3b74c8
Merge remote-tracking branch 'upstream/master'
CoveMB Jun 17, 2025
16ba867
Merge remote-tracking branch 'upstream/master'
CoveMB Jun 26, 2025
10442ac
Merge remote-tracking branch 'upstream/master'
CoveMB Jun 27, 2025
f3d5bea
Merge remote-tracking branch 'upstream/master'
CoveMB Jul 7, 2025
4427128
Merge remote-tracking branch 'upstream/master'
CoveMB Jul 10, 2025
e4734df
Merge remote-tracking branch 'upstream/master'
CoveMB Jul 24, 2025
52bd1e0
Merge remote-tracking branch 'upstream/master'
CoveMB Jul 28, 2025
0de594f
Merge branch 'master' of github.com:CoveMB/contracts-wizard
CoveMB Jul 28, 2025
5bac310
Merge remote-tracking branch 'upstream/master'
CoveMB Jul 30, 2025
63a50d5
Merge remote-tracking branch 'upstream/master'
CoveMB Aug 5, 2025
a10f142
Merge remote-tracking branch 'upstream/master'
CoveMB Aug 20, 2025
8a4840e
Merge remote-tracking branch 'upstream/master'
CoveMB Sep 12, 2025
c8bc5cf
Merge remote-tracking branch 'upstream/master'
CoveMB Sep 24, 2025
46ed7c0
Merge remote-tracking branch 'upstream/master'
CoveMB Sep 25, 2025
53ee786
Add stellar metadata
CoveMB Sep 25, 2025
0091f40
Add metadata for stellar contract
CoveMB Sep 25, 2025
c5d0aa8
Revert info input label
CoveMB Sep 25, 2025
f994389
Rename for security_contact
CoveMB Sep 26, 2025
8c207e6
Update .changeset/silent-bags-repair.md
CoveMB Sep 26, 2025
7348fa1
Throw error on already set metadata key
CoveMB Sep 26, 2025
37b9e33
Throw error on already set metadata key
CoveMB Sep 26, 2025
389df3d
Merge branch 'master' into stellar-security-contact
CoveMB Sep 26, 2025
80dd878
add tests
CoveMB Oct 6, 2025
5132555
Merge branch 'stellar-security-contact' of github.com:CoveMB/contract…
CoveMB Oct 6, 2025
a7db183
Merge remote-tracking branch 'upstream/master' into stellar-security-…
CoveMB Oct 6, 2025
40d3488
Merge branch 'master' into stellar-security-contact
CoveMB Oct 8, 2025
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
5 changes: 5 additions & 0 deletions .changeset/silent-bags-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openzeppelin/wizard-stellar': patch
---

Set security contact as contract metadata
27 changes: 23 additions & 4 deletions packages/core/stellar/src/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,34 @@ test('contract with documentation', t => {
t.snapshot(printContract(Foo));
});

test('contract with security info', t => {
test('contract with security contact metadata', t => {
const Foo = new ContractBuilder('Foo');
Foo.addSecurityTag('[email protected]');
Foo.addContractMetadata({ key: 'contact', value: '[email protected]' });
t.snapshot(printContract(Foo));
});

test('contract with security info and documentation', t => {
test('setting metadata with same key throws', t => {
const Foo = new ContractBuilder('Foo');
Foo.addSecurityTag('[email protected]');
Foo.addContractMetadata({ key: 'contact', value: '[email protected]' });

t.throws(() => Foo.addContractMetadata({ key: 'contact', value: '[email protected]' }));
});

test('contract with multiple metadata', t => {
const Foo = new ContractBuilder('Foo');
Foo.addContractMetadata([
{ key: 'contact', value: '[email protected]' },
{ key: 'meta', value: 'data' },
]);
t.snapshot(printContract(Foo));
});

test('contract with multiple metadata and documentation', t => {
const Foo = new ContractBuilder('Foo');
Foo.addContractMetadata([
{ key: 'contact', value: '[email protected]' },
{ key: 'meta', value: 'data' },
]);
Foo.addDocumentation('Some documentation');
t.snapshot(printContract(Foo));
});
36 changes: 27 additions & 9 deletions packages/core/stellar/src/contract.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,36 +155,54 @@ Generated by [AVA](https://avajs.dev).
pub struct Foo;␊
`

## contract with security info
## contract with security contact metadata

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Stellar Soroban Contracts ^0.4.1␊
#![no_std]␊
use soroban_sdk::contractmeta;␊
contractmeta!(key="contact", val="[email protected]");␊

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?

//! # Security␊
//!␊
//! For security issues, please contact: [email protected]
#[contract]␊
pub struct Foo;␊
`

## contract with multiple metadata

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Stellar Soroban Contracts ^0.4.1␊
#![no_std]␊
use soroban_sdk::contractmeta;␊
contractmeta!(key="contact", val="[email protected]");␊
contractmeta!(key="meta", val="data");␊
#[contract]␊
pub struct Foo;␊
`

## contract with security info and documentation
## contract with multiple metadata and documentation

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Stellar Soroban Contracts ^0.4.1␊
//! Some documentation␊
//! # Security␊
//!␊
//! For security issues, please contact: [email protected]
#![no_std]␊
use soroban_sdk::contractmeta;␊
contractmeta!(key="contact", val="[email protected]");␊
contractmeta!(key="meta", val="data");␊
#[contract]␊
pub struct Foo;␊
`
Binary file modified packages/core/stellar/src/contract.test.ts.snap
Binary file not shown.
14 changes: 10 additions & 4 deletions packages/core/stellar/src/contract.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { toIdentifier } from './utils/convert-strings';

export interface Contract {
metadata: Map<string, string>;
license: string;
securityContact: string;
documentations: string[];
name: string;
useClauses: UseClause[];
Expand Down Expand Up @@ -74,7 +74,7 @@ export interface Argument {
export class ContractBuilder implements Contract {
readonly name: string;
license = 'MIT';
securityContact = '';
metadata = new Map<string, string>();
ownable = false;

readonly documentations: string[] = [];
Expand Down Expand Up @@ -267,7 +267,13 @@ export class ContractBuilder implements Contract {
this.documentations.push(description);
}

addSecurityTag(securityContact: string) {
this.securityContact = securityContact;
addContractMetadata(metadata: { key: string; value: string }[] | { key: string; value: string }) {
(Array.isArray(metadata) ? metadata : [metadata]).forEach(({ key, value }) => {
if (this.metadata.has(key)) throw new Error(`Setting metadata failed: ${key} is already set`);

this.metadata.set(key, value);
});

if (this.metadata.size > 0) this.addUseClause('soroban_sdk', 'contractmeta');
}
}
21 changes: 21 additions & 0 deletions packages/core/stellar/src/fungible.compile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,24 @@ test.serial(
{ snapshotResult: false },
),
);

test.serial(
'compilation fungible upgradable mintable pausable with security contact metadata',
runRustCompilationTest(
buildFungible,
{
kind: 'Fungible',
name: 'MyToken',
symbol: 'MTK',
premint: '2000',
burnable: false,
mintable: true,
pausable: true,
upgradeable: true,
info: {
securityContact: '[email protected]',
},
},
{ snapshotResult: false },
),
);
6 changes: 3 additions & 3 deletions packages/core/stellar/src/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ export function printContract(contract: Contract): string {
`// SPDX-License-Identifier: ${contract.license}`,
`// Compatible with OpenZeppelin Stellar Soroban Contracts ${compatibleContractsSemver}`,
...(contract.documentations.length ? ['', ...printDocumentations(contract.documentations)] : []),
...(contract.securityContact ? ['', ...printSecurityTag(contract.securityContact)] : []),
...createLevelAttributes,
],
spaceBetween(
printUseClauses(contract),
printMetadata(contract),
printVariables(contract),
printContractStruct(contract),
printContractErrors(contract),
Expand Down Expand Up @@ -367,6 +367,6 @@ function printDocumentations(documentations: string[]): string[] {
return documentations.map(documentation => `//! ${documentation}`);
}

function printSecurityTag(securityContact: string) {
return ['//! # Security', '//!', `//! For security issues, please contact: ${securityContact}`];
function printMetadata(contract: Contract) {
return Array.from(contract.metadata.entries()).map(([key, value]) => `contractmeta!(key="${key}", val="${value}");`);
}
Comment on lines 371 to 375
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!

14 changes: 3 additions & 11 deletions packages/core/stellar/src/set-info.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { ContractBuilder } from './contract';

export const TAG_SECURITY_CONTACT = `@custom:security-contact`;

export const infoOptions = [{}, { license: 'WTFPL' }] as const;

export const defaults: Info = { license: 'MIT' };
Expand All @@ -11,14 +9,8 @@ export type Info = {
securityContact?: string;
};

export function setInfo(c: ContractBuilder, info: Info): void {
const { securityContact, license } = info;

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.

if (securityContact) c.addContractMetadata({ key: 'security_contact', value: securityContact });

if (license) {
c.license = license;
}
if (license) c.license = license;
}
Loading