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
35 changes: 31 additions & 4 deletions packages/core/stellar/src/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import test from 'ava';
import type { BaseFunction, BaseTraitImplBlock } from './contract';
import { ContractBuilder } from './contract';
import { printContract } from './print';
import { setInfo } from './set-info';

test('contract basics', t => {
const Foo = new ContractBuilder('Foo');
Expand Down Expand Up @@ -89,15 +90,41 @@ 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));
});

test('contract with setInfo', t => {
const Foo = new ContractBuilder('Foo');
setInfo(Foo, { securityContact: '[email protected]', license: 'MIT' });

t.snapshot(printContract(Foo));
});
50 changes: 42 additions & 8 deletions packages/core/stellar/src/contract.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,36 +155,70 @@ 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;␊
//! # Security␊
//!␊
//! For security issues, please contact: [email protected]
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?

#[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␊
#![no_std]␊
use soroban_sdk::contractmeta;␊
//! # Security␊
//!␊
//! For security issues, please contact: [email protected]
contractmeta!(key="contact", val="[email protected]");␊
contractmeta!(key="meta", val="data");␊
#[contract]␊
pub struct Foo;␊
`

## contract with setInfo

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Stellar Soroban Contracts ^0.4.1␊
#![no_std]␊
use soroban_sdk::contractmeta;␊
contractmeta!(key="security_contact", val="[email protected]");␊
#[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 },
),
);
9 changes: 6 additions & 3 deletions packages/core/stellar/src/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Lines } from './utils/format-lines';
import { formatLines, spaceBetween } from './utils/format-lines';
import { getSelfArg } from './common-options';
import { compatibleContractsSemver } from './utils/version';
import { toByteArray } from './utils/convert-strings';

const DEFAULT_SECTION = '1. with no section';
const STANDALONE_IMPORTS_GROUP = 'Standalone Imports';
Expand All @@ -24,11 +25,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 +368,8 @@ 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="${toByteArray(value)}");`,
Copy link
Member

@ericglau ericglau Oct 7, 2025

Choose a reason for hiding this comment

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

Just one additional feedback: It would be better to do the toByteArray(value) sanitization in the caller such as contract.ts, so that the model's content is already sanitized before printing.

For comparison, contract.ts also has a toIdentifier call for a similar purpose when setting the contract name.

);
}
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;
}