From 2bd4e47bad1580d8e5055354bfb7f5c3c9562583 Mon Sep 17 00:00:00 2001 From: VEERENDRA VAMSHI <136776337+MKVEERENDRA@users.noreply.github.com> Date: Wed, 5 Mar 2025 23:37:37 +0530 Subject: [PATCH 1/6] Update erc20.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: Use unicode string literal for token names with non-ASCII characters (#475) - Token names with accents/symbols (e.g., "MyTokeć") now use Solidity’s `unicode"..."` syntax to prevent compilation errors. - Double quotes in names/symbols are escaped (`\"`) to avoid syntax issues. - Developers can now use any Unicode name without manual code adjustments. Fixes #475 --- packages/core/solidity/src/erc20.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index 3b0f3419b..2f33bb5cd 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -109,13 +109,22 @@ export function buildERC20(opts: ERC20Options): ContractBuilder { return c; } +// Helper function to format string literals with the `unicode` keyword if they contain non-ASCII characters. +// Also escapes double quotes in the string. +function formatLiteral(str: string): string { + const escaped = str.replace(/"/g, '\\"'); // Escape double quotes + return /[^\x00-\x7F]/.test(escaped) + ? `unicode"${escaped}"` + : `"${escaped}"`; +} function addBase(c: ContractBuilder, name: string, symbol: string) { const ERC20 = { name: 'ERC20', path: '@openzeppelin/contracts/token/ERC20/ERC20.sol', }; - c.addParent(ERC20, [name, symbol]); + // Use formatLiteral to wrap name and symbol appropriately + c.addParent(ERC20, [formatLiteral(name), formatLiteral(symbol)]); c.addOverride(ERC20, functions._update); c.addOverride(ERC20, functions._approve); // allows override from stablecoin @@ -169,7 +178,8 @@ function addPermit(c: ContractBuilder, name: string) { name: 'ERC20Permit', path: '@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol', }; - c.addParent(ERC20Permit, [name]); + // Format the name so that it includes the `unicode` keyword if necessary + c.addParent(ERC20Permit, [formatLiteral(name)]); c.addOverride(ERC20Permit, functions.nonces); } From 058240891b0b0e2c598584e5fdee583c92bbf7fe Mon Sep 17 00:00:00 2001 From: VEERENDRA VAMSHI <136776337+MKVEERENDRA@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:59:14 +0530 Subject: [PATCH 2/6] Update erc20.ts From formatLiteral to sanitize --- packages/core/solidity/src/erc20.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index 2f33bb5cd..1ae9713da 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -111,7 +111,7 @@ export function buildERC20(opts: ERC20Options): ContractBuilder { } // Helper function to format string literals with the `unicode` keyword if they contain non-ASCII characters. // Also escapes double quotes in the string. -function formatLiteral(str: string): string { +function sanitize(str: string): string { const escaped = str.replace(/"/g, '\\"'); // Escape double quotes return /[^\x00-\x7F]/.test(escaped) ? `unicode"${escaped}"` @@ -123,8 +123,8 @@ function addBase(c: ContractBuilder, name: string, symbol: string) { name: 'ERC20', path: '@openzeppelin/contracts/token/ERC20/ERC20.sol', }; - // Use formatLiteral to wrap name and symbol appropriately - c.addParent(ERC20, [formatLiteral(name), formatLiteral(symbol)]); + // Use sanitize to wrap name and symbol appropriately + c.addParent(ERC20, [sanitize(name), sanitize(symbol)]); c.addOverride(ERC20, functions._update); c.addOverride(ERC20, functions._approve); // allows override from stablecoin @@ -179,7 +179,7 @@ function addPermit(c: ContractBuilder, name: string) { path: '@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol', }; // Format the name so that it includes the `unicode` keyword if necessary - c.addParent(ERC20Permit, [formatLiteral(name)]); + c.addParent(ERC20Permit, [sanitize(name)]); c.addOverride(ERC20Permit, functions.nonces); } From 005c15dc62e84f7336682bbb55a75e1e485e0577 Mon Sep 17 00:00:00 2001 From: CoveMB Date: Fri, 9 May 2025 16:00:11 -0400 Subject: [PATCH 3/6] Use stringifyUnicodeSafe at root of print value and add test cases --- packages/core/solidity/src/contract.test.ts | 5 ++ .../core/solidity/src/contract.test.ts.md | 12 +++++ .../core/solidity/src/contract.test.ts.snap | Bin 978 -> 1008 bytes packages/core/solidity/src/custom.test.ts | 4 ++ packages/core/solidity/src/custom.test.ts.md | 12 +++++ .../core/solidity/src/custom.test.ts.snap | Bin 829 -> 858 bytes packages/core/solidity/src/erc1155.test.ts | 2 + packages/core/solidity/src/erc1155.test.ts.md | 23 +++++++++ .../core/solidity/src/erc1155.test.ts.snap | Bin 1776 -> 1803 bytes packages/core/solidity/src/erc20.test.ts | 2 + packages/core/solidity/src/erc20.test.ts.md | 16 ++++++ packages/core/solidity/src/erc20.test.ts.snap | Bin 1967 -> 2018 bytes packages/core/solidity/src/erc20.ts | 14 +----- packages/core/solidity/src/erc721.test.ts | 2 + packages/core/solidity/src/erc721.test.ts.md | 15 ++++++ .../core/solidity/src/erc721.test.ts.snap | Bin 2167 -> 2208 bytes packages/core/solidity/src/print.ts | 3 +- .../core/solidity/src/utils/sanitize.test.ts | 46 ++++++++++++++++++ packages/core/solidity/src/utils/sanitize.ts | 6 +++ 19 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 packages/core/solidity/src/utils/sanitize.test.ts create mode 100644 packages/core/solidity/src/utils/sanitize.ts diff --git a/packages/core/solidity/src/contract.test.ts b/packages/core/solidity/src/contract.test.ts index 0a1824cee..9e81545bb 100644 --- a/packages/core/solidity/src/contract.test.ts +++ b/packages/core/solidity/src/contract.test.ts @@ -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'); diff --git a/packages/core/solidity/src/contract.test.ts.md b/packages/core/solidity/src/contract.test.ts.md index 6a6339fc1..fbae76cd0 100644 --- a/packages/core/solidity/src/contract.test.ts.md +++ b/packages/core/solidity/src/contract.test.ts.md @@ -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.22;␊ + ␊ + contract Footec {␊ + }␊ + ` + ## contract with a parent > Snapshot 1 diff --git a/packages/core/solidity/src/contract.test.ts.snap b/packages/core/solidity/src/contract.test.ts.snap index ddca348d738e707aefaac3813d3d478b07a43e0f..9ce1c5240bdfa9351e48e86144768b614e0c207d 100644 GIT binary patch literal 1008 zcmVD;;NE0O9Cq=WP8^<9JS? zCGvPz@*j%`00000000B+Si5i2Kp0Ozyt1+J68aPgl#0|25g^p65=sFHiASLwC^vefN9$ zySu#b>OOaF$)_(-QRO3dI8%Z+3hG2sM^ed1C>vbK`!8IVD021D*4&Pq{^YmicIWTf zHcU>OzB+uKI>;mrpL3BZ>QNLUaN*ok!M0(X2O&|k?jy`m)qqPOvTGQI=u-yc$$S#7 zAGa!2r4R}-6A+Nxr!G|saJ^!kw5rw7LZJoiEa$LTSSlo#=MS`c7zq%lgosgxyEsWa zOfz_FFu2zz1`3^a60@zocm@I?5gJDd{o8i*TVCZh5k3tA zDij&gd^M!`JV4TPNdN$V!rwKxQeOKk#GyT%1L^A~_K z{G(XY`R#^UKL$vxjRk3)qV6+A)f)0y6oR@Lm2v=U{r+?)n)tKae`dJkK*XvM5(M6ks6ZF$3ow}sZ584%nvcMZI(l5-6u*URN)RSJ z6eZ%i0;P=8REB0t@iIh|sp|0)Kmr~yH58AfKa?pHjQFh!O%$awSH`QAGQ?k#iLxut zEw*l!QHa9Igt7ubPn}FT8b8$@#zSc{UFvxl*TEjPZpj(8e*16h+HJk08Ym{>dipO+ z?I6MR=x38=X{ZIKCyX?Nj$zUX+S9p+2HcH(PZzUKMrPh^=0cE>nZJ-~u7T_#R}&j)fvLWAxKFen=|Eu(G z1eB>#AZILz%tcX#qZL3cMurAQdTS5|s_SWFI={F`yhOXgj^Cw*2= zZ&_fL_z}Vw9IKA@-rYA@WgLXx8?;^z8m-t)CG>_c{$FYgiZ^+K7263Ph^5hw{06vIgwO67?J2if~tjK ejafr(UO0vGBnW-993G@kpuYiGK>D#O6#xJQhRERn literal 978 zcmV;@11+Qn+rQuM z``zcyFTASHojdaBOH@?($Q{m02I?#zMg zecOiVsdLwdFH#4Y#Ni7rGDSU#ViYc2n912TOz%&*0XmNd9x4P(11pR`c#_{xsueCxVNPr4OiZovhX+HOnG#wlO0N`*Ym53b0MPW$y z7xFR3k$e%>{+07Xu)NYFP`RUBfHQvyIKw}RHJjaIsP&_d)Y_VnHYw_1Q&g=XpGP66 zi~mBJTs+cliaP$Z=sz=}|0vr#3OZi44KpYQlYFt&|4>4j>WT@%{bT|OqY5d z#(A(ySO+pjSik)b>&C-+Mb%JD#rbqQg4#lY>(wWdW?`rSrz?zh2rbLBJLq8RAsTQu zj(dAD`($|L-A*0^DW3TYsTOL;E^{?SB8je<#Rogl2EyFV34J!Z+YsS<|BKMoFD(jf z*geNg8Jmt%aCq8*xN6Avp~v!>$NxJ0TLERN5Xd=8B6Cp`VWb48#YoZMM0X8hLv_53 z%x3o+iu~%M@@nhF=;j1%sZbgoPj^%gi}}rcklSOkt?!xmFEwxNGi3VN|1#C{ z)<*Q&Na{+dr0S=b?75-dlOC(5OBR?XeuQuqPL{{ISNC;RDHq}Q2CdiqMk@}d z61pH{B+$M2+q}l|&4iD}BQX>iqC<%fPL1f5Q?LF{48{+8#kfL5h1_etmkBd)lzF@z z{YL}+o1H{&+cu0lkyJbwmFPr*s-8+LLV)M2kke|-*;iJ0%jBd01`*k AsQ>@~ diff --git a/packages/core/solidity/src/custom.test.ts b/packages/core/solidity/src/custom.test.ts index 751b40277..6262bf26a 100644 --- a/packages/core/solidity/src/custom.test.ts +++ b/packages/core/solidity/src/custom.test.ts @@ -34,6 +34,10 @@ function testAPIEquivalence(title: string, opts?: CustomOptions) { testCustom('custom', {}); +testCustom('custom name is unicode safe', { + name: 'ćontract', +}); + testCustom('pausable', { pausable: true, }); diff --git a/packages/core/solidity/src/custom.test.ts.md b/packages/core/solidity/src/custom.test.ts.md index 48150846a..5ca6420cb 100644 --- a/packages/core/solidity/src/custom.test.ts.md +++ b/packages/core/solidity/src/custom.test.ts.md @@ -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.22;␊ + ␊ + contract Contract {␊ + }␊ + ` + ## pausable > Snapshot 1 diff --git a/packages/core/solidity/src/custom.test.ts.snap b/packages/core/solidity/src/custom.test.ts.snap index 361eb9e9e1b8c7ce61c5b1f892c6e2a252e529d4..606cee4d2e8171f02709a54faaa9a4c50a48f311 100644 GIT binary patch literal 858 zcmV-g1Eu^yRzVuSwXk3sQ3G12VX`|( zhR)6mGqc7dc#z(DD*Xrc&-EYlT=vtQO>AN*28(-~ecq4fecpX`Zu&jS-OuXl53Gr% zxbFr^bDt=Ngc@?Ku5UaKX>|I1t@NT4KIP}7m(RYGY#WXb_fBgE#KlbEn&V-nNuOZ3 z4K3%yvTfMqzCfDvD25TyLud=kK4Kv-B@A{s(-OH_!THQK~1PZNj8U<79%1T!`;4EY#{f`Ad1dl(ez<5?o#3?kPnAu^A~P@q7e z9-VFZto+O9`Dgh7dnUf%Qp2=8VqtSv(3jkY^&KuSntdzs1YK90t~atA zhY7<)Ndir%vQu=DWbPUe1u1L;lAXy*fJ?HRGK8)j8Py45_vcx zrzK)aAQ=iiyC~XN6ugQ>apGx`$r4J}a?ETY`QLF%c|$uw`D z@k&9YDW{N3&wEUFoQnbn9?LT45Aep|@anN$74G`-{@K4h8>udgRC0>vSa$i;7^t&~ zfi|N~cjHEv9IFwtV7LbM4S{m52#R+lXCCkp#PV>6@{o>T%VKtjxa8 ke*%}a;*}+y(z1s6bf0;;&ph2{9_&8z8(b!^t*j9M0MlNgQvd(} literal 829 zcmV-D1H$}4RzV(D|a&eGi;@8YXD!ccO6H*f0( zvW*ZHryq+500000000B+R^4jbFceN!#=v*m6KK6kpovRiFq)f&wu3;^l(vC(um;6G zNyKDHNOIF8^kVF4w|j%`<@N@<9=2uIa?&Loqot$1l)v-)9ep}CgFfZnXZ7s|Xrd_$ zyijQ#5XG=iBd*n#8{fwopM75|y(&dt`DN+#i?1cuMJGplXSD<30aH+G`M@+85Rlub z-8yw#7wz&uU`_fI(3t2E>Ih&TK?tCPp4Q{odnK^L3# z_w}ubfr4Ni<-aPt!1}7BYngP{Wk+aehMPCZ?LF#wYQFWr0Ad1vH5~ zO<^e1JU?sH;^))a|HMy0DZ00&_laT)NX|(iO1FD-@|PjVgXfw`qH%WBGwpunznrmVI&|)73IHbDi2ZULI zG?bX>V@{#6xBsEpJvi+(_u8$)-f`z(f5SGLdp3)s9bkrs3vTCmxn(i2{di2YlN48E zA}L6^mhDQ680_+)ZzhteCso_BGXFn+1+Hqvl_lxYs)G5v&phul&-=`S?K8gtIKYQr HT-1u7 diff --git a/packages/core/solidity/src/erc1155.test.ts b/packages/core/solidity/src/erc1155.test.ts index bea4ae13e..5c818868b 100644 --- a/packages/core/solidity/src/erc1155.test.ts +++ b/packages/core/solidity/src/erc1155.test.ts @@ -36,6 +36,8 @@ function testAPIEquivalence(title: string, opts?: ERC1155Options) { testERC1155('basic', {}); +testERC1155('name is unicodeSafe', { name: 'MyTokeć' }); + testERC1155('basic + roles', { access: 'roles', }); diff --git a/packages/core/solidity/src/erc1155.test.ts.md b/packages/core/solidity/src/erc1155.test.ts.md index bcb1b8c24..3ea5cf30d 100644 --- a/packages/core/solidity/src/erc1155.test.ts.md +++ b/packages/core/solidity/src/erc1155.test.ts.md @@ -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.22;␊ + ␊ + 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 diff --git a/packages/core/solidity/src/erc1155.test.ts.snap b/packages/core/solidity/src/erc1155.test.ts.snap index 88e7ae19e5421afe8218f458504f44da770111ff..af5a8908867cbd82f81fd2282b40acc50a45914b 100644 GIT binary patch literal 1803 zcmV+m2lV(sRzVz;+~69t{mZbn?Nek2&vAg7$uGI0hJ%xoMd3K97Z-@icu3l*UjK%IA~;NQMh6e!>qcb+WWdh3??xqa`}yLZ;^ND`=3 zcAu{vU>OmPR?7+^0=F?@kHKNNo|7c7Lp2==+)@$fVbKLgIwCKSt|Ju_utSMpP!=4# z+$cUQKFsM1b~Fe$Rk4CaAG~~6{HVCTzLm>iO{YwN^S$cM+Sf#oMuN94?r zC&{inNzh|NBxkbwUF1|<-tt5p^$2XKXp|@@%ZPKyneE8Px#QrlUpI9D%z>k(0Gzqt zJO=;(G9_FvLl%_fp`tLvIlu%94Ar9^L2SjEWquq1`Q@$QFBl=T5YJ%iyDpc^d}%YYKKZ^>}1H9t+~ zMrZ1lHE@x;$T-NZBroba32>^?kq;DFA)D=-CZk-iI51;sFs_xy?TAG+N>TO z?17KLF_L9?yuR@$zZ@A{UfIeGBQ>Zs~|To0ZX|x4t9^)Vm0@sjAYRsm9gL0b~f!6T}!+5C;ssGB865 zX!SwOA!jiP*w9T%@iGyJwV{mi)-rzxuzR7b_-kQX;Qc+psOSB0_2!dFsa` zZ&CO1d1TOpg;+5TE{H1T38?D{G)Q0vDG5ObhkU_{G=h%(R1R&3WBLNyASQ+GO~)rV z%14Kzk|Da3VbPy)ND9HsCxf|EaAL?DmW5~hIMGQb6d1ZmK9fKO!*NpHRzjp~++NH~ zXc&1C*fL88A6SoW%V1W@lHeSE39ZAA3!afWSGOH|CliTI1tQ#xDN;nvtjnwdhychE z;Q_9ut3!h)Q~U1cA!QQHNOQ9B{s^@N-BM)~1#oH@w?3^I-g zZ`SoC-AyYaK;Gh-1S)j-@|Dl0uEP#uz#M<^G~ zzwN}^1!w~^0a^MTb#c$5VIq<}&(cpJ&+|hX)%TpXd9tqJ#4f<(nfE-PEK`FBZ(9JY z_`jiMk6G2M3&UjOz+%<|6>SRGN?m295?8n-Z!pqrDCBPb65HCA&!>jo2I6gFPkac! zU$VHnd2mnd-ru2J{l~qmrr zX6D|aw#T09!nnGzEE&t~Wi8cVEmanX1p~XPOMiu;!EfoEuw-4;8{Ji%owxK+Eu@Pa z4u7(AkeV(-?E{m-$Cz{y_W$pp{r~HN*Lcp=nq~G?drP*I)H+MH`mCtD?qk$B=;+u> zJ(YgW>3eNMRe_;*7*voI{sn`C>yVinIpzS~ekbGr{>`Ob4mNz`BR1J;z{tcZF(4}0 zPh3)0q9+=u%sm8|iqgS{JR@qixy!>>4Z>j?twPfy z2cuQ|@Hpt>6>L3MZ;cS+gH8ajH)lk1!#VvB^ZPT1{ErB%9*=UB? zXR8d2M>sfmhRZ~6#`Kyl0X7w!2fR?-nS{fms9h^pJBu1BF8N$4>mA*w-cfpaGPWhS^Au%&kLG9y9yeOO6`)?1yw}E%vp|J z01EaA@I^fB(M&`F~js_%kBZpU;9JGBL!+g1?Yib&*vU^I2W^ zl>xL5+s@no_}udL}R(hw2QoI6I=ABP;;8sRp4R^en7PI85Fx-#sbz^+UNzBX|G zyHMHl&Hc1r8@O88llV44R{mt=&zzM%ab`}t{F$c=KlY|dLav^6p6;xOtuh99R(X43 t1iRK3cp^D!vY^TG++-OkCb!v(L^DZaqd2q7b>eqF{sZ5}@e?RW000pFbld;{ literal 1776 zcmVz;?7}|lOr5&6G%lGA=UY4F-jWa*r*gy zYrH$QhwRR5XJ+d-QIPln{RN!b6XM1R@dprBkhsGy0qytK_#=tqjl=q4duR52pLge( z_iOe|r>#@*)kZ;4z!o)3C~#XxppQil?3;)@N2ZB%Ou!Z;fgqqjDqu`H&y+Om)1uzGmniBBl zf|CLO0H~C3!7No!R)m_y5a$3BEHKpf`vkEiXO{i(1QZv0LYVxaqI97^eK;tam_Px` zs!lCU!DfdmUl?j_y?4aO)=Fco(x@3PS2=u)Dtjvf^YOFwO;X|gE$y?-_0PAy-K{8# zp%M>VF%9A5wBUSqETRfb3E)UH>(wF`43jP}kU`l15Y)FAUIM1o*0BmG(FcwU&szJ_ z@^18|Za4#{h0}sZFvB4j_z*CvBc3MO!ap&h{kf0<(JIceJ2z5&LXO1lNw94S#2rn# zm~wA1+5>_3N2~r?IZT8^fM&hgI@oD6cIvJA{_YO=6dWQ|g@>za4~mP4!NsMG!cZP- zr~@rsR5SyV8S*$nwJw81)Txe&+dGdd&D}<;vb|S*>Oe2W@%cPFAITjZiD$Dkp7hR_ zqJTyZfezJm+PBsC+Btv>0s0CthBd?iLoY4NkOtZVaNv=%oCIu{Hl=u#2*f&2MMY`^$YMB6%9~n@l+By-nF);|kAy9=bnub&=(R28q%0EW@JnnR zew_1+)VsRr*?TsT=tv;K?U4)$?a=F?V4xr zMXBw}$&A`QfSlTrbT<%g(;F2>%kk#O1z2Pf5y7m>OS)T5Mu39FwFy=N{y#xWz`{hV zu08nr8~-6)vOQ0X5`RfHUO1u3b;yrWwsJu zxFc^k(oHDTUhy2;+Lh0zhTa7dY~w(D41ZX%_`7*{Pwmd%v0eSgoxG=(ZBK2Kti9?} zxtz$r#C^FiOc_qFnUg+U;;xA0CEt+j8hSHxZ%Nxz&vkBG-9(m5Zq0~i^PJV zUDbuZV$tBY>`qwnuIjb!s?N?^hN$M!MUIAlwser%E<+yxo5F{fbS3-$_t^gbbs*Nm8RcJN6N4QWL6^wPVxRd+^m zc$Bnj>0)P5BgM(jrSjgf&lv7hz=p-NUwHKL7T8U`n7?Jl|9n83G(tMFMqCB`IBbm zw9B8Xl;Nk|RFUNBS?B5AinuCMfM=DrOC#8&zQB{nQRRZpmgmZ4WSHFMFA~ipjZNar SGuJb}`|%%52RI Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.0.0␊ + pragma solidity ^0.8.22;␊ + ␊ + 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 diff --git a/packages/core/solidity/src/erc20.test.ts.snap b/packages/core/solidity/src/erc20.test.ts.snap index 65a672dc874ad6275663ae576ccc59b26fee664b..098d631908c7953e822494192f1c5bc87d7de768 100644 GIT binary patch delta 1977 zcmV;q2S)g>58@9pK~_N^Q*L2!b7*gLAa*kf0|5B>>KpD5)_k0fA6S-ppP8U0l4r&b z6cBmWw>t}BzR{5~K!18@Z$0*u{uRCTQs}MFV{bhc3cd7JD3slu{g~BAmSoG4;`Je6 zH8bzM+4tr*@Aqb;SFNT+jeY*z4@h8Pq5UT0*Z_zb^%~{`I(&}`{_>S+LIFRU`{vq} zH?Meqb8lRE>*|+t)hgIrU;4DXiVZ|KDzBJ`2;9PmJp_%Ft$$p#3KpsDK!KYU0&Ohz zz?y@|=g4u8g$Y=sL@;Ow4xZnu)GD={!{DwB0jCx=vFL#3waSA^y*{7IVcVfhfaB$j z#d_@ov>3HP{xNkBIg-+n1^y}@w~000b1xL~d!C@U0$_Cj6`%$GpEvGp`xOpc3Ni{SVq zcaoDHJAB7?79h|@o<^5ogPLd)wvfhw-zyIMHgjMf2O2{=P`-QVsh_{rJF=^n>QEB+ zv*|Jdo0iUr|0+)WYqp*Ek(}pQ+!)H3hE*QgGS)>eV}HbfF4xD%^{YyZ{Bv!lV`N>< z^GsfpGAf>_b-<^A;t z#;9E?DfAiL6|#Qw@k!4AzvU7`U`qU;MTH`8+@^&MK#A4yrAd>$za80U-v1;mz$Jr- zh7^1vC9g;LV0`U*( z;ga)#1rh<)7q&N-H+DAGR+qseu#XG_?$__#FXR&q`Qm)8Cmflm1zk%lm^LQy;_=t+ z5b)FwfMQJ8WsrysYN5i?@{@(_)vcX{rN+uL-^h|;iCjjhQHeQ~LKF2Er9r5)gyPu% zuYa$&4E#dyyQKp^8xpvSObz^%6!?30a`4v(OdJsYf(fyzGB$(35n)iizabN<1y}4* zhDB#^)Kb(HPLVTOdc_bI3>yNOqr;6}MussGW7;ZV@5jnsDDcvp1iZ`;bDu4kxe(Tu zt}bLGFe8D3U=)>U5JR@7SCjh8{i$Hn?|-vhRh*jIei#T08Q8&yi1X$&o<9qb^b)vR ztJT15aJL3--v%Z!unjFC=p>FBPY;tC`a-ZkwL5 zjo60|-EIYK>Jrg^d-Vr+>JygQ>&Lqw(rnvTaao9C-F|e`nY=28z z3`{hPPz>}_mZ>WSCgxNMP1NVA!>qBi=;Bc8&60b%Klf}mpM~9gKXBOr6Ev%LoSrIo-6)LAx|v8WR}NGcWR2J-7q>OL)Y! zn}}r?kYPZE0ha&++A0JHY$Gn9?P!h-{!jqmx7j{6$N=Di03ccvhPS)Km2xlaqi|Ov z`ojms?qCiY3jQ8diNdt2q|T|ehlwbg*e_U@2|5&I3lroUQugjAmu-BAOn(D%q#C%m zy0-Xqr?IxQ99!wQ36P4d^iCqlF0heJSw~}R-lisc6j_;{*~0&2ghY8t(Xt@l^SG{t z!TzT};oq~(V4rvsZD4XHG>n8y4@Z^{k!9!pork;(6xXtV>+CXUBEP&S7$n?*4C!L$ z##c)0h#QlxFK)+{pW>l%VtNC(~=lD5sn&0aPM81(&1xp`q?D!7a z@p^P`vh(SFVH5@ZyW7K9(zGiP)pNWbS1D_{yZK?(OGXrIQHS$8%;|lz8|W@s)cant z0HYohP35iei#x`e9e<8CAlPc5bNV=)VsqCPHJ$?atsWV?>pGfau$%8Isr%bEv(!CH z-Lup^OWh}*x`!&Zx7RmERFIrSO3Z7>4@w|4$0;HV{bX#ff4s4?O4c0L_n|a0E{I5 zAhb?#je74lV^^1gUA@@jVDVY-x{Y@ab6X&j+l+bD5B7zK!jsTcs(IO$}WUs zK&!Xq4FZ@0zkgc=BzlA60ssIQlyJdZLr_*K2koVz99XP?Vr%Ppv78(ix0b>2N#Ud* zJw|+Afws#CY+FdPxIMC(#7`b^}iIIP9%yf*b z%Xyy3i&DlV#7HQ6tvgg?L&HFv*Jbaa!AH)g0_4Ec0)GygCPSP9Ot8SvTI&+T%6>2J zFC!SEcB!N^V02%|`pw5D1^@q!OALW2@q-qX%D{2k7B&DSR?n9vP4@nFWS@Eele7Sr z3?m}yr3k6l!PXuE9co#$i^*;zl>^8Spa+OCY$6UAdgWqCuH-8^IEEN+C#qxY16OK&OfvzPMOdFGU z@%U?Z1b7+*KrtrlGDyS*wNPnk`Pstu>ekM}QfuXfZ)8QWL@uM$sKlHqp^18o(jZh? zLiucfH&9#-ej)hX(Se^03EV}d2L37v{Czk%_-h3w4het3gjih}o5SFkFeu;OhzZq# zD}VMV!=g7lYANbUr^p#Ey<&(9h7Ezt@!>`;n3KR^FpA1Fh#}k4t4V$4{#3B(_t~y0PE9E|3|jL1d2<@ipQT88 z1>A2m8sILt-vD>-0uve7h88cC`$K{$A%6jA2jm9?vvfe3c6rp=$z%#u6A3E+ZV2)%MB>_ORC=73Ri7Vw^*hk^6 zM)aqT%KgC{G?e^3suqQ5UrC))YY!7qHL+i?t`c-8sum{5H>66SjjA?2M5X~bQVm>O zU0Zy<(^^|vj;(au1W3hJdMA-&7ud+Atfw)yXj2nCiL5NnY~lYZLZUpS=zmy{?|EET z!(jhYpz!b6X0Xq^iMB8~6BXeEkDIW)x;#VWygdlT^Nk1sV|66Q4^j{pM$!rivp)u5QW?Yn4ptA z=-$Ewn4l4zz3MMQe_C*65`T69%F}XFx>tg;5PUSMPKVLMp3L7AA=*4}%!wt_!~QvF zaaFNJLTX}Y>PAR^izZl7wSCQ)w1bn`NdL#WC2>v{h6l+Q>71cnuY;$C%LTO`)1%NS zyy_ATJE#i1TrjycX|}3F30`Kmk#7*=SyxnW)Mu#6&hc~NG(YPIM1Q`KSOrTTZ|wLE z+VOhyZ?g00eqj^^{rlU)SkkmB5tTXKkE@h5{oVX9%aRcVTh!tF4s)_^b_3ldi{`*f z7GT_iqN%(!etE}Ov%}Gb1Y0e1P9LXJZ0_5lE~Wr}CnJM*T}N{acKc%`b$|bMp1S9$ zd!D-Isr%$p_fW<5_J8{3m4x-P(BAhO4pT@!M&k!l_WKee=j?jn6HU}(lk(jB>g4AC M13LG;Fq%~W0RK&@p#T5? diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index 1ae9713da..3b0f3419b 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -109,22 +109,13 @@ export function buildERC20(opts: ERC20Options): ContractBuilder { return c; } -// Helper function to format string literals with the `unicode` keyword if they contain non-ASCII characters. -// Also escapes double quotes in the string. -function sanitize(str: string): string { - const escaped = str.replace(/"/g, '\\"'); // Escape double quotes - return /[^\x00-\x7F]/.test(escaped) - ? `unicode"${escaped}"` - : `"${escaped}"`; -} function addBase(c: ContractBuilder, name: string, symbol: string) { const ERC20 = { name: 'ERC20', path: '@openzeppelin/contracts/token/ERC20/ERC20.sol', }; - // Use sanitize to wrap name and symbol appropriately - c.addParent(ERC20, [sanitize(name), sanitize(symbol)]); + c.addParent(ERC20, [name, symbol]); c.addOverride(ERC20, functions._update); c.addOverride(ERC20, functions._approve); // allows override from stablecoin @@ -178,8 +169,7 @@ function addPermit(c: ContractBuilder, name: string) { name: 'ERC20Permit', path: '@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol', }; - // Format the name so that it includes the `unicode` keyword if necessary - c.addParent(ERC20Permit, [sanitize(name)]); + c.addParent(ERC20Permit, [name]); c.addOverride(ERC20Permit, functions.nonces); } diff --git a/packages/core/solidity/src/erc721.test.ts b/packages/core/solidity/src/erc721.test.ts index 1b26718e0..a63cc4b88 100644 --- a/packages/core/solidity/src/erc721.test.ts +++ b/packages/core/solidity/src/erc721.test.ts @@ -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/', }); diff --git a/packages/core/solidity/src/erc721.test.ts.md b/packages/core/solidity/src/erc721.test.ts.md index 5d41379eb..476956cf8 100644 --- a/packages/core/solidity/src/erc721.test.ts.md +++ b/packages/core/solidity/src/erc721.test.ts.md @@ -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.22;␊ + ␊ + import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";␊ + ␊ + contract MyTokec is ERC721 {␊ + constructor() ERC721(unicode"MyTokeć", "MTK") {}␊ + }␊ + ` + ## base uri > Snapshot 1 diff --git a/packages/core/solidity/src/erc721.test.ts.snap b/packages/core/solidity/src/erc721.test.ts.snap index 9b37e507800fee4c0a86be2042e3be759d7c2c62..cf49c267792aad6bb90e3fc63a7772d19ba399aa 100644 GIT binary patch delta 2142 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 K4aUr`SpWcll<0#1 diff --git a/packages/core/solidity/src/print.ts b/packages/core/solidity/src/print.ts index 11fce8d12..c9e85615a 100644 --- a/packages/core/solidity/src/print.ts +++ b/packages/core/solidity/src/print.ts @@ -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); @@ -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); } } diff --git a/packages/core/solidity/src/utils/sanitize.test.ts b/packages/core/solidity/src/utils/sanitize.test.ts new file mode 100644 index 000000000..327e5dc95 --- /dev/null +++ b/packages/core/solidity/src/utils/sanitize.test.ts @@ -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); + } +}); diff --git a/packages/core/solidity/src/utils/sanitize.ts b/packages/core/solidity/src/utils/sanitize.ts new file mode 100644 index 000000000..7830cc51f --- /dev/null +++ b/packages/core/solidity/src/utils/sanitize.ts @@ -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); +} From 7c9435694d606421be4b2b14a6cab5d46831d6aa Mon Sep 17 00:00:00 2001 From: CoveMB Date: Fri, 9 May 2025 17:40:27 -0400 Subject: [PATCH 4/6] add changeset --- .changeset/many-flowers-sniff.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/many-flowers-sniff.md diff --git a/.changeset/many-flowers-sniff.md b/.changeset/many-flowers-sniff.md new file mode 100644 index 000000000..985067c23 --- /dev/null +++ b/.changeset/many-flowers-sniff.md @@ -0,0 +1,5 @@ +--- +'@openzeppelin/wizard': patch +--- + +Support unicode for contract parent constructor values From ee9a89d7d9325c612b196a21ab89aec3bc5ab596 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 9 May 2025 17:54:28 -0400 Subject: [PATCH 5/6] Add changeset --- .changeset/light-regions-design.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/light-regions-design.md diff --git a/.changeset/light-regions-design.md b/.changeset/light-regions-design.md new file mode 100644 index 000000000..9887520fc --- /dev/null +++ b/.changeset/light-regions-design.md @@ -0,0 +1,5 @@ +--- +'@openzeppelin/wizard': patch +--- + +Use unicode syntax for strings with non-ASCII characters From 07ebc197253b744f8381a020c32e085c9dd854c5 Mon Sep 17 00:00:00 2001 From: CoveMB Date: Mon, 12 May 2025 09:50:20 -0400 Subject: [PATCH 6/6] remove duplicate changeset --- .changeset/many-flowers-sniff.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/many-flowers-sniff.md diff --git a/.changeset/many-flowers-sniff.md b/.changeset/many-flowers-sniff.md deleted file mode 100644 index 985067c23..000000000 --- a/.changeset/many-flowers-sniff.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@openzeppelin/wizard': patch ---- - -Support unicode for contract parent constructor values