From 30e25dbc40e77d42d716aed8456496212f42370f Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 26 Nov 2025 17:35:55 -0500 Subject: [PATCH 01/20] Add ERC20Crosschain in core using unreleased --- packages/core/solidity/hardhat.config.js | 1 + packages/core/solidity/package.json | 2 +- packages/core/solidity/remappings.txt | 1 + packages/core/solidity/src/contract.ts | 25 ++++++++++------- .../src/environments/hardhat/package.json | 1 + .../hardhat/upgradeable/package.json | 1 + packages/core/solidity/src/erc20.test.ts | 4 +++ packages/core/solidity/src/erc20.ts | 27 +++++++++++++++++-- packages/core/solidity/src/print.ts | 8 +++--- yarn.lock | 7 +++-- 10 files changed, 57 insertions(+), 20 deletions(-) diff --git a/packages/core/solidity/hardhat.config.js b/packages/core/solidity/hardhat.config.js index 2ef5c7125..5c320d9b3 100644 --- a/packages/core/solidity/hardhat.config.js +++ b/packages/core/solidity/hardhat.config.js @@ -66,6 +66,7 @@ module.exports = { enabled: true, runs: 200, }, + viaIR: true, }, }, }; diff --git a/packages/core/solidity/package.json b/packages/core/solidity/package.json index 3d250dc97..495a75a81 100644 --- a/packages/core/solidity/package.json +++ b/packages/core/solidity/package.json @@ -28,7 +28,7 @@ }, "devDependencies": { "@openzeppelin/community-contracts": "git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#b0ddd27", - "@openzeppelin/contracts": "^5.5.0", + "@openzeppelin/contracts": "git+https://github.com/Amxx/openzeppelin-contracts.git#crosschain/erc20bridge", "@openzeppelin/contracts-upgradeable": "^5.5.0", "@types/node": "^20.0.0", "@types/semver": "^7.5.7", diff --git a/packages/core/solidity/remappings.txt b/packages/core/solidity/remappings.txt index 7f8667df0..db505e700 100644 --- a/packages/core/solidity/remappings.txt +++ b/packages/core/solidity/remappings.txt @@ -1,2 +1,3 @@ @openzeppelin/community-contracts/=node_modules/@openzeppelin/community-contracts/contracts/ @openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/ +@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/contracts/ \ No newline at end of file diff --git a/packages/core/solidity/src/contract.ts b/packages/core/solidity/src/contract.ts index 22056efe6..61d3382ab 100644 --- a/packages/core/solidity/src/contract.ts +++ b/packages/core/solidity/src/contract.ts @@ -20,6 +20,7 @@ export interface Parent { contract: ImportContract; params: Value[]; importOnly?: boolean; + constructionOnly?: boolean; } export interface ImportContract extends ReferencedContract { @@ -134,20 +135,26 @@ export class ContractBuilder implements Contract { return [...this.variableOrErrorMap.values()]; } - addParent(contract: ImportContract, params: Value[] = []): boolean { + private updateParentMap( + contract: ImportContract, + params: Value[] = [], + flags: Partial> = {}, + ): boolean { const present = this.parentMap.has(contract.name); - this.parentMap.set(contract.name, { contract, params }); + this.parentMap = new Map(this.parentMap).set(contract.name, { contract, params, ...flags }); return !present; } + addParent(contract: ImportContract, params: Value[] = []): boolean { + return this.updateParentMap(contract, params); + } + addImportOnly(contract: ImportContract): boolean { - const present = this.parentMap.has(contract.name); - this.parentMap.set(contract.name, { - contract, - params: [], - importOnly: true, - }); - return !present; + return this.updateParentMap(contract, [], { importOnly: true }); + } + + addConstructionOnly(contract: ImportContract, params: Value[] = []): boolean { + return this.updateParentMap(contract, params, { constructionOnly: true }); } addOverride(parent: ReferencedContract, baseFn: BaseFunction, mutability?: FunctionMutability) { diff --git a/packages/core/solidity/src/environments/hardhat/package.json b/packages/core/solidity/src/environments/hardhat/package.json index e8f6a2706..1252ce00d 100644 --- a/packages/core/solidity/src/environments/hardhat/package.json +++ b/packages/core/solidity/src/environments/hardhat/package.json @@ -11,6 +11,7 @@ "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^6.1.0", "@openzeppelin/contracts": "^5.5.0", + "@openzeppelin/contracts-unreleased": "git+https://github.com/Amxx/openzeppelin-contracts.git#crosschain/erc20bridge", "hardhat": "^2.22.0" } } \ No newline at end of file diff --git a/packages/core/solidity/src/environments/hardhat/upgradeable/package.json b/packages/core/solidity/src/environments/hardhat/upgradeable/package.json index da79327fe..2caf90d5a 100644 --- a/packages/core/solidity/src/environments/hardhat/upgradeable/package.json +++ b/packages/core/solidity/src/environments/hardhat/upgradeable/package.json @@ -11,6 +11,7 @@ "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^6.1.0", "@openzeppelin/contracts": "^5.5.0", + "@openzeppelin/contracts-unreleased": "git+https://github.com/Amxx/openzeppelin-contracts.git#crosschain/erc20bridge", "@openzeppelin/contracts-upgradeable": "^5.5.0", "@openzeppelin/hardhat-upgrades": "^3.0.0", "hardhat": "^2.22.0" diff --git a/packages/core/solidity/src/erc20.test.ts b/packages/core/solidity/src/erc20.test.ts index 41b3d0763..322f2211f 100644 --- a/packages/core/solidity/src/erc20.test.ts +++ b/packages/core/solidity/src/erc20.test.ts @@ -146,6 +146,10 @@ testERC20('erc20 crossChainBridging custom', { crossChainBridging: 'custom', }); +testERC20('erc20 crossChainBridging native', { + crossChainBridging: 'native', +}); + testERC20('erc20 crossChainBridging custom ownable', { crossChainBridging: 'custom', access: 'ownable', diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index 173a5c384..03db1356e 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -16,7 +16,7 @@ import { OptionsError } from './error'; import { toUint256, UINT256_MAX } from './utils/convert-strings'; import { setNamespacedStorage, toStorageStructInstantiation } from './set-namespaced-storage'; -export const crossChainBridgingOptions = [false, 'custom', 'superchain'] as const; +export const crossChainBridgingOptions = [false, 'custom', 'native', 'superchain'] as const; export type CrossChainBridging = (typeof crossChainBridgingOptions)[number]; export interface ERC20Options extends CommonOptions { @@ -309,11 +309,34 @@ function addFlashMint(c: ContractBuilder) { function addCrossChainBridging( c: ContractBuilder, - crossChainBridging: 'custom' | 'superchain', + crossChainBridging: 'custom' | 'native' | 'superchain', access: Access, upgradeable: Upgradeable, namespacePrefix: string, ) { + if (crossChainBridging === 'native') { + addERC20Crosschain(c); + } else { + addERC20Bridgeable(c, crossChainBridging, access, upgradeable, namespacePrefix); + } +} + +function addERC20Crosschain(c: ContractBuilder) { + const ERC20Crosschain = { + name: 'ERC20Crosschain', + path: '@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol', + }; + c.addParent(ERC20Crosschain); + + const CrosschainLinked = { + name: 'CrosschainLinked', + path: '@openzeppelin/contracts/crosschain/CrosschainLinked.sol', + }; + c.addConstructionOnly(CrosschainLinked, [{ lit: 'links' }]); + c.addConstructorArgument({ type: 'Link[] memory', name: 'links' }); +} + +function addERC20Bridgeable(c: ContractBuilder, crossChainBridging: 'custom' | 'superchain', access: Access, upgradeable: Upgradeable, namespacePrefix: string) { const ERC20Bridgeable = { name: 'ERC20Bridgeable', path: `@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Bridgeable.sol`, diff --git a/packages/core/solidity/src/print.ts b/packages/core/solidity/src/print.ts index 4ce73c4b7..fa7a68d63 100644 --- a/packages/core/solidity/src/print.ts +++ b/packages/core/solidity/src/print.ts @@ -87,11 +87,11 @@ function printCompatibleLibraryVersions(contract: Contract): string { } function printInheritance(contract: Contract, { transformName }: Helpers): [] | [string] { - if (contract.parents.length > 0) { - return ['is ' + contract.parents.map(p => transformName(p.contract)).join(', ')]; - } else { - return []; + const visibleParents = contract.parents.filter(p => !p.constructionOnly); + if (visibleParents.length > 0) { + return ['is ' + visibleParents.map(p => transformName(p.contract)).join(', ')]; } + return []; } function printConstructor(contract: Contract, helpers: Helpers): Lines[] { diff --git a/yarn.lock b/yarn.lock index 3481d29b6..aebc8b8f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -811,10 +811,9 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.5.0.tgz#e35b3ededa5ccc13205c2b42e2058cd5cb7d424a" integrity sha512-Va5hKG5oaK0EE5bXTVWugcGimMHazxL+SL523dH6WVbGiuLXwuWr9oxtLyPHQSVGtgmlIgtKNR5V+OUpCIUwFQ== -"@openzeppelin/contracts@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.5.0.tgz#24e8a2f9598de484dcb223512af656edf52bc0e8" - integrity sha512-R8hq4zmKKWP2c7OxeRgAcjZwvF5W0Qq2OIX7degrtdM52Q9xYr4MLJdUAVPKGUewNJ1qo+M6YiZLLnNUnjP/gg== +"@openzeppelin/contracts@git+https://github.com/Amxx/openzeppelin-contracts.git#crosschain/erc20bridge": + version "5.4.0" + resolved "git+https://github.com/Amxx/openzeppelin-contracts.git#63ef13b316e5a656ba69fcd4af42febde9314517" "@pkgjs/parseargs@^0.11.0": version "0.11.0" From bb07448a74602df5b11a3f30b530cce0f989b34c Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 26 Nov 2025 17:42:29 -0500 Subject: [PATCH 02/20] Add snapshot tests --- packages/core/solidity/src/erc20.test.ts | 35 +++- packages/core/solidity/src/erc20.test.ts.md | 153 ++++++++++++++++++ packages/core/solidity/src/erc20.test.ts.snap | Bin 3699 -> 4040 bytes 3 files changed, 184 insertions(+), 4 deletions(-) diff --git a/packages/core/solidity/src/erc20.test.ts b/packages/core/solidity/src/erc20.test.ts index 322f2211f..99301e0ce 100644 --- a/packages/core/solidity/src/erc20.test.ts +++ b/packages/core/solidity/src/erc20.test.ts @@ -146,10 +146,6 @@ testERC20('erc20 crossChainBridging custom', { crossChainBridging: 'custom', }); -testERC20('erc20 crossChainBridging native', { - crossChainBridging: 'native', -}); - testERC20('erc20 crossChainBridging custom ownable', { crossChainBridging: 'custom', access: 'ownable', @@ -172,6 +168,37 @@ testERC20('erc20 crossChainBridging custom managed', { access: 'managed', }); +testERC20('erc20 crossChainBridging native', { + crossChainBridging: 'native', +}); + +testERC20('erc20 crossChainBridging native ownable', { + crossChainBridging: 'native', + access: 'ownable', +}); + +testERC20('erc20 crossChainBridging native ownable mintable burnable', { + crossChainBridging: 'native', + access: 'ownable', + mintable: true, + burnable: true, +}); + +testERC20('erc20 crossChainBridging native roles', { + crossChainBridging: 'native', + access: 'roles', +}); + +testERC20('erc20 crossChainBridging native managed', { + crossChainBridging: 'native', + access: 'managed', +}); + +testERC20('erc20 crossChainBridging native upgradeable', { + crossChainBridging: 'native', + upgradeable: 'transparent', +}); + testERC20('erc20 crossChainBridging superchain', { crossChainBridging: 'superchain', }); diff --git a/packages/core/solidity/src/erc20.test.ts.md b/packages/core/solidity/src/erc20.test.ts.md index b383936ca..d2b1e5a18 100644 --- a/packages/core/solidity/src/erc20.test.ts.md +++ b/packages/core/solidity/src/erc20.test.ts.md @@ -706,6 +706,159 @@ Generated by [AVA](https://avajs.dev). }␊ ` +## erc20 crossChainBridging native + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.5.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {CrosschainLinked} from "@openzeppelin/contracts/crosschain/CrosschainLinked.sol";␊ + import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ + import {ERC20Crosschain} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol";␊ + import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ + ␊ + contract MyToken is ERC20, ERC20Crosschain, ERC20Permit {␊ + constructor(Link[] memory links)␊ + ERC20("MyToken", "MTK")␊ + CrosschainLinked(links)␊ + ERC20Permit("MyToken")␊ + {}␊ + }␊ + ` + +## erc20 crossChainBridging native ownable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.5.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {CrosschainLinked} from "@openzeppelin/contracts/crosschain/CrosschainLinked.sol";␊ + import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ + import {ERC20Crosschain} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol";␊ + import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ + import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ + ␊ + contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, Ownable {␊ + constructor(Link[] memory links, address initialOwner)␊ + ERC20("MyToken", "MTK")␊ + CrosschainLinked(links)␊ + ERC20Permit("MyToken")␊ + Ownable(initialOwner)␊ + {}␊ + }␊ + ` + +## erc20 crossChainBridging native ownable mintable burnable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.5.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {CrosschainLinked} from "@openzeppelin/contracts/crosschain/CrosschainLinked.sol";␊ + import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ + import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";␊ + import {ERC20Crosschain} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol";␊ + import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ + import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ + ␊ + contract MyToken is ERC20, ERC20Crosschain, ERC20Burnable, Ownable, ERC20Permit {␊ + constructor(Link[] memory links, address initialOwner)␊ + ERC20("MyToken", "MTK")␊ + CrosschainLinked(links)␊ + Ownable(initialOwner)␊ + ERC20Permit("MyToken")␊ + {}␊ + ␊ + function mint(address to, uint256 amount) public onlyOwner {␊ + _mint(to, amount);␊ + }␊ + }␊ + ` + +## erc20 crossChainBridging native roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.5.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";␊ + import {CrosschainLinked} from "@openzeppelin/contracts/crosschain/CrosschainLinked.sol";␊ + import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ + import {ERC20Crosschain} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol";␊ + import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ + ␊ + contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, AccessControl {␊ + constructor(Link[] memory links, address defaultAdmin)␊ + ERC20("MyToken", "MTK")␊ + CrosschainLinked(links)␊ + ERC20Permit("MyToken")␊ + {␊ + _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);␊ + }␊ + }␊ + ` + +## erc20 crossChainBridging native managed + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.5.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";␊ + import {CrosschainLinked} from "@openzeppelin/contracts/crosschain/CrosschainLinked.sol";␊ + import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ + import {ERC20Crosschain} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol";␊ + import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ + ␊ + contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, AccessManaged {␊ + constructor(Link[] memory links, address initialAuthority)␊ + ERC20("MyToken", "MTK")␊ + CrosschainLinked(links)␊ + ERC20Permit("MyToken")␊ + AccessManaged(initialAuthority)␊ + {}␊ + }␊ + ` + +## erc20 crossChainBridging native upgradeable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts ^5.5.0␊ + pragma solidity ^0.8.27;␊ + ␊ + import {CrosschainLinkedUpgradeable} from "@openzeppelin/contracts-upgradeable/crosschain/CrosschainLinkedUpgradeable.sol";␊ + import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";␊ + import {ERC20CrosschainUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20CrosschainUpgradeable.sol";␊ + import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";␊ + import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";␊ + ␊ + contract MyToken is Initializable, ERC20Upgradeable, ERC20CrosschainUpgradeable, ERC20PermitUpgradeable {␊ + /// @custom:oz-upgrades-unsafe-allow constructor␊ + constructor() {␊ + _disableInitializers();␊ + }␊ + ␊ + function initialize(Link[] memory links) public initializer {␊ + __ERC20_init("MyToken", "MTK");␊ + __ERC20Crosschain_init();␊ + __CrosschainLinked_init(links);␊ + __ERC20Permit_init("MyToken");␊ + }␊ + }␊ + ` + ## erc20 crossChainBridging superchain > Snapshot 1 diff --git a/packages/core/solidity/src/erc20.test.ts.snap b/packages/core/solidity/src/erc20.test.ts.snap index a99c9f791abc7368868005683d9e9df38acd74ff..38ddbc7f17dac7e3d05773cd5c291b615b1fe292 100644 GIT binary patch literal 4040 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 literal 3699 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 RXK~rU{{ee>13~<_008WmFwFn} From 5828060129b603dfd544e12914b272919eeef89f Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 26 Nov 2025 17:47:45 -0500 Subject: [PATCH 03/20] Use contract scoped Link --- packages/core/solidity/src/erc20.test.ts.md | 12 ++++++------ packages/core/solidity/src/erc20.test.ts.snap | Bin 4040 -> 4039 bytes packages/core/solidity/src/erc20.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/core/solidity/src/erc20.test.ts.md b/packages/core/solidity/src/erc20.test.ts.md index d2b1e5a18..e299f802e 100644 --- a/packages/core/solidity/src/erc20.test.ts.md +++ b/packages/core/solidity/src/erc20.test.ts.md @@ -720,7 +720,7 @@ Generated by [AVA](https://avajs.dev). import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ ␊ contract MyToken is ERC20, ERC20Crosschain, ERC20Permit {␊ - constructor(Link[] memory links)␊ + constructor(CrosschainLinked.Link[] memory links)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ ERC20Permit("MyToken")␊ @@ -743,7 +743,7 @@ Generated by [AVA](https://avajs.dev). import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, Ownable {␊ - constructor(Link[] memory links, address initialOwner)␊ + constructor(CrosschainLinked.Link[] memory links, address initialOwner)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ ERC20Permit("MyToken")␊ @@ -768,7 +768,7 @@ Generated by [AVA](https://avajs.dev). import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ contract MyToken is ERC20, ERC20Crosschain, ERC20Burnable, Ownable, ERC20Permit {␊ - constructor(Link[] memory links, address initialOwner)␊ + constructor(CrosschainLinked.Link[] memory links, address initialOwner)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ Ownable(initialOwner)␊ @@ -796,7 +796,7 @@ Generated by [AVA](https://avajs.dev). import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ ␊ contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, AccessControl {␊ - constructor(Link[] memory links, address defaultAdmin)␊ + constructor(CrosschainLinked.Link[] memory links, address defaultAdmin)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ ERC20Permit("MyToken")␊ @@ -821,7 +821,7 @@ Generated by [AVA](https://avajs.dev). import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ ␊ contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, AccessManaged {␊ - constructor(Link[] memory links, address initialAuthority)␊ + constructor(CrosschainLinked.Link[] memory links, address initialAuthority)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ ERC20Permit("MyToken")␊ @@ -850,7 +850,7 @@ Generated by [AVA](https://avajs.dev). _disableInitializers();␊ }␊ ␊ - function initialize(Link[] memory links) public initializer {␊ + function initialize(CrosschainLinked.Link[] memory links) public initializer {␊ __ERC20_init("MyToken", "MTK");␊ __ERC20Crosschain_init();␊ __CrosschainLinked_init(links);␊ diff --git a/packages/core/solidity/src/erc20.test.ts.snap b/packages/core/solidity/src/erc20.test.ts.snap index 38ddbc7f17dac7e3d05773cd5c291b615b1fe292..961a67048c909aff0e3a7d285673e01a5fe05022 100644 GIT binary patch literal 4039 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 literal 4040 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 diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index 03db1356e..d78d44773 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -333,7 +333,7 @@ function addERC20Crosschain(c: ContractBuilder) { path: '@openzeppelin/contracts/crosschain/CrosschainLinked.sol', }; c.addConstructionOnly(CrosschainLinked, [{ lit: 'links' }]); - c.addConstructorArgument({ type: 'Link[] memory', name: 'links' }); + c.addConstructorArgument({ type: 'CrosschainLinked.Link[] memory', name: 'links' }); } function addERC20Bridgeable(c: ContractBuilder, crossChainBridging: 'custom' | 'superchain', access: Access, upgradeable: Upgradeable, namespacePrefix: string) { From dd6d84181800040e8f55d2d50634a8ba5fc8d4eb Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 26 Nov 2025 17:48:32 -0500 Subject: [PATCH 04/20] Add todo --- packages/core/solidity/src/erc20.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index d78d44773..830a8564d 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -334,6 +334,8 @@ function addERC20Crosschain(c: ContractBuilder) { }; c.addConstructionOnly(CrosschainLinked, [{ lit: 'links' }]); c.addConstructorArgument({ type: 'CrosschainLinked.Link[] memory', name: 'links' }); + + // TODO add access controlled setLink } function addERC20Bridgeable(c: ContractBuilder, crossChainBridging: 'custom' | 'superchain', access: Access, upgradeable: Upgradeable, namespacePrefix: string) { From 38334ec7701c129ed5da3e30e2cb3e662bf1ea09 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 26 Nov 2025 18:01:37 -0500 Subject: [PATCH 05/20] Add access controlled setLink --- packages/core/solidity/src/erc20.test.ts.md | 74 +++++++++++++++--- packages/core/solidity/src/erc20.test.ts.snap | Bin 4039 -> 4162 bytes packages/core/solidity/src/erc20.ts | 16 +++- 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/packages/core/solidity/src/erc20.test.ts.md b/packages/core/solidity/src/erc20.test.ts.md index e299f802e..22ff37e0c 100644 --- a/packages/core/solidity/src/erc20.test.ts.md +++ b/packages/core/solidity/src/erc20.test.ts.md @@ -718,13 +718,22 @@ Generated by [AVA](https://avajs.dev). import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import {ERC20Crosschain} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol";␊ import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ + import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ - contract MyToken is ERC20, ERC20Crosschain, ERC20Permit {␊ - constructor(CrosschainLinked.Link[] memory links)␊ + contract MyToken is ERC20, ERC20Crosschain, Ownable, ERC20Permit {␊ + constructor(CrosschainLinked.Link[] memory links, address initialOwner)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ + Ownable(initialOwner)␊ ERC20Permit("MyToken")␊ {}␊ + ␊ + function setLink(address gateway, bytes memory counterpart, bool allowOverride)␊ + public␊ + onlyOwner␊ + {␊ + _setLink(gateway, counterpart, allowOverride);␊ + }␊ }␊ ` @@ -742,13 +751,20 @@ Generated by [AVA](https://avajs.dev). import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ - contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, Ownable {␊ + contract MyToken is ERC20, ERC20Crosschain, Ownable, ERC20Permit {␊ constructor(CrosschainLinked.Link[] memory links, address initialOwner)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ - ERC20Permit("MyToken")␊ Ownable(initialOwner)␊ + ERC20Permit("MyToken")␊ {}␊ + ␊ + function setLink(address gateway, bytes memory counterpart, bool allowOverride)␊ + public␊ + onlyOwner␊ + {␊ + _setLink(gateway, counterpart, allowOverride);␊ + }␊ }␊ ` @@ -767,13 +783,20 @@ Generated by [AVA](https://avajs.dev). import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ - contract MyToken is ERC20, ERC20Crosschain, ERC20Burnable, Ownable, ERC20Permit {␊ + contract MyToken is ERC20, ERC20Crosschain, Ownable, ERC20Burnable, ERC20Permit {␊ constructor(CrosschainLinked.Link[] memory links, address initialOwner)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ Ownable(initialOwner)␊ ERC20Permit("MyToken")␊ {}␊ + ␊ + function setLink(address gateway, bytes memory counterpart, bool allowOverride)␊ + public␊ + onlyOwner␊ + {␊ + _setLink(gateway, counterpart, allowOverride);␊ + }␊ ␊ function mint(address to, uint256 amount) public onlyOwner {␊ _mint(to, amount);␊ @@ -795,13 +818,23 @@ Generated by [AVA](https://avajs.dev). import {ERC20Crosschain} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol";␊ import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ ␊ - contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, AccessControl {␊ - constructor(CrosschainLinked.Link[] memory links, address defaultAdmin)␊ + contract MyToken is ERC20, ERC20Crosschain, AccessControl, ERC20Permit {␊ + bytes32 public constant CROSSCHAIN_LINKER_ROLE = keccak256("CROSSCHAIN_LINKER_ROLE");␊ + ␊ + constructor(CrosschainLinked.Link[] memory links, address defaultAdmin, address crosschainLinker)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ ERC20Permit("MyToken")␊ {␊ _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);␊ + _grantRole(CROSSCHAIN_LINKER_ROLE, crosschainLinker);␊ + }␊ + ␊ + function setLink(address gateway, bytes memory counterpart, bool allowOverride)␊ + public␊ + onlyRole(CROSSCHAIN_LINKER_ROLE)␊ + {␊ + _setLink(gateway, counterpart, allowOverride);␊ }␊ }␊ ` @@ -820,13 +853,20 @@ Generated by [AVA](https://avajs.dev). import {ERC20Crosschain} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol";␊ import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ ␊ - contract MyToken is ERC20, ERC20Crosschain, ERC20Permit, AccessManaged {␊ + contract MyToken is ERC20, ERC20Crosschain, AccessManaged, ERC20Permit {␊ constructor(CrosschainLinked.Link[] memory links, address initialAuthority)␊ ERC20("MyToken", "MTK")␊ CrosschainLinked(links)␊ - ERC20Permit("MyToken")␊ AccessManaged(initialAuthority)␊ + ERC20Permit("MyToken")␊ {}␊ + ␊ + function setLink(address gateway, bytes memory counterpart, bool allowOverride)␊ + public␊ + restricted␊ + {␊ + _setLink(gateway, counterpart, allowOverride);␊ + }␊ }␊ ` @@ -843,19 +883,31 @@ Generated by [AVA](https://avajs.dev). import {ERC20CrosschainUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20CrosschainUpgradeable.sol";␊ import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";␊ import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";␊ + import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";␊ ␊ - contract MyToken is Initializable, ERC20Upgradeable, ERC20CrosschainUpgradeable, ERC20PermitUpgradeable {␊ + contract MyToken is Initializable, ERC20Upgradeable, ERC20CrosschainUpgradeable, OwnableUpgradeable, ERC20PermitUpgradeable {␊ /// @custom:oz-upgrades-unsafe-allow constructor␊ constructor() {␊ _disableInitializers();␊ }␊ ␊ - function initialize(CrosschainLinked.Link[] memory links) public initializer {␊ + function initialize(CrosschainLinked.Link[] memory links, address initialOwner)␊ + public␊ + initializer␊ + {␊ __ERC20_init("MyToken", "MTK");␊ __ERC20Crosschain_init();␊ __CrosschainLinked_init(links);␊ + __Ownable_init(initialOwner);␊ __ERC20Permit_init("MyToken");␊ }␊ + ␊ + function setLink(address gateway, bytes memory counterpart, bool allowOverride)␊ + public␊ + onlyOwner␊ + {␊ + _setLink(gateway, counterpart, allowOverride);␊ + }␊ }␊ ` diff --git a/packages/core/solidity/src/erc20.test.ts.snap b/packages/core/solidity/src/erc20.test.ts.snap index 961a67048c909aff0e3a7d285673e01a5fe05022..dd7c98959fca8e9ba2a4c562ad569203393d4504 100644 GIT binary patch literal 4162 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 MAIR_~=Q!2?06^*M@Bjb+ literal 4039 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 diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index 830a8564d..a55c9d6e5 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -315,13 +315,13 @@ function addCrossChainBridging( namespacePrefix: string, ) { if (crossChainBridging === 'native') { - addERC20Crosschain(c); + addERC20Crosschain(c, access); } else { addERC20Bridgeable(c, crossChainBridging, access, upgradeable, namespacePrefix); } } -function addERC20Crosschain(c: ContractBuilder) { +function addERC20Crosschain(c: ContractBuilder, access: Access) { const ERC20Crosschain = { name: 'ERC20Crosschain', path: '@openzeppelin/contracts/token/ERC20/extensions/ERC20Crosschain.sol', @@ -335,7 +335,8 @@ function addERC20Crosschain(c: ContractBuilder) { c.addConstructionOnly(CrosschainLinked, [{ lit: 'links' }]); c.addConstructorArgument({ type: 'CrosschainLinked.Link[] memory', name: 'links' }); - // TODO add access controlled setLink + requireAccessControl(c, functions.setLink, access, 'CROSSCHAIN_LINKER', 'crosschainLinker'); + c.addFunctionCode('_setLink(gateway, counterpart, allowOverride);', functions.setLink); } function addERC20Bridgeable(c: ContractBuilder, crossChainBridging: 'custom' | 'superchain', access: Access, upgradeable: Upgradeable, namespacePrefix: string) { @@ -508,4 +509,13 @@ export const functions = defineFunctions({ kind: 'internal' as const, args: [{ name: 'caller', type: 'address' }], }, + + setLink: { + kind: 'public' as const, + args: [ + { name: 'gateway', type: 'address' }, + { name: 'counterpart', type: 'bytes memory' }, + { name: 'allowOverride', type: 'bool' }, + ], + }, }); From fd985af7641990ed34f29da6e965d073180f8634 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 26 Nov 2025 18:16:04 -0500 Subject: [PATCH 06/20] Require access control for custom bridge, add access controlled bridge setter --- packages/core/solidity/src/erc20.test.ts.md | 48 ++++++++++++++---- packages/core/solidity/src/erc20.test.ts.snap | Bin 4162 -> 4187 bytes packages/core/solidity/src/erc20.ts | 28 +++++++++- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/packages/core/solidity/src/erc20.test.ts.md b/packages/core/solidity/src/erc20.test.ts.md index 22ff37e0c..5d0ec68a3 100644 --- a/packages/core/solidity/src/erc20.test.ts.md +++ b/packages/core/solidity/src/erc20.test.ts.md @@ -546,13 +546,15 @@ Generated by [AVA](https://avajs.dev). import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import {ERC20Bridgeable} from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Bridgeable.sol";␊ import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ + import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ - contract MyToken is ERC20, ERC20Bridgeable, ERC20Permit {␊ + contract MyToken is ERC20, ERC20Bridgeable, Ownable, ERC20Permit {␊ address public tokenBridge;␊ error Unauthorized();␊ ␊ - constructor(address tokenBridge_)␊ + constructor(address tokenBridge_, address initialOwner)␊ ERC20("MyToken", "MTK")␊ + Ownable(initialOwner)␊ ERC20Permit("MyToken")␊ {␊ require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");␊ @@ -562,6 +564,10 @@ Generated by [AVA](https://avajs.dev). function _checkTokenBridge(address caller) internal view override {␊ if (caller != tokenBridge) revert Unauthorized();␊ }␊ + ␊ + function setTokenBridge(address tokenBridge_) public onlyOwner {␊ + tokenBridge = tokenBridge_;␊ + }␊ }␊ ` @@ -578,14 +584,14 @@ Generated by [AVA](https://avajs.dev). import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ - contract MyToken is ERC20, ERC20Bridgeable, ERC20Permit, Ownable {␊ + contract MyToken is ERC20, ERC20Bridgeable, Ownable, ERC20Permit {␊ address public tokenBridge;␊ error Unauthorized();␊ ␊ constructor(address tokenBridge_, address initialOwner)␊ ERC20("MyToken", "MTK")␊ - ERC20Permit("MyToken")␊ Ownable(initialOwner)␊ + ERC20Permit("MyToken")␊ {␊ require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");␊ tokenBridge = tokenBridge_;␊ @@ -594,6 +600,10 @@ Generated by [AVA](https://avajs.dev). function _checkTokenBridge(address caller) internal view override {␊ if (caller != tokenBridge) revert Unauthorized();␊ }␊ + ␊ + function setTokenBridge(address tokenBridge_) public onlyOwner {␊ + tokenBridge = tokenBridge_;␊ + }␊ }␊ ` @@ -611,7 +621,7 @@ Generated by [AVA](https://avajs.dev). import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ - contract MyToken is ERC20, ERC20Bridgeable, ERC20Burnable, Ownable, ERC20Permit {␊ + contract MyToken is ERC20, ERC20Bridgeable, Ownable, ERC20Burnable, ERC20Permit {␊ address public tokenBridge;␊ error Unauthorized();␊ ␊ @@ -627,6 +637,10 @@ Generated by [AVA](https://avajs.dev). function _checkTokenBridge(address caller) internal view override {␊ if (caller != tokenBridge) revert Unauthorized();␊ }␊ + ␊ + function setTokenBridge(address tokenBridge_) public onlyOwner {␊ + tokenBridge = tokenBridge_;␊ + }␊ ␊ function mint(address to, uint256 amount) public onlyOwner {␊ _mint(to, amount);␊ @@ -1066,8 +1080,9 @@ Generated by [AVA](https://avajs.dev). import {ERC20BridgeableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol";␊ import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";␊ import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";␊ + import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";␊ ␊ - contract MyToken is Initializable, ERC20Upgradeable, ERC20BridgeableUpgradeable, ERC20PermitUpgradeable {␊ + contract MyToken is Initializable, ERC20Upgradeable, ERC20BridgeableUpgradeable, OwnableUpgradeable, ERC20PermitUpgradeable {␊ /// @custom:storage-location erc7201:myProject.MyToken␊ struct MyTokenStorage {␊ address tokenBridge;␊ @@ -1083,9 +1098,13 @@ Generated by [AVA](https://avajs.dev). _disableInitializers();␊ }␊ ␊ - function initialize(address tokenBridge_) public initializer {␊ + function initialize(address tokenBridge_, address initialOwner)␊ + public␊ + initializer␊ + {␊ __ERC20_init("MyToken", "MTK");␊ __ERC20Bridgeable_init();␊ + __Ownable_init(initialOwner);␊ __ERC20Permit_init("MyToken");␊ ␊ require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");␊ @@ -1101,6 +1120,11 @@ Generated by [AVA](https://avajs.dev). function _getMyTokenStorage() private pure returns (MyTokenStorage storage $) {␊ assembly { $.slot := MYTOKEN_STORAGE_LOCATION }␊ }␊ + ␊ + function setTokenBridge(address tokenBridge_) public onlyOwner {␊ + MyTokenStorage storage $ = _getMyTokenStorage();␊ + $.tokenBridge = tokenBridge_;␊ + }␊ }␊ ` @@ -1175,13 +1199,15 @@ Generated by [AVA](https://avajs.dev). import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊ import {ERC20Bridgeable} from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Bridgeable.sol";␊ import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊ + import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";␊ ␊ - contract MyToken is ERC20, ERC20Bridgeable, ERC20Permit {␊ + contract MyToken is ERC20, ERC20Bridgeable, Ownable, ERC20Permit {␊ address public tokenBridge;␊ error Unauthorized();␊ ␊ - constructor(address tokenBridge_, address recipient)␊ + constructor(address tokenBridge_, address initialOwner, address recipient)␊ ERC20("MyToken", "MTK")␊ + Ownable(initialOwner)␊ ERC20Permit("MyToken")␊ {␊ require(tokenBridge_ != address(0), "Invalid tokenBridge_ address");␊ @@ -1194,6 +1220,10 @@ Generated by [AVA](https://avajs.dev). function _checkTokenBridge(address caller) internal view override {␊ if (caller != tokenBridge) revert Unauthorized();␊ }␊ + ␊ + function setTokenBridge(address tokenBridge_) public onlyOwner {␊ + tokenBridge = tokenBridge_;␊ + }␊ }␊ ` diff --git a/packages/core/solidity/src/erc20.test.ts.snap b/packages/core/solidity/src/erc20.test.ts.snap index dd7c98959fca8e9ba2a4c562ad569203393d4504..a9be98682d4619ef693abaec9c696f383e126783 100644 GIT binary patch literal 4187 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 literal 4162 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 MAIR_~=Q!2?06^*M@Bjb+ diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index a55c9d6e5..94df2c749 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -79,7 +79,7 @@ export function printERC20(opts: ERC20Options = defaults): string { } export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable || opts.pausable || opts.upgradeable === 'uups'; + return opts.mintable || opts.pausable || opts.upgradeable === 'uups' || opts.crossChainBridging === 'custom'; } export function buildERC20(opts: ERC20Options): ContractBuilder { @@ -365,10 +365,14 @@ function addERC20Bridgeable(c: ContractBuilder, crossChainBridging: 'custom' | ' } function addCustomBridging(c: ContractBuilder, access: Access, upgradeable: Upgradeable, namespacePrefix: string) { + if (access === false) { + access = 'ownable'; + } + switch (access) { - case false: case 'ownable': { if (!upgradeable) { + // Add variable and constructor logic using state variable const addedBridge = c.addStateVariable(`address public tokenBridge;`, false); if (addedBridge) { c.addConstructorArgument({ type: 'address', name: 'tokenBridge_' }); @@ -376,7 +380,12 @@ function addCustomBridging(c: ContractBuilder, access: Access, upgradeable: Upgr c.addConstructorCode(`tokenBridge = tokenBridge_;`); } c.setFunctionBody([`if (caller != tokenBridge) revert Unauthorized();`], functions._checkTokenBridge, 'view'); + + // Add bridge setter + requireAccessControl(c, functions.setTokenBridge, access, 'TOKEN_BRIDGE_SETTER', 'tokenBridgeSetter'); + c.addFunctionCode('tokenBridge = tokenBridge_;', functions.setTokenBridge); } else { + // Add variable and constructor logic using namespaced storage setNamespacedStorage(c, ['address tokenBridge;'], namespacePrefix); c.addConstructorArgument({ type: 'address', name: 'tokenBridge_' }); @@ -390,6 +399,16 @@ function addCustomBridging(c: ContractBuilder, access: Access, upgradeable: Upgr functions._checkTokenBridge, 'view', ); + + // Add bridge setter + requireAccessControl(c, functions.setTokenBridge, access, 'TOKEN_BRIDGE_SETTER', 'tokenBridgeSetter'); + c.setFunctionBody( + [ + toStorageStructInstantiation(c.name), + '$.tokenBridge = tokenBridge_;' + ], + functions.setTokenBridge + ); } break; } @@ -510,6 +529,11 @@ export const functions = defineFunctions({ args: [{ name: 'caller', type: 'address' }], }, + setTokenBridge: { + kind: 'public' as const, + args: [{ name: 'tokenBridge_', type: 'address' }], + }, + setLink: { kind: 'public' as const, args: [ From d37b000dfecdbb123bda1f37735592a1156334b7 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 27 Nov 2025 10:18:24 -0500 Subject: [PATCH 07/20] Add to UI --- packages/core/solidity/src/erc20.ts | 2 +- packages/ui/src/solidity/ERC20Controls.svelte | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/core/solidity/src/erc20.ts b/packages/core/solidity/src/erc20.ts index 94df2c749..13bc148ce 100644 --- a/packages/core/solidity/src/erc20.ts +++ b/packages/core/solidity/src/erc20.ts @@ -79,7 +79,7 @@ export function printERC20(opts: ERC20Options = defaults): string { } export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable || opts.pausable || opts.upgradeable === 'uups' || opts.crossChainBridging === 'custom'; + return opts.mintable || opts.pausable || opts.upgradeable === 'uups' || opts.crossChainBridging === 'custom' || opts.crossChainBridging === 'native'; } export function buildERC20(opts: ERC20Options): ContractBuilder { diff --git a/packages/ui/src/solidity/ERC20Controls.svelte b/packages/ui/src/solidity/ERC20Controls.svelte index 7c351a45d..f21c92dab 100644 --- a/packages/ui/src/solidity/ERC20Controls.svelte +++ b/packages/ui/src/solidity/ERC20Controls.svelte @@ -183,10 +183,16 @@
+ + {#if !omitFeatures?.includes('superchain')}