Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/light-regions-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openzeppelin/wizard': patch
---

Use unicode syntax for strings with non-ASCII characters
5 changes: 5 additions & 0 deletions packages/core/solidity/src/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ test('contract basics', t => {
t.snapshot(printContract(Foo));
});

test('contract name is unicodeSafe', t => {
const Foo = new ContractBuilder('Footeć');
t.snapshot(printContract(Foo));
});

test('contract with a parent', t => {
const Foo = new ContractBuilder('Foo');
const Bar = toParentContract('Bar', './Bar.sol');
Expand Down
12 changes: 12 additions & 0 deletions packages/core/solidity/src/contract.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## contract name is unicodeSafe

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Contracts ^5.0.0␊
pragma solidity ^0.8.27;␊
contract Footec {␊
}␊
`

## contract with a parent

> Snapshot 1
Expand Down
Binary file modified packages/core/solidity/src/contract.test.ts.snap
Binary file not shown.
4 changes: 4 additions & 0 deletions packages/core/solidity/src/custom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ function testAPIEquivalence(title: string, opts?: CustomOptions) {

testCustom('custom', {});

testCustom('custom name is unicode safe', {
name: 'ćontract',
});

testCustom('pausable', {
pausable: true,
});
Expand Down
12 changes: 12 additions & 0 deletions packages/core/solidity/src/custom.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## custom name is unicode safe

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Contracts ^5.0.0␊
pragma solidity ^0.8.27;␊
contract Contract {␊
}␊
`

## pausable

> Snapshot 1
Expand Down
Binary file modified packages/core/solidity/src/custom.test.ts.snap
Binary file not shown.
2 changes: 2 additions & 0 deletions packages/core/solidity/src/erc1155.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ function testAPIEquivalence(title: string, opts?: ERC1155Options) {

testERC1155('basic', {});

testERC1155('name is unicodeSafe', { name: 'MyTokeć' });

testERC1155('basic + roles', {
access: 'roles',
});
Expand Down
23 changes: 23 additions & 0 deletions packages/core/solidity/src/erc1155.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,29 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## name is unicodeSafe

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Contracts ^5.0.0␊
pragma solidity ^0.8.27;␊
import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";␊
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊
contract MyTokec is ERC1155, Ownable {␊
constructor(address initialOwner)␊
ERC1155("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/")␊
Ownable(initialOwner)␊
{}␊
function setURI(string memory newuri) public onlyOwner {␊
_setURI(newuri);␊
}␊
}␊
`

## basic + roles

> Snapshot 1
Expand Down
Binary file modified packages/core/solidity/src/erc1155.test.ts.snap
Binary file not shown.
2 changes: 2 additions & 0 deletions packages/core/solidity/src/erc20.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ function testAPIEquivalence(title: string, opts?: ERC20Options) {

testERC20('basic erc20', {});

testERC20('erc20 name is unicodeSafe', { name: 'MyTokeć' });

testERC20('erc20 burnable', {
burnable: true,
});
Expand Down
16 changes: 16 additions & 0 deletions packages/core/solidity/src/erc20.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## erc20 name is unicodeSafe

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Contracts ^5.0.0␊
pragma solidity ^0.8.27;␊
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊
contract MyTokec is ERC20, ERC20Permit {␊
constructor() ERC20(unicode"MyTokeć", "MTK") ERC20Permit(unicode"MyTokeć") {}␊
}␊
`

## erc20 burnable

> Snapshot 1
Expand Down
Binary file modified packages/core/solidity/src/erc20.test.ts.snap
Binary file not shown.
2 changes: 2 additions & 0 deletions packages/core/solidity/src/erc721.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ function testAPIEquivalence(title: string, opts?: ERC721Options) {

testERC721('basic', {});

testERC721('name is unicodeSafe', { name: 'MyTokeć' });

testERC721('base uri', {
baseUri: 'https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/',
});
Expand Down
15 changes: 15 additions & 0 deletions packages/core/solidity/src/erc721.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ Generated by [AVA](https://avajs.dev).
}␊
`

## name is unicodeSafe

> Snapshot 1

`// SPDX-License-Identifier: MIT␊
// Compatible with OpenZeppelin Contracts ^5.0.0␊
pragma solidity ^0.8.27;␊
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";␊
contract MyTokec is ERC721 {␊
constructor() ERC721(unicode"MyTokeć", "MTK") {}␊
}␊
`

## base uri

> Snapshot 1
Expand Down
Binary file modified packages/core/solidity/src/erc721.test.ts.snap
Binary file not shown.
3 changes: 2 additions & 1 deletion packages/core/solidity/src/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { mapValues } from './utils/map-values';
import SOLIDITY_VERSION from './solidity-version.json';
import { inferTranspiled } from './infer-transpiled';
import { compatibleContractsSemver } from './utils/version';
import { stringifyUnicodeSafe } from './utils/sanitize';

export function printContract(contract: Contract, opts?: Options): string {
const helpers = withHelpers(contract, opts);
Expand Down Expand Up @@ -148,7 +149,7 @@ export function printValue(value: Value): string {
throw new Error(`Number not representable (${value})`);
}
} else {
return JSON.stringify(value);
return stringifyUnicodeSafe(value);
}
}

Expand Down
46 changes: 46 additions & 0 deletions packages/core/solidity/src/utils/sanitize.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import test from 'ava';
import { stringifyUnicodeSafe } from './sanitize';

test('stringifyUnicodeSafe', t => {
const cases = [
{
input: 'My Token',
expected: '"My Token"',
description: 'should handle string with no special characters',
},
{
input: 'MyToke"ć"',
expected: 'unicode"MyToke\\"ć\\""',
description: 'should escape double quotes and wrap in unicode"" if unicode characters are present',
},
{
input: '',
expected: '""',
description: 'should handle empty string',
},
{
input: 'ć',
expected: 'unicode"ć"',
description: 'should handle string with only unicode characters',
},
{
input: 'MyToken',
expected: '"MyToken"',
description: 'should handle string with no special characters',
},
{
input: 'MyTok"e"n',
expected: '"MyTok\\"e\\"n"',
description: 'should handle escaped double quotes',
},
{
input: 'MyTokeć',
expected: 'unicode"MyTokeć"',
description: 'should handle string with mixed ASCII and unicode characters',
},
];

for (const { input, expected, description } of cases) {
t.is(stringifyUnicodeSafe(input), expected, description);
}
});
6 changes: 6 additions & 0 deletions packages/core/solidity/src/utils/sanitize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function stringifyUnicodeSafe(str: string): string {
// eslint-disable-next-line no-control-regex
const containsUnicode = /[^\x00-\x7F]/.test(str);

return containsUnicode ? `unicode"${str.replace(/"/g, '\\"')}"` : JSON.stringify(str);
}