From 98423970962ce1d2134d01133d19a09d55d1eedd Mon Sep 17 00:00:00 2001 From: typicalHuman Date: Fri, 24 Oct 2025 17:45:10 +0500 Subject: [PATCH 01/24] =?UTF-8?q?Support=20for=20ERC721=20with=20Mintable?= =?UTF-8?q?=20=E2=86=92=20Auto=20Increment=20IDs=20and=20Upgradeability,?= =?UTF-8?q?=20using=20=5FnextTokenId=20in=20namespaced=20storage=20for=20s?= =?UTF-8?q?afe=20upgradeable=20minting.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/rude-nails-mate.md | 5 + packages/core/solidity/package.json | 3 +- packages/core/solidity/src/contract.ts | 35 +++++++ packages/core/solidity/src/erc721.test.ts | 11 ++ packages/core/solidity/src/erc721.test.ts.md | 95 ++++++++++++++++++ .../core/solidity/src/erc721.test.ts.snap | Bin 2208 -> 2467 bytes packages/core/solidity/src/erc721.ts | 50 +++++++-- packages/core/solidity/src/print.ts | 19 +++- .../namespaced-storage-generator.test.ts | 23 +++++ .../src/utils/namespaced-storage-generator.ts | 26 +++++ 10 files changed, 257 insertions(+), 10 deletions(-) create mode 100644 .changeset/rude-nails-mate.md create mode 100644 packages/core/solidity/src/utils/namespaced-storage-generator.test.ts create mode 100644 packages/core/solidity/src/utils/namespaced-storage-generator.ts diff --git a/.changeset/rude-nails-mate.md b/.changeset/rude-nails-mate.md new file mode 100644 index 000000000..1d026f339 --- /dev/null +++ b/.changeset/rude-nails-mate.md @@ -0,0 +1,5 @@ +--- +'@openzeppelin/wizard': minor +--- + +Support for ERC721 with Mintable → Auto Increment IDs and Upgradeability, using \_nextTokenId in namespaced storage for safe upgradeable minting. diff --git a/packages/core/solidity/package.json b/packages/core/solidity/package.json index cb8a6ba91..90b60a4f3 100644 --- a/packages/core/solidity/package.json +++ b/packages/core/solidity/package.json @@ -30,10 +30,11 @@ "ava": "^6.0.0", "hardhat": "^2.22.11", "jszip": "^3.6.0", + "keccak": "^3.0.4", "rimraf": "^5.0.0", "semver": "^7.6.0", "solidity-ast": "^0.4.18", "ts-node": "^10.4.0", "typescript": "^5.0.0" } -} \ No newline at end of file +} diff --git a/packages/core/solidity/src/contract.ts b/packages/core/solidity/src/contract.ts index 6ed276124..11f5734bf 100644 --- a/packages/core/solidity/src/contract.ts +++ b/packages/core/solidity/src/contract.ts @@ -7,6 +7,7 @@ export interface Contract { natspecTags: NatspecTag[]; imports: ImportContract[]; functions: ContractFunction[]; + structs: ContractStruct[]; constructorCode: string[]; constructorArgs: FunctionArgument[]; variables: string[]; @@ -52,6 +53,12 @@ export interface ContractFunction extends BaseFunction { comments: string[]; } +export interface ContractStruct { + name: string; + comments: string[]; + variables: string[]; +} + export type FunctionKind = 'internal' | 'public'; export type FunctionMutability = (typeof mutabilityRank)[number]; @@ -86,6 +93,7 @@ export class ContractBuilder implements Contract { private parentMap: Map = new Map(); private functionMap: Map = new Map(); + private structMap: Map = new Map(); constructor(name: string) { this.name = toIdentifier(name, true); @@ -113,6 +121,10 @@ export class ContractBuilder implements Contract { return [...this.functionMap.values()]; } + get structs(): ContractStruct[] { + return [...this.structMap.values()]; + } + get variables(): string[] { return [...this.variableSet]; } @@ -172,6 +184,19 @@ export class ContractBuilder implements Contract { } } + private addStruct(_struct: ContractStruct): ContractStruct { + const got = this.structMap.get(_struct.name); + if (got !== undefined) { + return got; + } else { + const struct: ContractStruct = { + ..._struct, + }; + this.structMap.set(_struct.name, struct); + return struct; + } + } + addConstructorArgument(arg: FunctionArgument) { this.constructorArgs.push(arg); } @@ -219,4 +244,14 @@ export class ContractBuilder implements Contract { this.variableSet.add(code); return !present; } + + addStructVariable(baseStruct: ContractStruct, code: string): boolean { + let struct = this.structMap.get(baseStruct.name); + if (!struct) { + struct = this.addStruct(baseStruct); + } + const present = struct.variables.includes(code); + if (!present) struct.variables.push(code); + return !present; + } } diff --git a/packages/core/solidity/src/erc721.test.ts b/packages/core/solidity/src/erc721.test.ts index a63cc4b88..7f5b1381c 100644 --- a/packages/core/solidity/src/erc721.test.ts +++ b/packages/core/solidity/src/erc721.test.ts @@ -133,6 +133,17 @@ testERC721('full upgradeable uups + managed', { access: 'managed', }); +testERC721('full upgradeable uups + managed + incremental', { + mintable: true, + enumerable: true, + pausable: true, + burnable: true, + incremental:true, + votes: true, + upgradeable: 'uups', + access: 'managed', +}); + testAPIEquivalence('API default'); testAPIEquivalence('API basic', { name: 'CustomToken', symbol: 'CTK' }); diff --git a/packages/core/solidity/src/erc721.test.ts.md b/packages/core/solidity/src/erc721.test.ts.md index 5cbc3f8d9..840696afa 100644 --- a/packages/core/solidity/src/erc721.test.ts.md +++ b/packages/core/solidity/src/erc721.test.ts.md @@ -777,3 +777,98 @@ Generated by [AVA](https://avajs.dev). }␊ }␊ ` + +## full upgradeable uups + managed + incremental + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.4.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";␊ + import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";␊ + import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";␊ + import {ERC721BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";␊ + import {ERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";␊ + import {ERC721PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";␊ + import {ERC721VotesUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721VotesUpgradeable.sol";␊ + import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊ + import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊ + ␊ + contract MyToken is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721PausableUpgradeable, AccessManagedUpgradeable, ERC721BurnableUpgradeable, EIP712Upgradeable, ERC721VotesUpgradeable, UUPSUpgradeable {␊ + /// @custom:storage-location erc7201:myProject.MyToken␊ + struct Storage {␊ + uint256 _nextTokenId;␊ + }␊ + ␊ + bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ + ␊ + /// @custom:oz-upgrades-unsafe-allow constructor␊ + constructor() {␊ + _disableInitializers();␊ + }␊ + ␊ + function initialize(address initialAuthority) public initializer {␊ + __ERC721_init("MyToken", "MTK");␊ + __ERC721Enumerable_init();␊ + __ERC721Pausable_init();␊ + __AccessManaged_init(initialAuthority);␊ + __ERC721Burnable_init();␊ + __EIP712_init("MyToken", "1");␊ + __ERC721Votes_init();␊ + __UUPSUpgradeable_init();␊ + }␊ + ␊ + function pause() public restricted {␊ + _pause();␊ + }␊ + ␊ + function unpause() public restricted {␊ + _unpause();␊ + }␊ + ␊ + function safeMint(address to) public restricted returns (uint256) {␊ + Storage storage $ = _getStorage();␊ + uint256 tokenId = $._nextTokenId++;␊ + _safeMint(to, tokenId);␊ + return tokenId;␊ + }␊ + ␊ + function _getStorage() internal pure returns (Storage storage $) {␊ + assembly { $.slot := MYTOKEN_STORAGE_LOCATION}␊ + }␊ + ␊ + function _authorizeUpgrade(address newImplementation)␊ + internal␊ + override␊ + restricted␊ + {}␊ + ␊ + // The following functions are overrides required by Solidity.␊ + ␊ + function _update(address to, uint256 tokenId, address auth)␊ + internal␊ + override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721PausableUpgradeable, ERC721VotesUpgradeable)␊ + returns (address)␊ + {␊ + return super._update(to, tokenId, auth);␊ + }␊ + ␊ + function _increaseBalance(address account, uint128 value)␊ + internal␊ + override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721VotesUpgradeable)␊ + {␊ + super._increaseBalance(account, value);␊ + }␊ + ␊ + function supportsInterface(bytes4 interfaceId)␊ + public␊ + view␊ + override(ERC721Upgradeable, ERC721EnumerableUpgradeable)␊ + returns (bool)␊ + {␊ + return super.supportsInterface(interfaceId);␊ + }␊ + }␊ + ` diff --git a/packages/core/solidity/src/erc721.test.ts.snap b/packages/core/solidity/src/erc721.test.ts.snap index a2ed828abf8f15fc02f6192746c94ce9ce19f4ab..caf526bd045aa08c830620749276c88e395b4324 100644 GIT binary patch literal 2467 zcmV;U30(F;RzV5pu@g_e6lpz~446qK zA#ql_9M&p#m*bjrSNBv^C-IEtfH?3MAP$Jba^QfFIC0^yrC)Zbm* zWyi7O*v@t?QQTGUtKNI{ey^*XFFS4BRL|M3eup^bIzny8u!w)z#9SH z}8cCRjB!bOJj_V!j^; z=KJ=2FrPY{`Fy+hR{Z*xax-?@kRf1GEV13!g6;l%A8a>{>$$@_Hc>e?31~TnJ3Vd` zmH;LkQ37>f*%Sfu0#S-J1SsM*B@7f8ro305y#w#sD{q zz{Z4EYfpizdP6IM;32emcPaGFz^%g%0?vhg`Yt6rn}L9@_ax={%(f6M2QoUcMKU?E z1a6$r2@{nf$j~#WL!w5?K~*(v!aWTu)eUd~bsLSZQ6#r7a7uuHvByv@HnT0qMOnj< zq61Y_X!kf`Yo1un2MsNf)w0_MCpFuELTA=;*?4_t+`cyYN>3FX|T7%*;9cqYjx zi>i=Onu~LvQBIE=C!V(BWt51{@EIkVGXojLRnOV@5@}>C*NrrkxURT=4)Wnpwm_yS zMdCnpZaWe$3{1GY-7JHFLHX$SLQwvC{U0i?>XYlL@XCwPC`IudF?*uwW}xc#0HjuTYnjdaHyx#+~e;HY3}jkEX~bqnhRph zqWA)G1X=`Ld6D1HwsW(V2}ezQLYVj}CnF`1b-<2bi{<6*TT7m?xa5JQv!wbBNvhqp zO^Lh@SI19jsP^6?2^m z5)KX;ubO+ut>eS}J+K4Lk*dP;YVBzuAA68r8qlNAPzTyNuWJS-QHnC$tTd>uiyY;Q zLc)(t9TlGMy{Mn=H(T}R2aQ)Q>!LtEj{c)rF-eO7<32^xO`SSPe`#n3zx^#@f)$_O zFn3Vy(OA$MkiavfNw$iL5cc_pQw#e8PsAa^-cyJw!rZ$Lf1mO7n@HW}U6W$oyTL{x zuqen0jkl#x-N9N0pCNCX#aO~b|JtQwi8PPU{zNwudZ(wdgHokRab z7bce$I)4=0@wZcfx{agtO7%J~vN_h7qSBtl%`*zEZcp(h-g2H3>3r&#Z9LTMk{Al^ zy6Obj$(?qy#+64vcu<++({HW??#scqm@=cDd5aEcyTJ^WfqT%M*hU-CEVQ%G&O&?c zL;Fn-+8i5*acEeQ!)9Lzf&Hhc95%}W`%VHo^kUUX^-7A>MyC zn}`HS$_e%juK|MJ-ju6F&8wd8?E(>b`)EYn#I90W4e7Qmh!Y+=h~}p6`$!&2RxWH= zNrp?@KByZcqmzr`=UGKjz$dEBxM^&eZv#WHl1-exPzgGlVZ7v!lpwL5roX+1)N7gg0_6Ppg*mI9>g0rv^rb_ohgO1m>3v@E5^*B8ZtL6D*62kGnW& znJY4h-31ef%+!&AyL!7iSGC!#M7%s+=e^RqscGc;&-k^@VU@gt_mXz12)1oY(y0IN zUqbc!_Ybq`H>-ZL>Nl%?2V3#<^yp+n7I8%qWbRjqT?7gBvYIls zj^7MF$Z6arfEeL~`<)<&NUjV*WDp{Q5Mw}y(lrp`y89^~{YL;4|NbZg6d9n%07V8U zGC(n1K;eH+QAsydx4r_1s_WI2%9hbPqULL)^0IFUcdCaefcWLMAa>5Vl*3f2kWo0{;(l~=q8HAb=gpv^) zbdNI#v*3F)pEx%;&k*-NkJ~7H-hn4& hrkfmW?SQ`7;C%)hGvJs3$GHKH{|8&HP7yC@000;9t-b&N literal 2208 zcmV;R2w(R>RzVk@?;nc@00000000B+Tg`7Hw-sNncacWvp#^&AAwUSB2MLxmvAdi7Fcyf`>qXR9 zv1`jk(nADlhO!vXkh3Jmwsx_HqKEzi0eUEUNDny#1$xRQhnxZw0n%$P{YTncF&~^6 z&QP)}S+?YiFN+n)_r>>neD9I-MZafL^O%4BHzcsI5$ZvXP0R^&`G5-khc7G(3i!#& z&sJ}}am)W(xqIv9w^vpS103u<`?$V?O++}Vw=6^i?qkFrgLbP^G7PXq9Ty7Rvk^GO zVgPnsM1GH47ulGAElLD~rr_Yy`;B{zcS|mVM-Bv>+StNk2tIwc@vyP+V6#-hj!T&U zueSHM9&D_?27N{yP=24fh`fx*8Kxv>2zrc&5zdYNX>e+nH%lcs3ABeDe-^+Tgfqaa z5&!_0lyJd3Q&3i^MnjcyG`m~_<#y-8auvLKU3y)L48{p`kWa`X*rXOZfPJJf-!B#O zy?GPNXHI6m$S#o;zx$KkjDsHJ2zU%@Z1yrQ{Qo<&v5`#{>1@9FFeeU1$skp5Lvd7byLr^pgJbrYxW4faoIu+{vul*d9SNKhKr#*)>c!@s>$@m#`BLS`Pmj6S`N@R7to{i1;zb6=-&a@FdO6{?`kQXm4S1zC&dkh0ED1%3uh_Y%b z5v8-b^bzH7zjY95J6l9a=u93_k~IquQ3Cb+gD(?CW^&y`LyhaI=jS*dj%AZFT@w<= zs&m;$JaI4)!Ev(&#)9(BmkKEVy!^-f+(0SW?4FWIk_c#;h;t*FJq@sHAX=QUG1VwVNE*&VHp2Y+wxp`Im!;fObC2-Tj3U=I-7t!ra1!If*rw;S(qj z=n`}mBEMyAmKH4%P8;|Xm_#aPk&=lz;E7_3wYAMl3!W)l3Tf#gsQyTT>Y(Q_qVEE& z{85RUVj25=DxIg1uCql=NoDeQCs$10-9C}qbY4!s`fp{^{<-{x_FOh?5o`IGy`>1( zH$Pmv(Bt}Wz511c{J$(e^7kTLX9K0Aav^<7i8Q?BLfMa>oLvf<%CJ>!Z&0 zes_O&XB#{L$H+9{@y7l4D&^EbxjLptVWB?sY|*qFOp*}g1W{>RUY9t^5rahRQyW#D zZ9i`w?sU4%XYJNU0qdGVKVAJNt5TBIB;yfD(?y*+OTRj?gJ1p;F~^KgahNMe_gD(_ z4kYjhS(>9_U4ec1_O-#j9f~+1>_df^p{%`n_(zP-KSY|I7*K}A@B#;k!lI%lG~1EF zkY6&HBG%vnXG;2}aBK_xHc?!xdmS#eWZB3vb+R@2Nv=ITm)5kt?Hv0jI-x?F==@!A z$G=`nsoUCnu)cAg7kL8P+%VbD74(QfcQ7=#r!vHh;X1192kw*w{oom zPVE6EqHf`!Z(H}1(YlQZ3IwV99|qMOe1Q&J!Z}*kUeXXvvSgHJytX8sei*v$;r+rJP~k z3L7B!?0PmX~7u0!7UrIypx<~xJP#yY*VLIi|q#&uDW^}yA)YSs0J^V!d4Zen8iaEWG$G{0s-v^uj?cZHcTP1 zP)`P~>UMRha6EwbP7Ry;i1JD8G1T9FKyo0VeMKt{bH=gv-9_?$Z&&Fo{5m1mMh7(SPIDS;Am zCFr!}xOVtz#h*Jt!kGM^eu`Yw0!1xQ)B;m|mi>;l7#R0r@uzPnWx;>1qbyKf=FOJR zXdg0;$q|c!q$o&=g5+`vlABa2jEWyWx`H8jAiYpUgR(zL>5vG#RON&I?fAv&2R)6; iEFh+M;eI6*L?TyFLKG!LQ9?{nLi`_Tys?7zS^xl-WjZ?m diff --git a/packages/core/solidity/src/erc721.ts b/packages/core/solidity/src/erc721.ts index 2b945123f..101b49201 100644 --- a/packages/core/solidity/src/erc721.ts +++ b/packages/core/solidity/src/erc721.ts @@ -1,4 +1,4 @@ -import type { BaseFunction, Contract } from './contract'; +import type { BaseFunction, Contract, ContractStruct } from './contract'; import { ContractBuilder } from './contract'; import type { Access } from './set-access-control'; import { setAccessControl, requireAccessControl } from './set-access-control'; @@ -12,6 +12,7 @@ import { setInfo } from './set-info'; import { printContract } from './print'; import type { ClockMode } from './set-clock-mode'; import { clockModeDefault, setClockMode } from './set-clock-mode'; +import { generateNamespacesStorageSlot, getNamespacedStorageName } from './utils/namespaced-storage-generator'; export interface ERC721Options extends CommonOptions { name: string; @@ -99,7 +100,7 @@ export function buildERC721(opts: ERC721Options): Contract { } if (allOpts.mintable) { - addMintable(c, access, allOpts.incremental, allOpts.uriStorage); + addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable == 'uups'); } if (allOpts.votes) { @@ -174,14 +175,28 @@ function addBurnable(c: ContractBuilder) { }); } -function addMintable(c: ContractBuilder, access: Access, incremental = false, uriStorage = false) { +function addMintable(c: ContractBuilder, access: Access, incremental = false, uriStorage = false, uups = false) { const fn = getMintFunction(incremental, uriStorage); requireAccessControl(c, fn, access, 'MINTER', 'minter'); - if (incremental) { - c.addVariable('uint256 private _nextTokenId;'); - c.addFunctionCode('uint256 tokenId = _nextTokenId++;', fn); - c.addFunctionCode('_safeMint(to, tokenId);', fn); + if (!uups) { + c.addVariable('uint256 private _nextTokenId;'); + c.addFunctionCode('uint256 tokenId = _nextTokenId++;', fn); + c.addFunctionCode('_safeMint(to, tokenId);', fn); + } else { + const storageFn = getStorageFunction(); + const storageStruct = getStorageStruct(c.name); + const namespacedStorageName = `${c.name.toUpperCase()}_STORAGE_LOCATION`; + c.addStructVariable(storageStruct, 'uint256 _nextTokenId;'); + c.addVariable( + `bytes32 private constant ${namespacedStorageName} = ${generateNamespacesStorageSlot(getNamespacedStorageName(c.name))};`, + ); + c.addFunctionCode(`assembly { $.slot := ${namespacedStorageName}}`, storageFn); + + c.addFunctionCode('Storage storage $ = _getStorage();', fn); + c.addFunctionCode('uint256 tokenId = $._nextTokenId++;', fn); + c.addFunctionCode('_safeMint(to, tokenId);', fn); + } } else { c.addFunctionCode('_safeMint(to, tokenId);', fn); } @@ -264,3 +279,24 @@ function getMintFunction(incremental: boolean, uriStorage: boolean): BaseFunctio return fn; } + +function getStorageFunction(): BaseFunction { + const fn: BaseFunction = { + name: '_getStorage', + kind: 'internal' as const, + mutability: 'pure', + args: [], + returns: ['Storage storage $'], + }; + + return fn; +} + +function getStorageStruct(name: string) { + const struct: ContractStruct = { + name: 'Storage', + comments: [`/// @custom:storage-location erc7201:${getNamespacedStorageName(name)}`], + variables: [], + }; + return struct; +} diff --git a/packages/core/solidity/src/print.ts b/packages/core/solidity/src/print.ts index bc6202d7d..8f471b44e 100644 --- a/packages/core/solidity/src/print.ts +++ b/packages/core/solidity/src/print.ts @@ -6,6 +6,7 @@ import type { Value, NatspecTag, ImportContract, + ContractStruct, } from './contract'; import type { Options, Helpers } from './options'; import { withHelpers } from './options'; @@ -23,10 +24,9 @@ import { getCommunityContractsGitCommit } from './utils/community-contracts-git- export function printContract(contract: Contract, opts?: Options): string { const helpers = withHelpers(contract, opts); + const structs = contract.structs.map(_struct => printStruct(_struct)); const fns = mapValues(sortedFunctions(contract), fns => fns.map(fn => printFunction(fn, helpers))); - const hasOverrides = fns.override.some(l => l.length > 0); - return formatLines( ...spaceBetween( [ @@ -42,6 +42,7 @@ export function printContract(contract: Contract, opts?: Options): string { [`contract ${contract.name}`, ...printInheritance(contract, helpers), '{'].join(' '), spaceBetween( + ...structs, contract.variables, printConstructor(contract, helpers), ...fns.code, @@ -258,6 +259,20 @@ function printFunction2( return fn; } +function printStruct(_struct: ContractStruct): Lines[] { + const [comments, kindedName, code] = [_struct.comments, _struct.name, _struct.variables]; + const struct: Lines[] = [...comments]; + + const braces = code.length > 0 ? '{' : '{}'; + struct.push([`struct ${kindedName}`, braces].join(' ')); + + if (code.length > 0) { + struct.push(code, '}'); + } + + return struct; +} + function printArgument(arg: FunctionArgument, { transformName }: Helpers): string { let type: string; if (typeof arg.type === 'string') { diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts b/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts new file mode 100644 index 000000000..e48cddc8e --- /dev/null +++ b/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts @@ -0,0 +1,23 @@ +import test from 'ava'; +import { generateNamespacesStorageSlot } from './namespaced-storage-generator'; + +test('namespaced storage slot generation', t => { + const cases = [ + { + input: 'myProject.MyToken', + expected: '0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200', + }, + { + input: 'myProject.token', + expected: '0x86796099e489af07082cc4e6965fe431aadf035a7b4d4b46f81d8dfb81822d00', + }, + { + input: 'myProject.token123456', + expected: '0x824a9aeab482b3e91ee3e454c74509cca55ad57e0185a36d070359384be52800', + }, + ]; + + for (const { input, expected } of cases) { + t.is(generateNamespacesStorageSlot(input), expected); + } +}); diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.ts b/packages/core/solidity/src/utils/namespaced-storage-generator.ts new file mode 100644 index 000000000..cdfb9f19d --- /dev/null +++ b/packages/core/solidity/src/utils/namespaced-storage-generator.ts @@ -0,0 +1,26 @@ +import keccak from 'keccak'; + +export function getNamespacedStorageName(name: string) { + return `myProject.${name}`; +} + +export function generateNamespacesStorageSlot(inputString: string): string { + const innerHash = keccak('keccak256').update(inputString).digest('hex'); + const innerNum = BigInt('0x' + innerHash) - BigInt(1); + + const encoded = padTo32Bytes(innerNum); + + const outerHashBuffer = keccak('keccak256').update(encoded).digest(); + const outerHash = BigInt('0x' + outerHashBuffer.toString('hex')); + + const masked = outerHash & ~BigInt(0xff); + + return '0x' + masked.toString(16).padStart(64, '0'); +} + +function padTo32Bytes(b: bigint): Buffer { + let hex = b.toString(16); + if (hex.length > 64) throw new Error('Value too large for uint256'); + hex = hex.padStart(64, '0'); + return Buffer.from(hex, 'hex'); +} From 478eb878868b36e7beae183d318aee5b507623e2 Mon Sep 17 00:00:00 2001 From: typicalHuman Date: Fri, 24 Oct 2025 20:44:17 +0500 Subject: [PATCH 02/24] Formatting fixes --- packages/core/solidity/src/erc721.test.ts | 2 +- packages/core/solidity/src/erc721.ts | 2 +- .../core/solidity/src/utils/namespaced-storage-generator.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/solidity/src/erc721.test.ts b/packages/core/solidity/src/erc721.test.ts index 7f5b1381c..d9d77f776 100644 --- a/packages/core/solidity/src/erc721.test.ts +++ b/packages/core/solidity/src/erc721.test.ts @@ -138,7 +138,7 @@ testERC721('full upgradeable uups + managed + incremental', { enumerable: true, pausable: true, burnable: true, - incremental:true, + incremental: true, votes: true, upgradeable: 'uups', access: 'managed', diff --git a/packages/core/solidity/src/erc721.ts b/packages/core/solidity/src/erc721.ts index 101b49201..12e283881 100644 --- a/packages/core/solidity/src/erc721.ts +++ b/packages/core/solidity/src/erc721.ts @@ -100,7 +100,7 @@ export function buildERC721(opts: ERC721Options): Contract { } if (allOpts.mintable) { - addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable == 'uups'); + addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable === 'uups'); } if (allOpts.votes) { diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.ts b/packages/core/solidity/src/utils/namespaced-storage-generator.ts index cdfb9f19d..9d544a769 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-generator.ts +++ b/packages/core/solidity/src/utils/namespaced-storage-generator.ts @@ -1,7 +1,7 @@ import keccak from 'keccak'; -export function getNamespacedStorageName(name: string) { - return `myProject.${name}`; +export function getNamespacedStorageName(name: string, namespace: string = 'myProject') { + return `${namespace}.${name}`; } export function generateNamespacesStorageSlot(inputString: string): string { From 7069b484914698983d836f593cc5cb555dcf0d9c Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 24 Oct 2025 16:09:53 -0400 Subject: [PATCH 03/24] Use ethereum-cryptography, refactor --- package.json | 3 + packages/core/solidity/package.json | 6 +- packages/core/solidity/src/erc721.ts | 6 +- .../namespaced-storage-generator.test.ts | 4 +- .../src/utils/namespaced-storage-generator.ts | 33 +++-- tsconfig.base.json | 6 +- yarn.lock | 121 +++++++----------- 7 files changed, 73 insertions(+), 106 deletions(-) diff --git a/package.json b/package.json index 4f3757537..355458cac 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,9 @@ "**/typescript" ] }, + "resolutions": { + "ethereum-cryptography": "^3.2.0" + }, "devDependencies": { "@eslint/js": "^9.21.0", "concurrently": "^9.1.2", diff --git a/packages/core/solidity/package.json b/packages/core/solidity/package.json index 90b60a4f3..3cee624af 100644 --- a/packages/core/solidity/package.json +++ b/packages/core/solidity/package.json @@ -21,6 +21,9 @@ "test:watch": "ava --watch", "update-env": "rm ./src/environments/hardhat/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat && rm ./src/environments/hardhat/upgradeable/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat/upgradeable" }, + "dependencies": { + "ethereum-cryptography": "^3.2.0" + }, "devDependencies": { "@openzeppelin/community-contracts": "git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#b0ddd27", "@openzeppelin/contracts": "^5.4.0", @@ -30,11 +33,10 @@ "ava": "^6.0.0", "hardhat": "^2.22.11", "jszip": "^3.6.0", - "keccak": "^3.0.4", "rimraf": "^5.0.0", "semver": "^7.6.0", "solidity-ast": "^0.4.18", "ts-node": "^10.4.0", "typescript": "^5.0.0" } -} +} \ No newline at end of file diff --git a/packages/core/solidity/src/erc721.ts b/packages/core/solidity/src/erc721.ts index 12e283881..ee4e7345d 100644 --- a/packages/core/solidity/src/erc721.ts +++ b/packages/core/solidity/src/erc721.ts @@ -12,7 +12,7 @@ import { setInfo } from './set-info'; import { printContract } from './print'; import type { ClockMode } from './set-clock-mode'; import { clockModeDefault, setClockMode } from './set-clock-mode'; -import { generateNamespacesStorageSlot, getNamespacedStorageName } from './utils/namespaced-storage-generator'; +import { computeNamespacedStorageSlot, getNamespaceId } from './utils/namespaced-storage-generator'; export interface ERC721Options extends CommonOptions { name: string; @@ -189,7 +189,7 @@ function addMintable(c: ContractBuilder, access: Access, incremental = false, ur const namespacedStorageName = `${c.name.toUpperCase()}_STORAGE_LOCATION`; c.addStructVariable(storageStruct, 'uint256 _nextTokenId;'); c.addVariable( - `bytes32 private constant ${namespacedStorageName} = ${generateNamespacesStorageSlot(getNamespacedStorageName(c.name))};`, + `bytes32 private constant ${namespacedStorageName} = ${computeNamespacedStorageSlot(getNamespaceId(c.name))};`, ); c.addFunctionCode(`assembly { $.slot := ${namespacedStorageName}}`, storageFn); @@ -295,7 +295,7 @@ function getStorageFunction(): BaseFunction { function getStorageStruct(name: string) { const struct: ContractStruct = { name: 'Storage', - comments: [`/// @custom:storage-location erc7201:${getNamespacedStorageName(name)}`], + comments: [`/// @custom:storage-location erc7201:${getNamespaceId(name)}`], variables: [], }; return struct; diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts b/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts index e48cddc8e..0491360e6 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts +++ b/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts @@ -1,5 +1,5 @@ import test from 'ava'; -import { generateNamespacesStorageSlot } from './namespaced-storage-generator'; +import { computeNamespacedStorageSlot } from './namespaced-storage-generator'; test('namespaced storage slot generation', t => { const cases = [ @@ -18,6 +18,6 @@ test('namespaced storage slot generation', t => { ]; for (const { input, expected } of cases) { - t.is(generateNamespacesStorageSlot(input), expected); + t.is(computeNamespacedStorageSlot(input), expected); } }); diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.ts b/packages/core/solidity/src/utils/namespaced-storage-generator.ts index 9d544a769..140884577 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-generator.ts +++ b/packages/core/solidity/src/utils/namespaced-storage-generator.ts @@ -1,26 +1,23 @@ -import keccak from 'keccak'; -export function getNamespacedStorageName(name: string, namespace: string = 'myProject') { +import { keccak256 } from 'ethereum-cryptography/keccak'; +import { hexToBytes, toHex, utf8ToBytes } from 'ethereum-cryptography/utils'; + +export function getNamespaceId(name: string, namespace: string = 'myProject') { return `${namespace}.${name}`; } -export function generateNamespacesStorageSlot(inputString: string): string { - const innerHash = keccak('keccak256').update(inputString).digest('hex'); - const innerNum = BigInt('0x' + innerHash) - BigInt(1); - - const encoded = padTo32Bytes(innerNum); +/** + * Returns the ERC-7201 storage location for a given namespace id + */ +export function computeNamespacedStorageSlot(id: string): string { + const innerHash = keccak256(utf8ToBytes(id)); + const minusOne = BigInt('0x' + toHex(innerHash)) - 1n; + const minusOneBytes = hexToBytes(minusOne.toString(16).padStart(64, '0')); - const outerHashBuffer = keccak('keccak256').update(encoded).digest(); - const outerHash = BigInt('0x' + outerHashBuffer.toString('hex')); + const outerHash = keccak256(minusOneBytes); - const masked = outerHash & ~BigInt(0xff); + const mask = BigInt('0xff'); + const masked = BigInt('0x' + toHex(outerHash)) & ~mask; return '0x' + masked.toString(16).padStart(64, '0'); -} - -function padTo32Bytes(b: bigint): Buffer { - let hex = b.toString(16); - if (hex.length > 64) throw new Error('Value too large for uint256'); - hex = hex.padStart(64, '0'); - return Buffer.from(hex, 'hex'); -} +} \ No newline at end of file diff --git a/tsconfig.base.json b/tsconfig.base.json index a481b336f..beab1bc43 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,14 +1,14 @@ { "compilerOptions": { - "target": "es2018", + "target": "es2020", "strict": true, "noUncheckedIndexedAccess": true, "moduleResolution": "node", "esModuleInterop": true, "resolveJsonModule": true, "lib": [ - "es2019", - "es2019.array" + "es2020", + "es2020.bigint" ], "forceConsistentCasingInFileNames": true, "isolatedModules": true diff --git a/yarn.lock b/yarn.lock index bde08cb60..8d5332485 100644 --- a/yarn.lock +++ b/yarn.lock @@ -649,12 +649,17 @@ zod "^3.23.8" zod-to-json-schema "^3.24.1" -"@noble/curves@1.4.2", "@noble/curves@~1.4.0": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" - integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== +"@noble/ciphers@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.3.0.tgz#f64b8ff886c240e644e5573c097f86e5b43676dc" + integrity sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw== + +"@noble/curves@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.0.tgz#13e0ca8be4a0ce66c113693a94514e5599f40cfc" + integrity sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg== dependencies: - "@noble/hashes" "1.4.0" + "@noble/hashes" "1.8.0" "@noble/curves@~1.8.1": version "1.8.2" @@ -663,30 +668,22 @@ dependencies: "@noble/hashes" "1.7.2" -"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" - integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== - -"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" - integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== +"@noble/curves@~1.9.0": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.7.tgz#79d04b4758a43e4bca2cbdc62e7771352fa6b951" + integrity sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw== + dependencies: + "@noble/hashes" "1.8.0" "@noble/hashes@1.7.2", "@noble/hashes@~1.7.1": version "1.7.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== -"@noble/secp256k1@1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" - integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== - -"@noble/secp256k1@~1.7.0": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.2.tgz#c2c3343e2dce80e15a914d7442147507f8a98e7f" - integrity sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ== +"@noble/hashes@1.8.0", "@noble/hashes@~1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -807,7 +804,6 @@ "@openzeppelin/community-contracts@git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#b0ddd27": version "0.0.1" - uid b0ddd275a4c5d87ba2ded654b33e08db85094588 resolved "git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#b0ddd275a4c5d87ba2ded654b33e08db85094588" "@openzeppelin/contracts-upgradeable@^5.4.0": @@ -1019,49 +1015,27 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz#5b2dd648a960b8fa00d76f2cc4eea2f03daa80f4" integrity sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w== -"@scure/base@~1.1.0", "@scure/base@~1.1.6": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" - integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== - "@scure/base@~1.2.5": version "1.2.6" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== -"@scure/bip32@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" - integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== - dependencies: - "@noble/hashes" "~1.2.0" - "@noble/secp256k1" "~1.7.0" - "@scure/base" "~1.1.0" - -"@scure/bip32@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" - integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg== +"@scure/bip32@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.7.0.tgz#b8683bab172369f988f1589640e53c4606984219" + integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw== dependencies: - "@noble/curves" "~1.4.0" - "@noble/hashes" "~1.4.0" - "@scure/base" "~1.1.6" - -"@scure/bip39@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" - integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== - dependencies: - "@noble/hashes" "~1.2.0" - "@scure/base" "~1.1.0" + "@noble/curves" "~1.9.0" + "@noble/hashes" "~1.8.0" + "@scure/base" "~1.2.5" -"@scure/bip39@1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" - integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ== +"@scure/bip39@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.6.0.tgz#475970ace440d7be87a6086cbee77cb8f1a684f9" + integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A== dependencies: - "@noble/hashes" "~1.4.0" - "@scure/base" "~1.1.6" + "@noble/hashes" "~1.8.0" + "@scure/base" "~1.2.5" "@sentry/core@5.30.0": version "5.30.0" @@ -2725,25 +2699,16 @@ etag@^1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -ethereum-cryptography@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" - integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== - dependencies: - "@noble/hashes" "1.2.0" - "@noble/secp256k1" "1.7.1" - "@scure/bip32" "1.1.5" - "@scure/bip39" "1.1.1" - -ethereum-cryptography@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" - integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== - dependencies: - "@noble/curves" "1.4.2" - "@noble/hashes" "1.4.0" - "@scure/bip32" "1.4.0" - "@scure/bip39" "1.3.0" +ethereum-cryptography@^1.0.3, ethereum-cryptography@^2.2.1, ethereum-cryptography@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-3.2.0.tgz#42a04b57834bf536e552b50a70b9ee5057c71dc6" + integrity sha512-Urr5YVsalH+Jo0sYkTkv1MyI9bLYZwW8BENZCeE1QYaTHETEYx0Nv/SVsWkSqpYrzweg6d8KMY1wTjH/1m/BIg== + dependencies: + "@noble/ciphers" "1.3.0" + "@noble/curves" "1.9.0" + "@noble/hashes" "1.8.0" + "@scure/bip32" "1.7.0" + "@scure/bip39" "1.6.0" eventemitter3@^4.0.4: version "4.0.7" From 5ed815d31e60b4243f7560658e87e6d308e665ab Mon Sep 17 00:00:00 2001 From: typicalHuman Date: Sat, 25 Oct 2025 11:43:28 +0500 Subject: [PATCH 04/24] Suggested fixes --- .changeset/rude-nails-mate.md | 2 +- packages/core/solidity/src/contract.ts | 2 +- packages/core/solidity/src/erc721.test.ts.md | 9 ++-- .../core/solidity/src/erc721.test.ts.snap | Bin 2467 -> 2510 bytes packages/core/solidity/src/erc721.ts | 42 +++--------------- .../utils/namespaced-storage-functionality.ts | 40 +++++++++++++++++ .../src/utils/namespaced-storage-generator.ts | 1 - 7 files changed, 53 insertions(+), 43 deletions(-) create mode 100644 packages/core/solidity/src/utils/namespaced-storage-functionality.ts diff --git a/.changeset/rude-nails-mate.md b/.changeset/rude-nails-mate.md index 1d026f339..ff22c49f7 100644 --- a/.changeset/rude-nails-mate.md +++ b/.changeset/rude-nails-mate.md @@ -2,4 +2,4 @@ '@openzeppelin/wizard': minor --- -Support for ERC721 with Mintable → Auto Increment IDs and Upgradeability, using \_nextTokenId in namespaced storage for safe upgradeable minting. +**Breaking change**: For ERC-721, use namespaced storage for `_nextTokenId` when mintable, auto increment IDs, and upgradeability are enabled together. \ No newline at end of file diff --git a/packages/core/solidity/src/contract.ts b/packages/core/solidity/src/contract.ts index 11f5734bf..b118dba2c 100644 --- a/packages/core/solidity/src/contract.ts +++ b/packages/core/solidity/src/contract.ts @@ -59,7 +59,7 @@ export interface ContractStruct { variables: string[]; } -export type FunctionKind = 'internal' | 'public'; +export type FunctionKind = 'internal' | 'public' | 'private'; export type FunctionMutability = (typeof mutabilityRank)[number]; // Order is important diff --git a/packages/core/solidity/src/erc721.test.ts.md b/packages/core/solidity/src/erc721.test.ts.md index 840696afa..874b0b009 100644 --- a/packages/core/solidity/src/erc721.test.ts.md +++ b/packages/core/solidity/src/erc721.test.ts.md @@ -798,10 +798,11 @@ Generated by [AVA](https://avajs.dev). ␊ contract MyToken is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721PausableUpgradeable, AccessManagedUpgradeable, ERC721BurnableUpgradeable, EIP712Upgradeable, ERC721VotesUpgradeable, UUPSUpgradeable {␊ /// @custom:storage-location erc7201:myProject.MyToken␊ - struct Storage {␊ + struct MyTokenStorage {␊ uint256 _nextTokenId;␊ }␊ ␊ + // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff));␊ bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ ␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ @@ -829,14 +830,14 @@ Generated by [AVA](https://avajs.dev). }␊ ␊ function safeMint(address to) public restricted returns (uint256) {␊ - Storage storage $ = _getStorage();␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ uint256 tokenId = $._nextTokenId++;␊ _safeMint(to, tokenId);␊ return tokenId;␊ }␊ ␊ - function _getStorage() internal pure returns (Storage storage $) {␊ - assembly { $.slot := MYTOKEN_STORAGE_LOCATION}␊ + function _getMyTokenStorage() private pure returns (MyTokenStorage storage $) {␊ + assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ }␊ ␊ function _authorizeUpgrade(address newImplementation)␊ diff --git a/packages/core/solidity/src/erc721.test.ts.snap b/packages/core/solidity/src/erc721.test.ts.snap index caf526bd045aa08c830620749276c88e395b4324..2d9645e0079b653f43f7adb0acec2bfa3e384703 100644 GIT binary patch literal 2510 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 diff --git a/packages/core/solidity/src/erc721.ts b/packages/core/solidity/src/erc721.ts index ee4e7345d..30ac558fa 100644 --- a/packages/core/solidity/src/erc721.ts +++ b/packages/core/solidity/src/erc721.ts @@ -1,4 +1,4 @@ -import type { BaseFunction, Contract, ContractStruct } from './contract'; +import type { BaseFunction, Contract } from './contract'; import { ContractBuilder } from './contract'; import type { Access } from './set-access-control'; import { setAccessControl, requireAccessControl } from './set-access-control'; @@ -12,7 +12,7 @@ import { setInfo } from './set-info'; import { printContract } from './print'; import type { ClockMode } from './set-clock-mode'; import { clockModeDefault, setClockMode } from './set-clock-mode'; -import { computeNamespacedStorageSlot, getNamespaceId } from './utils/namespaced-storage-generator'; +import { addNamespacedFunctionImplementation } from './utils/namespaced-storage-functionality'; export interface ERC721Options extends CommonOptions { name: string; @@ -100,7 +100,7 @@ export function buildERC721(opts: ERC721Options): Contract { } if (allOpts.mintable) { - addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable === 'uups'); + addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable !== false); } if (allOpts.votes) { @@ -175,25 +175,16 @@ function addBurnable(c: ContractBuilder) { }); } -function addMintable(c: ContractBuilder, access: Access, incremental = false, uriStorage = false, uups = false) { +function addMintable(c: ContractBuilder, access: Access, incremental = false, uriStorage = false, proxy = false) { const fn = getMintFunction(incremental, uriStorage); requireAccessControl(c, fn, access, 'MINTER', 'minter'); if (incremental) { - if (!uups) { + if (!proxy) { c.addVariable('uint256 private _nextTokenId;'); c.addFunctionCode('uint256 tokenId = _nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } else { - const storageFn = getStorageFunction(); - const storageStruct = getStorageStruct(c.name); - const namespacedStorageName = `${c.name.toUpperCase()}_STORAGE_LOCATION`; - c.addStructVariable(storageStruct, 'uint256 _nextTokenId;'); - c.addVariable( - `bytes32 private constant ${namespacedStorageName} = ${computeNamespacedStorageSlot(getNamespaceId(c.name))};`, - ); - c.addFunctionCode(`assembly { $.slot := ${namespacedStorageName}}`, storageFn); - - c.addFunctionCode('Storage storage $ = _getStorage();', fn); + addNamespacedFunctionImplementation(c, fn); c.addFunctionCode('uint256 tokenId = $._nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } @@ -279,24 +270,3 @@ function getMintFunction(incremental: boolean, uriStorage: boolean): BaseFunctio return fn; } - -function getStorageFunction(): BaseFunction { - const fn: BaseFunction = { - name: '_getStorage', - kind: 'internal' as const, - mutability: 'pure', - args: [], - returns: ['Storage storage $'], - }; - - return fn; -} - -function getStorageStruct(name: string) { - const struct: ContractStruct = { - name: 'Storage', - comments: [`/// @custom:storage-location erc7201:${getNamespaceId(name)}`], - variables: [], - }; - return struct; -} diff --git a/packages/core/solidity/src/utils/namespaced-storage-functionality.ts b/packages/core/solidity/src/utils/namespaced-storage-functionality.ts new file mode 100644 index 000000000..048e81901 --- /dev/null +++ b/packages/core/solidity/src/utils/namespaced-storage-functionality.ts @@ -0,0 +1,40 @@ +import { BaseFunction, ContractBuilder, ContractStruct } from '../contract'; +import { computeNamespacedStorageSlot, getNamespaceId } from './namespaced-storage-generator'; + +export function addNamespacedFunctionImplementation( + c: ContractBuilder, + fn: BaseFunction, + namespace: string = 'myProject', +) { + const storageFn = getStorageFunction(c.name); + const storageStruct = getStorageStruct(c.name, namespace); + const namespacedStorageName = `${c.name.toUpperCase()}_STORAGE_LOCATION`; + const namespaceId = getNamespaceId(c.name, namespace); + c.addStructVariable(storageStruct, 'uint256 _nextTokenId;'); + c.addVariable(`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff));`); + c.addVariable(`bytes32 private constant ${namespacedStorageName} = ${computeNamespacedStorageSlot(namespaceId)};`); + c.addFunctionCode(`assembly { $.slot := ${namespacedStorageName} }`, storageFn); + + c.addFunctionCode(`${c.name}Storage storage $ = ${storageFn.name}();`, fn); +} + +function getStorageFunction(name: string): BaseFunction { + const fn: BaseFunction = { + name: `_get${name}Storage`, + kind: 'private' as const, + mutability: 'pure', + args: [], + returns: [`${name}Storage storage $`], + }; + + return fn; +} + +function getStorageStruct(name: string, namespace: string) { + const struct: ContractStruct = { + name: `${name}Storage`, + comments: [`/// @custom:storage-location erc7201:${getNamespaceId(name, namespace)}`], + variables: [], + }; + return struct; +} diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.ts b/packages/core/solidity/src/utils/namespaced-storage-generator.ts index 140884577..4272739f4 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-generator.ts +++ b/packages/core/solidity/src/utils/namespaced-storage-generator.ts @@ -1,4 +1,3 @@ - import { keccak256 } from 'ethereum-cryptography/keccak'; import { hexToBytes, toHex, utf8ToBytes } from 'ethereum-cryptography/utils'; From a282e2c3b49a3dd9c843cd38bf7119c2c54b886c Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 27 Oct 2025 11:07:53 -0400 Subject: [PATCH 05/24] Refactor, cleanup --- packages/core/solidity/src/erc721.test.ts.md | 4 ++-- .../core/solidity/src/erc721.test.ts.snap | Bin 2510 -> 2509 bytes packages/core/solidity/src/erc721.ts | 12 +++++----- .../utils/namespaced-storage-functionality.ts | 21 +++++++++++------- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/core/solidity/src/erc721.test.ts.md b/packages/core/solidity/src/erc721.test.ts.md index 874b0b009..0cf4df410 100644 --- a/packages/core/solidity/src/erc721.test.ts.md +++ b/packages/core/solidity/src/erc721.test.ts.md @@ -802,7 +802,7 @@ Generated by [AVA](https://avajs.dev). uint256 _nextTokenId;␊ }␊ ␊ - // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff));␊ + // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ ␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ @@ -837,7 +837,7 @@ Generated by [AVA](https://avajs.dev). }␊ ␊ function _getMyTokenStorage() private pure returns (MyTokenStorage storage $) {␊ - assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ + assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ }␊ ␊ function _authorizeUpgrade(address newImplementation)␊ diff --git a/packages/core/solidity/src/erc721.test.ts.snap b/packages/core/solidity/src/erc721.test.ts.snap index 2d9645e0079b653f43f7adb0acec2bfa3e384703..a60e7035891f2bd40463a765df2025ba9560163e 100644 GIT binary patch delta 2488 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 D`s>*l diff --git a/packages/core/solidity/src/erc721.ts b/packages/core/solidity/src/erc721.ts index 30ac558fa..f6f8135c1 100644 --- a/packages/core/solidity/src/erc721.ts +++ b/packages/core/solidity/src/erc721.ts @@ -7,12 +7,12 @@ import { supportsInterface } from './common-functions'; import { defineFunctions } from './utils/define-functions'; import type { CommonOptions } from './common-options'; import { withCommonDefaults, defaults as commonDefaults } from './common-options'; -import { setUpgradeable } from './set-upgradeable'; +import { setUpgradeable, Upgradeable } from './set-upgradeable'; import { setInfo } from './set-info'; import { printContract } from './print'; import type { ClockMode } from './set-clock-mode'; import { clockModeDefault, setClockMode } from './set-clock-mode'; -import { addNamespacedFunctionImplementation } from './utils/namespaced-storage-functionality'; +import { setNamespacedStorage } from './utils/namespaced-storage-functionality'; export interface ERC721Options extends CommonOptions { name: string; @@ -100,7 +100,7 @@ export function buildERC721(opts: ERC721Options): Contract { } if (allOpts.mintable) { - addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable !== false); + addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable); } if (allOpts.votes) { @@ -175,16 +175,16 @@ function addBurnable(c: ContractBuilder) { }); } -function addMintable(c: ContractBuilder, access: Access, incremental = false, uriStorage = false, proxy = false) { +function addMintable(c: ContractBuilder, access: Access, incremental = false, uriStorage = false, upgradeable: Upgradeable) { const fn = getMintFunction(incremental, uriStorage); requireAccessControl(c, fn, access, 'MINTER', 'minter'); if (incremental) { - if (!proxy) { + if (!upgradeable) { c.addVariable('uint256 private _nextTokenId;'); c.addFunctionCode('uint256 tokenId = _nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } else { - addNamespacedFunctionImplementation(c, fn); + setNamespacedStorage(c, fn, ['uint256 _nextTokenId;']); c.addFunctionCode('uint256 tokenId = $._nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } diff --git a/packages/core/solidity/src/utils/namespaced-storage-functionality.ts b/packages/core/solidity/src/utils/namespaced-storage-functionality.ts index 048e81901..abce52a3a 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-functionality.ts +++ b/packages/core/solidity/src/utils/namespaced-storage-functionality.ts @@ -1,24 +1,29 @@ import { BaseFunction, ContractBuilder, ContractStruct } from '../contract'; import { computeNamespacedStorageSlot, getNamespaceId } from './namespaced-storage-generator'; -export function addNamespacedFunctionImplementation( +/** + * Sets namespaced variables in storage struct, and adds a function to retrieve namespaced storage. + */ +export function setNamespacedStorage( c: ContractBuilder, fn: BaseFunction, + structVariables: string[], namespace: string = 'myProject', ) { - const storageFn = getStorageFunction(c.name); - const storageStruct = getStorageStruct(c.name, namespace); + const storageFn = makeStorageFunction(c.name); + const storageStruct = makeStorageStruct(c.name, namespace); const namespacedStorageName = `${c.name.toUpperCase()}_STORAGE_LOCATION`; const namespaceId = getNamespaceId(c.name, namespace); - c.addStructVariable(storageStruct, 'uint256 _nextTokenId;'); - c.addVariable(`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff));`); + structVariables.forEach((v) => c.addStructVariable(storageStruct, v)); + + c.addVariable(`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`); c.addVariable(`bytes32 private constant ${namespacedStorageName} = ${computeNamespacedStorageSlot(namespaceId)};`); - c.addFunctionCode(`assembly { $.slot := ${namespacedStorageName} }`, storageFn); + c.addFunctionCode(`assembly { $.slot := ${namespacedStorageName} }`, storageFn); c.addFunctionCode(`${c.name}Storage storage $ = ${storageFn.name}();`, fn); } -function getStorageFunction(name: string): BaseFunction { +function makeStorageFunction(name: string): BaseFunction { const fn: BaseFunction = { name: `_get${name}Storage`, kind: 'private' as const, @@ -30,7 +35,7 @@ function getStorageFunction(name: string): BaseFunction { return fn; } -function getStorageStruct(name: string, namespace: string) { +function makeStorageStruct(name: string, namespace: string) { const struct: ContractStruct = { name: `${name}Storage`, comments: [`/// @custom:storage-location erc7201:${getNamespaceId(name, namespace)}`], From 9e1c58601fd01879827be23eb570d89d024f4489 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 27 Oct 2025 11:19:08 -0400 Subject: [PATCH 06/24] lint --- packages/core/solidity/src/erc721.ts | 11 +++++++++-- .../src/utils/namespaced-storage-functionality.ts | 4 ++-- .../src/utils/namespaced-storage-generator.ts | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/core/solidity/src/erc721.ts b/packages/core/solidity/src/erc721.ts index f6f8135c1..6bfe58ea2 100644 --- a/packages/core/solidity/src/erc721.ts +++ b/packages/core/solidity/src/erc721.ts @@ -7,7 +7,8 @@ import { supportsInterface } from './common-functions'; import { defineFunctions } from './utils/define-functions'; import type { CommonOptions } from './common-options'; import { withCommonDefaults, defaults as commonDefaults } from './common-options'; -import { setUpgradeable, Upgradeable } from './set-upgradeable'; +import { setUpgradeable } from './set-upgradeable'; +import type { Upgradeable } from './set-upgradeable'; import { setInfo } from './set-info'; import { printContract } from './print'; import type { ClockMode } from './set-clock-mode'; @@ -175,7 +176,13 @@ function addBurnable(c: ContractBuilder) { }); } -function addMintable(c: ContractBuilder, access: Access, incremental = false, uriStorage = false, upgradeable: Upgradeable) { +function addMintable( + c: ContractBuilder, + access: Access, + incremental = false, + uriStorage = false, + upgradeable: Upgradeable, +) { const fn = getMintFunction(incremental, uriStorage); requireAccessControl(c, fn, access, 'MINTER', 'minter'); if (incremental) { diff --git a/packages/core/solidity/src/utils/namespaced-storage-functionality.ts b/packages/core/solidity/src/utils/namespaced-storage-functionality.ts index abce52a3a..58ac9d622 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-functionality.ts +++ b/packages/core/solidity/src/utils/namespaced-storage-functionality.ts @@ -1,4 +1,4 @@ -import { BaseFunction, ContractBuilder, ContractStruct } from '../contract'; +import type { BaseFunction, ContractBuilder, ContractStruct } from '../contract'; import { computeNamespacedStorageSlot, getNamespaceId } from './namespaced-storage-generator'; /** @@ -14,7 +14,7 @@ export function setNamespacedStorage( const storageStruct = makeStorageStruct(c.name, namespace); const namespacedStorageName = `${c.name.toUpperCase()}_STORAGE_LOCATION`; const namespaceId = getNamespaceId(c.name, namespace); - structVariables.forEach((v) => c.addStructVariable(storageStruct, v)); + structVariables.forEach(v => c.addStructVariable(storageStruct, v)); c.addVariable(`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`); c.addVariable(`bytes32 private constant ${namespacedStorageName} = ${computeNamespacedStorageSlot(namespaceId)};`); diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.ts b/packages/core/solidity/src/utils/namespaced-storage-generator.ts index 4272739f4..96116bde9 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-generator.ts +++ b/packages/core/solidity/src/utils/namespaced-storage-generator.ts @@ -19,4 +19,4 @@ export function computeNamespacedStorageSlot(id: string): string { const masked = BigInt('0x' + toHex(outerHash)) & ~mask; return '0x' + masked.toString(16).padStart(64, '0'); -} \ No newline at end of file +} From f8086dc7ab6a4a32d2dc1112a4c4f79953604df7 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 27 Oct 2025 14:06:09 -0400 Subject: [PATCH 07/24] Add namespace prefix option, refactor --- .../common/src/ai/descriptions/solidity.ts | 1 + packages/core/solidity/src/erc721.test.ts | 25 +++ packages/core/solidity/src/erc721.test.ts.md | 211 ++++++++++++++++++ .../core/solidity/src/erc721.test.ts.snap | Bin 2509 -> 2922 bytes packages/core/solidity/src/erc721.ts | 10 +- packages/core/solidity/src/generate/erc721.ts | 1 + .../solidity/src/set-namespaced-storage.ts | 58 +++++ ...erator.test.ts => namespaced-slot.test.ts} | 12 +- ...torage-generator.ts => namespaced-slot.ts} | 4 - .../utils/namespaced-storage-functionality.ts | 45 ---- packages/mcp/src/solidity/schemas.ts | 1 + .../mcp/src/solidity/tools/erc721.test.ts | 1 + packages/mcp/src/solidity/tools/erc721.ts | 2 + .../function-definitions/shared.ts | 2 +- .../function-definitions/solidity.ts | 5 + 15 files changed, 323 insertions(+), 55 deletions(-) create mode 100644 packages/core/solidity/src/set-namespaced-storage.ts rename packages/core/solidity/src/utils/{namespaced-storage-generator.test.ts => namespaced-slot.test.ts} (60%) rename packages/core/solidity/src/utils/{namespaced-storage-generator.ts => namespaced-slot.ts} (84%) delete mode 100644 packages/core/solidity/src/utils/namespaced-storage-functionality.ts diff --git a/packages/common/src/ai/descriptions/solidity.ts b/packages/common/src/ai/descriptions/solidity.ts index cd87ad1fd..47cd13d25 100644 --- a/packages/common/src/ai/descriptions/solidity.ts +++ b/packages/common/src/ai/descriptions/solidity.ts @@ -18,6 +18,7 @@ export const solidityCommonDescriptions = { 'The type of access control to provision. Ownable is a simple mechanism with a single account authorized for all privileged actions. Roles is a flexible mechanism with a separate role for each privileged action. A role can have many authorized accounts. Managed enables a central contract to define a policy that allows certain callers to access certain functions.', upgradeable: 'Whether the smart contract is upgradeable. Transparent uses more complex proxy with higher overhead, requires less changes in your contract. Can also be used with beacons. UUPS uses simpler proxy with less overhead, requires including extra code in your contract. Allows flexibility for authorizing upgrades.', + namespacePrefix: 'The prefix for the namespace of the contract. Only used if the contract has storage variables and upgradeability is enabled. Default is "myProject"', }; export const solidityERC20Descriptions = { diff --git a/packages/core/solidity/src/erc721.test.ts b/packages/core/solidity/src/erc721.test.ts index d9d77f776..372dbbfc3 100644 --- a/packages/core/solidity/src/erc721.test.ts +++ b/packages/core/solidity/src/erc721.test.ts @@ -105,6 +105,31 @@ testERC721('votes + timestamp', { votes: 'timestamp', }); +testERC721('custom name + upgradeable uups + mintable + incremental', { + name: 'My NFT Token', + mintable: true, + incremental: true, + votes: true, + upgradeable: 'uups', +}); + +testERC721('custom name + upgradeable transparent + mintable + incremental + namespacePrefix', { + name: 'My NFT Token', + mintable: true, + incremental: true, + votes: true, + upgradeable: 'transparent', + namespacePrefix: 'myNftProject', +}); + +testERC721('upgradeable transparent + mintable + incremental + empty namespacePrefix', { + mintable: true, + incremental: true, + votes: true, + upgradeable: 'transparent', + namespacePrefix: '', +}); + testERC721('full upgradeable transparent', { mintable: true, enumerable: true, diff --git a/packages/core/solidity/src/erc721.test.ts.md b/packages/core/solidity/src/erc721.test.ts.md index 0cf4df410..c11019b0f 100644 --- a/packages/core/solidity/src/erc721.test.ts.md +++ b/packages/core/solidity/src/erc721.test.ts.md @@ -543,6 +543,217 @@ Generated by [AVA](https://avajs.dev). }␊ ` +## custom name + upgradeable uups + mintable + incremental + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.4.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";␊ + import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";␊ + import {ERC721VotesUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721VotesUpgradeable.sol";␊ + import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊ + import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";␊ + import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊ + ␊ + contract MyNFTToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, EIP712Upgradeable, ERC721VotesUpgradeable, UUPSUpgradeable {␊ + /// @custom:storage-location erc7201:myProject.MyNFTToken␊ + struct MyNFTTokenStorage {␊ + uint256 _nextTokenId;␊ + }␊ + ␊ + // keccak256(abi.encode(uint256(keccak256("myProject.MyNFTToken")) - 1)) & ~bytes32(uint256(0xff))␊ + bytes32 private constant MYNFTTOKEN_STORAGE_LOCATION = 0x32ea4af318b6c543ad94e548435f2d525d7e5150dce5b73780df1d77bd4d8c00;␊ + ␊ + /// @custom:oz-upgrades-unsafe-allow constructor␊ + constructor() {␊ + _disableInitializers();␊ + }␊ + ␊ + function initialize(address initialOwner) public initializer {␊ + __ERC721_init("My NFT Token", "MTK");␊ + __Ownable_init(initialOwner);␊ + __EIP712_init("My NFT Token", "1");␊ + __ERC721Votes_init();␊ + __UUPSUpgradeable_init();␊ + }␊ + ␊ + function safeMint(address to) public onlyOwner returns (uint256) {␊ + MyNFTTokenStorage storage $ = _getMyNFTTokenStorage();␊ + uint256 tokenId = $._nextTokenId++;␊ + _safeMint(to, tokenId);␊ + return tokenId;␊ + }␊ + ␊ + function _getMyNFTTokenStorage()␊ + private␊ + pure␊ + returns (MyNFTTokenStorage storage $)␊ + {␊ + assembly { $.slot := MYNFTTOKEN_STORAGE_LOCATION }␊ + }␊ + ␊ + function _authorizeUpgrade(address newImplementation)␊ + internal␊ + override␊ + onlyOwner␊ + {}␊ + ␊ + // The following functions are overrides required by Solidity.␊ + ␊ + function _update(address to, uint256 tokenId, address auth)␊ + internal␊ + override(ERC721Upgradeable, ERC721VotesUpgradeable)␊ + returns (address)␊ + {␊ + return super._update(to, tokenId, auth);␊ + }␊ + ␊ + function _increaseBalance(address account, uint128 value)␊ + internal␊ + override(ERC721Upgradeable, ERC721VotesUpgradeable)␊ + {␊ + super._increaseBalance(account, value);␊ + }␊ + }␊ + ` + +## custom name + upgradeable transparent + mintable + incremental + namespacePrefix + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.4.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";␊ + import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";␊ + import {ERC721VotesUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721VotesUpgradeable.sol";␊ + import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊ + import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";␊ + ␊ + contract MyNFTToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, EIP712Upgradeable, ERC721VotesUpgradeable {␊ + /// @custom:storage-location erc7201:myNftProject.MyNFTToken␊ + struct MyNFTTokenStorage {␊ + uint256 _nextTokenId;␊ + }␊ + ␊ + // keccak256(abi.encode(uint256(keccak256("myNftProject.MyNFTToken")) - 1)) & ~bytes32(uint256(0xff))␊ + bytes32 private constant MYNFTTOKEN_STORAGE_LOCATION = 0xbc340a7d926d36b5de4ded13cac8359cd837661c277311536754e62ca069dd00;␊ + ␊ + /// @custom:oz-upgrades-unsafe-allow constructor␊ + constructor() {␊ + _disableInitializers();␊ + }␊ + ␊ + function initialize(address initialOwner) public initializer {␊ + __ERC721_init("My NFT Token", "MTK");␊ + __Ownable_init(initialOwner);␊ + __EIP712_init("My NFT Token", "1");␊ + __ERC721Votes_init();␊ + }␊ + ␊ + function safeMint(address to) public onlyOwner returns (uint256) {␊ + MyNFTTokenStorage storage $ = _getMyNFTTokenStorage();␊ + uint256 tokenId = $._nextTokenId++;␊ + _safeMint(to, tokenId);␊ + return tokenId;␊ + }␊ + ␊ + function _getMyNFTTokenStorage()␊ + private␊ + pure␊ + returns (MyNFTTokenStorage storage $)␊ + {␊ + assembly { $.slot := MYNFTTOKEN_STORAGE_LOCATION }␊ + }␊ + ␊ + // The following functions are overrides required by Solidity.␊ + ␊ + function _update(address to, uint256 tokenId, address auth)␊ + internal␊ + override(ERC721Upgradeable, ERC721VotesUpgradeable)␊ + returns (address)␊ + {␊ + return super._update(to, tokenId, auth);␊ + }␊ + ␊ + function _increaseBalance(address account, uint128 value)␊ + internal␊ + override(ERC721Upgradeable, ERC721VotesUpgradeable)␊ + {␊ + super._increaseBalance(account, value);␊ + }␊ + }␊ + ` + +## upgradeable transparent + mintable + incremental + empty namespacePrefix + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.4.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";␊ + import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";␊ + import {ERC721VotesUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721VotesUpgradeable.sol";␊ + import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊ + import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";␊ + ␊ + contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, EIP712Upgradeable, ERC721VotesUpgradeable {␊ + /// @custom:storage-location erc7201:MyToken␊ + struct MyTokenStorage {␊ + uint256 _nextTokenId;␊ + }␊ + ␊ + // keccak256(abi.encode(uint256(keccak256("MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ + bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xe50b25623ebee85cbe908e55dc189e9b1da401843a56196aa3162de9203a5100;␊ + ␊ + /// @custom:oz-upgrades-unsafe-allow constructor␊ + constructor() {␊ + _disableInitializers();␊ + }␊ + ␊ + function initialize(address initialOwner) public initializer {␊ + __ERC721_init("MyToken", "MTK");␊ + __Ownable_init(initialOwner);␊ + __EIP712_init("MyToken", "1");␊ + __ERC721Votes_init();␊ + }␊ + ␊ + function safeMint(address to) public onlyOwner returns (uint256) {␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + uint256 tokenId = $._nextTokenId++;␊ + _safeMint(to, tokenId);␊ + return tokenId;␊ + }␊ + ␊ + function _getMyTokenStorage() private pure returns (MyTokenStorage storage $) {␊ + assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ + }␊ + ␊ + // The following functions are overrides required by Solidity.␊ + ␊ + function _update(address to, uint256 tokenId, address auth)␊ + internal␊ + override(ERC721Upgradeable, ERC721VotesUpgradeable)␊ + returns (address)␊ + {␊ + return super._update(to, tokenId, auth);␊ + }␊ + ␊ + function _increaseBalance(address account, uint128 value)␊ + internal␊ + override(ERC721Upgradeable, ERC721VotesUpgradeable)␊ + {␊ + super._increaseBalance(account, value);␊ + }␊ + }␊ + ` + ## full upgradeable transparent > Snapshot 1 diff --git a/packages/core/solidity/src/erc721.test.ts.snap b/packages/core/solidity/src/erc721.test.ts.snap index a60e7035891f2bd40463a765df2025ba9560163e..781a2ce684f79ebd5e857f8b2a6f3b6655b44c6b 100644 GIT binary patch literal 2922 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 U^fjTcD@|Yj55BcJ_~?fK01fY(@Bjb+ literal 2509 zcmV;;2{QIURzVp+Uh-VOyE@4lM~P)TsGm&Ha74 zxS(tgtRIUA00000000B+T+MGINfn<=X0+f%E08#FSc#G~LbejSV<(<`Dbji}88DMf zLgK7;IjmLgF2^3l+o>oFgaOJ{30yos(UEO8J zvE$gzb}pkyRlToz@74RguF994wr;BD>^FZv9CIC^He^`Ej6jQZP0oJ#rKUj+KVSIn z;)4$!I6n)IAAIlO!h)iJlcVRKmG-fU2t%cYh6u+UjOZ3PXf$(*0(MQqf*iMX1THb} zf$eR7H#kW;vMj)>yT_5Ud|Q_pYba=|2U&}%xg0A|3O0p8^R z06;YfIkq92KCZQN$SF<}$D` z;nmtx;HuuxmO(HGZQd=0-Wj-c_=|vZp`X6Rgl97l@byMgp3iIx(Q+W8BU>bsBTL}M z37s%eDS`|=gE}N?q#RUL(0uu|Os7f`p+_!>oW`vRu~2pD?|2)_`lAq(zn%YMequoxusOUW!xRxvRS{!~KYNtGUIzYRNrVT- z8zz`zs2^SuL?gfI7yaP`pKu|)JXO>(*g*%F@WJJR5RZ<_^^Fyi=slNT!rfaTqu816 zd$u=@cry{p%PuD&ptHPopYz()6M13-OTewa3`988(`oMU_k}d~_;HrzW;V?Qv1UN3g}x()O(-&sbdYz|vV#{e~pfZri3r z-UV9tm5?|2JofuUI?sGv$IF@lmErSUuwwM?_C>Hw*VXik{}wjw@AD6|C$ed?h~>xj zmMmT0|8#9Zo5|Dl;-7@b|K0pY{*kZictIIZx!V#q;5r*P-tih#DEjUlRikkT?$41& zkxhmn5zd-i0eJ`CFM_R`*w>3Fngc+aj^>Bo5~BHk^IUvQEYxNpw1#Ps4UU=t@Ga&m zR;>9!O&u}G3GffWXZ|*g6QJ&HqPx)4hum>tjr1g#$Or#rATai~#H|iitHF-BP6Y`E z2aQ+Fz2nyL;r<@j0q00n;d!<8w2+Sts@P#ah#Fu{o4~(aFwZMJ3_!d)U)H`p{1#LH&!4hyUx)a-IBbtSF7TQ^8&wXgW z2|}A=12GN_OLE!lD^p17_-7On6DdZr`@#6r&{_6XXg~avlbi419$&6*43WxVwM2`?7U#_QP|EyV_rSAM*C*9oecZ8i9oW_xg&K149;c8n3sNV2rP5yRUqagZDmS&XcN&R& zO`0jiwCkS@WAx-7)kote$(Pr03O<^GQr;jZ$uC?O(xhD86S6Z(lS?M z61y8F5Sghf19$axb*^f&TZwpiyv}>2cT>~I^`G%)ox>`52j3;_R1s|3mZVYt;eUkc z_n#kT)o)h)X4P+2{SLO`>FLqQh%DlYB*@&a5_=gW)XQq_il-1|BY4x$1O^-kBG^5cgm9MNh;(Zyvtf zd(}E=9v;_!yw}=4+^shohp)g6Sh?!7+w1BkTB}r7J8EaGU4c-mt*YxQtF?{Jdb^@l zv~{&p-B{VIc50RNjdpDn)oNP1_OymJt1Bzpxs-7;w3eZ@DWNqP!9h<(6A>N!<<2M0 z+tD+`#o@Tc(r- = { @@ -46,6 +47,7 @@ export const defaults: Required = { access: commonDefaults.access, upgradeable: commonDefaults.upgradeable, info: commonDefaults.info, + namespacePrefix: 'myProject', } as const; function withDefaults(opts: ERC721Options): Required { @@ -60,6 +62,7 @@ function withDefaults(opts: ERC721Options): Required { mintable: opts.mintable ?? defaults.mintable, incremental: opts.incremental ?? defaults.incremental, votes: opts.votes ?? defaults.votes, + namespacePrefix: opts.namespacePrefix ?? defaults.namespacePrefix, }; } @@ -101,7 +104,7 @@ export function buildERC721(opts: ERC721Options): Contract { } if (allOpts.mintable) { - addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable); + addMintable(c, access, allOpts.incremental, allOpts.uriStorage, allOpts.upgradeable, allOpts.namespacePrefix); } if (allOpts.votes) { @@ -182,6 +185,7 @@ function addMintable( incremental = false, uriStorage = false, upgradeable: Upgradeable, + namespacePrefix: string, ) { const fn = getMintFunction(incremental, uriStorage); requireAccessControl(c, fn, access, 'MINTER', 'minter'); @@ -191,7 +195,7 @@ function addMintable( c.addFunctionCode('uint256 tokenId = _nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } else { - setNamespacedStorage(c, fn, ['uint256 _nextTokenId;']); + setNamespacedStorage(c, fn, ['uint256 _nextTokenId;'], namespacePrefix); c.addFunctionCode('uint256 tokenId = $._nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } diff --git a/packages/core/solidity/src/generate/erc721.ts b/packages/core/solidity/src/generate/erc721.ts index 1f91b7c30..da1dfb5f6 100644 --- a/packages/core/solidity/src/generate/erc721.ts +++ b/packages/core/solidity/src/generate/erc721.ts @@ -21,6 +21,7 @@ const blueprint = { upgradeable: upgradeableOptions, info: infoOptions, votes: [...booleans, ...clockModeOptions] as const, + namespacePrefix: ['myProject'], }; export function* generateERC721Options(): Generator> { diff --git a/packages/core/solidity/src/set-namespaced-storage.ts b/packages/core/solidity/src/set-namespaced-storage.ts new file mode 100644 index 000000000..9f3723d9e --- /dev/null +++ b/packages/core/solidity/src/set-namespaced-storage.ts @@ -0,0 +1,58 @@ +import type { BaseFunction, ContractBuilder, ContractStruct } from './contract'; +import { computeNamespacedStorageSlot } from './utils/namespaced-slot'; + +/** + * Sets namespaced variables in storage struct, and adds a function to retrieve namespaced storage. + */ +export function setNamespacedStorage( + c: ContractBuilder, + fn: BaseFunction, + structVariables: string[], + namespacePrefix: string, +) { + const namespaceId = toNamespaceId(namespacePrefix, c.name); + const storageFn = makeStorageFunction(c.name); + const storageStruct = makeStorageStruct(c.name, namespaceId); + const namespacedStorageConstant = `${c.name.toUpperCase()}_STORAGE_LOCATION`; + + structVariables.forEach(v => c.addStructVariable(storageStruct, v)); + + c.addVariable(`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`); + c.addVariable(`bytes32 private constant ${namespacedStorageConstant} = ${computeNamespacedStorageSlot(namespaceId)};`); + c.addFunctionCode(`assembly { $.slot := ${namespacedStorageConstant} }`, storageFn); + + c.addFunctionCode(`${c.name}Storage storage $ = ${storageFn.name}();`, fn); +} + +/** + * Creates a namespace ID from a namespace prefix and a contract name. + * If the namespace prefix is empty, returns the contract name. + */ +function toNamespaceId(namespacePrefix: string, name: string) { + if (namespacePrefix.length === 0) { + return name; + } else { + return `${namespacePrefix}.${name}`; + } +} + +function makeStorageFunction(name: string): BaseFunction { + const fn: BaseFunction = { + name: `_get${name}Storage`, + kind: 'private' as const, + mutability: 'pure', + args: [], + returns: [`${name}Storage storage $`], + }; + + return fn; +} + +function makeStorageStruct(name: string, namespaceId: string) { + const struct: ContractStruct = { + name: `${name}Storage`, + comments: [`/// @custom:storage-location erc7201:${namespaceId}`], + variables: [], + }; + return struct; +} diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts b/packages/core/solidity/src/utils/namespaced-slot.test.ts similarity index 60% rename from packages/core/solidity/src/utils/namespaced-storage-generator.test.ts rename to packages/core/solidity/src/utils/namespaced-slot.test.ts index 0491360e6..382b63155 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-generator.test.ts +++ b/packages/core/solidity/src/utils/namespaced-slot.test.ts @@ -1,7 +1,7 @@ import test from 'ava'; -import { computeNamespacedStorageSlot } from './namespaced-storage-generator'; +import { computeNamespacedStorageSlot } from './namespaced-slot'; -test('namespaced storage slot generation', t => { +test('namespaced storage slot computation', t => { const cases = [ { input: 'myProject.MyToken', @@ -15,6 +15,14 @@ test('namespaced storage slot generation', t => { input: 'myProject.token123456', expected: '0x824a9aeab482b3e91ee3e454c74509cca55ad57e0185a36d070359384be52800', }, + { + input: 'example.main', + expected: '0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500', + }, + { + input: 'MyToken', + expected: '0xe50b25623ebee85cbe908e55dc189e9b1da401843a56196aa3162de9203a5100', + }, ]; for (const { input, expected } of cases) { diff --git a/packages/core/solidity/src/utils/namespaced-storage-generator.ts b/packages/core/solidity/src/utils/namespaced-slot.ts similarity index 84% rename from packages/core/solidity/src/utils/namespaced-storage-generator.ts rename to packages/core/solidity/src/utils/namespaced-slot.ts index 96116bde9..88fecb674 100644 --- a/packages/core/solidity/src/utils/namespaced-storage-generator.ts +++ b/packages/core/solidity/src/utils/namespaced-slot.ts @@ -1,10 +1,6 @@ import { keccak256 } from 'ethereum-cryptography/keccak'; import { hexToBytes, toHex, utf8ToBytes } from 'ethereum-cryptography/utils'; -export function getNamespaceId(name: string, namespace: string = 'myProject') { - return `${namespace}.${name}`; -} - /** * Returns the ERC-7201 storage location for a given namespace id */ diff --git a/packages/core/solidity/src/utils/namespaced-storage-functionality.ts b/packages/core/solidity/src/utils/namespaced-storage-functionality.ts deleted file mode 100644 index 58ac9d622..000000000 --- a/packages/core/solidity/src/utils/namespaced-storage-functionality.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { BaseFunction, ContractBuilder, ContractStruct } from '../contract'; -import { computeNamespacedStorageSlot, getNamespaceId } from './namespaced-storage-generator'; - -/** - * Sets namespaced variables in storage struct, and adds a function to retrieve namespaced storage. - */ -export function setNamespacedStorage( - c: ContractBuilder, - fn: BaseFunction, - structVariables: string[], - namespace: string = 'myProject', -) { - const storageFn = makeStorageFunction(c.name); - const storageStruct = makeStorageStruct(c.name, namespace); - const namespacedStorageName = `${c.name.toUpperCase()}_STORAGE_LOCATION`; - const namespaceId = getNamespaceId(c.name, namespace); - structVariables.forEach(v => c.addStructVariable(storageStruct, v)); - - c.addVariable(`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`); - c.addVariable(`bytes32 private constant ${namespacedStorageName} = ${computeNamespacedStorageSlot(namespaceId)};`); - c.addFunctionCode(`assembly { $.slot := ${namespacedStorageName} }`, storageFn); - - c.addFunctionCode(`${c.name}Storage storage $ = ${storageFn.name}();`, fn); -} - -function makeStorageFunction(name: string): BaseFunction { - const fn: BaseFunction = { - name: `_get${name}Storage`, - kind: 'private' as const, - mutability: 'pure', - args: [], - returns: [`${name}Storage storage $`], - }; - - return fn; -} - -function makeStorageStruct(name: string, namespace: string) { - const struct: ContractStruct = { - name: `${name}Storage`, - comments: [`/// @custom:storage-location erc7201:${getNamespaceId(name, namespace)}`], - variables: [], - }; - return struct; -} diff --git a/packages/mcp/src/solidity/schemas.ts b/packages/mcp/src/solidity/schemas.ts index 9bdebed84..57f3b1937 100644 --- a/packages/mcp/src/solidity/schemas.ts +++ b/packages/mcp/src/solidity/schemas.ts @@ -83,6 +83,7 @@ export const erc721Schema = { incremental: z.boolean().optional().describe(solidityERC721Descriptions.incremental), votes: z.literal('blocknumber').or(z.literal('timestamp')).optional().describe(solidityERC721Descriptions.votes), ...commonSchema, + namespacePrefix: z.string().optional().describe(solidityCommonDescriptions.namespacePrefix), } as const satisfies z.ZodRawShape; export const erc1155Schema = { diff --git a/packages/mcp/src/solidity/tools/erc721.test.ts b/packages/mcp/src/solidity/tools/erc721.test.ts index c52f041c5..32f1d1eac 100644 --- a/packages/mcp/src/solidity/tools/erc721.test.ts +++ b/packages/mcp/src/solidity/tools/erc721.test.ts @@ -56,6 +56,7 @@ test('all', async t => { license: 'MIT', securityContact: 'security@example.com', }, + namespacePrefix: 'myNftProject', }; assertHasAllSupportedFields(t, params); await assertAPIEquivalence(t, params, erc721.print); diff --git a/packages/mcp/src/solidity/tools/erc721.ts b/packages/mcp/src/solidity/tools/erc721.ts index d83e22ee9..1c4353413 100644 --- a/packages/mcp/src/solidity/tools/erc721.ts +++ b/packages/mcp/src/solidity/tools/erc721.ts @@ -24,6 +24,7 @@ export function registerSolidityERC721(server: McpServer): RegisteredTool { access, upgradeable, info, + namespacePrefix, }) => { const opts: ERC721Options = { name, @@ -39,6 +40,7 @@ export function registerSolidityERC721(server: McpServer): RegisteredTool { access, upgradeable, info, + namespacePrefix, }; return { content: [ diff --git a/packages/ui/api/ai-assistant/function-definitions/shared.ts b/packages/ui/api/ai-assistant/function-definitions/shared.ts index ae762b2ed..562c4315c 100644 --- a/packages/ui/api/ai-assistant/function-definitions/shared.ts +++ b/packages/ui/api/ai-assistant/function-definitions/shared.ts @@ -32,7 +32,7 @@ export const addFunctionPropertiesFrom = < TContract, TCommonOptions extends Record = Record, TCommonOptionName extends keyof (typeof sharedFunctionDescription & TCommonOptions) & - keyof TContract = keyof (typeof sharedFunctionDescription & TCommonOptions) & keyof TContract, + keyof TContract = keyof (typeof sharedFunctionDescription & TCommonOptions) & keyof TContract, >( commonOptions: TCommonOptions, commonOptionNames: TCommonOptionName[], diff --git a/packages/ui/api/ai-assistant/function-definitions/solidity.ts b/packages/ui/api/ai-assistant/function-definitions/solidity.ts index 81cc84a4f..0f34e3286 100644 --- a/packages/ui/api/ai-assistant/function-definitions/solidity.ts +++ b/packages/ui/api/ai-assistant/function-definitions/solidity.ts @@ -3,6 +3,7 @@ import { addFunctionPropertiesFrom } from './shared.ts'; import { commonFunctionDescription } from './solidity-shared.ts'; import { solidityPrompts, + solidityCommonDescriptions, solidityAccountDescriptions, solidityERC20Descriptions, solidityERC721Descriptions, @@ -103,6 +104,10 @@ export const solidityERC721AIFunctionDefinition = { ], description: solidityERC721Descriptions.votes, }, + namespacePrefix: { + type: 'string', + description: solidityCommonDescriptions.namespacePrefix, + }, }, required: ['name', 'symbol'], additionalProperties: false, From 8addbc71965ff256788103223a986a2af6a0ecad Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 27 Oct 2025 14:27:43 -0400 Subject: [PATCH 08/24] Add namespace prefix to UI --- .../common/src/ai/descriptions/solidity.ts | 2 +- .../ui/src/solidity/ERC721Controls.svelte | 6 +++++- .../src/solidity/UpgradeabilitySection.svelte | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/common/src/ai/descriptions/solidity.ts b/packages/common/src/ai/descriptions/solidity.ts index 47cd13d25..869bad323 100644 --- a/packages/common/src/ai/descriptions/solidity.ts +++ b/packages/common/src/ai/descriptions/solidity.ts @@ -18,7 +18,7 @@ export const solidityCommonDescriptions = { 'The type of access control to provision. Ownable is a simple mechanism with a single account authorized for all privileged actions. Roles is a flexible mechanism with a separate role for each privileged action. A role can have many authorized accounts. Managed enables a central contract to define a policy that allows certain callers to access certain functions.', upgradeable: 'Whether the smart contract is upgradeable. Transparent uses more complex proxy with higher overhead, requires less changes in your contract. Can also be used with beacons. UUPS uses simpler proxy with less overhead, requires including extra code in your contract. Allows flexibility for authorizing upgrades.', - namespacePrefix: 'The prefix for the namespace of the contract. Only used if the contract has storage variables and upgradeability is enabled. Default is "myProject"', + namespacePrefix: 'The prefix for namespace ids. Should be based on the project name or a naming convention unique to the project. Only used if the contract has storage variables and upgradeability is enabled. Default is "myProject"', }; export const solidityERC20Descriptions = { diff --git a/packages/ui/src/solidity/ERC721Controls.svelte b/packages/ui/src/solidity/ERC721Controls.svelte index 32bb0f7fa..7f3756c71 100644 --- a/packages/ui/src/solidity/ERC721Controls.svelte +++ b/packages/ui/src/solidity/ERC721Controls.svelte @@ -129,6 +129,10 @@ - + diff --git a/packages/ui/src/solidity/UpgradeabilitySection.svelte b/packages/ui/src/solidity/UpgradeabilitySection.svelte index 3bf0c453e..9f95e2446 100644 --- a/packages/ui/src/solidity/UpgradeabilitySection.svelte +++ b/packages/ui/src/solidity/UpgradeabilitySection.svelte @@ -8,6 +8,9 @@ export let disabled: boolean = false; export let disabledReason: string | undefined = undefined; + export let namespaceRequired: boolean = false; + export let namespacePrefix: string | undefined = undefined; + let defaultValueWhenEnabled: 'transparent' | 'uups' = 'transparent'; let wasDisabled = disabled; let wasUpgradeable = upgradeable; @@ -54,4 +57,20 @@ + + {#if namespaceRequired} +
+ + + {/if} From 1d4fc3302c1a7daa13443089a35f8e2e5f25eaed Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 27 Oct 2025 14:30:06 -0400 Subject: [PATCH 09/24] Fix lint --- packages/common/src/ai/descriptions/solidity.ts | 3 ++- packages/core/solidity/src/set-namespaced-storage.ts | 4 +++- packages/ui/api/ai-assistant/function-definitions/shared.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/common/src/ai/descriptions/solidity.ts b/packages/common/src/ai/descriptions/solidity.ts index 869bad323..f9f70e067 100644 --- a/packages/common/src/ai/descriptions/solidity.ts +++ b/packages/common/src/ai/descriptions/solidity.ts @@ -18,7 +18,8 @@ export const solidityCommonDescriptions = { 'The type of access control to provision. Ownable is a simple mechanism with a single account authorized for all privileged actions. Roles is a flexible mechanism with a separate role for each privileged action. A role can have many authorized accounts. Managed enables a central contract to define a policy that allows certain callers to access certain functions.', upgradeable: 'Whether the smart contract is upgradeable. Transparent uses more complex proxy with higher overhead, requires less changes in your contract. Can also be used with beacons. UUPS uses simpler proxy with less overhead, requires including extra code in your contract. Allows flexibility for authorizing upgrades.', - namespacePrefix: 'The prefix for namespace ids. Should be based on the project name or a naming convention unique to the project. Only used if the contract has storage variables and upgradeability is enabled. Default is "myProject"', + namespacePrefix: + 'The prefix for namespace ids. Should be based on the project name or a naming convention unique to the project. Only used if the contract has storage variables and upgradeability is enabled. Default is "myProject"', }; export const solidityERC20Descriptions = { diff --git a/packages/core/solidity/src/set-namespaced-storage.ts b/packages/core/solidity/src/set-namespaced-storage.ts index 9f3723d9e..89d13d846 100644 --- a/packages/core/solidity/src/set-namespaced-storage.ts +++ b/packages/core/solidity/src/set-namespaced-storage.ts @@ -18,7 +18,9 @@ export function setNamespacedStorage( structVariables.forEach(v => c.addStructVariable(storageStruct, v)); c.addVariable(`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`); - c.addVariable(`bytes32 private constant ${namespacedStorageConstant} = ${computeNamespacedStorageSlot(namespaceId)};`); + c.addVariable( + `bytes32 private constant ${namespacedStorageConstant} = ${computeNamespacedStorageSlot(namespaceId)};`, + ); c.addFunctionCode(`assembly { $.slot := ${namespacedStorageConstant} }`, storageFn); c.addFunctionCode(`${c.name}Storage storage $ = ${storageFn.name}();`, fn); diff --git a/packages/ui/api/ai-assistant/function-definitions/shared.ts b/packages/ui/api/ai-assistant/function-definitions/shared.ts index 562c4315c..ae762b2ed 100644 --- a/packages/ui/api/ai-assistant/function-definitions/shared.ts +++ b/packages/ui/api/ai-assistant/function-definitions/shared.ts @@ -32,7 +32,7 @@ export const addFunctionPropertiesFrom = < TContract, TCommonOptions extends Record = Record, TCommonOptionName extends keyof (typeof sharedFunctionDescription & TCommonOptions) & - keyof TContract = keyof (typeof sharedFunctionDescription & TCommonOptions) & keyof TContract, + keyof TContract = keyof (typeof sharedFunctionDescription & TCommonOptions) & keyof TContract, >( commonOptions: TCommonOptions, commonOptionNames: TCommonOptionName[], From d353f086b31718145ee832e05b33f53f0c144423 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 27 Oct 2025 15:52:08 -0400 Subject: [PATCH 10/24] Use namespace for token bridge --- .changeset/rude-nails-mate.md | 4 +- packages/core/solidity/src/contract.test.ts | 27 +++- .../core/solidity/src/contract.test.ts.md | 27 ++++ .../core/solidity/src/contract.test.ts.snap | Bin 1009 -> 1072 bytes packages/core/solidity/src/contract.ts | 22 ++- packages/core/solidity/src/erc1155.ts | 4 +- packages/core/solidity/src/erc20.test.ts.md | 18 ++- packages/core/solidity/src/erc20.test.ts.snap | Bin 3450 -> 3715 bytes packages/core/solidity/src/erc20.ts | 57 ++++++-- packages/core/solidity/src/erc721.ts | 11 +- packages/core/solidity/src/generate/erc20.ts | 1 + packages/core/solidity/src/generate/erc721.ts | 2 +- .../core/solidity/src/generate/stablecoin.ts | 2 + .../core/solidity/src/set-access-control.ts | 4 +- .../solidity/src/set-namespaced-storage.ts | 15 +-- packages/core/solidity/src/stablecoin.ts | 4 +- .../core/solidity/src/zip-foundry.test.ts.md | 18 ++- .../solidity/src/zip-foundry.test.ts.snap | Bin 4530 -> 4761 bytes .../core/solidity/src/zip-hardhat.test.ts.md | 18 ++- .../solidity/src/zip-hardhat.test.ts.snap | Bin 3264 -> 3505 bytes packages/mcp/src/solidity/schemas.ts | 1 + packages/mcp/src/solidity/tools/erc20.test.ts | 1 + packages/mcp/src/solidity/tools/erc721.ts | 4 +- packages/mcp/src/solidity/tools/rwa.test.ts | 1 + .../mcp/src/solidity/tools/stablecoin.test.ts | 1 + .../function-definitions/solidity-shared.ts | 5 + .../function-definitions/solidity.ts | 7 +- packages/ui/deno.lock | 126 +++++++++++++----- packages/ui/src/solidity/ERC20Controls.svelte | 6 +- 29 files changed, 296 insertions(+), 90 deletions(-) diff --git a/.changeset/rude-nails-mate.md b/.changeset/rude-nails-mate.md index ff22c49f7..dd4fee009 100644 --- a/.changeset/rude-nails-mate.md +++ b/.changeset/rude-nails-mate.md @@ -2,4 +2,6 @@ '@openzeppelin/wizard': minor --- -**Breaking change**: For ERC-721, use namespaced storage for `_nextTokenId` when mintable, auto increment IDs, and upgradeability are enabled together. \ No newline at end of file +**Breaking changes**: Use namespaced storage instead of state variables when upgradeability is enabled. + - For ERC-20, use namespaced storage for `tokenBridge` when cross-chain bridging is set to `'custom'` and upgradeability is enabled. + - For ERC-721, use namespaced storage for `_nextTokenId` when mintable, auto increment IDs, and upgradeability are enabled. \ No newline at end of file diff --git a/packages/core/solidity/src/contract.test.ts b/packages/core/solidity/src/contract.test.ts index 9e81545bb..989b4d544 100644 --- a/packages/core/solidity/src/contract.test.ts +++ b/packages/core/solidity/src/contract.test.ts @@ -135,17 +135,38 @@ test('contract with overridden function with code', t => { test('contract with one variable', t => { const Foo = new ContractBuilder('Foo'); - Foo.addVariable('uint value = 42;'); + Foo.addVariable('uint value = 42;', false); t.snapshot(printContract(Foo)); }); test('contract with two variables', t => { const Foo = new ContractBuilder('Foo'); - Foo.addVariable('uint value = 42;'); - Foo.addVariable('string name = "john";'); + Foo.addVariable('uint value = 42;', false); + Foo.addVariable('string name = "john";', false); t.snapshot(printContract(Foo)); }); +test('contract with immutable', t => { + const Foo = new ContractBuilder('Foo'); + Foo.addConstantOrImmutableOrCustomError('uint immutable value = 42;'); + t.snapshot(printContract(Foo)); +}); + +test('contract with constant and comment', t => { + const Foo = new ContractBuilder('Foo'); + Foo.addConstantOrImmutableOrCustomError('uint constant value = 42;', '// This is a comment'); + t.snapshot(printContract(Foo)); +}); + +test('contract with variable and upgradeable - error', t => { + const Foo = new ContractBuilder('Foo'); + const error = t.throws(() => Foo.addVariable('uint value = 42;', true)); + t.is( + (error as Error).message, + 'State variables should not be used when upgradeable is true. Set namespaced storage instead.', + ); +}); + test('name with special characters', t => { const Foo = new ContractBuilder('foo bar baz'); t.snapshot(printContract(Foo)); diff --git a/packages/core/solidity/src/contract.test.ts.md b/packages/core/solidity/src/contract.test.ts.md index f0f01afb2..b3080a284 100644 --- a/packages/core/solidity/src/contract.test.ts.md +++ b/packages/core/solidity/src/contract.test.ts.md @@ -262,6 +262,33 @@ Generated by [AVA](https://avajs.dev). }␊ ` +## contract with immutable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.4.0␊ + pragma solidity ^0.8.27;␊ + ␊ + contract Foo {␊ + uint immutable value = 42;␊ + }␊ + ` + +## contract with constant and comment + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.4.0␊ + pragma solidity ^0.8.27;␊ + ␊ + contract Foo {␊ + // This is a comment␊ + uint constant value = 42;␊ + }␊ + ` + ## name with special characters > Snapshot 1 diff --git a/packages/core/solidity/src/contract.test.ts.snap b/packages/core/solidity/src/contract.test.ts.snap index f14822349451cbe78deed32ceabf528d107894a1..80d963c79b966ed8081a5390565797d4e6dc8275 100644 GIT binary patch literal 1072 zcmV-01kd|HRzVje8O(CaUcs3EIaCQui)R%PYuHJ6Zh zK&Seo${&je00000000B+Si6thL>S)<3XkpRctq&aNl{rPr?J!59Zw|5*T1O){WMT%531Uir?fC@wdT`F2C{stNlRA}aG@6LL5$z7sb z7r5E=Yv%XzH=lhs8pJ%fCGUKJimDiSz?l*xP%t2p29iofQaR*Get0(wi6YlOd8F}J zLvPK~jVGJ$`#x-}zjA%~H5wq3xO_E4rf5V_T!h!JZnk_M)_9T5;ytmRy1=&srNFLLWsy(=|;=SzkURrInN@!O&hyB(;E62QhraZ$) zf z4j>Xdfwt%OiSQ(k+tUV734J0gpPsfksu9mwI%4*lMe|;bXcnj6%(C6_dA&DBhc4&~ zEH$2YJASyA5+xKZb|cNjii~8004l1GpR%tC@wln`go^pIG$^F;qYIty#)IQ z69V;S#sxTw_kc6}PqDU|k6LQ|RU@??EJ()`^%+}Kts&n*A!z8Rlr^mN&sIZG;CH!y z$8!JU=He)5zkDAyhX@gmV?Ls68={N_igG4_h_Mk81iqV5fgud`U?U&eYnd^MTj1sZ zBQ9{0-$J%22$KWLkN26e9BdG*JK#aR?Q4)6rrhub8J1;ux$O~e_M}k z>oqk*v2N<=!!UJ<1lOZq$#*+TC7ii1st~4zsV3;D=|wc*q4B+1%zm*l^ZiLK1dhzy zhg72>vNyO|Cz-_1uHt8B(FVf8*#&*8dD;@;&-#lnH!o8P9XUPE*)mQnr=VXSsu{Xx z$@k-e<+GRnG5wnfWvY|NZBHT#QFP(l3ZNFlp}`CDH86pi`8KlEe8N)XpBlAS58jL( zPtd6jrOdcHQ44J5kM}@6oS5a0b%<;{XP!T2-Fn=T>F@f>RMf4b=yjCR<>=#gh@fN; zJ3{ukrQYoYtEXEQ*dcL-a0$-$R_Ay31uMsk@DCQPuj@w3*r|kW2+en?OVECY4_SMH z@W|#%lO$7a3%|0sepUyrCE6KgwjC=AValv10Z$U==dGVDp5HB)r|Ime)T;+6FpmW& z8M*nNF|Hd(GR1(B7=j_uGdMr%+_sp1v?%6%BKqXL$#-rs6;c-QD*B%+^xvE$df)fq qa*#>IlZz6AOi;DAtTAiI#evJXOOiB3FW^b_>%zZzT*tBp7XScv&H-uw literal 1009 zcmV=)~qR8b3TXQ>d`jg+5+nv8{ z+b}(K`ts0O>L8OiH0dH!)T1a)z`4npf^EY%4??17%|}?Esty-IWLGc@(WeZ?lldfE z9koWR;X)|LTtGl_pSn~n!PQ~wxK%keRwy)~O>hp&g_S~rd2yiG!$^QYB}9xm+{J0) zVVc21gTb91F;M8Vl9+Gy#WN5HiO@Jw=wG*@-|`~2iSTI?#qK z?4|u?u${hxYn;*U34gezQIuAP`2Z%t&j*3 zphA%$%@;$OPkkg!hXeosIGjl(B1dsi8r0WI#hBxfVi{I{Rf>bKywW02rDI%xGk*y< z!{3TEo8NAz^}UbO`nw=)P}F^2#xMFW49`%et_@8^3*LHlLf zFjGhHxbO1?%I3g}n4>6X5{OtcLW01X5f$h{Z3(86p{+tZM&l8xnNU_C=&6$_$Kt12!+0=lrb|5!<2u;I)-5^1*0296`;As?rxJQY82>MI5{lP(ofTUN55zq&6d9sJ zi4RVl=#evz{*MgC_j<*+N<@|1YCV?;(>TgJo<{%PK>unp(c88Sr<_PC9-NTqM1rcN fA&pr>Zd^EpizEnrv>YC!PoTd5=1kbuD-{3$zh&v2 diff --git a/packages/core/solidity/src/contract.ts b/packages/core/solidity/src/contract.ts index b118dba2c..17ef91ac9 100644 --- a/packages/core/solidity/src/contract.ts +++ b/packages/core/solidity/src/contract.ts @@ -237,9 +237,27 @@ export class ContractBuilder implements Contract { } /** - * Note: The type in the variable is not currently transpiled, even if it refers to a contract + * Note: The type in the code is not currently transpiled, even if it refers to a contract */ - addVariable(code: string): boolean { + addVariable(code: string, upgradeable: boolean): boolean { + if (upgradeable) { + throw new Error('State variables should not be used when upgradeable is true. Set namespaced storage instead.'); + } else { + return this._addVariable(code); + } + } + + /** + * Note: The type in the code is not currently transpiled, even if it refers to a contract + */ + addConstantOrImmutableOrCustomError(code: string, comment?: string): boolean { + if (comment) { + this._addVariable(comment); + } + return this._addVariable(code); + } + + private _addVariable(code: string): boolean { const present = this.variableSet.has(code); this.variableSet.add(code); return !present; diff --git a/packages/core/solidity/src/erc1155.ts b/packages/core/solidity/src/erc1155.ts index 9bb93ded1..b940417b3 100644 --- a/packages/core/solidity/src/erc1155.ts +++ b/packages/core/solidity/src/erc1155.ts @@ -22,6 +22,7 @@ export interface ERC1155Options extends CommonOptions { } export const defaults: Required = { + ...commonDefaults, name: 'MyToken', uri: '', burnable: false, @@ -29,9 +30,6 @@ export const defaults: Required = { mintable: false, supply: false, updatableUri: true, - access: commonDefaults.access, - upgradeable: commonDefaults.upgradeable, - info: commonDefaults.info, } as const; function withDefaults(opts: ERC1155Options): Required { diff --git a/packages/core/solidity/src/erc20.test.ts.md b/packages/core/solidity/src/erc20.test.ts.md index e422ee059..07b0f50a0 100644 --- a/packages/core/solidity/src/erc20.test.ts.md +++ b/packages/core/solidity/src/erc20.test.ts.md @@ -863,7 +863,13 @@ Generated by [AVA](https://avajs.dev). import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊ ␊ contract MyToken is Initializable, ERC20Upgradeable, ERC20BridgeableUpgradeable, ERC20PermitUpgradeable {␊ - address public tokenBridge;␊ + /// @custom:storage-location erc7201:myProject.MyToken␊ + struct MyTokenStorage {␊ + address tokenBridge;␊ + }␊ + ␊ + // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ + bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ error Unauthorized();␊ ␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ @@ -877,11 +883,17 @@ Generated by [AVA](https://avajs.dev). __ERC20Permit_init("MyToken");␊ ␊ require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");␊ - tokenBridge = tokenBridge_;␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + $.tokenBridge = tokenBridge_;␊ }␊ ␊ function _checkTokenBridge(address caller) internal view override {␊ - if (caller != tokenBridge) revert Unauthorized();␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + if (caller != $.tokenBridge) revert Unauthorized();␊ + }␊ + ␊ + function _getMyTokenStorage() private pure returns (MyTokenStorage storage $) {␊ + assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ }␊ }␊ ` diff --git a/packages/core/solidity/src/erc20.test.ts.snap b/packages/core/solidity/src/erc20.test.ts.snap index c78c752bc840a89a9df2bbf0425f5cdc36f6a9f2..6239e8cae84da21dda825dd84fca6f473c04ef52 100644 GIT binary patch literal 3715 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 = { + ...commonDefaults, name: 'MyToken', symbol: 'MTK', burnable: false, @@ -49,9 +53,7 @@ export const defaults: Required = { votes: false, flashmint: false, crossChainBridging: false, - access: commonDefaults.access, - upgradeable: commonDefaults.upgradeable, - info: commonDefaults.info, + namespacePrefix: 'myProject', } as const; export function withDefaults(opts: ERC20Options): Required { @@ -68,6 +70,7 @@ export function withDefaults(opts: ERC20Options): Required { votes: opts.votes ?? defaults.votes, flashmint: opts.flashmint ?? defaults.flashmint, crossChainBridging: opts.crossChainBridging ?? defaults.crossChainBridging, + namespacePrefix: opts.namespacePrefix ?? defaults.namespacePrefix, }; } @@ -89,7 +92,7 @@ export function buildERC20(opts: ERC20Options): ContractBuilder { addBase(c, allOpts.name, allOpts.symbol); if (allOpts.crossChainBridging) { - addCrossChainBridging(c, allOpts.crossChainBridging, access); + addCrossChainBridging(c, allOpts.crossChainBridging, access, upgradeable, allOpts.namespacePrefix); } if (allOpts.premint) { @@ -304,7 +307,13 @@ function addFlashMint(c: ContractBuilder) { }); } -function addCrossChainBridging(c: ContractBuilder, crossChainBridging: 'custom' | 'superchain', access: Access) { +function addCrossChainBridging( + c: ContractBuilder, + crossChainBridging: 'custom' | 'superchain', + access: Access, + upgradeable: Upgradeable, + namespacePrefix: string, +) { const ERC20Bridgeable = { name: 'ERC20Bridgeable', path: `@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Bridgeable.sol`, @@ -316,7 +325,7 @@ function addCrossChainBridging(c: ContractBuilder, crossChainBridging: 'custom' c.addOverride(ERC20Bridgeable, functions._checkTokenBridge); switch (crossChainBridging) { case 'custom': - addCustomBridging(c, access); + addCustomBridging(c, access, upgradeable, namespacePrefix); break; case 'superchain': addSuperchainERC20(c); @@ -326,27 +335,45 @@ function addCrossChainBridging(c: ContractBuilder, crossChainBridging: 'custom' throw new Error('Unknown value for `crossChainBridging`'); } } - c.addVariable('error Unauthorized();'); + c.addConstantOrImmutableOrCustomError('error Unauthorized();'); } -function addCustomBridging(c: ContractBuilder, access: Access) { +function addCustomBridging(c: ContractBuilder, access: Access, upgradeable: Upgradeable, namespacePrefix: string) { switch (access) { case false: case 'ownable': { - const addedBridgeImmutable = c.addVariable(`address public tokenBridge;`); - if (addedBridgeImmutable) { + if (!upgradeable) { + const addedBridge = c.addVariable(`address public tokenBridge;`, false); + if (addedBridge) { + c.addConstructorArgument({ type: 'address', name: 'tokenBridge_' }); + c.addConstructorCode(`require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");`); + c.addConstructorCode(`tokenBridge = tokenBridge_;`); + } + c.setFunctionBody([`if (caller != tokenBridge) revert Unauthorized();`], functions._checkTokenBridge, 'view'); + } else { + setNamespacedStorage(c, ['address tokenBridge;'], namespacePrefix); + c.addConstructorArgument({ type: 'address', name: 'tokenBridge_' }); c.addConstructorCode(`require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");`); - c.addConstructorCode(`tokenBridge = tokenBridge_;`); + + c.addConstructorCode(toStorageStructInstantiation(c.name)); + c.addConstructorCode(`$.tokenBridge = tokenBridge_;`); + + c.setFunctionBody( + [toStorageStructInstantiation(c.name), `if (caller != $.tokenBridge) revert Unauthorized();`], + functions._checkTokenBridge, + 'view', + ); } - c.setFunctionBody([`if (caller != tokenBridge) revert Unauthorized();`], functions._checkTokenBridge, 'view'); break; } case 'roles': { setAccessControl(c, access); const roleOwner = 'tokenBridge'; const roleId = 'TOKEN_BRIDGE_ROLE'; - const addedRoleConstant = c.addVariable(`bytes32 public constant ${roleId} = keccak256("${roleId}");`); + const addedRoleConstant = c.addConstantOrImmutableOrCustomError( + `bytes32 public constant ${roleId} = keccak256("${roleId}");`, + ); if (addedRoleConstant) { c.addConstructorArgument({ type: 'address', name: roleOwner }); c.addConstructorCode(`_grantRole(${roleId}, ${roleOwner});`); @@ -382,7 +409,9 @@ function addCustomBridging(c: ContractBuilder, access: Access) { } function addSuperchainERC20(c: ContractBuilder) { - c.addVariable('address internal constant SUPERCHAIN_TOKEN_BRIDGE = 0x4200000000000000000000000000000000000028;'); + c.addConstantOrImmutableOrCustomError( + 'address internal constant SUPERCHAIN_TOKEN_BRIDGE = 0x4200000000000000000000000000000000000028;', + ); c.setFunctionBody( ['if (caller != SUPERCHAIN_TOKEN_BRIDGE) revert Unauthorized();'], functions._checkTokenBridge, diff --git a/packages/core/solidity/src/erc721.ts b/packages/core/solidity/src/erc721.ts index bbfc37eca..4d8ee6321 100644 --- a/packages/core/solidity/src/erc721.ts +++ b/packages/core/solidity/src/erc721.ts @@ -13,7 +13,7 @@ import { setInfo } from './set-info'; import { printContract } from './print'; import type { ClockMode } from './set-clock-mode'; import { clockModeDefault, setClockMode } from './set-clock-mode'; -import { setNamespacedStorage } from './set-namespaced-storage'; +import { setNamespacedStorage, toStorageStructInstantiation } from './set-namespaced-storage'; export interface ERC721Options extends CommonOptions { name: string; @@ -34,6 +34,7 @@ export interface ERC721Options extends CommonOptions { } export const defaults: Required = { + ...commonDefaults, name: 'MyToken', symbol: 'MTK', baseUri: '', @@ -44,9 +45,6 @@ export const defaults: Required = { mintable: false, incremental: false, votes: false, - access: commonDefaults.access, - upgradeable: commonDefaults.upgradeable, - info: commonDefaults.info, namespacePrefix: 'myProject', } as const; @@ -191,11 +189,12 @@ function addMintable( requireAccessControl(c, fn, access, 'MINTER', 'minter'); if (incremental) { if (!upgradeable) { - c.addVariable('uint256 private _nextTokenId;'); + c.addVariable('uint256 private _nextTokenId;', upgradeable); c.addFunctionCode('uint256 tokenId = _nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } else { - setNamespacedStorage(c, fn, ['uint256 _nextTokenId;'], namespacePrefix); + setNamespacedStorage(c, ['uint256 _nextTokenId;'], namespacePrefix); + c.addFunctionCode(toStorageStructInstantiation(c.name), fn); c.addFunctionCode('uint256 tokenId = $._nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } diff --git a/packages/core/solidity/src/generate/erc20.ts b/packages/core/solidity/src/generate/erc20.ts index 49040e511..cfdfcc711 100644 --- a/packages/core/solidity/src/generate/erc20.ts +++ b/packages/core/solidity/src/generate/erc20.ts @@ -22,6 +22,7 @@ const blueprint = { crossChainBridging: crossChainBridgingOptions, access: accessOptions, upgradeable: upgradeableOptions, + namespacePrefix: ['myProject'], info: infoOptions, }; diff --git a/packages/core/solidity/src/generate/erc721.ts b/packages/core/solidity/src/generate/erc721.ts index da1dfb5f6..13749101c 100644 --- a/packages/core/solidity/src/generate/erc721.ts +++ b/packages/core/solidity/src/generate/erc721.ts @@ -19,9 +19,9 @@ const blueprint = { incremental: booleans, access: accessOptions, upgradeable: upgradeableOptions, + namespacePrefix: ['myProject'], info: infoOptions, votes: [...booleans, ...clockModeOptions] as const, - namespacePrefix: ['myProject'], }; export function* generateERC721Options(): Generator> { diff --git a/packages/core/solidity/src/generate/stablecoin.ts b/packages/core/solidity/src/generate/stablecoin.ts index 581f8e999..eab81f7f2 100644 --- a/packages/core/solidity/src/generate/stablecoin.ts +++ b/packages/core/solidity/src/generate/stablecoin.ts @@ -21,6 +21,7 @@ const erc20Basic = { crossChainBridging: [false] as const, access: [false] as const, info: [{}] as const, + namespacePrefix: ['myProject'], }; const erc20Full = { @@ -38,6 +39,7 @@ const erc20Full = { crossChainBridging: crossChainBridgingOptions, access: accessOptions, info: infoOptions, + namespacePrefix: ['myProject'], }; const stablecoinExtensions = { diff --git a/packages/core/solidity/src/set-access-control.ts b/packages/core/solidity/src/set-access-control.ts index a4779231d..af1b57d36 100644 --- a/packages/core/solidity/src/set-access-control.ts +++ b/packages/core/solidity/src/set-access-control.ts @@ -65,7 +65,9 @@ export function requireAccessControl( } case 'roles': { const roleId = roleIdPrefix + '_ROLE'; - const addedConstant = c.addVariable(`bytes32 public constant ${roleId} = keccak256("${roleId}");`); + const addedConstant = c.addConstantOrImmutableOrCustomError( + `bytes32 public constant ${roleId} = keccak256("${roleId}");`, + ); if (roleOwner && addedConstant) { c.addConstructorArgument({ type: 'address', name: roleOwner }); c.addConstructorCode(`_grantRole(${roleId}, ${roleOwner});`); diff --git a/packages/core/solidity/src/set-namespaced-storage.ts b/packages/core/solidity/src/set-namespaced-storage.ts index 89d13d846..948e2dc61 100644 --- a/packages/core/solidity/src/set-namespaced-storage.ts +++ b/packages/core/solidity/src/set-namespaced-storage.ts @@ -4,12 +4,7 @@ import { computeNamespacedStorageSlot } from './utils/namespaced-slot'; /** * Sets namespaced variables in storage struct, and adds a function to retrieve namespaced storage. */ -export function setNamespacedStorage( - c: ContractBuilder, - fn: BaseFunction, - structVariables: string[], - namespacePrefix: string, -) { +export function setNamespacedStorage(c: ContractBuilder, structVariables: string[], namespacePrefix: string) { const namespaceId = toNamespaceId(namespacePrefix, c.name); const storageFn = makeStorageFunction(c.name); const storageStruct = makeStorageStruct(c.name, namespaceId); @@ -17,13 +12,15 @@ export function setNamespacedStorage( structVariables.forEach(v => c.addStructVariable(storageStruct, v)); - c.addVariable(`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`); - c.addVariable( + c.addConstantOrImmutableOrCustomError( `bytes32 private constant ${namespacedStorageConstant} = ${computeNamespacedStorageSlot(namespaceId)};`, + `// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`, ); c.addFunctionCode(`assembly { $.slot := ${namespacedStorageConstant} }`, storageFn); +} - c.addFunctionCode(`${c.name}Storage storage $ = ${storageFn.name}();`, fn); +export function toStorageStructInstantiation(name: string) { + return `${name}Storage storage $ = ${makeStorageFunction(name).name}();`; } /** diff --git a/packages/core/solidity/src/stablecoin.ts b/packages/core/solidity/src/stablecoin.ts index 8d1134989..39f813a74 100644 --- a/packages/core/solidity/src/stablecoin.ts +++ b/packages/core/solidity/src/stablecoin.ts @@ -107,7 +107,9 @@ function addCustodian(c: ContractBuilder, access: Access) { case 'roles': { const roleOwner = 'custodian'; const roleId = 'CUSTODIAN_ROLE'; - const addedConstant = c.addVariable(`bytes32 public constant ${roleId} = keccak256("${roleId}");`); + const addedConstant = c.addConstantOrImmutableOrCustomError( + `bytes32 public constant ${roleId} = keccak256("${roleId}");`, + ); if (roleOwner && addedConstant) { c.addConstructorArgument({ type: 'address', name: roleOwner }); c.addConstructorCode(`_grantRole(${roleId}, ${roleOwner});`); diff --git a/packages/core/solidity/src/zip-foundry.test.ts.md b/packages/core/solidity/src/zip-foundry.test.ts.md index f3f4dfc1f..835438c47 100644 --- a/packages/core/solidity/src/zip-foundry.test.ts.md +++ b/packages/core/solidity/src/zip-foundry.test.ts.md @@ -346,7 +346,13 @@ Generated by [AVA](https://avajs.dev). import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊ ␊ contract MyToken is Initializable, ERC20Upgradeable, ERC20BridgeableUpgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, OwnableUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊ - address public tokenBridge;␊ + /// @custom:storage-location erc7201:myProject.MyToken␊ + struct MyTokenStorage {␊ + address tokenBridge;␊ + }␊ + ␊ + // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ + bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ error Unauthorized();␊ ␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ @@ -369,14 +375,20 @@ Generated by [AVA](https://avajs.dev). __UUPSUpgradeable_init();␊ ␊ require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");␊ - tokenBridge = tokenBridge_;␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + $.tokenBridge = tokenBridge_;␊ if (block.chainid == 10) {␊ _mint(recipient, 2000 * 10 ** decimals());␊ }␊ }␊ ␊ function _checkTokenBridge(address caller) internal view override {␊ - if (caller != tokenBridge) revert Unauthorized();␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + if (caller != $.tokenBridge) revert Unauthorized();␊ + }␊ + ␊ + function _getMyTokenStorage() private pure returns (MyTokenStorage storage $) {␊ + assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ }␊ ␊ function pause() public onlyOwner {␊ diff --git a/packages/core/solidity/src/zip-foundry.test.ts.snap b/packages/core/solidity/src/zip-foundry.test.ts.snap index 7debb3adfe7ff4fb7bda4090285ede867da2d7cc..015c890b917f4d6ac9e9066b78ed2bb1650a92a9 100644 GIT binary patch literal 4761 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 literal 4530 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 QLBQDm1L_x4xfR3!0E6?jwg3PC diff --git a/packages/core/solidity/src/zip-hardhat.test.ts.md b/packages/core/solidity/src/zip-hardhat.test.ts.md index 745b178b5..5a160b69c 100644 --- a/packages/core/solidity/src/zip-hardhat.test.ts.md +++ b/packages/core/solidity/src/zip-hardhat.test.ts.md @@ -153,7 +153,13 @@ Generated by [AVA](https://avajs.dev). import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊ ␊ contract MyToken is Initializable, ERC20Upgradeable, ERC20BridgeableUpgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, OwnableUpgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable, ERC20FlashMintUpgradeable, UUPSUpgradeable {␊ - address public tokenBridge;␊ + /// @custom:storage-location erc7201:myProject.MyToken␊ + struct MyTokenStorage {␊ + address tokenBridge;␊ + }␊ + ␊ + // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ + bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ error Unauthorized();␊ ␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ @@ -176,14 +182,20 @@ Generated by [AVA](https://avajs.dev). __UUPSUpgradeable_init();␊ ␊ require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");␊ - tokenBridge = tokenBridge_;␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + $.tokenBridge = tokenBridge_;␊ if (block.chainid == 10) {␊ _mint(recipient, 2000 * 10 ** decimals());␊ }␊ }␊ ␊ function _checkTokenBridge(address caller) internal view override {␊ - if (caller != tokenBridge) revert Unauthorized();␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + if (caller != $.tokenBridge) revert Unauthorized();␊ + }␊ + ␊ + function _getMyTokenStorage() private pure returns (MyTokenStorage storage $) {␊ + assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ }␊ ␊ function pause() public onlyOwner {␊ diff --git a/packages/core/solidity/src/zip-hardhat.test.ts.snap b/packages/core/solidity/src/zip-hardhat.test.ts.snap index 29d2250f42da5e5abdc94cc6518077cc54c321ea..b101ce26bbda6da77eb00f43f28894691b9f89a5 100644 GIT binary patch literal 3505 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 literal 3264 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 { crossChainBridging: 'superchain', access: 'roles', upgradeable: 'transparent', + namespacePrefix: 'myProject', info: { license: 'MIT', securityContact: 'security@example.com', diff --git a/packages/mcp/src/solidity/tools/erc721.ts b/packages/mcp/src/solidity/tools/erc721.ts index 1c4353413..e4449bb93 100644 --- a/packages/mcp/src/solidity/tools/erc721.ts +++ b/packages/mcp/src/solidity/tools/erc721.ts @@ -23,8 +23,8 @@ export function registerSolidityERC721(server: McpServer): RegisteredTool { votes, access, upgradeable, - info, namespacePrefix, + info, }) => { const opts: ERC721Options = { name, @@ -39,8 +39,8 @@ export function registerSolidityERC721(server: McpServer): RegisteredTool { votes, access, upgradeable, - info, namespacePrefix, + info, }; return { content: [ diff --git a/packages/mcp/src/solidity/tools/rwa.test.ts b/packages/mcp/src/solidity/tools/rwa.test.ts index d497b3a11..a2f9c2d12 100644 --- a/packages/mcp/src/solidity/tools/rwa.test.ts +++ b/packages/mcp/src/solidity/tools/rwa.test.ts @@ -55,6 +55,7 @@ test('all', async t => { premintChainId: '10', limitations: 'allowlist', custodian: true, + namespacePrefix: 'myProject', info: { license: 'MIT', securityContact: 'security@example.com', diff --git a/packages/mcp/src/solidity/tools/stablecoin.test.ts b/packages/mcp/src/solidity/tools/stablecoin.test.ts index 149b20873..8fa5798bc 100644 --- a/packages/mcp/src/solidity/tools/stablecoin.test.ts +++ b/packages/mcp/src/solidity/tools/stablecoin.test.ts @@ -55,6 +55,7 @@ test('all', async t => { premintChainId: '10', limitations: 'allowlist', custodian: true, + namespacePrefix: 'myProject', info: { license: 'MIT', securityContact: 'security@example.com', diff --git a/packages/ui/api/ai-assistant/function-definitions/solidity-shared.ts b/packages/ui/api/ai-assistant/function-definitions/solidity-shared.ts index 512c38832..91221c3b7 100644 --- a/packages/ui/api/ai-assistant/function-definitions/solidity-shared.ts +++ b/packages/ui/api/ai-assistant/function-definitions/solidity-shared.ts @@ -34,4 +34,9 @@ export const commonFunctionDescription = { }, }, }, + + namespacePrefix: { + type: 'string', + description: solidityCommonDescriptions.namespacePrefix, + }, } as const satisfies AiFunctionPropertyDefinition['properties']; diff --git a/packages/ui/api/ai-assistant/function-definitions/solidity.ts b/packages/ui/api/ai-assistant/function-definitions/solidity.ts index 0f34e3286..b3e3dc302 100644 --- a/packages/ui/api/ai-assistant/function-definitions/solidity.ts +++ b/packages/ui/api/ai-assistant/function-definitions/solidity.ts @@ -3,7 +3,6 @@ import { addFunctionPropertiesFrom } from './shared.ts'; import { commonFunctionDescription } from './solidity-shared.ts'; import { solidityPrompts, - solidityCommonDescriptions, solidityAccountDescriptions, solidityERC20Descriptions, solidityERC721Descriptions, @@ -26,6 +25,7 @@ export const solidityERC20AIFunctionDefinition = { 'mintable', 'access', 'upgradeable', + 'namespacePrefix', 'info', ]), premint: { @@ -82,6 +82,7 @@ export const solidityERC721AIFunctionDefinition = { 'mintable', 'access', 'upgradeable', + 'namespacePrefix', 'info', ]), baseUri: { type: 'string', description: solidityERC721Descriptions.baseUri }, @@ -104,10 +105,6 @@ export const solidityERC721AIFunctionDefinition = { ], description: solidityERC721Descriptions.votes, }, - namespacePrefix: { - type: 'string', - description: solidityCommonDescriptions.namespacePrefix, - }, }, required: ['name', 'symbol'], additionalProperties: false, diff --git a/packages/ui/deno.lock b/packages/ui/deno.lock index f107df782..25d41623a 100644 --- a/packages/ui/deno.lock +++ b/packages/ui/deno.lock @@ -4,7 +4,8 @@ "https://deno.land/std/fmt/colors.ts": "https://deno.land/std@0.224.0/fmt/colors.ts", "https://deno.land/std/path/mod.ts": "https://deno.land/std@0.224.0/path/mod.ts", "https://esm.sh/crypto-js@^4.2.0/enc-hex?target=denonext": "https://esm.sh/crypto-js@4.2.0/enc-hex?target=denonext", - "https://esm.sh/crypto-js@^4.2.0/sha1?target=denonext": "https://esm.sh/crypto-js@4.2.0/sha1?target=denonext" + "https://esm.sh/crypto-js@^4.2.0/sha1?target=denonext": "https://esm.sh/crypto-js@4.2.0/sha1?target=denonext", + "https://esm.sh/uncrypto@^0.1.3?target=denonext": "https://esm.sh/uncrypto@0.1.3?target=denonext" }, "remote": { "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", @@ -89,6 +90,9 @@ "https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c", "https://esm.sh/@upstash/redis@1.25.2": "b9400ffe20e12237188f35ee3658f02e273ecd1abd421106af12f6ea916c81b0", "https://esm.sh/@upstash/redis@1.25.2/denonext/redis.mjs": "d8487c833ec520cc90f52b680b172282e91941b937bc9ea879b8347e407833cc", + "https://esm.sh/@upstash/redis@1.35.6": "18026bc0000af7b6b158f6e2ff7ae244c9745da61b9302e176479f8b3bc145dd", + "https://esm.sh/@upstash/redis@1.35.6/denonext/chunk-TAJI6TAE.mjs": "2ea1c6793c40ca7f431a1260e2926170a9af57dac2a6ef8dc55ff95bdfe3bb25", + "https://esm.sh/@upstash/redis@1.35.6/denonext/redis.mjs": "0d5bccf5bbc8b67335530487a16587640ac40b9b176237ee18759bae489ffc33", "https://esm.sh/crypto-js@4.2.0/denonext/enc-hex.mjs": "d9b57ee9b036cb023e8c87f0063f616405c34f166327e6bc561ce36059eabfa6", "https://esm.sh/crypto-js@4.2.0/denonext/sha1.mjs": "7c20a11f45d6390bfd5babfb769457ad42775bca38d140dc497e548a94f7745b", "https://esm.sh/crypto-js@4.2.0/enc-hex?target=denonext": "e7bbfb366ad3cbea21a865f646e2dd1109398362c4779291bffb03bb6332310f", @@ -145,46 +149,104 @@ "https://esm.sh/openai@5.13.1/denonext/resources/webhooks.mjs": "20db2401be99bbc5225a4e7c90db2f4e7d5bb7e29700e8041f2244c200a902ed", "https://esm.sh/openai@5.13.1/denonext/streaming.mjs": "9eb0a252d1fb6e345b1b1a802b1e98dd61f063b4f4929d9673c9d8dea1c62702", "https://esm.sh/openai@5.13.1/denonext/version.mjs": "882faa862a3c0b55f59198719bd3b66cd7d1e0934fe4ce571ff7664b343e7f73", - "https://esm.sh/openai@5.13.1/lib/ChatCompletionStream.mjs": "5e893f12c80efe27ea3585280237c97dc5f77ff93e13a4b7ff36dc9be945970b" + "https://esm.sh/openai@5.13.1/lib/ChatCompletionStream.mjs": "5e893f12c80efe27ea3585280237c97dc5f77ff93e13a4b7ff36dc9be945970b", + "https://esm.sh/openai@5.23.2": "18c9a853acea5ea20099acc7af13d1f90de40e60d072138257f6cee73131cee2", + "https://esm.sh/openai@5.23.2/denonext/azure.mjs": "861a920db3ef442226f9afba3371625f22854970b9c2563e15c8b049101604cc", + "https://esm.sh/openai@5.23.2/denonext/client.mjs": "cd14f5748ad5702353b8b1ac6289937fed0909bdd652842d33816a6421ae1b3e", + "https://esm.sh/openai@5.23.2/denonext/core/api-promise.mjs": "3d306a063412dca41b2ec293d22de25f4998613f1560cd2d16b25b11c6bbac60", + "https://esm.sh/openai@5.23.2/denonext/core/error.mjs": "1c0fc71bbe3409121aea6b02cb94a339f35f84d586c1cce881dfc036fd76c31c", + "https://esm.sh/openai@5.23.2/denonext/core/pagination.mjs": "68f1d809ff8af4019f8aec2bb1b55261bfd1a6735d2737f2ff42c4d046bfd457", + "https://esm.sh/openai@5.23.2/denonext/core/resource.mjs": "dc9b968c6e12958b4842d3bb317e612df1407c627c62e7a5712a285c5ccc2830", + "https://esm.sh/openai@5.23.2/denonext/core/streaming.mjs": "c5d81d565282a14800274a8dba998cc7d732f8a871117a35e1acc56611700627", + "https://esm.sh/openai@5.23.2/denonext/core/uploads.mjs": "61f6c87b387bc15d267b06415d978cb761b760169586748da96b47ece60bc8b0", + "https://esm.sh/openai@5.23.2/denonext/error.mjs": "7b6e16fae0c8cdc1a2666940c9a3f5b0a3d96ffbcb5dda55cd1135b4dbc09cd7", + "https://esm.sh/openai@5.23.2/denonext/internal/errors.mjs": "339efff273a9b2653e9bbd3bf80e4787607058542639abc46d9a9b7f69d5065d", + "https://esm.sh/openai@5.23.2/denonext/internal/headers.mjs": "0dc4a75adb1bcd0ce69387ed74ec00ac0dbf654305626e019262b7d0ffcc34b5", + "https://esm.sh/openai@5.23.2/denonext/internal/parse.mjs": "4406db1122214957c9844cb4f3f3fd89d0eb214bd733878844166ff93ecfb657", + "https://esm.sh/openai@5.23.2/denonext/internal/shims.mjs": "869afba5c9cad4ddbeac636a1672e15f28b4511837c8768e78052ff1ecc2aadc", + "https://esm.sh/openai@5.23.2/denonext/internal/tslib.mjs": "b3f95f57fb360dd1e8fa1d976b70f88c0087b0e7626238bf1ac011c08aab9029", + "https://esm.sh/openai@5.23.2/denonext/internal/uploads.mjs": "cb7ca0d8ad424e9c4c7d0e8d660894de67275e09264d751a286a37b55b06a60f", + "https://esm.sh/openai@5.23.2/denonext/internal/utils.mjs": "f90b350e2d58df257c65b0d0d038793b0c7c28007bb9b2e1af3330de530726e1", + "https://esm.sh/openai@5.23.2/denonext/internal/utils/bytes.mjs": "7a173944415c7c98092f3f64d4869b06352891c86224a6288cd5b948f6099ed1", + "https://esm.sh/openai@5.23.2/denonext/internal/utils/env.mjs": "989300dc3c0155101310ef586c4ec11b1b28374390008466a82a5358a0bf3cd9", + "https://esm.sh/openai@5.23.2/denonext/internal/utils/log.mjs": "ff989515d620763b6c50d5a0911bf87dedd05499f08e9b6c60c0939bff7dd63b", + "https://esm.sh/openai@5.23.2/denonext/internal/utils/path.mjs": "aa2e93f9627bf2ff1ddba81a900c9e8c3bc0bc340d773282d264edecf0201cb5", + "https://esm.sh/openai@5.23.2/denonext/internal/utils/sleep.mjs": "8cc3607942d5456fcff0791bd76159c0bdcf4045f0df5bcc631e7aa0007f5bed", + "https://esm.sh/openai@5.23.2/denonext/internal/utils/uuid.mjs": "930d86b806f784ec4f4ca5f281dbc81275c5502e559333e4f7531fcfbdeeec6e", + "https://esm.sh/openai@5.23.2/denonext/internal/utils/values.mjs": "6ae7003a2e877ba3c5a9907c86d4ae6127e30b547c2799fed26e3511ef34b211", + "https://esm.sh/openai@5.23.2/denonext/lib/AbstractChatCompletionRunner.mjs": "23b10704473a21f4f99673e0c97214bbaa2047ef3c68010a33242e592f1a367b", + "https://esm.sh/openai@5.23.2/denonext/lib/EventStream.mjs": "9d55a5e80ce128c8cfe397ce6eca3f9769cbf5983bd6d7ea19c90a078fa3f120", + "https://esm.sh/openai@5.23.2/denonext/lib/RunnableFunction.mjs": "bcacb84aeefde5fab68ea7f3ecc005ca8c8b5ddec4c95c7b4b2765703536f6de", + "https://esm.sh/openai@5.23.2/denonext/lib/chatCompletionUtils.mjs": "a59f5c19e7eaba4288fce3da901ddf459dff267fd0f5e625c14bc41822877e41", + "https://esm.sh/openai@5.23.2/denonext/lib/parser.mjs": "fb3d5d41d3288fc5432f4c96b9e936dc235560dc459cb54aa551defdfacacf3c", + "https://esm.sh/openai@5.23.2/denonext/openai.mjs": "bd1c14bddec48f9287ebc694a1f15af9a7edda353f5ec724c24955bf39da8553", + "https://esm.sh/openai@5.23.2/denonext/resources/audio/audio.mjs": "f6c9e7c018be327e39721ce14ca2b6f0036a2d11932b868769a7259505b41e4c", + "https://esm.sh/openai@5.23.2/denonext/resources/batches.mjs": "862e98d3ee070ea44dcb7129ab3706ea659ff271e158f4220bd59db8aac0584e", + "https://esm.sh/openai@5.23.2/denonext/resources/beta/beta.mjs": "3c81b7c65fb6acffbd557dc27a50840b8816c56ae52a95492712252c8ae62fe4", + "https://esm.sh/openai@5.23.2/denonext/resources/chat/chat.mjs": "40068f56f54bfcb8d337d0540d004c1e0ffeaa4afa651061473578a60de00546", + "https://esm.sh/openai@5.23.2/denonext/resources/chat/completions/index.mjs": "84a10ad22b033369a1d1a157ce7ce06ecf32ea7e4ac1c49f3202b6dd2c471b95", + "https://esm.sh/openai@5.23.2/denonext/resources/completions.mjs": "d9885e2303e397806512c92e17fb7665855e73cc96a2bd070b1ab35d3c55b515", + "https://esm.sh/openai@5.23.2/denonext/resources/containers/containers.mjs": "8579666d685277e06bd9e4157b7955271278da054de4d932fb8ccaa4d5855d74", + "https://esm.sh/openai@5.23.2/denonext/resources/conversations/conversations.mjs": "fa055ced06c8c143dc15f9d20014b9d6f2af077f7fdf7d4dd078fc2e2bcc74bc", + "https://esm.sh/openai@5.23.2/denonext/resources/embeddings.mjs": "8155a273a2b745737644114c0bd564e88d383fa1b7f76aef17e2bc0edf9de573", + "https://esm.sh/openai@5.23.2/denonext/resources/evals/evals.mjs": "951234c3751c8fe11977a3b112e5ff81ba793bb61036c35e46a0ecc359f46472", + "https://esm.sh/openai@5.23.2/denonext/resources/files.mjs": "b8a0fc1f7750ed8ae0692149d63acc66618f7db078023170bd94c48206aff344", + "https://esm.sh/openai@5.23.2/denonext/resources/fine-tuning/fine-tuning.mjs": "338edaa2b94083ff1d238026a785b5c487748564fa67956ef3fed00703f2d9a1", + "https://esm.sh/openai@5.23.2/denonext/resources/graders/graders.mjs": "5a8979f09bafb70245317f499c5bc649981857679b5568ee5140dd459c759f5c", + "https://esm.sh/openai@5.23.2/denonext/resources/images.mjs": "0e55d6c712ea07d3ab2ecc9fce3a860ff916669c1b91969d434c242d4591eaa4", + "https://esm.sh/openai@5.23.2/denonext/resources/index.mjs": "c6f4739086a037feb6698e897ff48e4663a25366a0009d906460ae80cc59e9ae", + "https://esm.sh/openai@5.23.2/denonext/resources/models.mjs": "2b406d2ddb2e682fdd6444d072311faf6ebd2e0afe3d1e614b3c56f8efd2877b", + "https://esm.sh/openai@5.23.2/denonext/resources/moderations.mjs": "3fae384a7cefdd5020b20a550553aa0b5fd847455649e81d27a0c19228cd9de7", + "https://esm.sh/openai@5.23.2/denonext/resources/realtime/realtime.mjs": "f433992a4a5c3b6350828191c5a4c0eb790684aea873531f254c4e7ef4d1526f", + "https://esm.sh/openai@5.23.2/denonext/resources/responses/responses.mjs": "9c1d4db5078327eda92434e82227f0e5bf21f8689b20d66b3162129ed416d29c", + "https://esm.sh/openai@5.23.2/denonext/resources/uploads/uploads.mjs": "8d8c996cff88595454cbbaab6f8f2220d19258e17af4d249513c6972b8a70fee", + "https://esm.sh/openai@5.23.2/denonext/resources/vector-stores/vector-stores.mjs": "0d6d13b6def2e77c4b4d16f0d81e0dc55876f77994d48fd4538a66276ced0fee", + "https://esm.sh/openai@5.23.2/denonext/resources/webhooks.mjs": "f08af37a3ede2e096b447182bc53c970ae72845b70fb66f56a4393de031c0639", + "https://esm.sh/openai@5.23.2/denonext/streaming.mjs": "222c46a59c680af6cd7952bab85e2e9b3de6c85aa7323162b2dcb41e17a6401c", + "https://esm.sh/openai@5.23.2/denonext/version.mjs": "0fcc73b57c217be9679bede707faa89db6055de8d1b0bbfcf69d56ea3cce4f1d", + "https://esm.sh/uncrypto@0.1.3/denonext/uncrypto.mjs": "610754e3fadd2110d6623dc68ce172f530491167504ce8b1b886c9e071cfe14f", + "https://esm.sh/uncrypto@0.1.3?target=denonext": "bc6d905780a009deb8e198516e0b404a22cb7ca992fbd372fef508e0ad534e02" }, "workspace": { "packageJson": { "dependencies": [ - "npm:@rollup/plugin-alias@^5.0.0", - "npm:@rollup/plugin-commonjs@^28.0.0", - "npm:@rollup/plugin-json@^6.0.0", - "npm:@rollup/plugin-node-resolve@^16.0.0", - "npm:@rollup/plugin-replace@^6.0.0", - "npm:@rollup/plugin-typescript@^12.0.0", - "npm:@types/file-saver@^2.0.1", - "npm:@types/node@^20.0.0", - "npm:@types/resize-observer-browser@^0.1.5", - "npm:@types/semver@^7.5.7", - "npm:@upstash/redis@1.25.2", - "npm:autoprefixer@^10.4.2", + "npm:@rollup/plugin-alias@^5.1.1", + "npm:@rollup/plugin-commonjs@^28.0.8", + "npm:@rollup/plugin-json@^6.1.0", + "npm:@rollup/plugin-node-resolve@^16.0.3", + "npm:@rollup/plugin-replace@^6.0.2", + "npm:@rollup/plugin-typescript@^12.1.4", + "npm:@types/file-saver@^2.0.7", + "npm:@types/node@^20.19.21", + "npm:@types/resize-observer-browser@^0.1.11", + "npm:@types/semver@^7.7.1", + "npm:@upstash/redis@1.35.6", + "npm:autoprefixer@^10.4.21", + "npm:ava@^6.4.1", "npm:file-saver@^2.0.5", - "npm:highlight.js@^11.0.0", + "npm:highlight.js@^11.11.1", "npm:highlightjs-cairo@^0.4.0", - "npm:highlightjs-solidity@^2.0.0", - "npm:openai@5.13.1", + "npm:highlightjs-solidity@^2.0.6", + "npm:openai@5.23.2", "npm:path-browserify@^1.0.1", - "npm:postcss-load-config@^6.0.0", - "npm:postcss@^8.2.8", - "npm:rollup-plugin-livereload@^2.0.0", + "npm:postcss-load-config@^6.0.1", + "npm:postcss@^8.5.6", + "npm:rollup-plugin-livereload@^2.0.5", "npm:rollup-plugin-styles@^4.0.0", - "npm:rollup-plugin-svelte@^7.0.0", - "npm:rollup-plugin-terser@^7.0.0", - "npm:rollup@^4.0.0", - "npm:semver@^7.6.0", - "npm:sirv-cli@^3.0.0", - "npm:svelte-check@^3.0.1", - "npm:svelte-preprocess@^5.0.0", + "npm:rollup-plugin-svelte@^7.2.3", + "npm:rollup-plugin-terser@^7.0.2", + "npm:rollup@^4.52.4", + "npm:semver@^7.7.3", + "npm:sirv-cli@^3.0.1", + "npm:svelte-check@^3.8.6", + "npm:svelte-preprocess@^5.1.4", "npm:svelte@^3.55.0", - "npm:tailwindcss@^3.0.15", - "npm:tippy.js@^6.3.1", - "npm:tslib@^2.0.0", - "npm:typescript@^5.0.0", - "npm:util@^0.12.4" + "npm:tailwindcss@^3.4.18", + "npm:tippy.js@^6.3.7", + "npm:ts-node@^10.9.2", + "npm:tslib@^2.8.1", + "npm:typescript@^5.9.3", + "npm:util@^0.12.5" ] } } diff --git a/packages/ui/src/solidity/ERC20Controls.svelte b/packages/ui/src/solidity/ERC20Controls.svelte index 51f68dfa3..b4174299f 100644 --- a/packages/ui/src/solidity/ERC20Controls.svelte +++ b/packages/ui/src/solidity/ERC20Controls.svelte @@ -202,6 +202,10 @@ - + From c809aa4d5bf5c12e371390c458e7daa77b0d0c08 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 27 Oct 2025 17:20:47 -0400 Subject: [PATCH 11/24] Use spaces between vars that have comments --- packages/core/solidity/src/contract.test.ts | 12 ++++---- packages/core/solidity/src/contract.ts | 30 ++++++++++--------- packages/core/solidity/src/erc20.ts | 8 ++--- packages/core/solidity/src/erc721.ts | 2 +- packages/core/solidity/src/print.ts | 18 ++++++++++- .../core/solidity/src/set-access-control.ts | 2 +- .../solidity/src/set-namespaced-storage.ts | 4 +-- packages/core/solidity/src/stablecoin.ts | 2 +- 8 files changed, 48 insertions(+), 30 deletions(-) diff --git a/packages/core/solidity/src/contract.test.ts b/packages/core/solidity/src/contract.test.ts index 989b4d544..4d178b22b 100644 --- a/packages/core/solidity/src/contract.test.ts +++ b/packages/core/solidity/src/contract.test.ts @@ -135,32 +135,32 @@ test('contract with overridden function with code', t => { test('contract with one variable', t => { const Foo = new ContractBuilder('Foo'); - Foo.addVariable('uint value = 42;', false); + Foo.addStateVariable('uint value = 42;', false); t.snapshot(printContract(Foo)); }); test('contract with two variables', t => { const Foo = new ContractBuilder('Foo'); - Foo.addVariable('uint value = 42;', false); - Foo.addVariable('string name = "john";', false); + Foo.addStateVariable('uint value = 42;', false); + Foo.addStateVariable('string name = "john";', false); t.snapshot(printContract(Foo)); }); test('contract with immutable', t => { const Foo = new ContractBuilder('Foo'); - Foo.addConstantOrImmutableOrCustomError('uint immutable value = 42;'); + Foo.addConstantOrImmutableOrErrorDefinition('uint immutable value = 42;'); t.snapshot(printContract(Foo)); }); test('contract with constant and comment', t => { const Foo = new ContractBuilder('Foo'); - Foo.addConstantOrImmutableOrCustomError('uint constant value = 42;', '// This is a comment'); + Foo.addConstantOrImmutableOrErrorDefinition('uint constant value = 42;', ['// This is a comment']); t.snapshot(printContract(Foo)); }); test('contract with variable and upgradeable - error', t => { const Foo = new ContractBuilder('Foo'); - const error = t.throws(() => Foo.addVariable('uint value = 42;', true)); + const error = t.throws(() => Foo.addStateVariable('uint value = 42;', true)); t.is( (error as Error).message, 'State variables should not be used when upgradeable is true. Set namespaced storage instead.', diff --git a/packages/core/solidity/src/contract.ts b/packages/core/solidity/src/contract.ts index 17ef91ac9..22056efe6 100644 --- a/packages/core/solidity/src/contract.ts +++ b/packages/core/solidity/src/contract.ts @@ -10,7 +10,7 @@ export interface Contract { structs: ContractStruct[]; constructorCode: string[]; constructorArgs: FunctionArgument[]; - variables: string[]; + variableOrErrorDefinitions: VariableOrErrorDefinition[]; upgradeable: boolean; } @@ -79,6 +79,11 @@ export interface NatspecTag { value: string; } +export interface VariableOrErrorDefinition { + code: string; + comments?: string[]; +} + export class ContractBuilder implements Contract { readonly name: string; license: string = 'MIT'; @@ -89,8 +94,8 @@ export class ContractBuilder implements Contract { readonly constructorArgs: FunctionArgument[] = []; readonly constructorCode: string[] = []; - readonly variableSet: Set = new Set(); + readonly variableOrErrorMap: Map = new Map(); private parentMap: Map = new Map(); private functionMap: Map = new Map(); private structMap: Map = new Map(); @@ -125,8 +130,8 @@ export class ContractBuilder implements Contract { return [...this.structMap.values()]; } - get variables(): string[] { - return [...this.variableSet]; + get variableOrErrorDefinitions(): VariableOrErrorDefinition[] { + return [...this.variableOrErrorMap.values()]; } addParent(contract: ImportContract, params: Value[] = []): boolean { @@ -239,27 +244,24 @@ export class ContractBuilder implements Contract { /** * Note: The type in the code is not currently transpiled, even if it refers to a contract */ - addVariable(code: string, upgradeable: boolean): boolean { + addStateVariable(code: string, upgradeable: boolean): boolean { if (upgradeable) { throw new Error('State variables should not be used when upgradeable is true. Set namespaced storage instead.'); } else { - return this._addVariable(code); + return this._addVariableOrErrorDefinition({ code }); } } /** * Note: The type in the code is not currently transpiled, even if it refers to a contract */ - addConstantOrImmutableOrCustomError(code: string, comment?: string): boolean { - if (comment) { - this._addVariable(comment); - } - return this._addVariable(code); + addConstantOrImmutableOrErrorDefinition(code: string, comments?: string[]): boolean { + return this._addVariableOrErrorDefinition({ code, comments }); } - private _addVariable(code: string): boolean { - const present = this.variableSet.has(code); - this.variableSet.add(code); + private _addVariableOrErrorDefinition(variableOrErrorDefinition: VariableOrErrorDefinition): boolean { + const present = this.variableOrErrorMap.has(variableOrErrorDefinition.code); + this.variableOrErrorMap.set(variableOrErrorDefinition.code, variableOrErrorDefinition); return !present; } diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index a1066c2bb..f39b6a59b 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -335,7 +335,7 @@ function addCrossChainBridging( throw new Error('Unknown value for `crossChainBridging`'); } } - c.addConstantOrImmutableOrCustomError('error Unauthorized();'); + c.addConstantOrImmutableOrErrorDefinition('error Unauthorized();'); } function addCustomBridging(c: ContractBuilder, access: Access, upgradeable: Upgradeable, namespacePrefix: string) { @@ -343,7 +343,7 @@ function addCustomBridging(c: ContractBuilder, access: Access, upgradeable: Upgr case false: case 'ownable': { if (!upgradeable) { - const addedBridge = c.addVariable(`address public tokenBridge;`, false); + const addedBridge = c.addStateVariable(`address public tokenBridge;`, false); if (addedBridge) { c.addConstructorArgument({ type: 'address', name: 'tokenBridge_' }); c.addConstructorCode(`require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");`); @@ -371,7 +371,7 @@ function addCustomBridging(c: ContractBuilder, access: Access, upgradeable: Upgr setAccessControl(c, access); const roleOwner = 'tokenBridge'; const roleId = 'TOKEN_BRIDGE_ROLE'; - const addedRoleConstant = c.addConstantOrImmutableOrCustomError( + const addedRoleConstant = c.addConstantOrImmutableOrErrorDefinition( `bytes32 public constant ${roleId} = keccak256("${roleId}");`, ); if (addedRoleConstant) { @@ -409,7 +409,7 @@ function addCustomBridging(c: ContractBuilder, access: Access, upgradeable: Upgr } function addSuperchainERC20(c: ContractBuilder) { - c.addConstantOrImmutableOrCustomError( + c.addConstantOrImmutableOrErrorDefinition( 'address internal constant SUPERCHAIN_TOKEN_BRIDGE = 0x4200000000000000000000000000000000000028;', ); c.setFunctionBody( diff --git a/packages/core/solidity/src/erc721.ts b/packages/core/solidity/src/erc721.ts index 4d8ee6321..dc5258aff 100644 --- a/packages/core/solidity/src/erc721.ts +++ b/packages/core/solidity/src/erc721.ts @@ -189,7 +189,7 @@ function addMintable( requireAccessControl(c, fn, access, 'MINTER', 'minter'); if (incremental) { if (!upgradeable) { - c.addVariable('uint256 private _nextTokenId;', upgradeable); + c.addStateVariable('uint256 private _nextTokenId;', upgradeable); c.addFunctionCode('uint256 tokenId = _nextTokenId++;', fn); c.addFunctionCode('_safeMint(to, tokenId);', fn); } else { diff --git a/packages/core/solidity/src/print.ts b/packages/core/solidity/src/print.ts index 8f471b44e..36dbdab20 100644 --- a/packages/core/solidity/src/print.ts +++ b/packages/core/solidity/src/print.ts @@ -7,6 +7,7 @@ import type { NatspecTag, ImportContract, ContractStruct, + VariableOrErrorDefinition, } from './contract'; import type { Options, Helpers } from './options'; import { withHelpers } from './options'; @@ -43,7 +44,8 @@ export function printContract(contract: Contract, opts?: Options): string { spaceBetween( ...structs, - contract.variables, + printVariableOrErrorDefinitionsWithComments(contract.variableOrErrorDefinitions), + printVariableOrErrorDefinitionsWithoutComments(contract.variableOrErrorDefinitions), printConstructor(contract, helpers), ...fns.code, ...fns.modifiers, @@ -57,6 +59,20 @@ export function printContract(contract: Contract, opts?: Options): string { ); } +function printVariableOrErrorDefinitionsWithComments(variableOrErrorDefinitions: VariableOrErrorDefinition[]): Lines[] { + const withComments = variableOrErrorDefinitions.filter(v => v.comments?.length); + // Spaces between each item that has comments + return spaceBetween(...withComments.map(v => [...v.comments!, v.code])); +} + +function printVariableOrErrorDefinitionsWithoutComments( + variableOrErrorDefinitions: VariableOrErrorDefinition[], +): Lines[] { + const withoutComments = variableOrErrorDefinitions.filter(v => !v.comments?.length); + // No spaces between items that don't have comments + return withoutComments.map(v => v.code); +} + function printCompatibleLibraryVersions(contract: Contract): string { let result = `// Compatible with OpenZeppelin Contracts ${compatibleContractsSemver}`; if (importsCommunityContracts(contract)) { diff --git a/packages/core/solidity/src/set-access-control.ts b/packages/core/solidity/src/set-access-control.ts index af1b57d36..befb05eb3 100644 --- a/packages/core/solidity/src/set-access-control.ts +++ b/packages/core/solidity/src/set-access-control.ts @@ -65,7 +65,7 @@ export function requireAccessControl( } case 'roles': { const roleId = roleIdPrefix + '_ROLE'; - const addedConstant = c.addConstantOrImmutableOrCustomError( + const addedConstant = c.addConstantOrImmutableOrErrorDefinition( `bytes32 public constant ${roleId} = keccak256("${roleId}");`, ); if (roleOwner && addedConstant) { diff --git a/packages/core/solidity/src/set-namespaced-storage.ts b/packages/core/solidity/src/set-namespaced-storage.ts index 948e2dc61..642e32b78 100644 --- a/packages/core/solidity/src/set-namespaced-storage.ts +++ b/packages/core/solidity/src/set-namespaced-storage.ts @@ -12,9 +12,9 @@ export function setNamespacedStorage(c: ContractBuilder, structVariables: string structVariables.forEach(v => c.addStructVariable(storageStruct, v)); - c.addConstantOrImmutableOrCustomError( + c.addConstantOrImmutableOrErrorDefinition( `bytes32 private constant ${namespacedStorageConstant} = ${computeNamespacedStorageSlot(namespaceId)};`, - `// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`, + [`// keccak256(abi.encode(uint256(keccak256("${namespaceId}")) - 1)) & ~bytes32(uint256(0xff))`], ); c.addFunctionCode(`assembly { $.slot := ${namespacedStorageConstant} }`, storageFn); } diff --git a/packages/core/solidity/src/stablecoin.ts b/packages/core/solidity/src/stablecoin.ts index 39f813a74..86dc930e3 100644 --- a/packages/core/solidity/src/stablecoin.ts +++ b/packages/core/solidity/src/stablecoin.ts @@ -107,7 +107,7 @@ function addCustodian(c: ContractBuilder, access: Access) { case 'roles': { const roleOwner = 'custodian'; const roleId = 'CUSTODIAN_ROLE'; - const addedConstant = c.addConstantOrImmutableOrCustomError( + const addedConstant = c.addConstantOrImmutableOrErrorDefinition( `bytes32 public constant ${roleId} = keccak256("${roleId}");`, ); if (roleOwner && addedConstant) { From 2e1258b28fbf01009ead8545962d2dfdddcfa9e9 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 27 Oct 2025 17:25:07 -0400 Subject: [PATCH 12/24] Update snapshots --- packages/core/solidity/src/erc20.test.ts.md | 1 + packages/core/solidity/src/erc20.test.ts.snap | Bin 3715 -> 3717 bytes 2 files changed, 1 insertion(+) diff --git a/packages/core/solidity/src/erc20.test.ts.md b/packages/core/solidity/src/erc20.test.ts.md index 07b0f50a0..f7e305dbe 100644 --- a/packages/core/solidity/src/erc20.test.ts.md +++ b/packages/core/solidity/src/erc20.test.ts.md @@ -870,6 +870,7 @@ Generated by [AVA](https://avajs.dev). ␊ // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ + ␊ error Unauthorized();␊ ␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ diff --git a/packages/core/solidity/src/erc20.test.ts.snap b/packages/core/solidity/src/erc20.test.ts.snap index 6239e8cae84da21dda825dd84fca6f473c04ef52..c912c72892297c242711efc8496724c9471fcb79 100644 GIT binary patch literal 3717 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 Date: Tue, 28 Oct 2025 08:21:47 -0400 Subject: [PATCH 13/24] Update snapshots --- .../core/solidity/src/zip-hardhat.test.ts.md | 1 + .../solidity/src/zip-hardhat.test.ts.snap | Bin 3505 -> 3504 bytes 2 files changed, 1 insertion(+) diff --git a/packages/core/solidity/src/zip-hardhat.test.ts.md b/packages/core/solidity/src/zip-hardhat.test.ts.md index 5a160b69c..bf05873a7 100644 --- a/packages/core/solidity/src/zip-hardhat.test.ts.md +++ b/packages/core/solidity/src/zip-hardhat.test.ts.md @@ -160,6 +160,7 @@ Generated by [AVA](https://avajs.dev). ␊ // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ + ␊ error Unauthorized();␊ ␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ diff --git a/packages/core/solidity/src/zip-hardhat.test.ts.snap b/packages/core/solidity/src/zip-hardhat.test.ts.snap index b101ce26bbda6da77eb00f43f28894691b9f89a5..e447f879e84c5ff772ffd9c9b83b497c65e93915 100644 GIT binary patch delta 3437 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 From db46fd1eec038882f9833b43caa5ffd639316fee Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 28 Oct 2025 09:46:13 -0400 Subject: [PATCH 14/24] Update snapshots --- .../core/solidity/src/zip-foundry.test.ts.md | 1 + .../solidity/src/zip-foundry.test.ts.snap | Bin 4761 -> 4762 bytes 2 files changed, 1 insertion(+) diff --git a/packages/core/solidity/src/zip-foundry.test.ts.md b/packages/core/solidity/src/zip-foundry.test.ts.md index 835438c47..ba30a61d0 100644 --- a/packages/core/solidity/src/zip-foundry.test.ts.md +++ b/packages/core/solidity/src/zip-foundry.test.ts.md @@ -353,6 +353,7 @@ Generated by [AVA](https://avajs.dev). ␊ // keccak256(abi.encode(uint256(keccak256("myProject.MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xfbb7c9e4123fcf4b1aad53c70358f7b1c1d7cf28092f5178b53e55db565e9200;␊ + ␊ error Unauthorized();␊ ␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ diff --git a/packages/core/solidity/src/zip-foundry.test.ts.snap b/packages/core/solidity/src/zip-foundry.test.ts.snap index 015c890b917f4d6ac9e9066b78ed2bb1650a92a9..bbf47b6abd12f960586bcc20e4bf1cb138c5f2ae 100644 GIT binary patch literal 4762 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 From 18b671d8126f35e9b0ffc20296b9613840b6ccdf Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 28 Oct 2025 11:02:50 -0400 Subject: [PATCH 15/24] Fix deno check --- .../function-definitions/solidity-shared.ts | 5 ----- .../api/ai-assistant/function-definitions/solidity.ts | 11 +++++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/ui/api/ai-assistant/function-definitions/solidity-shared.ts b/packages/ui/api/ai-assistant/function-definitions/solidity-shared.ts index 91221c3b7..512c38832 100644 --- a/packages/ui/api/ai-assistant/function-definitions/solidity-shared.ts +++ b/packages/ui/api/ai-assistant/function-definitions/solidity-shared.ts @@ -34,9 +34,4 @@ export const commonFunctionDescription = { }, }, }, - - namespacePrefix: { - type: 'string', - description: solidityCommonDescriptions.namespacePrefix, - }, } as const satisfies AiFunctionPropertyDefinition['properties']; diff --git a/packages/ui/api/ai-assistant/function-definitions/solidity.ts b/packages/ui/api/ai-assistant/function-definitions/solidity.ts index b3e3dc302..ce1a63c8d 100644 --- a/packages/ui/api/ai-assistant/function-definitions/solidity.ts +++ b/packages/ui/api/ai-assistant/function-definitions/solidity.ts @@ -9,6 +9,7 @@ import { solidityERC1155Descriptions, solidityStablecoinDescriptions, solidityGovernorDescriptions, + solidityCommonDescriptions, } from '../../../../common/src/ai/descriptions/solidity.ts'; export const solidityERC20AIFunctionDefinition = { @@ -25,7 +26,6 @@ export const solidityERC20AIFunctionDefinition = { 'mintable', 'access', 'upgradeable', - 'namespacePrefix', 'info', ]), premint: { @@ -62,6 +62,10 @@ export const solidityERC20AIFunctionDefinition = { type: 'boolean', description: solidityERC20Descriptions.callback, }, + namespacePrefix: { + type: 'string', + description: solidityCommonDescriptions.namespacePrefix, + }, }, required: ['name', 'symbol'], additionalProperties: false, @@ -82,7 +86,6 @@ export const solidityERC721AIFunctionDefinition = { 'mintable', 'access', 'upgradeable', - 'namespacePrefix', 'info', ]), baseUri: { type: 'string', description: solidityERC721Descriptions.baseUri }, @@ -105,6 +108,10 @@ export const solidityERC721AIFunctionDefinition = { ], description: solidityERC721Descriptions.votes, }, + namespacePrefix: { + type: 'string', + description: solidityCommonDescriptions.namespacePrefix, + }, }, required: ['name', 'symbol'], additionalProperties: false, From c56c34c60e404866687d0047171b74bba3d4d884 Mon Sep 17 00:00:00 2001 From: typicalHuman Date: Tue, 28 Oct 2025 22:18:05 +0500 Subject: [PATCH 16/24] Added test for ERC721 with namespacePrefix --- packages/core/solidity/src/erc721.test.ts | 12 +++ packages/core/solidity/src/erc721.test.ts.md | 96 ++++++++++++++++++ .../core/solidity/src/erc721.test.ts.snap | Bin 2922 -> 2974 bytes 3 files changed, 108 insertions(+) diff --git a/packages/core/solidity/src/erc721.test.ts b/packages/core/solidity/src/erc721.test.ts index 372dbbfc3..102cbdc18 100644 --- a/packages/core/solidity/src/erc721.test.ts +++ b/packages/core/solidity/src/erc721.test.ts @@ -169,6 +169,18 @@ testERC721('full upgradeable uups + managed + incremental', { access: 'managed', }); +testERC721('full upgradeable uups + managed + incremental + empty namespacePrefix', { + mintable: true, + enumerable: true, + pausable: true, + burnable: true, + incremental: true, + votes: true, + upgradeable: 'uups', + access: 'managed', + namespacePrefix: '', +}); + testAPIEquivalence('API default'); testAPIEquivalence('API basic', { name: 'CustomToken', symbol: 'CTK' }); diff --git a/packages/core/solidity/src/erc721.test.ts.md b/packages/core/solidity/src/erc721.test.ts.md index c11019b0f..30fbf1faf 100644 --- a/packages/core/solidity/src/erc721.test.ts.md +++ b/packages/core/solidity/src/erc721.test.ts.md @@ -1084,3 +1084,99 @@ Generated by [AVA](https://avajs.dev). }␊ }␊ ` + +## full upgradeable uups + managed + incremental + empty namespacePrefix + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.4.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";␊ + import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";␊ + import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";␊ + import {ERC721BurnableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";␊ + import {ERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";␊ + import {ERC721PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";␊ + import {ERC721VotesUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721VotesUpgradeable.sol";␊ + import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";␊ + import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";␊ + ␊ + contract MyToken is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721PausableUpgradeable, AccessManagedUpgradeable, ERC721BurnableUpgradeable, EIP712Upgradeable, ERC721VotesUpgradeable, UUPSUpgradeable {␊ + /// @custom:storage-location erc7201:MyToken␊ + struct MyTokenStorage {␊ + uint256 _nextTokenId;␊ + }␊ + ␊ + // keccak256(abi.encode(uint256(keccak256("MyToken")) - 1)) & ~bytes32(uint256(0xff))␊ + bytes32 private constant MYTOKEN_STORAGE_LOCATION = 0xe50b25623ebee85cbe908e55dc189e9b1da401843a56196aa3162de9203a5100;␊ + ␊ + /// @custom:oz-upgrades-unsafe-allow constructor␊ + constructor() {␊ + _disableInitializers();␊ + }␊ + ␊ + function initialize(address initialAuthority) public initializer {␊ + __ERC721_init("MyToken", "MTK");␊ + __ERC721Enumerable_init();␊ + __ERC721Pausable_init();␊ + __AccessManaged_init(initialAuthority);␊ + __ERC721Burnable_init();␊ + __EIP712_init("MyToken", "1");␊ + __ERC721Votes_init();␊ + __UUPSUpgradeable_init();␊ + }␊ + ␊ + function pause() public restricted {␊ + _pause();␊ + }␊ + ␊ + function unpause() public restricted {␊ + _unpause();␊ + }␊ + ␊ + function safeMint(address to) public restricted returns (uint256) {␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + uint256 tokenId = $._nextTokenId++;␊ + _safeMint(to, tokenId);␊ + return tokenId;␊ + }␊ + ␊ + function _getMyTokenStorage() private pure returns (MyTokenStorage storage $) {␊ + assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ + }␊ + ␊ + function _authorizeUpgrade(address newImplementation)␊ + internal␊ + override␊ + restricted␊ + {}␊ + ␊ + // The following functions are overrides required by Solidity.␊ + ␊ + function _update(address to, uint256 tokenId, address auth)␊ + internal␊ + override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721PausableUpgradeable, ERC721VotesUpgradeable)␊ + returns (address)␊ + {␊ + return super._update(to, tokenId, auth);␊ + }␊ + ␊ + function _increaseBalance(address account, uint128 value)␊ + internal␊ + override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721VotesUpgradeable)␊ + {␊ + super._increaseBalance(account, value);␊ + }␊ + ␊ + function supportsInterface(bytes4 interfaceId)␊ + public␊ + view␊ + override(ERC721Upgradeable, ERC721EnumerableUpgradeable)␊ + returns (bool)␊ + {␊ + return super.supportsInterface(interfaceId);␊ + }␊ + }␊ + ` diff --git a/packages/core/solidity/src/erc721.test.ts.snap b/packages/core/solidity/src/erc721.test.ts.snap index 781a2ce684f79ebd5e857f8b2a6f3b6655b44c6b..a2991d8677f2c1d59f42d51d89166e1b43cc7b19 100644 GIT binary patch literal 2974 zcmV;P3t{v@RzV_6Q?oIDYmxt6>l$mIlFsn(bSK zM=hnhWFLzN00000000B+T+MIW$Q9pwVH<4`v_KCnus{bwP)M-#K}pm{i~#F6PFIb; zYRik=9@ZERhc**($Z&?Xl^_cgJ@ij#fue`)p@*VCPrdZ8rv-`v?X{QwBkir=m&lPQ zS+*ogvd5Po;_%ITGw;3m{oc&*YfUjM^qT(mM}NRfX9mU!q&m_mfi^v{82#nfstOtW z;?DQ(eemH2-rt=EAAJArojauxIM{psd0|UOm{443tC%od)3NgeY_~g^QVDEWrVW{{ z7#N)C>;&xEnEV3UHa2tuHY~y%h!_Q5%Eel-oUt8vY(hXSLsxay1z(nn&0?jomdWU* zZ8;3Q+uYx1RHXMnb1V~NKe22~-iG2zC=yp<)@w{kLEG@3BDIX{S|$@U0^8k=*9zzq z1TDb33;+Nii%{mch*?f9A3n-u!`9hVklpUQ%;v$n_nG&ZaKI>mCiVomL`Rm251@uq zhVO^m@IAZBD%UK#GCu=qY3OEjM<5y$NGCiR<~zHJ2c!TLc_= z19!OJ&gFql7!GuI&$#cBoa*|IXE1tW*M>6AU!VFsw7raU!3i04Ds40tdfwbqA|J>4JlBWQ+Z7>e?z~ zI0seL!IT1KtpZmktRmNeZ+cr@1<^z3vXgwzJ0rL5{Sft>8)k35&$B6r`VF28cs_M) z>=Yv%J>6oRJY6C;?z2fJ$ia}}XV8EI#l}H|ESIprLaEXOr_gZm5KrMwD-qr~l4Hvk0^jvOe ztXRb8`u+8}`%$l>#F`&^cJLgtPG2mq`aN+0E%Mq;&THq7(-Rw50Y3gRF=5acOmh!@ zz@@nd4@8<<*fbZ#n(gSPkYR8{@OcpVZFMcPXqoV;iH{4D(B%F|>B~CcDK{1?D{E6r zo{6|*(MXG=dP|b(iQ+mWy$W>aS6tp?%UJK@mq-}@66`M+EK$lnWXoh&FLDc1{eqh1$- z$9rCbbNSG{V>Fti;9;BeD00a-B6_W62OzIu|00^YneAT0(Hs%l!Dznw9WI*xx6I&c zW}&tSp*2p4Y;@F&pl=CZu@l7)$1*TYSpojR`OM$uu>!37ljy%_8Dq{k_fG0cFp~}c zRls!|PKl2Ws+DNQe5Znhf$jEAXLJ8(e|KvWJO!^YLhyA(uII9e2ig3H8ik5A=o)NY zHFXlFDAUhMqi|j9D8~*YY~M0)?)m16^~0^s(faf4_Kx56Dkne5{^Pe2nyyA24*^Y= zb?Sck`LP{*`b)$N5g+F;*8umNM9`a%z+k;`<0mXOo6nRV0kw=1ov?zc`*qub2Sy8qPOz^?!Mhk#F!gh+ zB1kc>;KtVO#>=Da-RGOJmRG&CVOlDF+S4+-)HQIC>V~Ac^9FJ$vrOP$H(LR&ee6IL z`^3+6Z7&IqOR6(paT(Fd%q=4x#ztI_feHzGhbPK}y!8F{tM!+XN-yfkpZaNZm!52g zci#?(xc>ICz1og--VKMA6OvxWxT0-*bz`_0hlhIySL`BbNS~OLHEQQYr?*UTX7{$) z$&`(m>L5)gTdSZ6>9P1TEtQhO; zza1=_2}}4J8X!?O;Y&gMDiLoiKt(TN;$7K{YB0IPu0xt0*?b-pfRxXJpMbBzeT?3_ z<#SET=lLy;urw9#aeU#4+I_jXb9B(z-CzId=F!&f#(Jl{y91tr@_Ds_VGU|ksj1YF zT&qI0Rl{@+jfSGu)FvvI*D?bGZM}`0h8A4n zm5U0{TO*T5Bn6oyN}b`V-*X!5&|$mSdaq4-y)8ljaXW6K?kMo5Bk!??eZbC(4&aed zqV#nXI%&|H>nGVNaN8j=C0=5}pMw{;r+r?{wU~YpaM01C;oC;3dP$%K`bP;g&f@10R_1cM)%_jNGUcIqYK`F$3<#b3_N){GQi%a>kWg= zg11q!>&+O!XKm9q0(jxI9RnD}KkyeZmJFqik>r;hyN+$*g$)p0k3U0$45= zuoMvQHbT5luK?<>X|wJa(5~-`S_0Y$Xt#*aZV_=^s1=Xnt^veF+jf(Kwh@R8%Vnh^ z*DF=5VBD0Ef?MS#mSq)5%@%Gck_u~OsadN+xh}Qp5LTsnMa8X3xe8_J#v!&TgSAD# zX%~RB#X@MjHa%bVH+vH>YzPLs7=H=yCBRoY`1)9L4P#hYEoEEyUmT$NMr;dDA5cYC ztY?C$QiNadP0E?5kt%-VJ=>Y6ks@4R-1%eN5kAs?5B6egJk0GUgUvS+?e!0Q%0@0f z{3#8_M2y_Dr1djhNIBO_=GS!}ciC~=yjAQXa72tpwU#e50HSYO_l&$0Od4Q6|QUQ?6?YT=qxscJ~8DH4RL zTt$s?Rc>kxMM9F=Kw70)ZdEi{YBUwO>K(6F+x^n$J3KDL%7P+TvV>h%Ia zUj=;?^mQrd>*l5C>-e{EA3=`R?h9HgXsw{Ng4POJyQs8Q+yP(8JK%!C3JNPIte~)h U!U_s|yD04c0elLyFRPXS02-&yfB*mh literal 2922 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 U^fjTcD@|Yj55BcJ_~?fK01fY(@Bjb+ From 17ca0294fd8dec52e578b551054d426b56d8ef0a Mon Sep 17 00:00:00 2001 From: typicalHuman Date: Tue, 28 Oct 2025 22:20:36 +0500 Subject: [PATCH 17/24] Updated namespacePrefix description --- packages/common/src/ai/descriptions/solidity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/src/ai/descriptions/solidity.ts b/packages/common/src/ai/descriptions/solidity.ts index f9f70e067..d0ebca2df 100644 --- a/packages/common/src/ai/descriptions/solidity.ts +++ b/packages/common/src/ai/descriptions/solidity.ts @@ -19,7 +19,7 @@ export const solidityCommonDescriptions = { upgradeable: 'Whether the smart contract is upgradeable. Transparent uses more complex proxy with higher overhead, requires less changes in your contract. Can also be used with beacons. UUPS uses simpler proxy with less overhead, requires including extra code in your contract. Allows flexibility for authorizing upgrades.', namespacePrefix: - 'The prefix for namespace ids. Should be based on the project name or a naming convention unique to the project. Only used if the contract has storage variables and upgradeability is enabled. Default is "myProject"', + 'The prefix for an ERC-7201 namespace identifier. It should be derived from the project name or a unique naming convention specific to the project. Used only if the contract includes storage variables and upgradeability is enabled. Default is "myProject".', }; export const solidityERC20Descriptions = { From ddf8d2183f50e4771920cbf7541677163d694ee1 Mon Sep 17 00:00:00 2001 From: typicalHuman Date: Tue, 28 Oct 2025 22:33:20 +0500 Subject: [PATCH 18/24] Removed "ethereum-cryptography" dependency & changed "computeNamespacedStorageSlot" --- packages/core/solidity/package.json | 4 +--- .../core/solidity/src/utils/namespaced-slot.ts | 18 +++++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/core/solidity/package.json b/packages/core/solidity/package.json index 3cee624af..62f94977f 100644 --- a/packages/core/solidity/package.json +++ b/packages/core/solidity/package.json @@ -21,9 +21,7 @@ "test:watch": "ava --watch", "update-env": "rm ./src/environments/hardhat/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat && rm ./src/environments/hardhat/upgradeable/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat/upgradeable" }, - "dependencies": { - "ethereum-cryptography": "^3.2.0" - }, + "dependencies": {}, "devDependencies": { "@openzeppelin/community-contracts": "git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#b0ddd27", "@openzeppelin/contracts": "^5.4.0", diff --git a/packages/core/solidity/src/utils/namespaced-slot.ts b/packages/core/solidity/src/utils/namespaced-slot.ts index 88fecb674..2285c2dd9 100644 --- a/packages/core/solidity/src/utils/namespaced-slot.ts +++ b/packages/core/solidity/src/utils/namespaced-slot.ts @@ -1,18 +1,22 @@ -import { keccak256 } from 'ethereum-cryptography/keccak'; -import { hexToBytes, toHex, utf8ToBytes } from 'ethereum-cryptography/utils'; +import { keccak256 } from '@ethersproject/keccak256'; +import { toUtf8Bytes } from '@ethersproject/strings'; +import { arrayify } from '@ethersproject/bytes'; /** * Returns the ERC-7201 storage location for a given namespace id */ export function computeNamespacedStorageSlot(id: string): string { - const innerHash = keccak256(utf8ToBytes(id)); - const minusOne = BigInt('0x' + toHex(innerHash)) - 1n; - const minusOneBytes = hexToBytes(minusOne.toString(16).padStart(64, '0')); + const innerHash = keccak256(toUtf8Bytes(id)); + + const minusOne = BigInt(innerHash) - 1n; + + const minusOneHex = '0x' + minusOne.toString(16).padStart(64, '0'); + const minusOneBytes = arrayify(minusOneHex); const outerHash = keccak256(minusOneBytes); - const mask = BigInt('0xff'); - const masked = BigInt('0x' + toHex(outerHash)) & ~mask; + const mask = 0xffn; + const masked = BigInt(outerHash) & ~mask; return '0x' + masked.toString(16).padStart(64, '0'); } From a14b9cc23e322bc0d5067e46b8c7f07ca8dc0ba5 Mon Sep 17 00:00:00 2001 From: Ruslan <56787163+typicalHuman@users.noreply.github.com> Date: Tue, 28 Oct 2025 22:54:00 +0500 Subject: [PATCH 19/24] Update packages/core/solidity/package.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ernesto García --- packages/core/solidity/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/solidity/package.json b/packages/core/solidity/package.json index 62f94977f..cb8a6ba91 100644 --- a/packages/core/solidity/package.json +++ b/packages/core/solidity/package.json @@ -21,7 +21,6 @@ "test:watch": "ava --watch", "update-env": "rm ./src/environments/hardhat/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat && rm ./src/environments/hardhat/upgradeable/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat/upgradeable" }, - "dependencies": {}, "devDependencies": { "@openzeppelin/community-contracts": "git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#b0ddd27", "@openzeppelin/contracts": "^5.4.0", From 417c581133256824f347e04e87bba113268614dc Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 28 Oct 2025 14:27:25 -0400 Subject: [PATCH 20/24] Revert "Update packages/core/solidity/package.json" This reverts commit a14b9cc23e322bc0d5067e46b8c7f07ca8dc0ba5. --- packages/core/solidity/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/solidity/package.json b/packages/core/solidity/package.json index cb8a6ba91..62f94977f 100644 --- a/packages/core/solidity/package.json +++ b/packages/core/solidity/package.json @@ -21,6 +21,7 @@ "test:watch": "ava --watch", "update-env": "rm ./src/environments/hardhat/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat && rm ./src/environments/hardhat/upgradeable/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat/upgradeable" }, + "dependencies": {}, "devDependencies": { "@openzeppelin/community-contracts": "git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#b0ddd27", "@openzeppelin/contracts": "^5.4.0", From 269561881c3b99094ce3b93ec2076053f3118268 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 28 Oct 2025 14:27:39 -0400 Subject: [PATCH 21/24] Revert "Removed "ethereum-cryptography" dependency & changed "computeNamespacedStorageSlot"" This reverts commit ddf8d2183f50e4771920cbf7541677163d694ee1. --- packages/core/solidity/package.json | 4 +++- .../core/solidity/src/utils/namespaced-slot.ts | 18 +++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/core/solidity/package.json b/packages/core/solidity/package.json index 62f94977f..3cee624af 100644 --- a/packages/core/solidity/package.json +++ b/packages/core/solidity/package.json @@ -21,7 +21,9 @@ "test:watch": "ava --watch", "update-env": "rm ./src/environments/hardhat/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat && rm ./src/environments/hardhat/upgradeable/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat/upgradeable" }, - "dependencies": {}, + "dependencies": { + "ethereum-cryptography": "^3.2.0" + }, "devDependencies": { "@openzeppelin/community-contracts": "git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#b0ddd27", "@openzeppelin/contracts": "^5.4.0", diff --git a/packages/core/solidity/src/utils/namespaced-slot.ts b/packages/core/solidity/src/utils/namespaced-slot.ts index 2285c2dd9..88fecb674 100644 --- a/packages/core/solidity/src/utils/namespaced-slot.ts +++ b/packages/core/solidity/src/utils/namespaced-slot.ts @@ -1,22 +1,18 @@ -import { keccak256 } from '@ethersproject/keccak256'; -import { toUtf8Bytes } from '@ethersproject/strings'; -import { arrayify } from '@ethersproject/bytes'; +import { keccak256 } from 'ethereum-cryptography/keccak'; +import { hexToBytes, toHex, utf8ToBytes } from 'ethereum-cryptography/utils'; /** * Returns the ERC-7201 storage location for a given namespace id */ export function computeNamespacedStorageSlot(id: string): string { - const innerHash = keccak256(toUtf8Bytes(id)); - - const minusOne = BigInt(innerHash) - 1n; - - const minusOneHex = '0x' + minusOne.toString(16).padStart(64, '0'); - const minusOneBytes = arrayify(minusOneHex); + const innerHash = keccak256(utf8ToBytes(id)); + const minusOne = BigInt('0x' + toHex(innerHash)) - 1n; + const minusOneBytes = hexToBytes(minusOne.toString(16).padStart(64, '0')); const outerHash = keccak256(minusOneBytes); - const mask = 0xffn; - const masked = BigInt(outerHash) & ~mask; + const mask = BigInt('0xff'); + const masked = BigInt('0x' + toHex(outerHash)) & ~mask; return '0x' + masked.toString(16).padStart(64, '0'); } From 762152282ee780fb649c8bc9b6b1d17cfcea6a19 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 29 Oct 2025 13:53:14 -0400 Subject: [PATCH 22/24] Update prefix tooltip and prompt --- packages/common/src/ai/descriptions/solidity.ts | 2 +- packages/ui/src/solidity/UpgradeabilitySection.svelte | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/common/src/ai/descriptions/solidity.ts b/packages/common/src/ai/descriptions/solidity.ts index d0ebca2df..05f15b74f 100644 --- a/packages/common/src/ai/descriptions/solidity.ts +++ b/packages/common/src/ai/descriptions/solidity.ts @@ -19,7 +19,7 @@ export const solidityCommonDescriptions = { upgradeable: 'Whether the smart contract is upgradeable. Transparent uses more complex proxy with higher overhead, requires less changes in your contract. Can also be used with beacons. UUPS uses simpler proxy with less overhead, requires including extra code in your contract. Allows flexibility for authorizing upgrades.', namespacePrefix: - 'The prefix for an ERC-7201 namespace identifier. It should be derived from the project name or a unique naming convention specific to the project. Used only if the contract includes storage variables and upgradeability is enabled. Default is "myProject".', + 'The prefix for ERC-7201 namespace identifiers. It should be derived from the project name or a unique naming convention specific to the project. Used only if the contract includes storage variables and upgradeability is enabled. Default is "myProject".', }; export const solidityERC20Descriptions = { diff --git a/packages/ui/src/solidity/UpgradeabilitySection.svelte b/packages/ui/src/solidity/UpgradeabilitySection.svelte index 9f95e2446..4c01bf137 100644 --- a/packages/ui/src/solidity/UpgradeabilitySection.svelte +++ b/packages/ui/src/solidity/UpgradeabilitySection.svelte @@ -67,7 +67,8 @@ - Prefix for namespace ids. Should be based on your project name or a naming convention unique to your project. + Prefix for ERC-7201 namespace identifiers. Should be derived from your project name or a unique naming + convention specific to your project. From 77eb539a4ad437e0596998288acf18f26b5f22de Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 29 Oct 2025 14:18:13 -0400 Subject: [PATCH 23/24] Check for whitespace --- packages/core/solidity/src/set-namespaced-storage.ts | 12 ++++++++++++ packages/ui/src/solidity/App.svelte | 2 +- packages/ui/src/solidity/ERC20Controls.svelte | 1 + packages/ui/src/solidity/ERC721Controls.svelte | 5 ++++- .../ui/src/solidity/UpgradeabilitySection.svelte | 7 +++++-- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/core/solidity/src/set-namespaced-storage.ts b/packages/core/solidity/src/set-namespaced-storage.ts index 642e32b78..712a32139 100644 --- a/packages/core/solidity/src/set-namespaced-storage.ts +++ b/packages/core/solidity/src/set-namespaced-storage.ts @@ -1,10 +1,13 @@ import type { BaseFunction, ContractBuilder, ContractStruct } from './contract'; import { computeNamespacedStorageSlot } from './utils/namespaced-slot'; +import { OptionsError } from './error'; /** * Sets namespaced variables in storage struct, and adds a function to retrieve namespaced storage. */ export function setNamespacedStorage(c: ContractBuilder, structVariables: string[], namespacePrefix: string) { + validateNoWhitespace(namespacePrefix); + const namespaceId = toNamespaceId(namespacePrefix, c.name); const storageFn = makeStorageFunction(c.name); const storageStruct = makeStorageStruct(c.name, namespaceId); @@ -23,6 +26,15 @@ export function toStorageStructInstantiation(name: string) { return `${name}Storage storage $ = ${makeStorageFunction(name).name}();`; } +/** + * According to ERC-7201, namespace ids should not contain any whitespace characters. + */ +function validateNoWhitespace(namespacePrefix: string) { + if (namespacePrefix.match(/\s+/)) { + throw new OptionsError({ namespacePrefix: 'Namespace prefix should not contain whitespace characters' }); + } +} + /** * Creates a namespace ID from a namespace prefix and a contract name. * If the namespace prefix is empty, returns the contract name. diff --git a/packages/ui/src/solidity/App.svelte b/packages/ui/src/solidity/App.svelte index 7440a0aee..c1b05f91e 100644 --- a/packages/ui/src/solidity/App.svelte +++ b/packages/ui/src/solidity/App.svelte @@ -350,7 +350,7 @@ />
- +
diff --git a/packages/ui/src/solidity/ERC20Controls.svelte b/packages/ui/src/solidity/ERC20Controls.svelte index b4174299f..7c351a45d 100644 --- a/packages/ui/src/solidity/ERC20Controls.svelte +++ b/packages/ui/src/solidity/ERC20Controls.svelte @@ -206,6 +206,7 @@ bind:upgradeable={opts.upgradeable} namespaceRequired={opts.upgradeable !== false && opts.crossChainBridging === 'custom'} bind:namespacePrefix={opts.namespacePrefix} + {errors} /> diff --git a/packages/ui/src/solidity/ERC721Controls.svelte b/packages/ui/src/solidity/ERC721Controls.svelte index 7f3756c71..8f352233a 100644 --- a/packages/ui/src/solidity/ERC721Controls.svelte +++ b/packages/ui/src/solidity/ERC721Controls.svelte @@ -1,7 +1,7 @@