├── ABIs ├── Address.abi.json ├── AppCore.abi.json ├── Base64.abi.json ├── CallLib.abi.json ├── Math.abi.json ├── Strings.abi.json ├── console.abi.json ├── stdJson.abi.json ├── stdMath.abi.json ├── BaseAction.abi.json ├── CommonBase.abi.json ├── FeedCore.abi.json ├── GraphCore.abi.json ├── GroupCore.abi.json ├── KeyValueLib.abi.json ├── LibBytes.abi.json ├── RulesLib.abi.json ├── SafeERC20.abi.json ├── ScriptBase.abi.json ├── SignedMath.abi.json ├── StdChains.abi.json ├── StdCheats.abi.json ├── StdStyle.abi.json ├── StdUtils.abi.json ├── StorageSlot.abi.json ├── TestBase.abi.json ├── safeconsole.abi.json ├── stdStorage.abi.json ├── AccessControlLib.abi.json ├── EIP712EncodingLib.abi.json ├── Initializable.abi.json ├── NamespaceCore.abi.json ├── PermissionsHelper.abi.json ├── StdCheatsSafe.abi.json ├── AddressUpgradeable.abi.json ├── EmptyImplementation.abi.json ├── KeyValueStorageLib.abi.json ├── LensPaymentHandler.abi.json ├── LensRulePaymentHandler.abi.json ├── RuleBasedPrimitive.abi.json ├── PayableUsingNativePaymentHelper.abi.json ├── Proxy.abi.json ├── ILock.abi.json ├── Script.abi.json ├── IBeacon.abi.json ├── IERC1822Proxiable.abi.json ├── IAccessControlled.abi.json ├── LibString.abi.json ├── ERC165.abi.json ├── IToken.abi.json ├── IERC165.abi.json ├── ITokenURIProvider.abi.json ├── IMigrationNamespace.abi.json ├── SetTokenDistributorSigner.abi.json ├── PrimitiveFactory.abi.json ├── IOwnable.abi.json ├── Query.abi.json ├── IMetadataBased.abi.json ├── MultiPoke.abi.json ├── MyScript.abi.json ├── ConfigurePrimitiveRules.abi.json ├── DeployAndSetAccessControls.abi.json ├── Namespace_SetNameAndSymbol.abi.json ├── ILensNativePaymentHelper.abi.json ├── IVersionedBeacon.abi.json ├── IERC4906Events.abi.json ├── ERC721Holder.abi.json ├── IERC721Receiver.abi.json ├── IERC721ReceiverUpgradeable.abi.json ├── IERC721TokenReceiver.abi.json ├── PrepareTokenDistribution.abi.json ├── Events.abi.json ├── ExtraDataBased.abi.json ├── ExtraStorageBased.abi.json ├── SourceStampBased.abi.json ├── EntityExtraDataBased.abi.json ├── LensNativePaymentHelper.abi.json ├── IAccountGroupAdditionSettings.abi.json ├── IERC1967.abi.json ├── ERC1967Upgrade.abi.json ├── MetadataBased.abi.json ├── Ownable.abi.json ├── stdStorageSafe.abi.json ├── ILensFees.abi.json ├── AccessControlFactory.abi.json ├── TrustBasedRule.abi.json ├── ILensCreate2.abi.json ├── LensUsernameTokenURIProvider.abi.json ├── PermissionlessAccessControl.abi.json ├── ISource.abi.json ├── IAccessControl.abi.json ├── ERC1967Proxy.abi.json ├── LensFees.abi.json ├── AccessControlled.abi.json ├── TransparentUpgradeableProxy.abi.json └── IERC20Permit.abi.json ├── deployments-zk ├── lensMainnet │ ├── .chainId │ └── contracts │ │ └── core │ │ └── upgradeability │ │ └── EmptyImplementation.sol │ │ └── EmptyImplementation.json ├── lensSepoliaTestnet │ ├── .chainId │ └── contracts │ │ └── core │ │ └── upgradeability │ │ └── EmptyImplementation.sol │ │ └── EmptyImplementation.json └── zkSyncEraTestNode │ └── .chainId ├── LICENSE ├── .prettierignore ├── .husky └── pre-commit ├── foundry.lock ├── .gitmodules ├── .vscode └── settings.json ├── audits ├── LensCore-security-review_2025-01-06.pdf ├── LensRules-security-review_2025-01-06.pdf ├── LensActions-security-review_2025-01-06.pdf └── LensExtensions-security-review_2025-01-06.pdf ├── contracts ├── core │ ├── upgradeability │ │ ├── EmptyImplementation.sol │ │ ├── Initializable.sol │ │ └── Lock.sol │ ├── interfaces │ │ ├── ITokenURIProvider.sol │ │ ├── IOwnable.sol │ │ ├── ILock.sol │ │ ├── IMetadataBased.sol │ │ ├── IAccessControlled.sol │ │ ├── ISource.sol │ │ ├── IAccountGroupAdditionSettings.sol │ │ ├── IRequestBasedGroupRule.sol │ │ ├── IFollowRule.sol │ │ ├── IERC721Namespace.sol │ │ ├── IERC4906Events.sol │ │ ├── IVersionedBeacon.sol │ │ ├── IPostRule.sol │ │ ├── IGraphRule.sol │ │ ├── IGroupRule.sol │ │ ├── IAccessControl.sol │ │ ├── IFeedRule.sol │ │ ├── INamespaceRule.sol │ │ └── IRoleBasedAccessControl.sol │ ├── types │ │ ├── Events.sol │ │ ├── Constants.sol │ │ ├── Types.sol │ │ └── Errors.sol │ ├── libraries │ │ ├── KeyValueLib.sol │ │ └── KeyValueStorageLib.sol │ ├── base │ │ ├── ExtraDataBased.sol │ │ ├── EntityExtraDataBased.sol │ │ └── MetadataBased.sol │ └── access │ │ └── Ownable.sol ├── migration │ └── EventEmitterEarly.sol ├── extensions │ ├── access │ │ └── PermissionlessAccessControl.sol │ ├── factories │ │ ├── PrimitiveFactory.sol │ │ ├── AccessControlFactory.sol │ │ ├── AppFactory.sol │ │ ├── FeedFactory.sol │ │ ├── GraphFactory.sol │ │ ├── GroupFactory.sol │ │ └── AccountFactory.sol │ └── fees │ │ ├── LensFees.sol │ │ └── LensRulePaymentHandler.sol ├── rules │ └── base │ │ ├── OwnableMetadataBasedRule.sol │ │ └── TrustBasedRule.sol └── actions │ ├── post │ └── base │ │ └── OwnableMetadataBasedPostAction.sol │ ├── account │ └── base │ │ └── OwnableMetadataBasedAccountAction.sol │ └── base │ └── BaseAction.sol ├── checkProxy.sh ├── remappings.txt ├── .prettierrc ├── tsconfig.json ├── test ├── mocks │ ├── MockLensCreate2.sol │ ├── MockCurrency.sol │ ├── MockSimpleCollectAction.sol │ ├── MockVersionedBeacon.sol │ ├── MockWrapperCurrency.sol │ ├── MockNft.sol │ ├── MockUniversal.sol │ ├── MockAccessControl.sol │ └── MockFeed.sol ├── harness │ └── HarnessAccount.sol ├── helpers │ ├── ZkTest.sol │ ├── MockAccessControlLib.sol │ └── FuzzZkTest.sol ├── upgradeability │ └── Initializable.t.sol └── token-uri │ └── expected-svgs │ ├── lens___w.base64 │ ├── lens___ww.base64 │ ├── lens___www.base64 │ ├── lens___lens.base64 │ ├── lens___stani.base64 │ ├── lens___wwww.base64 │ ├── lens___wwwww.base64 │ ├── lens___wwwwww.base64 │ ├── orb___jordan.base64 │ ├── lens___satoshi.base64 │ ├── lens___wwwwwww.base64 │ ├── lens___wwwwwwww.base64 │ ├── lens___wwwwwwwww.base64 │ ├── orb___customer23.base64 │ ├── lens___wwwwwwwwww.base64 │ ├── lens___wwwwwwwwwww.base64 │ ├── CAPITALIZATION___TEST2.base64 │ ├── lens___wwwwwwwwwwww.base64 │ ├── lens___wwwwwwwwwwwww.base64 │ ├── UpperC4se___c0nVERSiOn.base64 │ ├── lens___donosonaumczuk.base64 │ ├── lens___wwwwwwwwwwwwww.base64 │ ├── somelongerapp___im_alan.base64 │ └── somelongerapp___vitalik.base64 ├── .github └── workflows │ ├── dependency-review.yml │ └── ci.yml ├── .env.example ├── end-env.sh ├── getAddressesCSV.js ├── scripts └── getImplementations.sh ├── start-env.sh ├── filtered-coverage.sh ├── script └── SetTokenDistributorSigner.s.sol ├── parseAbisToBackendFolders.ts ├── deploy ├── deployImplementations.ts ├── logAddressFromPk.ts ├── upgradeActions.ts ├── verify.ts ├── deployActions.ts ├── deploySingleContract.ts └── deployProxyStuff.ts ├── coverage.sh └── hashed-constants-standard.js /ABIs/Address.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/AppCore.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/Base64.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/CallLib.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/Math.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/Strings.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/console.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/stdJson.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/stdMath.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/BaseAction.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/CommonBase.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/FeedCore.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/GraphCore.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/GroupCore.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/KeyValueLib.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/LibBytes.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/RulesLib.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/SafeERC20.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/ScriptBase.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/SignedMath.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/StdChains.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/StdCheats.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/StdStyle.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/StdUtils.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/StorageSlot.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/TestBase.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/safeconsole.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/stdStorage.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/AccessControlLib.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/EIP712EncodingLib.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/Initializable.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/NamespaceCore.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/PermissionsHelper.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/StdCheatsSafe.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/AddressUpgradeable.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/EmptyImplementation.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/KeyValueStorageLib.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/LensPaymentHandler.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/LensRulePaymentHandler.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABIs/RuleBasedPrimitive.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /deployments-zk/lensMainnet/.chainId: -------------------------------------------------------------------------------- 1 | 0xe8 -------------------------------------------------------------------------------- /ABIs/PayableUsingNativePaymentHelper.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /deployments-zk/lensSepoliaTestnet/.chainId: -------------------------------------------------------------------------------- 1 | 0x90f7 -------------------------------------------------------------------------------- /deployments-zk/zkSyncEraTestNode/.chainId: -------------------------------------------------------------------------------- 1 | 0x104 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2024 Lens Labs. All Rights Reserved. -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore all Solidity (.sol) files: 2 | **/*.sol 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn lint-staged -------------------------------------------------------------------------------- /foundry.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lib/forge-std": { 3 | "rev": "2b59872eee0b8088ddcade39fe8c041e17bb79c0" 4 | } 5 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.formatter": "forge", 3 | "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" 4 | } 5 | -------------------------------------------------------------------------------- /audits/LensCore-security-review_2025-01-06.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stalim17/lens-v3/HEAD/audits/LensCore-security-review_2025-01-06.pdf -------------------------------------------------------------------------------- /audits/LensRules-security-review_2025-01-06.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stalim17/lens-v3/HEAD/audits/LensRules-security-review_2025-01-06.pdf -------------------------------------------------------------------------------- /audits/LensActions-security-review_2025-01-06.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stalim17/lens-v3/HEAD/audits/LensActions-security-review_2025-01-06.pdf -------------------------------------------------------------------------------- /audits/LensExtensions-security-review_2025-01-06.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stalim17/lens-v3/HEAD/audits/LensExtensions-security-review_2025-01-06.pdf -------------------------------------------------------------------------------- /ABIs/Proxy.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "fallback", 4 | "stateMutability": "payable" 5 | }, 6 | { 7 | "type": "receive", 8 | "stateMutability": "payable" 9 | } 10 | ] -------------------------------------------------------------------------------- /contracts/core/upgradeability/EmptyImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.0; 4 | 5 | contract EmptyImplementation {} 6 | -------------------------------------------------------------------------------- /checkProxy.sh: -------------------------------------------------------------------------------- 1 | if [ -z "$1" ]; then 2 | echo "Usage: $0 " 3 | exit 1 4 | fi 5 | 6 | echo "Proxy admin:" 7 | cast admin $1 --rpc-url https://rpc.testnet.lens.dev 8 | 9 | echo "Proxy implementation:" 10 | cast implementation $1 --rpc-url https://rpc.testnet.lens.dev 11 | -------------------------------------------------------------------------------- /contracts/core/interfaces/ITokenURIProvider.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | interface ITokenURIProvider { 6 | function tokenURI(uint256 tokenId) external view returns (string memory); 7 | } 8 | -------------------------------------------------------------------------------- /ABIs/ILock.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "isLocked", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | } 15 | ] -------------------------------------------------------------------------------- /ABIs/Script.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | } 15 | ] -------------------------------------------------------------------------------- /ABIs/IBeacon.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "implementation", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "address" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | } 15 | ] -------------------------------------------------------------------------------- /ABIs/IERC1822Proxiable.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "proxiableUUID", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bytes32", 10 | "internalType": "bytes32" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | } 15 | ] -------------------------------------------------------------------------------- /contracts/core/interfaces/IOwnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | interface IOwnable { 6 | function transferOwnership(address newOwner) external; 7 | 8 | function owner() external view returns (address); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/core/interfaces/ILock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | interface ILock { 6 | /** 7 | * @dev Returns true if locked, false if not. 8 | */ 9 | function isLocked() external view returns (bool); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/migration/EventEmitterEarly.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | import "./EventEmitter.sol"; 5 | 6 | contract EventEmitterEarly is EventEmitter { 7 | function _allowedToEmitEvents() internal view override returns (bool) { 8 | return block.number < 30_000; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ABIs/IAccessControlled.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "getAccessControl", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "contract IAccessControl" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | } 15 | ] -------------------------------------------------------------------------------- /ABIs/LibString.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "error", 4 | "name": "HexLengthInsufficient", 5 | "inputs": [] 6 | }, 7 | { 8 | "type": "error", 9 | "name": "StringNot7BitASCII", 10 | "inputs": [] 11 | }, 12 | { 13 | "type": "error", 14 | "name": "TooBigForSmallString", 15 | "inputs": [] 16 | } 17 | ] -------------------------------------------------------------------------------- /contracts/core/interfaces/IMetadataBased.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | interface IMetadataBased { 6 | function getMetadataURI() external view returns (string memory); 7 | function setMetadataURI(string memory metadata) external; 8 | } 9 | -------------------------------------------------------------------------------- /contracts/core/types/Events.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | library Events { 6 | event Lens_Contract_Deployed(string contractType, string flavour); 7 | 8 | event Lens_PermissionId_Available(uint256 indexed permissionId, string name); 9 | } 10 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | @matterlabs/=node_modules/@matterlabs/ 2 | @openzeppelin/=node_modules/@openzeppelin/ 3 | solady/=node_modules/solady/ 4 | forge-std/=lib/forge-std/src/ 5 | hardhat/=node_modules/hardhat/ 6 | contracts/=contracts/ 7 | @core/=contracts/core/ 8 | @extensions/=contracts/extensions/ 9 | @actions/=contracts/actions/ 10 | @rules/=contracts/rules/ 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "trailingComma": "es5", 4 | "semi": true, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "overrides": [ 8 | { 9 | "files": "*.svg", 10 | "options": { 11 | "bracketSameLine": true, 12 | "parser": "html", 13 | "printWidth": 10000 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /contracts/core/interfaces/IAccessControlled.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "contracts/core/interfaces/IAccessControl.sol"; 6 | 7 | interface IAccessControlled { 8 | function getAccessControl() external view returns (IAccessControl); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/core/interfaces/ISource.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {SourceStamp} from "contracts/core/types/Types.sol"; 6 | 7 | interface ISource { 8 | function getTreasury() external view returns (address); 9 | 10 | function validateSource(SourceStamp calldata sourceStamp) external; 11 | } 12 | -------------------------------------------------------------------------------- /ABIs/ERC165.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "supportsInterface", 5 | "inputs": [ 6 | { 7 | "name": "interfaceId", 8 | "type": "bytes4", 9 | "internalType": "bytes4" 10 | } 11 | ], 12 | "outputs": [ 13 | { 14 | "name": "", 15 | "type": "bool", 16 | "internalType": "bool" 17 | } 18 | ], 19 | "stateMutability": "view" 20 | } 21 | ] -------------------------------------------------------------------------------- /ABIs/IToken.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "balanceOf", 5 | "inputs": [ 6 | { 7 | "name": "account", 8 | "type": "address", 9 | "internalType": "address" 10 | } 11 | ], 12 | "outputs": [ 13 | { 14 | "name": "", 15 | "type": "uint256", 16 | "internalType": "uint256" 17 | } 18 | ], 19 | "stateMutability": "view" 20 | } 21 | ] -------------------------------------------------------------------------------- /ABIs/IERC165.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "supportsInterface", 5 | "inputs": [ 6 | { 7 | "name": "interfaceId", 8 | "type": "bytes4", 9 | "internalType": "bytes4" 10 | } 11 | ], 12 | "outputs": [ 13 | { 14 | "name": "", 15 | "type": "bool", 16 | "internalType": "bool" 17 | } 18 | ], 19 | "stateMutability": "view" 20 | } 21 | ] -------------------------------------------------------------------------------- /ABIs/ITokenURIProvider.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "tokenURI", 5 | "inputs": [ 6 | { 7 | "name": "tokenId", 8 | "type": "uint256", 9 | "internalType": "uint256" 10 | } 11 | ], 12 | "outputs": [ 13 | { 14 | "name": "", 15 | "type": "string", 16 | "internalType": "string" 17 | } 18 | ], 19 | "stateMutability": "view" 20 | } 21 | ] -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "moduleResolution": "node", 8 | "forceConsistentCasingInFileNames": true, 9 | "outDir": "dist", 10 | "resolveJsonModule": true 11 | }, 12 | "include": [ 13 | "./hardhat.config.ts", 14 | "./scripts", 15 | "./deploy", 16 | "./test", 17 | "typechain/**/*" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /contracts/core/interfaces/IAccountGroupAdditionSettings.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {KeyValue} from "contracts/core/types/Types.sol"; 6 | 7 | interface IAccountGroupAdditionSettings { 8 | function canBeAddedToGroup(address group, address addedBy, KeyValue[] calldata params) 9 | external 10 | view 11 | returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /ABIs/IMigrationNamespace.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "migration_force__setNameAndSymbol", 5 | "inputs": [ 6 | { 7 | "name": "name", 8 | "type": "string", 9 | "internalType": "string" 10 | }, 11 | { 12 | "name": "symbol", 13 | "type": "string", 14 | "internalType": "string" 15 | } 16 | ], 17 | "outputs": [], 18 | "stateMutability": "nonpayable" 19 | } 20 | ] -------------------------------------------------------------------------------- /ABIs/SetTokenDistributorSigner.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "run", 18 | "inputs": [], 19 | "outputs": [], 20 | "stateMutability": "nonpayable" 21 | } 22 | ] -------------------------------------------------------------------------------- /test/mocks/MockLensCreate2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | contract MockLensCreate2 { 6 | mapping(bytes32 => address) public addresses; 7 | 8 | function getAddress(bytes32 salt) public view returns (address) { 9 | return addresses[salt]; 10 | } 11 | 12 | function setAddress(bytes32 salt, address addr) public { 13 | addresses[salt] = addr; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ABIs/PrimitiveFactory.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "primitiveBeacon", 7 | "type": "address", 8 | "internalType": "address" 9 | }, 10 | { 11 | "name": "proxyAdminLock", 12 | "type": "address", 13 | "internalType": "address" 14 | }, 15 | { 16 | "name": "lensFactory", 17 | "type": "address", 18 | "internalType": "address" 19 | } 20 | ], 21 | "stateMutability": "nonpayable" 22 | } 23 | ] -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | name: Dependency Review 2 | on: 3 | - pull_request 4 | 5 | permissions: 6 | contents: read 7 | pull-requests: write 8 | 9 | jobs: 10 | dependency-review: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout Repository 14 | uses: actions/checkout@v5 15 | - name: Dependency Review 16 | uses: actions/dependency-review-action@v4 17 | with: 18 | comment-summary-in-pr: on-failure 19 | fail-on-severity: moderate 20 | license-check: false 21 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Deployer Wallet's private key 2 | WALLET_PRIVATE_KEY= 3 | 4 | # Lock Owners for Proxy Admin & Access Control 5 | PROXY_ADMIN_LOCK_OWNER= 6 | ACCESS_CONTROL_LOCK_OWNER= 7 | 8 | # Rules & Actions Owner 9 | RULES_OWNER= 10 | ACTIONS_OWNER= 11 | 12 | # Beacon Owner, responsible for releasing new versions for all upgradable contracts that use Beacon pattern 13 | BEACON_OWNER= 14 | 15 | # Proxy Admin for factories 16 | FACTORIES_PROXY_OWNER= 17 | 18 | # Primitives owner 19 | PRIMITIVES_OWNER= 20 | 21 | # DEPLOY_FR= 22 | # DEPLOY_MIGRATION= 23 | 24 | TREASURY_ADDRESS= 25 | TREASURY_FEE_BPS= 26 | -------------------------------------------------------------------------------- /ABIs/IOwnable.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "owner", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "address" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "transferOwnership", 18 | "inputs": [ 19 | { 20 | "name": "newOwner", 21 | "type": "address", 22 | "internalType": "address" 23 | } 24 | ], 25 | "outputs": [], 26 | "stateMutability": "nonpayable" 27 | } 28 | ] -------------------------------------------------------------------------------- /ABIs/Query.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "run", 18 | "inputs": [], 19 | "outputs": [], 20 | "stateMutability": "view" 21 | }, 22 | { 23 | "type": "function", 24 | "name": "testQuery", 25 | "inputs": [], 26 | "outputs": [], 27 | "stateMutability": "nonpayable" 28 | } 29 | ] -------------------------------------------------------------------------------- /contracts/core/interfaces/IRequestBasedGroupRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IGroupRule} from "contracts/core/interfaces/IGroupRule.sol"; 6 | import {KeyValue} from "contracts/core/types/Types.sol"; 7 | 8 | interface IRequestBasedGroupRule is IGroupRule { 9 | function sendMembershipRequest(bytes32 configSalt, address group, KeyValue[] calldata params) external; 10 | 11 | function cancelMembershipRequest(bytes32 configSalt, address group, KeyValue[] calldata params) external; 12 | } 13 | -------------------------------------------------------------------------------- /ABIs/IMetadataBased.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "getMetadataURI", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "string", 10 | "internalType": "string" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "setMetadataURI", 18 | "inputs": [ 19 | { 20 | "name": "metadata", 21 | "type": "string", 22 | "internalType": "string" 23 | } 24 | ], 25 | "outputs": [], 26 | "stateMutability": "nonpayable" 27 | } 28 | ] -------------------------------------------------------------------------------- /ABIs/MultiPoke.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "run", 18 | "inputs": [], 19 | "outputs": [], 20 | "stateMutability": "nonpayable" 21 | }, 22 | { 23 | "type": "function", 24 | "name": "testMultiPoke", 25 | "inputs": [], 26 | "outputs": [], 27 | "stateMutability": "nonpayable" 28 | } 29 | ] -------------------------------------------------------------------------------- /ABIs/MyScript.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "run", 18 | "inputs": [], 19 | "outputs": [], 20 | "stateMutability": "nonpayable" 21 | }, 22 | { 23 | "type": "function", 24 | "name": "testMyScript", 25 | "inputs": [], 26 | "outputs": [], 27 | "stateMutability": "nonpayable" 28 | } 29 | ] -------------------------------------------------------------------------------- /ABIs/ConfigurePrimitiveRules.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "run", 18 | "inputs": [], 19 | "outputs": [], 20 | "stateMutability": "nonpayable" 21 | }, 22 | { 23 | "type": "function", 24 | "name": "testConfigurePrimitiveRules", 25 | "inputs": [], 26 | "outputs": [], 27 | "stateMutability": "nonpayable" 28 | } 29 | ] -------------------------------------------------------------------------------- /ABIs/DeployAndSetAccessControls.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "run", 18 | "inputs": [], 19 | "outputs": [], 20 | "stateMutability": "nonpayable" 21 | }, 22 | { 23 | "type": "function", 24 | "name": "testDeployAndSetAccessControls", 25 | "inputs": [], 26 | "outputs": [], 27 | "stateMutability": "nonpayable" 28 | } 29 | ] -------------------------------------------------------------------------------- /ABIs/Namespace_SetNameAndSymbol.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "run", 18 | "inputs": [], 19 | "outputs": [], 20 | "stateMutability": "nonpayable" 21 | }, 22 | { 23 | "type": "function", 24 | "name": "testNamespace_SetNameAndSymbol", 25 | "inputs": [], 26 | "outputs": [], 27 | "stateMutability": "nonpayable" 28 | } 29 | ] -------------------------------------------------------------------------------- /end-env.sh: -------------------------------------------------------------------------------- 1 | # Run: 2 | # ./end-env 3 | 4 | if [ ! -f .env.active ]; then 5 | echo "No active environment found" 6 | exit 1 7 | fi 8 | 9 | if [ -f .env ]; then 10 | echo "○ .env removed" 11 | rm .env 12 | fi 13 | 14 | if [ "$(cat .env.active)" == "mainnet" ]; then 15 | cp addressBook.json addressBook.mainnet.json 16 | echo "○ addressBook.json copied to addressBook.mainnet.json" 17 | echo "⦿ Mainnet environment ended" 18 | else 19 | cp addressBook.json addressBook.testnet.json 20 | echo "○ addressBook.json copied to addressBook.testnet.json" 21 | echo "⦿ Testnet environment ended" 22 | fi 23 | 24 | rm addressBook.json 25 | 26 | rm .env.active 27 | -------------------------------------------------------------------------------- /contracts/core/interfaces/IFollowRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {KeyValue} from "contracts/core/types/Types.sol"; 6 | 7 | interface IFollowRule { 8 | function configure(bytes32 configSalt, address account, KeyValue[] calldata ruleParams) external; 9 | 10 | function processFollow( 11 | bytes32 configSalt, 12 | address originalMsgSender, 13 | address followerAccount, 14 | address accountToFollow, 15 | KeyValue[] calldata primitiveParams, 16 | KeyValue[] calldata ruleParams 17 | ) external; 18 | } 19 | -------------------------------------------------------------------------------- /getAddressesCSV.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | function getAddressesCSV() { 4 | // Load address book JSON 5 | const addressBook = JSON.parse(fs.readFileSync('./addressBook.testnet.json', 'utf8')); 6 | 7 | // Get all contract keys 8 | const contracts = Object.keys(addressBook); 9 | 10 | // Build CSV string with header 11 | let csv = 'Contract,Address\n'; 12 | 13 | // Add each contract and address 14 | contracts.forEach(contract => { 15 | const address = addressBook[contract].address; 16 | csv += `${contract},${address}\n`; 17 | }); 18 | 19 | return csv; 20 | } 21 | 22 | // Write to file 23 | const csv = getAddressesCSV(); 24 | fs.writeFileSync('addresses.csv', csv); 25 | -------------------------------------------------------------------------------- /scripts/getImplementations.sh: -------------------------------------------------------------------------------- 1 | source .env 2 | 3 | # Define factories as key-value pairs in the format "name:address" 4 | factories=( 5 | "TippingAccountAction:0xda614A06972C70a8d50D494FB678d48cf536f769" 6 | "TippingPostAction:0x34EF0F5e41cB6c7ad9438079c179d70C7567ae00" 7 | "SimpleCollectAction:0x17d5B3917Eab14Ab4923DEc597B39EF64863C830" 8 | ) 9 | 10 | # Print CSV header 11 | echo "Factory,Proxy,Implementation" 12 | 13 | # Process each factory 14 | for factory in "${factories[@]}"; do 15 | name="${factory%%:*}" 16 | proxy="${factory#*:}" 17 | implementation=$(cast implementation --rpc-headers "AUTH: 34ee052393a9ce518100915707ac3191" --rpc-url $RPC_URL "$proxy") 18 | echo "$name,$proxy,$implementation" 19 | done 20 | -------------------------------------------------------------------------------- /ABIs/ILensNativePaymentHelper.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "refundNative", 5 | "inputs": [ 6 | { 7 | "name": "to", 8 | "type": "address", 9 | "internalType": "address" 10 | } 11 | ], 12 | "outputs": [], 13 | "stateMutability": "nonpayable" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "transferNative", 18 | "inputs": [ 19 | { 20 | "name": "to", 21 | "type": "address", 22 | "internalType": "address" 23 | }, 24 | { 25 | "name": "amount", 26 | "type": "uint256", 27 | "internalType": "uint256" 28 | } 29 | ], 30 | "outputs": [], 31 | "stateMutability": "nonpayable" 32 | } 33 | ] -------------------------------------------------------------------------------- /ABIs/IVersionedBeacon.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "implementation", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "address" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "implementation", 18 | "inputs": [ 19 | { 20 | "name": "implementationVersion", 21 | "type": "uint256", 22 | "internalType": "uint256" 23 | } 24 | ], 25 | "outputs": [ 26 | { 27 | "name": "", 28 | "type": "address", 29 | "internalType": "address" 30 | } 31 | ], 32 | "stateMutability": "view" 33 | } 34 | ] -------------------------------------------------------------------------------- /contracts/core/interfaces/IERC721Namespace.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 6 | import {INamespace} from "contracts/core/interfaces/INamespace.sol"; 7 | 8 | interface IERC721Namespace is INamespace, IERC721 { 9 | event Lens_Username_Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 10 | 11 | function exists(uint256 tokenId) external view returns (bool); 12 | 13 | function getTokenIdByUsername(string calldata username) external view returns (uint256); 14 | 15 | function getUsernameByTokenId(uint256 tokenId) external view returns (string memory); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/core/interfaces/IERC4906Events.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | library IERC4906Events { 6 | /// @dev This event emits when the metadata of a token is changed. 7 | /// So that the third-party platforms such as NFT market could 8 | /// timely update the images and related attributes of the NFT. 9 | event MetadataUpdate(uint256 _tokenId); 10 | 11 | /// @dev This event emits when the metadata of a range of tokens is changed. 12 | /// So that the third-party platforms such as NFT market could 13 | /// timely update the images and related attributes of the NFTs. 14 | event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); 15 | } 16 | -------------------------------------------------------------------------------- /ABIs/IERC4906Events.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "BatchMetadataUpdate", 5 | "inputs": [ 6 | { 7 | "name": "_fromTokenId", 8 | "type": "uint256", 9 | "indexed": false, 10 | "internalType": "uint256" 11 | }, 12 | { 13 | "name": "_toTokenId", 14 | "type": "uint256", 15 | "indexed": false, 16 | "internalType": "uint256" 17 | } 18 | ], 19 | "anonymous": false 20 | }, 21 | { 22 | "type": "event", 23 | "name": "MetadataUpdate", 24 | "inputs": [ 25 | { 26 | "name": "_tokenId", 27 | "type": "uint256", 28 | "indexed": false, 29 | "internalType": "uint256" 30 | } 31 | ], 32 | "anonymous": false 33 | } 34 | ] -------------------------------------------------------------------------------- /start-env.sh: -------------------------------------------------------------------------------- 1 | # For mainnet run: 2 | # ./start-env mainnet 3 | # For testnet run: 4 | # ./start-env testnet 5 | 6 | if [ "$1" == "mainnet" ]; then 7 | echo "mainnet" > .env.active 8 | cp .env.mainnet .env 9 | echo "○ .env.mainnet copied to .env" 10 | cp addressBook.mainnet.json addressBook.json 11 | echo "○ addressBook.mainnet.json copied to addressBook.json" 12 | echo "⦿ Environment ready for mainnet!" 13 | elif [ "$1" == "testnet" ]; then 14 | echo "testnet" > .env.active 15 | cp .env.testnet .env 16 | echo "○ .env.testnet copied to .env" 17 | cp addressBook.testnet.json addressBook.json 18 | echo "○ addressBook.testnet.json copied to addressBook.json" 19 | echo "⦿ Environment ready for testnet!" 20 | else 21 | echo "Invalid argument: $1" 22 | exit 1 23 | fi 24 | -------------------------------------------------------------------------------- /ABIs/ERC721Holder.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "onERC721Received", 5 | "inputs": [ 6 | { 7 | "name": "", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "", 13 | "type": "address", 14 | "internalType": "address" 15 | }, 16 | { 17 | "name": "", 18 | "type": "uint256", 19 | "internalType": "uint256" 20 | }, 21 | { 22 | "name": "", 23 | "type": "bytes", 24 | "internalType": "bytes" 25 | } 26 | ], 27 | "outputs": [ 28 | { 29 | "name": "", 30 | "type": "bytes4", 31 | "internalType": "bytes4" 32 | } 33 | ], 34 | "stateMutability": "nonpayable" 35 | } 36 | ] -------------------------------------------------------------------------------- /ABIs/IERC721Receiver.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "onERC721Received", 5 | "inputs": [ 6 | { 7 | "name": "operator", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "from", 13 | "type": "address", 14 | "internalType": "address" 15 | }, 16 | { 17 | "name": "tokenId", 18 | "type": "uint256", 19 | "internalType": "uint256" 20 | }, 21 | { 22 | "name": "data", 23 | "type": "bytes", 24 | "internalType": "bytes" 25 | } 26 | ], 27 | "outputs": [ 28 | { 29 | "name": "", 30 | "type": "bytes4", 31 | "internalType": "bytes4" 32 | } 33 | ], 34 | "stateMutability": "nonpayable" 35 | } 36 | ] -------------------------------------------------------------------------------- /contracts/core/interfaces/IVersionedBeacon.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | interface IVersionedBeacon { 6 | /** 7 | * @dev Returns the default implementation held by the Beacon. Usually this will be the latest or the most 8 | * stable version. 9 | * @return Address of the implementation. 10 | */ 11 | function implementation() external view returns (address); 12 | 13 | /** 14 | * @dev Returns the implementation that corresponds to the requested version. 15 | * @param implementationVersion Version of the implementation to return. 16 | * @return Address of the implementation. 17 | */ 18 | function implementation(uint256 implementationVersion) external view returns (address); 19 | } 20 | -------------------------------------------------------------------------------- /test/mocks/MockCurrency.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract MockCurrency is ERC20 { 8 | constructor(string memory name, string memory symbol) ERC20(name, symbol) {} 9 | 10 | function mint(address to, uint256 amount) external payable virtual { 11 | _mint(to, amount); 12 | } 13 | 14 | function mint(uint256 amount) external payable virtual { 15 | _mint(msg.sender, amount); 16 | } 17 | 18 | function burn(address from, uint256 amount) external virtual { 19 | _burn(from, amount); 20 | } 21 | 22 | function burn(uint256 amount) external virtual { 23 | _burn(msg.sender, amount); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ABIs/IERC721ReceiverUpgradeable.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "onERC721Received", 5 | "inputs": [ 6 | { 7 | "name": "operator", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "from", 13 | "type": "address", 14 | "internalType": "address" 15 | }, 16 | { 17 | "name": "tokenId", 18 | "type": "uint256", 19 | "internalType": "uint256" 20 | }, 21 | { 22 | "name": "data", 23 | "type": "bytes", 24 | "internalType": "bytes" 25 | } 26 | ], 27 | "outputs": [ 28 | { 29 | "name": "", 30 | "type": "bytes4", 31 | "internalType": "bytes4" 32 | } 33 | ], 34 | "stateMutability": "nonpayable" 35 | } 36 | ] -------------------------------------------------------------------------------- /ABIs/IERC721TokenReceiver.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "onERC721Received", 5 | "inputs": [ 6 | { 7 | "name": "_operator", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "_from", 13 | "type": "address", 14 | "internalType": "address" 15 | }, 16 | { 17 | "name": "_tokenId", 18 | "type": "uint256", 19 | "internalType": "uint256" 20 | }, 21 | { 22 | "name": "_data", 23 | "type": "bytes", 24 | "internalType": "bytes" 25 | } 26 | ], 27 | "outputs": [ 28 | { 29 | "name": "", 30 | "type": "bytes4", 31 | "internalType": "bytes4" 32 | } 33 | ], 34 | "stateMutability": "nonpayable" 35 | } 36 | ] -------------------------------------------------------------------------------- /test/harness/HarnessAccount.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Account} from "contracts/extensions/account/Account.sol"; 6 | import {IAccount} from "contracts/extensions/account/IAccount.sol"; 7 | import {KeyValue} from "contracts/core/types/Types.sol"; 8 | 9 | interface IHarnessAccount is IAccount { 10 | function extractGraphFromParams(KeyValue[] calldata params) external pure returns (address); 11 | } 12 | 13 | contract HarnessAccount is IHarnessAccount, Account { 14 | constructor(address nativeGHO, address wrappedGHO) Account(nativeGHO, wrappedGHO) {} 15 | 16 | function extractGraphFromParams(KeyValue[] calldata params) external pure returns (address) { 17 | return _extractGraphFromParams(params); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/mocks/MockSimpleCollectAction.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {SimpleCollectAction} from "contracts/actions/post/collect/SimpleCollectAction.sol"; 6 | 7 | contract MockSimpleCollectAction is SimpleCollectAction { 8 | function $collectDataStorage2() private pure returns (CollectActionStorage storage _storage) { 9 | assembly { 10 | _storage.slot := STORAGE__SIMPLE_COLLECT_ACTION 11 | } 12 | } 13 | 14 | constructor(address actionHub) SimpleCollectAction(actionHub) {} 15 | 16 | function setCollectionAddress(address feed, uint256 postId, address collectionAddress) external { 17 | $collectDataStorage2().collectData[feed][postId].collectionAddress = collectionAddress; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/core/libraries/KeyValueLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {KeyValue} from "../types/Types.sol"; 6 | 7 | library KeyValueLib { 8 | // TODO: Replace for an optimized version later. Copying chunks of calldata into memory directly. 9 | function concat(KeyValue[] calldata start, KeyValue[] calldata end) internal pure returns (KeyValue[] memory) { 10 | KeyValue[] memory concatenated = new KeyValue[](start.length + end.length); 11 | for (uint256 i = 0; i < start.length; i++) { 12 | concatenated[i] = start[i]; 13 | } 14 | for (uint256 i = 0; i < end.length; i++) { 15 | concatenated[start.length + i] = end[i]; 16 | } 17 | return concatenated; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ABIs/PrepareTokenDistribution.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "IS_SCRIPT", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bool", 10 | "internalType": "bool" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "_getCalldataWithoutSelector", 18 | "inputs": [ 19 | { 20 | "name": "encodeCall", 21 | "type": "bytes", 22 | "internalType": "bytes" 23 | } 24 | ], 25 | "outputs": [ 26 | { 27 | "name": "", 28 | "type": "bytes", 29 | "internalType": "bytes" 30 | } 31 | ], 32 | "stateMutability": "pure" 33 | }, 34 | { 35 | "type": "function", 36 | "name": "run", 37 | "inputs": [], 38 | "outputs": [], 39 | "stateMutability": "nonpayable" 40 | } 41 | ] -------------------------------------------------------------------------------- /contracts/core/types/Constants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | // 100.00% represented as Basis Points, each Basis Point is 0.01% 6 | uint256 constant BPS_MAX = 10_000; 7 | 8 | uint256 constant SELECTOR_BYTE_LENGTH = 4; 9 | 10 | /// @custom:keccak lens.contract.ActionHub 11 | bytes32 constant CONTRACT__ACTION_HUB = 0x914706a68d66e273351f2cd4c1f0c739a3d2d211eb45a40f548eb964a6081fef; 12 | 13 | /// @custom:keccak lens.contract.LensFees 14 | bytes32 constant CONTRACT__LENS_FEES = 0x5072b519d346fa8b5598f9e3ddfc010379305b352a80b2bc6c119eb92ac9a520; 15 | 16 | /// @custom:keccak lens.contract.LensNativePaymentHelper 17 | bytes32 constant CONTRACT__LENS_NATIVE_PAYMENT_HELPER = 18 | 0x4181add33551df9049a16e8f6b64e891696eba958d766672bd32e1d3bf635636; 19 | 20 | address constant NATIVE_TOKEN = address(0x800A); 21 | -------------------------------------------------------------------------------- /test/helpers/ZkTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import "forge-std/Test.sol"; 6 | 7 | contract ZkTester { 8 | function isZkEvm() public view returns (bool) { 9 | return block.coinbase == address(0x8001); 10 | } 11 | } 12 | 13 | contract ZkTest is Test { 14 | function isZkEvm() public returns (bool) { 15 | ZkTester zkTester = new ZkTester(); 16 | return zkTester.isZkEvm(); 17 | } 18 | 19 | modifier onlyZkEvm() { 20 | vm.skip(!isZkEvm()); 21 | _; 22 | } 23 | 24 | modifier onlyEvm() { 25 | vm.skip(isZkEvm()); 26 | _; 27 | } 28 | 29 | modifier nonFork() { 30 | vm.skip(isFork()); 31 | _; 32 | } 33 | 34 | modifier onlyFork() { 35 | vm.skip(!isFork()); 36 | _; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ABIs/Events.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "Lens_Contract_Deployed", 5 | "inputs": [ 6 | { 7 | "name": "contractType", 8 | "type": "string", 9 | "indexed": false, 10 | "internalType": "string" 11 | }, 12 | { 13 | "name": "flavour", 14 | "type": "string", 15 | "indexed": false, 16 | "internalType": "string" 17 | } 18 | ], 19 | "anonymous": false 20 | }, 21 | { 22 | "type": "event", 23 | "name": "Lens_PermissionId_Available", 24 | "inputs": [ 25 | { 26 | "name": "permissionId", 27 | "type": "uint256", 28 | "indexed": true, 29 | "internalType": "uint256" 30 | }, 31 | { 32 | "name": "name", 33 | "type": "string", 34 | "indexed": false, 35 | "internalType": "string" 36 | } 37 | ], 38 | "anonymous": false 39 | } 40 | ] -------------------------------------------------------------------------------- /ABIs/ExtraDataBased.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "Lens_ExtraStorageSet", 5 | "inputs": [ 6 | { 7 | "name": "addressScope", 8 | "type": "address", 9 | "indexed": false, 10 | "internalType": "address" 11 | }, 12 | { 13 | "name": "entityType", 14 | "type": "uint256", 15 | "indexed": true, 16 | "internalType": "uint256" 17 | }, 18 | { 19 | "name": "entityId", 20 | "type": "uint256", 21 | "indexed": true, 22 | "internalType": "uint256" 23 | }, 24 | { 25 | "name": "key", 26 | "type": "bytes32", 27 | "indexed": true, 28 | "internalType": "bytes32" 29 | }, 30 | { 31 | "name": "value", 32 | "type": "bytes", 33 | "indexed": false, 34 | "internalType": "bytes" 35 | } 36 | ], 37 | "anonymous": false 38 | } 39 | ] -------------------------------------------------------------------------------- /ABIs/ExtraStorageBased.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "Lens_ExtraStorageSet", 5 | "inputs": [ 6 | { 7 | "name": "addressScope", 8 | "type": "address", 9 | "indexed": false, 10 | "internalType": "address" 11 | }, 12 | { 13 | "name": "entityType", 14 | "type": "uint256", 15 | "indexed": true, 16 | "internalType": "uint256" 17 | }, 18 | { 19 | "name": "entityId", 20 | "type": "uint256", 21 | "indexed": true, 22 | "internalType": "uint256" 23 | }, 24 | { 25 | "name": "key", 26 | "type": "bytes32", 27 | "indexed": true, 28 | "internalType": "bytes32" 29 | }, 30 | { 31 | "name": "value", 32 | "type": "bytes", 33 | "indexed": false, 34 | "internalType": "bytes" 35 | } 36 | ], 37 | "anonymous": false 38 | } 39 | ] -------------------------------------------------------------------------------- /ABIs/SourceStampBased.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "Lens_ExtraStorageSet", 5 | "inputs": [ 6 | { 7 | "name": "addressScope", 8 | "type": "address", 9 | "indexed": false, 10 | "internalType": "address" 11 | }, 12 | { 13 | "name": "entityType", 14 | "type": "uint256", 15 | "indexed": true, 16 | "internalType": "uint256" 17 | }, 18 | { 19 | "name": "entityId", 20 | "type": "uint256", 21 | "indexed": true, 22 | "internalType": "uint256" 23 | }, 24 | { 25 | "name": "key", 26 | "type": "bytes32", 27 | "indexed": true, 28 | "internalType": "bytes32" 29 | }, 30 | { 31 | "name": "value", 32 | "type": "bytes", 33 | "indexed": false, 34 | "internalType": "bytes" 35 | } 36 | ], 37 | "anonymous": false 38 | } 39 | ] -------------------------------------------------------------------------------- /ABIs/EntityExtraDataBased.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "Lens_ExtraStorageSet", 5 | "inputs": [ 6 | { 7 | "name": "addressScope", 8 | "type": "address", 9 | "indexed": false, 10 | "internalType": "address" 11 | }, 12 | { 13 | "name": "entityType", 14 | "type": "uint256", 15 | "indexed": true, 16 | "internalType": "uint256" 17 | }, 18 | { 19 | "name": "entityId", 20 | "type": "uint256", 21 | "indexed": true, 22 | "internalType": "uint256" 23 | }, 24 | { 25 | "name": "key", 26 | "type": "bytes32", 27 | "indexed": true, 28 | "internalType": "bytes32" 29 | }, 30 | { 31 | "name": "value", 32 | "type": "bytes", 33 | "indexed": false, 34 | "internalType": "bytes" 35 | } 36 | ], 37 | "anonymous": false 38 | } 39 | ] -------------------------------------------------------------------------------- /filtered-coverage.sh: -------------------------------------------------------------------------------- 1 | # To generate the filtered coverage report run the following command: 2 | # 3 | # yarn coverage:report:filtered -vvv 4 | # 5 | rm -fr coverage lcov.info 6 | mkdir -p coverage 7 | forge coverage --report lcov 8 | 9 | # Filter out directories 10 | lcov --remove lcov.info "test/*" "contracts/actions/*" "contracts/migration/*" "contracts/rules/*" -o lcov_filtered.info --ignore-errors inconsistent 11 | 12 | genhtml --ignore-errors inconsistent --ignore-errors corrupt --ignore-errors category --rc derive_function_end_line=0 lcov_filtered.info -o coverage/html --branch-coverage >/dev/null 2>&1 || { echo "Error generating coverage report"; exit 1; } 13 | 14 | rm lcov_filtered.info 15 | 16 | echo -e "\n\n" 17 | echo -e "- - - - -\n" 18 | echo -e "Coverage report generated at:\n" 19 | echo -e "\t $PWD/coverage/html/index.html \n" 20 | echo -e "You can go to your browser and paste that path in the address bar to look at the report.\n" 21 | echo -e "- - - - -\n\n" 22 | -------------------------------------------------------------------------------- /ABIs/LensNativePaymentHelper.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "receive", 4 | "stateMutability": "payable" 5 | }, 6 | { 7 | "type": "function", 8 | "name": "refundNative", 9 | "inputs": [ 10 | { 11 | "name": "to", 12 | "type": "address", 13 | "internalType": "address" 14 | } 15 | ], 16 | "outputs": [], 17 | "stateMutability": "nonpayable" 18 | }, 19 | { 20 | "type": "function", 21 | "name": "transferNative", 22 | "inputs": [ 23 | { 24 | "name": "to", 25 | "type": "address", 26 | "internalType": "address" 27 | }, 28 | { 29 | "name": "amount", 30 | "type": "uint256", 31 | "internalType": "uint256" 32 | } 33 | ], 34 | "outputs": [], 35 | "stateMutability": "nonpayable" 36 | }, 37 | { 38 | "type": "error", 39 | "name": "FailedToTransferNative", 40 | "inputs": [] 41 | }, 42 | { 43 | "type": "error", 44 | "name": "NotEnoughBalance", 45 | "inputs": [] 46 | } 47 | ] -------------------------------------------------------------------------------- /contracts/core/interfaces/IPostRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {CreatePostParams, EditPostParams} from "contracts/core/interfaces/IFeed.sol"; 6 | import {KeyValue} from "contracts/core/types/Types.sol"; 7 | 8 | interface IPostRule { 9 | function configure(bytes32 configSalt, uint256 postId, KeyValue[] calldata ruleParams) external; 10 | 11 | function processCreatePost( 12 | bytes32 configSalt, 13 | uint256 rootPostId, 14 | uint256 postId, 15 | CreatePostParams calldata postParams, 16 | KeyValue[] calldata primitiveParams, 17 | KeyValue[] calldata ruleParams 18 | ) external; 19 | 20 | function processEditPost( 21 | bytes32 configSalt, 22 | uint256 rootPostId, 23 | uint256 postId, 24 | EditPostParams calldata postParams, 25 | KeyValue[] calldata primitiveParams, 26 | KeyValue[] calldata ruleParams 27 | ) external; 28 | } 29 | -------------------------------------------------------------------------------- /script/SetTokenDistributorSigner.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.17; 4 | 5 | import {Script} from "forge-std/Script.sol"; 6 | import {console} from "forge-std/console.sol"; 7 | 8 | import {TokenDistributor} from "contracts/extensions/misc/TokenDistributor.sol"; 9 | 10 | /// @dev Run this script using the following command: 11 | /// forge script script/SetTokenDistributorSigner.s.sol --rpc-url --zksync -vvvvv 12 | /// Then add the --broadcast flag to actually send the transactions to the network. 13 | contract SetTokenDistributorSigner is Script { 14 | function run() external { 15 | TokenDistributor tokenDistributor = TokenDistributor(0x2a705184A6Bb7Dd185E4534d79E441B3edA1082c); 16 | 17 | uint256 pk = vm.envUint("WALLET_PRIVATE_KEY"); 18 | 19 | vm.startBroadcast(pk); 20 | 21 | tokenDistributor.updateSigner(0x801Bc450BAde425745434e9147eFEe16cA169dC2); 22 | 23 | vm.stopBroadcast(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ABIs/IAccountGroupAdditionSettings.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "canBeAddedToGroup", 5 | "inputs": [ 6 | { 7 | "name": "group", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "addedBy", 13 | "type": "address", 14 | "internalType": "address" 15 | }, 16 | { 17 | "name": "params", 18 | "type": "tuple[]", 19 | "internalType": "struct KeyValue[]", 20 | "components": [ 21 | { 22 | "name": "key", 23 | "type": "bytes32", 24 | "internalType": "bytes32" 25 | }, 26 | { 27 | "name": "value", 28 | "type": "bytes", 29 | "internalType": "bytes" 30 | } 31 | ] 32 | } 33 | ], 34 | "outputs": [ 35 | { 36 | "name": "", 37 | "type": "bool", 38 | "internalType": "bool" 39 | } 40 | ], 41 | "stateMutability": "view" 42 | } 43 | ] -------------------------------------------------------------------------------- /parseAbisToBackendFolders.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | 4 | const sourceDir = path.join(__dirname, 'out', '__ABIS__'); 5 | const targetDir = path.join(__dirname, 'out', 'abis'); 6 | 7 | if (!fs.existsSync(targetDir)) { 8 | fs.mkdirSync(targetDir, { recursive: true }); 9 | } 10 | 11 | function consolidateAbis(dir: string) { 12 | const entries = fs.readdirSync(dir, { withFileTypes: true }); 13 | 14 | for (const entry of entries) { 15 | const srcPath = path.join(dir, entry.name); 16 | 17 | if (entry.isDirectory()) { 18 | consolidateAbis(srcPath); 19 | } else if (entry.isFile() && entry.name.endsWith('.abi.json')) { 20 | const targetPath = path.join(targetDir, entry.name); 21 | fs.copyFileSync(srcPath, targetPath); 22 | console.log(`Copied ${srcPath} to ${targetPath}`); 23 | } 24 | } 25 | } 26 | 27 | try { 28 | consolidateAbis(sourceDir); 29 | console.log('ABI consolidation complete!'); 30 | } catch (error) { 31 | console.error('An error occurred:', error); 32 | } 33 | -------------------------------------------------------------------------------- /ABIs/IERC1967.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "AdminChanged", 5 | "inputs": [ 6 | { 7 | "name": "previousAdmin", 8 | "type": "address", 9 | "indexed": false, 10 | "internalType": "address" 11 | }, 12 | { 13 | "name": "newAdmin", 14 | "type": "address", 15 | "indexed": false, 16 | "internalType": "address" 17 | } 18 | ], 19 | "anonymous": false 20 | }, 21 | { 22 | "type": "event", 23 | "name": "BeaconUpgraded", 24 | "inputs": [ 25 | { 26 | "name": "beacon", 27 | "type": "address", 28 | "indexed": true, 29 | "internalType": "address" 30 | } 31 | ], 32 | "anonymous": false 33 | }, 34 | { 35 | "type": "event", 36 | "name": "Upgraded", 37 | "inputs": [ 38 | { 39 | "name": "implementation", 40 | "type": "address", 41 | "indexed": true, 42 | "internalType": "address" 43 | } 44 | ], 45 | "anonymous": false 46 | } 47 | ] -------------------------------------------------------------------------------- /ABIs/ERC1967Upgrade.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "AdminChanged", 5 | "inputs": [ 6 | { 7 | "name": "previousAdmin", 8 | "type": "address", 9 | "indexed": false, 10 | "internalType": "address" 11 | }, 12 | { 13 | "name": "newAdmin", 14 | "type": "address", 15 | "indexed": false, 16 | "internalType": "address" 17 | } 18 | ], 19 | "anonymous": false 20 | }, 21 | { 22 | "type": "event", 23 | "name": "BeaconUpgraded", 24 | "inputs": [ 25 | { 26 | "name": "beacon", 27 | "type": "address", 28 | "indexed": true, 29 | "internalType": "address" 30 | } 31 | ], 32 | "anonymous": false 33 | }, 34 | { 35 | "type": "event", 36 | "name": "Upgraded", 37 | "inputs": [ 38 | { 39 | "name": "implementation", 40 | "type": "address", 41 | "indexed": true, 42 | "internalType": "address" 43 | } 44 | ], 45 | "anonymous": false 46 | } 47 | ] -------------------------------------------------------------------------------- /test/mocks/MockVersionedBeacon.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IVersionedBeacon} from "@core/interfaces/IVersionedBeacon.sol"; 6 | 7 | contract MockVersionedBeacon is IVersionedBeacon { 8 | address _mockedImplementation; 9 | mapping(uint256 => address) internal _mockedImplementations; 10 | 11 | function implementation() external view override returns (address) { 12 | return _mockedImplementation; 13 | } 14 | 15 | function implementation(uint256 implementationVersion) external view override returns (address) { 16 | return _mockedImplementations[implementationVersion]; 17 | } 18 | 19 | function mockImplementation(address mockedImplementation) external { 20 | _mockedImplementation = mockedImplementation; 21 | } 22 | 23 | function mockImplementationForVersion(uint256 version, address mockedImplementation) external { 24 | _mockedImplementations[version] = mockedImplementation; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/extensions/access/PermissionlessAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "contracts/core/interfaces/IAccessControl.sol"; 6 | 7 | contract PermissionlessAccessControl is IAccessControl { 8 | /// @custom:keccak lens.contract.AccessControl.PermissionlessAccessControl 9 | bytes32 constant CONTRACT_TYPE = 0xb5440aae9cc7331e30d1f5f4d93e4b545e210d2a6887783d935991d99a3c4dae; 10 | 11 | function getType() external pure returns (bytes32) { 12 | return CONTRACT_TYPE; 13 | } 14 | 15 | function canChangeAccessControl(address, /* account */ address /* contractAddress */ ) 16 | external 17 | pure 18 | returns (bool) 19 | { 20 | return true; 21 | } 22 | 23 | function hasAccess(address, /* account */ address, /* contractAddress */ uint256 /* permissionId */ ) 24 | external 25 | pure 26 | returns (bool) 27 | { 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /deployments-zk/lensMainnet/contracts/core/upgradeability/EmptyImplementation.sol/EmptyImplementation.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceName": "contracts/core/upgradeability/EmptyImplementation.sol", 3 | "contractName": "EmptyImplementation", 4 | "abi": [], 5 | "bytecode": "0x00000001002001900000000c0000613d0000008001000039000000400010043f0000000001000416000000000001004b0000000c0000c13d00000020010000390000010000100443000001200000044300000005010000410000000f0001042e000000000100001900000010000104300000000e000004320000000f0001042e000000100001043000000000000000000000000000000000000000000000000000000002000000000000000000000000000000400000010000000000000000000000000000000000000000000000000000000000000000000000000000000000", 6 | "entries": [ 7 | { 8 | "constructorArgs": [], 9 | "salt": "0x0000000000000000000000000000000000000000000000000000000000000000", 10 | "deploymentType": "create", 11 | "factoryDeps": [], 12 | "address": "0x90E43C17b6684c877F5caDBf250D0652A8C97967", 13 | "txHash": "0xbcaf614017c41d69e4de3f64a5f8e4b000ead499de182978fd58f682613ace50" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /deployments-zk/lensSepoliaTestnet/contracts/core/upgradeability/EmptyImplementation.sol/EmptyImplementation.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceName": "contracts/core/upgradeability/EmptyImplementation.sol", 3 | "contractName": "EmptyImplementation", 4 | "abi": [], 5 | "bytecode": "0x00000001002001900000000c0000613d0000008001000039000000400010043f0000000001000416000000000001004b0000000c0000c13d00000020010000390000010000100443000001200000044300000005010000410000000f0001042e000000000100001900000010000104300000000e000004320000000f0001042e000000100001043000000000000000000000000000000000000000000000000000000002000000000000000000000000000000400000010000000000000000000000000000000000000000000000000000000000000000000000000000000000", 6 | "entries": [ 7 | { 8 | "constructorArgs": [], 9 | "salt": "0x0000000000000000000000000000000000000000000000000000000000000000", 10 | "deploymentType": "create", 11 | "factoryDeps": [], 12 | "address": "0x284aF6D56a5aA030813cFb44663e0792783c1Ca5", 13 | "txHash": "0x0bcf4cef1423033ad6c9142c5ba44b716e216fdf7aaa63b1b00d7d777114e8ca" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /contracts/core/upgradeability/Initializable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Errors} from "contracts/core/types/Errors.sol"; 6 | 7 | abstract contract Initializable { 8 | // Storage 9 | 10 | struct InitializableStorage { 11 | bool initialized; 12 | } 13 | 14 | /// @custom:keccak lens.storage.Initializable 15 | bytes32 constant STORAGE__INITIALIZABLE = 0xbd2c04feebbff2d29fe1b04edf9a1d94ba7a836bad797bdd99c9e722e172cdd0; 16 | 17 | function $initializableStorage() internal pure returns (InitializableStorage storage _storage) { 18 | assembly { 19 | _storage.slot := STORAGE__INITIALIZABLE 20 | } 21 | } 22 | 23 | modifier initializer() { 24 | require(!$initializableStorage().initialized, Errors.AlreadyInitialized()); 25 | $initializableStorage().initialized = true; 26 | _; 27 | } 28 | 29 | function _disableInitializers() internal virtual { 30 | $initializableStorage().initialized = true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ABIs/MetadataBased.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "getMetadataURI", 5 | "inputs": [ 6 | { 7 | "name": "source", 8 | "type": "address", 9 | "internalType": "address" 10 | } 11 | ], 12 | "outputs": [ 13 | { 14 | "name": "", 15 | "type": "string", 16 | "internalType": "string" 17 | } 18 | ], 19 | "stateMutability": "view" 20 | }, 21 | { 22 | "type": "function", 23 | "name": "getMetadataURI", 24 | "inputs": [], 25 | "outputs": [ 26 | { 27 | "name": "", 28 | "type": "string", 29 | "internalType": "string" 30 | } 31 | ], 32 | "stateMutability": "view" 33 | }, 34 | { 35 | "type": "function", 36 | "name": "setMetadataURI", 37 | "inputs": [ 38 | { 39 | "name": "metadataURI", 40 | "type": "string", 41 | "internalType": "string" 42 | } 43 | ], 44 | "outputs": [], 45 | "stateMutability": "nonpayable" 46 | }, 47 | { 48 | "type": "error", 49 | "name": "NotImplemented", 50 | "inputs": [] 51 | } 52 | ] -------------------------------------------------------------------------------- /contracts/rules/base/OwnableMetadataBasedRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Ownable} from "contracts/core/access/Ownable.sol"; 6 | import {MetadataBased} from "contracts/core/base/MetadataBased.sol"; 7 | 8 | abstract contract OwnableMetadataBasedRule is Ownable, MetadataBased { 9 | event Lens_Rule_MetadataURISet(string metadataURI); 10 | 11 | constructor(address owner, string memory metadataURI) { 12 | _transferOwnership(owner); 13 | _setMetadataURI(metadataURI); 14 | } 15 | 16 | function _initialize(address owner, string memory metadataURI) internal virtual { 17 | _transferOwnership(owner); 18 | _setMetadataURI(metadataURI); 19 | } 20 | 21 | function _emitMetadataURISet(string memory metadataURI, address /* source */ ) internal virtual override { 22 | emit Lens_Rule_MetadataURISet(metadataURI); 23 | } 24 | 25 | function _beforeMetadataURIUpdate(string memory /* metadataURI */ ) internal virtual override onlyOwner { 26 | return; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/extensions/factories/PrimitiveFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "contracts/core/interfaces/IAccessControl.sol"; 6 | import {PermissionlessAccessControl} from "contracts/extensions/access/PermissionlessAccessControl.sol"; 7 | import {Errors} from "contracts/core/types/Errors.sol"; 8 | 9 | contract PrimitiveFactory { 10 | IAccessControl internal immutable TEMPORARY_ACCESS_CONTROL; 11 | address internal immutable PRIMITIVE_BEACON; 12 | address internal immutable PROXY_ADMIN_LOCK; 13 | 14 | address internal immutable LENS_FACTORY; 15 | 16 | modifier onlyLensFactory() { 17 | require(msg.sender == LENS_FACTORY, Errors.AccessDenied()); 18 | _; 19 | } 20 | 21 | constructor(address primitiveBeacon, address proxyAdminLock, address lensFactory) { 22 | TEMPORARY_ACCESS_CONTROL = new PermissionlessAccessControl(); 23 | PRIMITIVE_BEACON = primitiveBeacon; 24 | PROXY_ADMIN_LOCK = proxyAdminLock; 25 | LENS_FACTORY = lensFactory; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /deploy/deployImplementations.ts: -------------------------------------------------------------------------------- 1 | import { deployLensContract, ContractType, ContractInfo } from './lensUtils'; 2 | 3 | export default async function deployImplementations(DEPLOYING_MIGRATION: boolean): Promise { 4 | const contracts: ContractInfo[] = [ 5 | { name: 'AppImpl', contractName: DEPLOYING_MIGRATION ? 'MigrationApp' : 'App', contractType: ContractType.Implementation }, 6 | { name: 'AccountImpl', contractName: DEPLOYING_MIGRATION ? 'MigrationAccount' : 'Account', contractType: ContractType.Implementation }, 7 | { name: 'FeedImpl', contractName: DEPLOYING_MIGRATION ? 'MigrationFeed' : 'Feed', contractType: ContractType.Implementation }, 8 | { name: 'GraphImpl', contractName: DEPLOYING_MIGRATION ? 'MigrationGraph' : 'Graph', contractType: ContractType.Implementation }, 9 | { name: 'GroupImpl', contractName: 'Group', contractType: ContractType.Implementation }, 10 | { name: 'NamespaceImpl', contractName: DEPLOYING_MIGRATION ? 'MigrationNamespace' : 'Namespace', contractType: ContractType.Implementation }, 11 | ]; 12 | 13 | for (const contract of contracts) { 14 | await deployLensContract(contract); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/mocks/MockWrapperCurrency.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {MockCurrency} from "./MockCurrency.sol"; 6 | 7 | contract MockWrapperCurrency is MockCurrency { 8 | constructor(string memory name, string memory symbol) MockCurrency(name, symbol) {} 9 | 10 | function deposit() external payable { 11 | _mint(msg.sender, msg.value); 12 | } 13 | 14 | function mint(address to, uint256 amount) external payable override { 15 | require(msg.value == amount, "MockWrapperCurrency::mint - msg.value != amount"); 16 | _mint(to, amount); 17 | } 18 | 19 | function mint(uint256 amount) external payable override { 20 | require(msg.value == amount, "MockWrapperCurrency::mint - msg.value != amount"); 21 | _mint(msg.sender, amount); 22 | } 23 | 24 | function withdraw(uint256 amount) external { 25 | _burn(msg.sender, amount); 26 | (bool callSucceeded,) = msg.sender.call{value: amount}(""); 27 | require(callSucceeded, "MockWrapperCurrency::withdraw - transfer failed"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /deploy/logAddressFromPk.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | import { getWallet } from './utils'; 3 | 4 | /** 5 | * This script logs the address derived from a private key and its balance. 6 | * 7 | * To run this script: 8 | * 9 | * npx hardhat deploy-zksync --script logAddressFromPk.ts --network 10 | */ 11 | 12 | async function deploy() { 13 | /////////////////// SETUP /////////////////// 14 | 15 | const pk = process.env.SOME_PK; // Set your private key here from .env file 16 | if (!pk) { 17 | throw new Error('Private key not found in environment variables'); 18 | } 19 | 20 | ///////////////////////////////////////////// 21 | 22 | const wallet = getWallet(pk); 23 | const balance = await wallet.getBalance(); 24 | const address = await wallet.getAddress(); 25 | 26 | console.log(`Private key's derived address: ${address}`); 27 | console.log(`Address balance: ${ethers.formatEther(balance)}`); 28 | } 29 | 30 | if (require.main === module) { 31 | deploy() 32 | .then(() => process.exit(0)) 33 | .catch((error) => { 34 | console.error(error); 35 | process.exit(1); 36 | }); 37 | } 38 | 39 | export default deploy; 40 | -------------------------------------------------------------------------------- /ABIs/Ownable.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "owner", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "address" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "transferOwnership", 18 | "inputs": [ 19 | { 20 | "name": "newOwner", 21 | "type": "address", 22 | "internalType": "address" 23 | } 24 | ], 25 | "outputs": [], 26 | "stateMutability": "nonpayable" 27 | }, 28 | { 29 | "type": "event", 30 | "name": "Lens_Ownable_OwnershipTransferred", 31 | "inputs": [ 32 | { 33 | "name": "previousOwner", 34 | "type": "address", 35 | "indexed": true, 36 | "internalType": "address" 37 | }, 38 | { 39 | "name": "newOwner", 40 | "type": "address", 41 | "indexed": true, 42 | "internalType": "address" 43 | } 44 | ], 45 | "anonymous": false 46 | }, 47 | { 48 | "type": "error", 49 | "name": "InvalidMsgSender", 50 | "inputs": [] 51 | } 52 | ] -------------------------------------------------------------------------------- /contracts/core/interfaces/IGraphRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {KeyValue, RuleChange} from "contracts/core/types/Types.sol"; 6 | 7 | interface IGraphRule { 8 | function configure(bytes32 configSalt, KeyValue[] calldata ruleParams) external; 9 | 10 | function processFollow( 11 | bytes32 configSalt, 12 | address originalMsgSender, 13 | address followerAccount, 14 | address accountToFollow, 15 | KeyValue[] calldata primitiveParams, 16 | KeyValue[] calldata ruleParams 17 | ) external; 18 | 19 | function processUnfollow( 20 | bytes32 configSalt, 21 | address originalMsgSender, 22 | address followerAccount, 23 | address accountToUnfollow, 24 | KeyValue[] calldata primitiveParams, 25 | KeyValue[] calldata ruleParams 26 | ) external; 27 | 28 | function processFollowRuleChanges( 29 | bytes32 configSalt, 30 | address account, 31 | RuleChange[] calldata ruleChanges, 32 | KeyValue[] calldata ruleParams 33 | ) external; 34 | } 35 | -------------------------------------------------------------------------------- /test/mocks/MockNft.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 6 | 7 | contract MockNft is ERC721 { 8 | constructor(string memory name, string memory symbol) ERC721(name, symbol) {} 9 | 10 | function mint(address to, uint256 tokenId) external { 11 | _mint(to, tokenId); 12 | } 13 | 14 | function safeMint(address to, uint256 tokenId) external { 15 | _safeMint(to, tokenId); 16 | } 17 | 18 | function safeMint(address to, uint256 tokenId, bytes memory _data) external { 19 | _safeMint(to, tokenId, _data); 20 | } 21 | 22 | function mint(uint256 tokenId) external { 23 | _mint(msg.sender, tokenId); 24 | } 25 | 26 | function safeMint(uint256 tokenId) external { 27 | _safeMint(msg.sender, tokenId); 28 | } 29 | 30 | function safeMint(uint256 tokenId, bytes memory _data) external { 31 | _safeMint(msg.sender, tokenId, _data); 32 | } 33 | 34 | function burn(uint256 tokenId) external { 35 | _burn(tokenId); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | ### START TEMPORAL FIX FOR COVERAGE - PART I ### 2 | mv 'contracts/migration/WhitelistedAddresses.sol' 'contracts/migration/WhitelistedAddresses.sol.bak' 3 | echo 'pragma solidity ^0.8.26; import "contracts/core/types/Errors.sol"; library WhitelistedAddresses { function requireWhitelisted(address account) internal pure { require(isWhitelisted(account), Errors.InvalidMsgSender()); } function isWhitelisted(address account) internal pure returns (bool) { return account == address(0x76Ba7483A15F4bA358D38eC14B80bCeB7193A190); } }' > 'contracts/migration/WhitelistedAddresses.sol' 4 | ### END TEMPORAL FIX FOR COVERAGE - PART I ### 5 | 6 | rm -fr coverage lcov.info 7 | mkdir -p coverage 8 | forge coverage --report lcov 9 | genhtml --ignore-errors inconsistent --ignore-errors corrupt --ignore-errors category --rc derive_function_end_line=0 lcov.info -o coverage/html --branch-coverage >/dev/null 2>&1 || { echo "Error generating coverage report"; exit 1; } 10 | 11 | ### START TEMPORAL FIX FOR COVERAGE - PART II ### 12 | rm 'contracts/migration/WhitelistedAddresses.sol' 13 | mv 'contracts/migration/WhitelistedAddresses.sol.bak' 'contracts/migration/WhitelistedAddresses.sol' 14 | ### END TEMPORAL FIX FOR COVERAGE - PART II ### 15 | -------------------------------------------------------------------------------- /ABIs/stdStorageSafe.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "name": "SlotFound", 5 | "inputs": [ 6 | { 7 | "name": "who", 8 | "type": "address", 9 | "indexed": false, 10 | "internalType": "address" 11 | }, 12 | { 13 | "name": "fsig", 14 | "type": "bytes4", 15 | "indexed": false, 16 | "internalType": "bytes4" 17 | }, 18 | { 19 | "name": "keysHash", 20 | "type": "bytes32", 21 | "indexed": false, 22 | "internalType": "bytes32" 23 | }, 24 | { 25 | "name": "slot", 26 | "type": "uint256", 27 | "indexed": false, 28 | "internalType": "uint256" 29 | } 30 | ], 31 | "anonymous": false 32 | }, 33 | { 34 | "type": "event", 35 | "name": "WARNING_UninitedSlot", 36 | "inputs": [ 37 | { 38 | "name": "who", 39 | "type": "address", 40 | "indexed": false, 41 | "internalType": "address" 42 | }, 43 | { 44 | "name": "slot", 45 | "type": "uint256", 46 | "indexed": false, 47 | "internalType": "uint256" 48 | } 49 | ], 50 | "anonymous": false 51 | } 52 | ] -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | concurrency: 4 | group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}' 5 | cancel-in-progress: true 6 | 7 | on: 8 | push: 9 | branches: 10 | - master 11 | - development 12 | pull_request: 13 | branches: 14 | - master 15 | - development 16 | 17 | permissions: 18 | contents: read 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v3 26 | - name: Setup Node.js 27 | uses: actions/setup-node@v3 28 | with: 29 | node-version: 18 30 | - name: Install ZKsync's Foundry 31 | uses: dutterbutter/foundry-zksync-toolchain@v1 32 | - name: Output Foundry Version 33 | run: forge --version 34 | - name: Install Dependencies 35 | run: yarn 36 | - name: Compile Project 37 | run: npx hardhat compile 38 | - name: Verify Hashed Constants 39 | run: node verify_hashed_constants.js 40 | - name: Run Tests 41 | run: forge test -vvv 42 | - name: Run Tests - ZKsync Environment 43 | run: DAPP_TEST_FUZZ_RUNS=5 forge test --zksync -vvv --suppress-warnings assemblycreate 44 | -------------------------------------------------------------------------------- /ABIs/ILensFees.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "getLensFeesData", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "tuple", 10 | "internalType": "struct LensFeesData", 11 | "components": [ 12 | { 13 | "name": "treasuryAddress", 14 | "type": "address", 15 | "internalType": "address" 16 | }, 17 | { 18 | "name": "treasuryFeeBps", 19 | "type": "uint16", 20 | "internalType": "uint16" 21 | } 22 | ] 23 | } 24 | ], 25 | "stateMutability": "view" 26 | }, 27 | { 28 | "type": "function", 29 | "name": "getTreasuryAddress", 30 | "inputs": [], 31 | "outputs": [ 32 | { 33 | "name": "", 34 | "type": "address", 35 | "internalType": "address" 36 | } 37 | ], 38 | "stateMutability": "view" 39 | }, 40 | { 41 | "type": "function", 42 | "name": "getTreasuryFeeBps", 43 | "inputs": [], 44 | "outputs": [ 45 | { 46 | "name": "", 47 | "type": "uint16", 48 | "internalType": "uint16" 49 | } 50 | ], 51 | "stateMutability": "view" 52 | } 53 | ] -------------------------------------------------------------------------------- /contracts/core/interfaces/IGroupRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {KeyValue} from "contracts/core/types/Types.sol"; 6 | 7 | interface IGroupRule { 8 | function configure(bytes32 configSalt, KeyValue[] calldata ruleParams) external; 9 | 10 | function processAddition( 11 | bytes32 configSalt, 12 | address originalMsgSender, 13 | address account, 14 | KeyValue[] calldata primitiveParams, 15 | KeyValue[] calldata ruleParams 16 | ) external; 17 | 18 | function processRemoval( 19 | bytes32 configSalt, 20 | address originalMsgSender, 21 | address account, 22 | KeyValue[] calldata primitiveParams, 23 | KeyValue[] calldata ruleParams 24 | ) external; 25 | 26 | function processJoining( 27 | bytes32 configSalt, 28 | address account, 29 | KeyValue[] calldata primitiveParams, 30 | KeyValue[] calldata ruleParams 31 | ) external; 32 | 33 | function processLeaving( 34 | bytes32 configSalt, 35 | address account, 36 | KeyValue[] calldata primitiveParams, 37 | KeyValue[] calldata ruleParams 38 | ) external; 39 | } 40 | -------------------------------------------------------------------------------- /test/helpers/MockAccessControlLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {AccessControlled} from "contracts/core/access/AccessControlled.sol"; 6 | import {MockAccessControl} from "test/mocks/MockAccessControl.sol"; 7 | 8 | library MockAccessControlLib { 9 | function mockAccess( 10 | address accessControlledContract, 11 | address account, 12 | address contractAddress, 13 | uint256 permissionId, 14 | bool access 15 | ) internal { 16 | _getMockAccessControl(accessControlledContract).mockAccess(account, contractAddress, permissionId, access); 17 | } 18 | 19 | function mockCanChangeAccessControl( 20 | address accessControlledContract, 21 | address account, 22 | address contractAddress, 23 | bool canChange 24 | ) internal { 25 | _getMockAccessControl(accessControlledContract).mockCanChangeAccessControl(account, contractAddress, canChange); 26 | } 27 | 28 | function _getMockAccessControl(address accessControlledContract) private view returns (MockAccessControl) { 29 | return MockAccessControl(address(AccessControlled(accessControlledContract).getAccessControl())); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/actions/post/base/OwnableMetadataBasedPostAction.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Ownable} from "contracts/core/access/Ownable.sol"; 6 | import {MetadataBased} from "contracts/core/base/MetadataBased.sol"; 7 | import {BasePostAction} from "contracts/actions/post/base/BasePostAction.sol"; 8 | 9 | abstract contract OwnableMetadataBasedPostAction is BasePostAction, Ownable, MetadataBased { 10 | event Lens_PostAction_MetadataURISet(string metadataURI); 11 | 12 | constructor(address actionHub, address owner, string memory metadataURI) BasePostAction(actionHub) { 13 | _transferOwnership(owner); 14 | _setMetadataURI(metadataURI); 15 | } 16 | 17 | function _initialize(address owner, string memory metadataURI) internal { 18 | _transferOwnership(owner); 19 | _setMetadataURI(metadataURI); 20 | } 21 | 22 | function _emitMetadataURISet(string memory metadataURI, address /* source */ ) internal virtual override { 23 | emit Lens_PostAction_MetadataURISet(metadataURI); 24 | } 25 | 26 | function _beforeMetadataURIUpdate(string memory /* metadataURI */ ) internal virtual override onlyOwner { 27 | return; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/helpers/FuzzZkTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import "forge-std/Test.sol"; 6 | import "./ZkTest.sol"; 7 | 8 | contract FuzzZkTest is ZkTest { 9 | function _boundAmount(uint256 amount) internal pure returns (uint256) { 10 | // Bound amount (0, 2^95], as test contract's native balance is 2^96, and vm.deal has issues in zksync foundry 11 | return bound(amount, 1, 1 << 95); 12 | } 13 | 14 | function _boundAmountAllowZero(uint256 amount) internal pure returns (uint256) { 15 | // Bound amount [0, 2^95], as test contract's native balance is 2^96, and vm.deal has issues in zksync foundry 16 | return bound(amount, 0, 1 << 95); 17 | } 18 | 19 | function _assumeEOA(address someAddress) internal view { 20 | assumeNotForgeAddress(someAddress); 21 | vm.assume(someAddress.code.length == 0); 22 | vm.assume(uint160(someAddress) > type(uint16).max); // skip system contracts 23 | } 24 | 25 | function _boundBlockTimestamp(uint256 blockTimestamp) internal pure returns (uint256) { 26 | // Bound block timestamp (0, 2^64), as vm.warp expects a timestamp smaller than 2^64 27 | return bound(blockTimestamp, 1, 1 << 64 - 1); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/actions/account/base/OwnableMetadataBasedAccountAction.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Ownable} from "contracts/core/access/Ownable.sol"; 6 | import {MetadataBased} from "contracts/core/base/MetadataBased.sol"; 7 | import {BaseAccountAction} from "contracts/actions/account/base/BaseAccountAction.sol"; 8 | 9 | abstract contract OwnableMetadataBasedAccountAction is BaseAccountAction, Ownable, MetadataBased { 10 | event Lens_AccountAction_MetadataURISet(string metadataURI); 11 | 12 | constructor(address actionHub, address owner, string memory metadataURI) BaseAccountAction(actionHub) { 13 | _transferOwnership(owner); 14 | _setMetadataURI(metadataURI); 15 | } 16 | 17 | function _initialize(address owner, string memory metadataURI) internal { 18 | _transferOwnership(owner); 19 | _setMetadataURI(metadataURI); 20 | } 21 | 22 | function _emitMetadataURISet(string memory metadataURI, address /* source */ ) internal virtual override { 23 | emit Lens_AccountAction_MetadataURISet(metadataURI); 24 | } 25 | 26 | function _beforeMetadataURIUpdate(string memory /* metadataURI */ ) internal virtual override onlyOwner { 27 | return; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/extensions/fees/LensFees.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | struct LensFeesData { 6 | address treasuryAddress; 7 | uint16 treasuryFeeBps; 8 | } 9 | 10 | interface ILensFees { 11 | function getTreasuryAddress() external view returns (address); 12 | 13 | function getTreasuryFeeBps() external view returns (uint16); 14 | 15 | function getLensFeesData() external view returns (LensFeesData memory); 16 | } 17 | 18 | contract LensFees is ILensFees { 19 | address internal immutable LENS_TREASURY_ADDRESS; 20 | uint16 internal immutable LENS_TREASURY_FEE_BPS; 21 | 22 | constructor(address treasuryAddress, uint16 treasuryFeeBps) { 23 | LENS_TREASURY_ADDRESS = treasuryAddress; 24 | LENS_TREASURY_FEE_BPS = treasuryFeeBps; 25 | } 26 | 27 | function getTreasuryAddress() external view override returns (address) { 28 | return LENS_TREASURY_ADDRESS; 29 | } 30 | 31 | function getTreasuryFeeBps() external view override returns (uint16) { 32 | return LENS_TREASURY_FEE_BPS; 33 | } 34 | 35 | function getLensFeesData() external view override returns (LensFeesData memory) { 36 | return LensFeesData(LENS_TREASURY_ADDRESS, LENS_TREASURY_FEE_BPS); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/core/interfaces/IAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | interface IAccessControl { 6 | /** 7 | * Returns the implementation type of the access control. 8 | */ 9 | function getType() external view returns (bytes32); 10 | 11 | /** 12 | * Returns true if the account is allowed to change the access control in the given contract address, false if not. 13 | * 14 | * @param account The account to check if is allowed to change the access control. 15 | * @param contractAddress The address where the access control is being changed. 16 | */ 17 | function canChangeAccessControl(address account, address contractAddress) external view returns (bool); 18 | 19 | /** 20 | * Returns true if the account has granted access to the specified permission, false otherwise. 21 | * This function MUST NOT revert. Instead, return false. 22 | * 23 | * @param account The account to check if has access to a permission. 24 | * @param contractAddress The address where the permission is queried. 25 | * @param permissionId The ID of the permission. 26 | */ 27 | function hasAccess(address account, address contractAddress, uint256 permissionId) external view returns (bool); 28 | } 29 | -------------------------------------------------------------------------------- /ABIs/AccessControlFactory.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "lock", 7 | "type": "address", 8 | "internalType": "address" 9 | } 10 | ], 11 | "stateMutability": "nonpayable" 12 | }, 13 | { 14 | "type": "function", 15 | "name": "deployOwnerAdminOnlyAccessControl", 16 | "inputs": [ 17 | { 18 | "name": "owner", 19 | "type": "address", 20 | "internalType": "address" 21 | }, 22 | { 23 | "name": "admins", 24 | "type": "address[]", 25 | "internalType": "address[]" 26 | } 27 | ], 28 | "outputs": [ 29 | { 30 | "name": "", 31 | "type": "address", 32 | "internalType": "contract IRoleBasedAccessControl" 33 | } 34 | ], 35 | "stateMutability": "nonpayable" 36 | }, 37 | { 38 | "type": "event", 39 | "name": "Lens_AccessControlFactory_OwnerAdminDeployment", 40 | "inputs": [ 41 | { 42 | "name": "accessControl", 43 | "type": "address", 44 | "indexed": true, 45 | "internalType": "address" 46 | }, 47 | { 48 | "name": "owner", 49 | "type": "address", 50 | "indexed": false, 51 | "internalType": "address" 52 | } 53 | ], 54 | "anonymous": false 55 | } 56 | ] -------------------------------------------------------------------------------- /ABIs/TrustBasedRule.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "setTrust", 5 | "inputs": [ 6 | { 7 | "name": "target", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "isTrusted", 13 | "type": "bool", 14 | "internalType": "bool" 15 | } 16 | ], 17 | "outputs": [], 18 | "stateMutability": "nonpayable" 19 | }, 20 | { 21 | "type": "event", 22 | "name": "Lens_Rule_Trusted", 23 | "inputs": [ 24 | { 25 | "name": "account", 26 | "type": "address", 27 | "indexed": true, 28 | "internalType": "address" 29 | }, 30 | { 31 | "name": "trustedAddress", 32 | "type": "address", 33 | "indexed": true, 34 | "internalType": "address" 35 | } 36 | ], 37 | "anonymous": false 38 | }, 39 | { 40 | "type": "event", 41 | "name": "Lens_Rule_Untrusted", 42 | "inputs": [ 43 | { 44 | "name": "account", 45 | "type": "address", 46 | "indexed": true, 47 | "internalType": "address" 48 | }, 49 | { 50 | "name": "untrustedAddress", 51 | "type": "address", 52 | "indexed": true, 53 | "internalType": "address" 54 | } 55 | ], 56 | "anonymous": false 57 | } 58 | ] -------------------------------------------------------------------------------- /contracts/actions/base/BaseAction.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {UNIVERSAL_ACTION_MAGIC_VALUE} from "contracts/extensions/actions/ActionHub.sol"; 6 | import {Errors} from "contracts/core/types/Errors.sol"; 7 | 8 | abstract contract BaseAction { 9 | address immutable ACTION_HUB; 10 | 11 | /// @custom:keccak lens.storage.Action.configured 12 | bytes32 constant STORAGE__ACTION_CONFIGURED = 0x852bead036b7ef35b8026346140cc688bafe817a6c3491812e6d994b1bcda6d9; 13 | 14 | modifier onlyActionHub() { 15 | require(msg.sender == ACTION_HUB, Errors.InvalidMsgSender()); 16 | _; 17 | } 18 | 19 | constructor(address actionHub) { 20 | ACTION_HUB = actionHub; 21 | } 22 | 23 | function _configureUniversalAction(address originalMsgSender) internal onlyActionHub returns (bytes memory) { 24 | bool configured; 25 | assembly { 26 | configured := sload(STORAGE__ACTION_CONFIGURED) 27 | } 28 | require(!configured, Errors.RedundantStateChange()); 29 | require(originalMsgSender == address(0), Errors.InvalidParameter()); 30 | assembly { 31 | sstore(STORAGE__ACTION_CONFIGURED, 1) 32 | } 33 | return abi.encode(UNIVERSAL_ACTION_MAGIC_VALUE); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/core/types/Types.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | struct KeyValue { 6 | bytes32 key; 7 | bytes value; 8 | } 9 | 10 | struct Rule { 11 | address ruleAddress; 12 | bytes32 configSalt; 13 | } 14 | 15 | struct RuleChange { 16 | address ruleAddress; 17 | bytes32 configSalt; 18 | RuleConfigurationChange configurationChanges; 19 | RuleSelectorChange[] selectorChanges; 20 | } 21 | 22 | struct RuleConfigurationChange { 23 | bool configure; 24 | KeyValue[] ruleParams; 25 | } 26 | 27 | struct RuleSelectorChange { 28 | bytes4 ruleSelector; 29 | bool isRequired; 30 | bool enabled; 31 | } 32 | 33 | struct RuleProcessingParams { 34 | address ruleAddress; 35 | bytes32 configSalt; 36 | KeyValue[] ruleParams; 37 | } 38 | 39 | struct SourceStamp { 40 | address source; 41 | address originalMsgSender; // Who called the validator to execute some action with a source (e.g. Account) 42 | address validator; // Who is calling the source to validate the source stamp (e.g. Lens Primitives) 43 | uint256 nonce; 44 | uint256 deadline; 45 | bytes signature; 46 | } 47 | 48 | struct RecipientData { 49 | address recipient; 50 | uint16 splitBps; // In Basis Points, each Basis Point represents 0.01% 51 | } 52 | -------------------------------------------------------------------------------- /contracts/core/interfaces/IFeedRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {CreatePostParams, EditPostParams} from "contracts/core/interfaces/IFeed.sol"; 6 | import {KeyValue, RuleChange} from "contracts/core/types/Types.sol"; 7 | 8 | interface IFeedRule { 9 | function configure(bytes32 configSalt, KeyValue[] calldata ruleParams) external; 10 | 11 | function processCreatePost( 12 | bytes32 configSalt, 13 | uint256 postId, 14 | CreatePostParams calldata postParams, 15 | KeyValue[] calldata primitiveParams, 16 | KeyValue[] calldata ruleParams 17 | ) external; 18 | 19 | function processEditPost( 20 | bytes32 configSalt, 21 | uint256 postId, 22 | EditPostParams calldata postParams, 23 | KeyValue[] calldata primitiveParams, 24 | KeyValue[] calldata ruleParams 25 | ) external; 26 | 27 | function processDeletePost( 28 | bytes32 configSalt, 29 | uint256 postId, 30 | KeyValue[] calldata primitiveParams, 31 | KeyValue[] calldata ruleParams 32 | ) external; 33 | 34 | function processPostRuleChanges( 35 | bytes32 configSalt, 36 | uint256 postId, 37 | RuleChange[] calldata ruleChanges, 38 | KeyValue[] calldata ruleParams 39 | ) external; 40 | } 41 | -------------------------------------------------------------------------------- /ABIs/ILensCreate2.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "createTransparentUpgradeableProxy", 5 | "inputs": [ 6 | { 7 | "name": "salt", 8 | "type": "bytes32", 9 | "internalType": "bytes32" 10 | }, 11 | { 12 | "name": "implementation", 13 | "type": "address", 14 | "internalType": "address" 15 | }, 16 | { 17 | "name": "proxyAdmin", 18 | "type": "address", 19 | "internalType": "address" 20 | }, 21 | { 22 | "name": "initializerCall", 23 | "type": "bytes", 24 | "internalType": "bytes" 25 | }, 26 | { 27 | "name": "expectedAddress", 28 | "type": "address", 29 | "internalType": "address" 30 | } 31 | ], 32 | "outputs": [ 33 | { 34 | "name": "", 35 | "type": "address", 36 | "internalType": "address" 37 | } 38 | ], 39 | "stateMutability": "nonpayable" 40 | }, 41 | { 42 | "type": "function", 43 | "name": "getAddress", 44 | "inputs": [ 45 | { 46 | "name": "salt", 47 | "type": "bytes32", 48 | "internalType": "bytes32" 49 | } 50 | ], 51 | "outputs": [ 52 | { 53 | "name": "", 54 | "type": "address", 55 | "internalType": "address" 56 | } 57 | ], 58 | "stateMutability": "view" 59 | } 60 | ] -------------------------------------------------------------------------------- /contracts/core/libraries/KeyValueStorageLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {KeyValue} from "contracts/core/types/Types.sol"; 6 | 7 | library KeyValueStorageLib { 8 | function set(mapping(bytes32 => bytes) storage _keyValueStorage, KeyValue memory keyValueToSet) 9 | internal 10 | returns (bool) 11 | { 12 | return _setKeyValueToStorage(_keyValueStorage, keyValueToSet); 13 | } 14 | 15 | function set(mapping(bytes32 => bytes) storage _keyValueStorage, KeyValue[] calldata keyValuesToSet) 16 | internal 17 | returns (bool[] memory) 18 | { 19 | bool[] memory werePreviousValuesSet = new bool[](keyValuesToSet.length); 20 | for (uint256 i = 0; i < keyValuesToSet.length; i++) { 21 | werePreviousValuesSet[i] = _setKeyValueToStorage(_keyValueStorage, keyValuesToSet[i]); 22 | } 23 | return werePreviousValuesSet; 24 | } 25 | 26 | function _setKeyValueToStorage(mapping(bytes32 => bytes) storage _keyValueStorage, KeyValue memory keyValueToSet) 27 | internal 28 | returns (bool) 29 | { 30 | bool wasPreviousValueSet = _keyValueStorage[keyValueToSet.key].length != 0; 31 | _keyValueStorage[keyValueToSet.key] = keyValueToSet.value; 32 | return wasPreviousValueSet; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/mocks/MockUniversal.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | contract MockUniversal { 6 | bool _revertNextCall; 7 | string _errorMessage; 8 | 9 | function mockToSucceedOnNextCall() external { 10 | _revertNextCall = false; 11 | } 12 | 13 | function mockToRevertOnNextCall() external { 14 | _revertNextCall = true; 15 | } 16 | 17 | function mockToRevertOnNextCallWith(bytes4 errorSelector) external { 18 | _revertNextCall = true; 19 | bytes memory errorSelectorAsBytes = new bytes(4); 20 | for (uint256 i = 0; i < 4; i++) { 21 | errorSelectorAsBytes[i] = bytes4(errorSelector)[i]; 22 | } 23 | _errorMessage = string(errorSelectorAsBytes); 24 | } 25 | 26 | function mockToRevertOnNextCallWith(string memory errorMessage) external { 27 | _revertNextCall = true; 28 | _errorMessage = errorMessage; 29 | } 30 | 31 | fallback() external { 32 | if (_revertNextCall) { 33 | delete _revertNextCall; 34 | if (bytes(_errorMessage).length == 0) { 35 | revert(); 36 | } else { 37 | string memory errorMessage = _errorMessage; 38 | delete _errorMessage; 39 | revert(errorMessage); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ABIs/LensUsernameTokenURIProvider.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [], 5 | "stateMutability": "nonpayable" 6 | }, 7 | { 8 | "type": "function", 9 | "name": "_toLowercase", 10 | "inputs": [ 11 | { 12 | "name": "str", 13 | "type": "string", 14 | "internalType": "string" 15 | } 16 | ], 17 | "outputs": [ 18 | { 19 | "name": "", 20 | "type": "string", 21 | "internalType": "string" 22 | } 23 | ], 24 | "stateMutability": "pure" 25 | }, 26 | { 27 | "type": "function", 28 | "name": "tokenURI", 29 | "inputs": [ 30 | { 31 | "name": "tokenId", 32 | "type": "uint256", 33 | "internalType": "uint256" 34 | } 35 | ], 36 | "outputs": [ 37 | { 38 | "name": "", 39 | "type": "string", 40 | "internalType": "string" 41 | } 42 | ], 43 | "stateMutability": "view" 44 | }, 45 | { 46 | "type": "event", 47 | "name": "Lens_Contract_Deployed", 48 | "inputs": [ 49 | { 50 | "name": "contractType", 51 | "type": "string", 52 | "indexed": false, 53 | "internalType": "string" 54 | }, 55 | { 56 | "name": "flavour", 57 | "type": "string", 58 | "indexed": false, 59 | "internalType": "string" 60 | } 61 | ], 62 | "anonymous": false 63 | } 64 | ] -------------------------------------------------------------------------------- /contracts/core/base/ExtraDataBased.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.26; 3 | 4 | import {KeyValue} from "contracts/core/types/Types.sol"; 5 | import {ExtraStorageBased} from "contracts/core/base/ExtraStorageBased.sol"; 6 | 7 | abstract contract ExtraDataBased is ExtraStorageBased { 8 | function _emitExtraDataAddedEvent(KeyValue calldata extraDataAdded) internal virtual; 9 | 10 | function _emitExtraDataUpdatedEvent(KeyValue calldata extraDataUpdated) internal virtual; 11 | 12 | function _emitExtraDataRemovedEvent(KeyValue calldata extraDataRemoved) internal virtual; 13 | 14 | function _setExtraData(KeyValue[] calldata extraDataToSet) internal { 15 | for (uint256 i = 0; i < extraDataToSet.length; i++) { 16 | bool hadAValueSetBefore = _setExtraStorage_Self(extraDataToSet[i]); 17 | bool isNewValueEmpty = extraDataToSet[i].value.length == 0; 18 | if (hadAValueSetBefore) { 19 | if (isNewValueEmpty) { 20 | _emitExtraDataRemovedEvent(extraDataToSet[i]); 21 | } else { 22 | _emitExtraDataUpdatedEvent(extraDataToSet[i]); 23 | } 24 | } else if (!isNewValueEmpty) { 25 | _emitExtraDataAddedEvent(extraDataToSet[i]); 26 | } 27 | } 28 | } 29 | 30 | function _getExtraData(bytes32 key) internal view returns (bytes memory) { 31 | return _getExtraStorage_Self(key); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/core/interfaces/INamespaceRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {KeyValue} from "contracts/core/types/Types.sol"; 6 | 7 | interface INamespaceRule { 8 | function configure(bytes32 configSalt, KeyValue[] calldata ruleParams) external; 9 | 10 | function processCreation( 11 | bytes32 configSalt, 12 | address originalMsgSender, 13 | address account, 14 | string calldata username, 15 | KeyValue[] calldata primitiveParams, 16 | KeyValue[] calldata ruleParams 17 | ) external; 18 | 19 | function processRemoval( 20 | bytes32 configSalt, 21 | address originalMsgSender, 22 | string calldata username, 23 | KeyValue[] calldata primitiveParams, 24 | KeyValue[] calldata ruleParams 25 | ) external; 26 | 27 | function processAssigning( 28 | bytes32 configSalt, 29 | address originalMsgSender, 30 | address account, 31 | string calldata username, 32 | KeyValue[] calldata primitiveParams, 33 | KeyValue[] calldata ruleParams 34 | ) external; 35 | 36 | function processUnassigning( 37 | bytes32 configSalt, 38 | address originalMsgSender, 39 | address account, 40 | string calldata username, 41 | KeyValue[] calldata primitiveParams, 42 | KeyValue[] calldata ruleParams 43 | ) external; 44 | } 45 | -------------------------------------------------------------------------------- /ABIs/PermissionlessAccessControl.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "canChangeAccessControl", 5 | "inputs": [ 6 | { 7 | "name": "", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "", 13 | "type": "address", 14 | "internalType": "address" 15 | } 16 | ], 17 | "outputs": [ 18 | { 19 | "name": "", 20 | "type": "bool", 21 | "internalType": "bool" 22 | } 23 | ], 24 | "stateMutability": "pure" 25 | }, 26 | { 27 | "type": "function", 28 | "name": "getType", 29 | "inputs": [], 30 | "outputs": [ 31 | { 32 | "name": "", 33 | "type": "bytes32", 34 | "internalType": "bytes32" 35 | } 36 | ], 37 | "stateMutability": "pure" 38 | }, 39 | { 40 | "type": "function", 41 | "name": "hasAccess", 42 | "inputs": [ 43 | { 44 | "name": "", 45 | "type": "address", 46 | "internalType": "address" 47 | }, 48 | { 49 | "name": "", 50 | "type": "address", 51 | "internalType": "address" 52 | }, 53 | { 54 | "name": "", 55 | "type": "uint256", 56 | "internalType": "uint256" 57 | } 58 | ], 59 | "outputs": [ 60 | { 61 | "name": "", 62 | "type": "bool", 63 | "internalType": "bool" 64 | } 65 | ], 66 | "stateMutability": "pure" 67 | } 68 | ] -------------------------------------------------------------------------------- /contracts/extensions/fees/LensRulePaymentHandler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {LensPaymentHandler} from "contracts/extensions/fees/LensPaymentHandler.sol"; 6 | import {ILensNativePaymentHelper} from "contracts/extensions/fees/LensNativePaymentHelper.sol"; 7 | import {LENS_CREATE_2_ADDRESS, ILensCreate2} from "contracts/core/upgradeability/LensCreate2.sol"; 8 | import {CONTRACT__LENS_NATIVE_PAYMENT_HELPER, NATIVE_TOKEN} from "contracts/core/types/Constants.sol"; 9 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 10 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 11 | 12 | abstract contract LensRulePaymentHandler is LensPaymentHandler { 13 | using SafeERC20 for IERC20; 14 | 15 | ILensNativePaymentHelper private immutable LENS_NATIVE_PAYMENT_HELPER; 16 | 17 | constructor() { 18 | LENS_NATIVE_PAYMENT_HELPER = ILensNativePaymentHelper( 19 | ILensCreate2(LENS_CREATE_2_ADDRESS).getAddress(CONTRACT__LENS_NATIVE_PAYMENT_HELPER) 20 | ); 21 | } 22 | 23 | function _sendToken(address token, address payer, address recipient, uint256 amount) internal virtual override { 24 | if (token == NATIVE_TOKEN) { 25 | LENS_NATIVE_PAYMENT_HELPER.transferNative(recipient, amount); 26 | } else { 27 | IERC20(token).safeTransferFrom(payer, recipient, amount); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/mocks/MockAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "@core/interfaces/IAccessControl.sol"; 6 | 7 | contract MockAccessControl is IAccessControl { 8 | mapping(address => mapping(address => bool)) _mockedCanChangeAccessControl; 9 | mapping(address => mapping(address => mapping(uint256 => bool))) _mockedAccess; 10 | 11 | function getType() external pure override returns (bytes32) { 12 | return keccak256("lens.contract.AccessControl.MockAccessControl"); 13 | } 14 | 15 | function canChangeAccessControl(address account, address contractAddress) external view override returns (bool) { 16 | return _mockedCanChangeAccessControl[account][contractAddress]; 17 | } 18 | 19 | function hasAccess(address account, address contractAddress, uint256 permissionId) 20 | external 21 | view 22 | override 23 | returns (bool) 24 | { 25 | return _mockedAccess[account][contractAddress][permissionId]; 26 | } 27 | 28 | function mockCanChangeAccessControl(address account, address contractAddress, bool canChange) external { 29 | _mockedCanChangeAccessControl[account][contractAddress] = canChange; 30 | } 31 | 32 | function mockAccess(address account, address contractAddress, uint256 permissionId, bool access) external { 33 | _mockedAccess[account][contractAddress][permissionId] = access; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ABIs/ISource.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "getTreasury", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "address" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "validateSource", 18 | "inputs": [ 19 | { 20 | "name": "sourceStamp", 21 | "type": "tuple", 22 | "internalType": "struct SourceStamp", 23 | "components": [ 24 | { 25 | "name": "source", 26 | "type": "address", 27 | "internalType": "address" 28 | }, 29 | { 30 | "name": "originalMsgSender", 31 | "type": "address", 32 | "internalType": "address" 33 | }, 34 | { 35 | "name": "validator", 36 | "type": "address", 37 | "internalType": "address" 38 | }, 39 | { 40 | "name": "nonce", 41 | "type": "uint256", 42 | "internalType": "uint256" 43 | }, 44 | { 45 | "name": "deadline", 46 | "type": "uint256", 47 | "internalType": "uint256" 48 | }, 49 | { 50 | "name": "signature", 51 | "type": "bytes", 52 | "internalType": "bytes" 53 | } 54 | ] 55 | } 56 | ], 57 | "outputs": [], 58 | "stateMutability": "nonpayable" 59 | } 60 | ] -------------------------------------------------------------------------------- /ABIs/IAccessControl.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "canChangeAccessControl", 5 | "inputs": [ 6 | { 7 | "name": "account", 8 | "type": "address", 9 | "internalType": "address" 10 | }, 11 | { 12 | "name": "contractAddress", 13 | "type": "address", 14 | "internalType": "address" 15 | } 16 | ], 17 | "outputs": [ 18 | { 19 | "name": "", 20 | "type": "bool", 21 | "internalType": "bool" 22 | } 23 | ], 24 | "stateMutability": "view" 25 | }, 26 | { 27 | "type": "function", 28 | "name": "getType", 29 | "inputs": [], 30 | "outputs": [ 31 | { 32 | "name": "", 33 | "type": "bytes32", 34 | "internalType": "bytes32" 35 | } 36 | ], 37 | "stateMutability": "view" 38 | }, 39 | { 40 | "type": "function", 41 | "name": "hasAccess", 42 | "inputs": [ 43 | { 44 | "name": "account", 45 | "type": "address", 46 | "internalType": "address" 47 | }, 48 | { 49 | "name": "contractAddress", 50 | "type": "address", 51 | "internalType": "address" 52 | }, 53 | { 54 | "name": "permissionId", 55 | "type": "uint256", 56 | "internalType": "uint256" 57 | } 58 | ], 59 | "outputs": [ 60 | { 61 | "name": "", 62 | "type": "bool", 63 | "internalType": "bool" 64 | } 65 | ], 66 | "stateMutability": "view" 67 | } 68 | ] -------------------------------------------------------------------------------- /contracts/rules/base/TrustBasedRule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Errors} from "contracts/core/types/Errors.sol"; 6 | 7 | abstract contract TrustBasedRule { 8 | event Lens_Rule_Trusted(address indexed account, address indexed trustedAddress); 9 | event Lens_Rule_Untrusted(address indexed account, address indexed untrustedAddress); 10 | 11 | /// @custom:keccak lens.storage.TrustBasedRule.isTrusted 12 | bytes32 constant STORAGE__IS_TRUSTED = 0x463a00e6fc84151780a21ba2127d78a0c153372a15d5669ad3912100980952bc; 13 | 14 | function $isTrusted() private pure returns (mapping(address => mapping(address => bool)) storage _storage) { 15 | assembly { 16 | _storage.slot := STORAGE__IS_TRUSTED 17 | } 18 | } 19 | 20 | function setTrust(address target, bool isTrusted) external virtual { 21 | $isTrusted()[msg.sender][target] = isTrusted; 22 | if (isTrusted) { 23 | emit Lens_Rule_Trusted(msg.sender, target); 24 | } else { 25 | emit Lens_Rule_Untrusted(msg.sender, target); 26 | } 27 | } 28 | 29 | function _requireTrust(address fromAccount, address toTarget) internal view virtual { 30 | require(_isTrusted(fromAccount, toTarget), Errors.Untrusted()); 31 | } 32 | 33 | function _isTrusted(address fromAccount, address toTarget) internal view virtual returns (bool) { 34 | return $isTrusted()[fromAccount][toTarget]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/core/access/Ownable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Errors} from "contracts/core/types/Errors.sol"; 6 | import {IOwnable} from "contracts/core/interfaces/IOwnable.sol"; 7 | 8 | abstract contract Ownable is IOwnable { 9 | event Lens_Ownable_OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 10 | 11 | struct OwnableStorage { 12 | address owner; 13 | } 14 | 15 | /// @custom:keccak lens.storage.Ownable 16 | bytes32 constant STORAGE__OWNABLE = 0x29cf0539cdb8487ad7dbc33f1a5f82174ca0f44de05580c9bd8cfe649fa8c9fe; 17 | 18 | function $ownableStorage() private pure returns (OwnableStorage storage _storage) { 19 | assembly { 20 | _storage.slot := STORAGE__OWNABLE 21 | } 22 | } 23 | 24 | modifier onlyOwner() { 25 | require(msg.sender == $ownableStorage().owner, Errors.InvalidMsgSender()); 26 | _; 27 | } 28 | 29 | function owner() public view virtual override returns (address) { 30 | return $ownableStorage().owner; 31 | } 32 | 33 | function transferOwnership(address newOwner) public virtual override onlyOwner { 34 | _transferOwnership(newOwner); 35 | } 36 | 37 | function _transferOwnership(address newOwner) internal virtual { 38 | address oldOwner = $ownableStorage().owner; 39 | $ownableStorage().owner = newOwner; 40 | emit Lens_Ownable_OwnershipTransferred(oldOwner, newOwner); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ABIs/ERC1967Proxy.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "_logic", 7 | "type": "address", 8 | "internalType": "address" 9 | }, 10 | { 11 | "name": "_data", 12 | "type": "bytes", 13 | "internalType": "bytes" 14 | } 15 | ], 16 | "stateMutability": "payable" 17 | }, 18 | { 19 | "type": "fallback", 20 | "stateMutability": "payable" 21 | }, 22 | { 23 | "type": "receive", 24 | "stateMutability": "payable" 25 | }, 26 | { 27 | "type": "event", 28 | "name": "AdminChanged", 29 | "inputs": [ 30 | { 31 | "name": "previousAdmin", 32 | "type": "address", 33 | "indexed": false, 34 | "internalType": "address" 35 | }, 36 | { 37 | "name": "newAdmin", 38 | "type": "address", 39 | "indexed": false, 40 | "internalType": "address" 41 | } 42 | ], 43 | "anonymous": false 44 | }, 45 | { 46 | "type": "event", 47 | "name": "BeaconUpgraded", 48 | "inputs": [ 49 | { 50 | "name": "beacon", 51 | "type": "address", 52 | "indexed": true, 53 | "internalType": "address" 54 | } 55 | ], 56 | "anonymous": false 57 | }, 58 | { 59 | "type": "event", 60 | "name": "Upgraded", 61 | "inputs": [ 62 | { 63 | "name": "implementation", 64 | "type": "address", 65 | "indexed": true, 66 | "internalType": "address" 67 | } 68 | ], 69 | "anonymous": false 70 | } 71 | ] -------------------------------------------------------------------------------- /contracts/extensions/factories/AccessControlFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IRoleBasedAccessControl} from "contracts/core/interfaces/IRoleBasedAccessControl.sol"; 6 | import {OwnerAdminOnlyAccessControl} from "contracts/extensions/access/OwnerAdminOnlyAccessControl.sol"; 7 | import {ILock} from "contracts/core/interfaces/ILock.sol"; 8 | 9 | contract AccessControlFactory { 10 | /// @custom:keccak lens.role.Admin 11 | uint256 constant ADMIN_ROLE_ID = uint256(0xfcbeadd75a96b5f8140d8c80f7c8d81ccbd7c4caa9592217bc8936b9eaabee75); 12 | 13 | event Lens_AccessControlFactory_OwnerAdminDeployment(address indexed accessControl, address owner); 14 | 15 | address immutable LOCK; 16 | 17 | constructor(address lock) { 18 | LOCK = lock; 19 | ILock(LOCK).isLocked(); // Aims to verify the given address follows ILock interface 20 | } 21 | 22 | function deployOwnerAdminOnlyAccessControl(address owner, address[] calldata admins) 23 | external 24 | returns (IRoleBasedAccessControl) 25 | { 26 | OwnerAdminOnlyAccessControl accessControl = new OwnerAdminOnlyAccessControl({owner: address(this), lock: LOCK}); 27 | emit Lens_AccessControlFactory_OwnerAdminDeployment(address(accessControl), owner); 28 | for (uint256 i = 0; i < admins.length; i++) { 29 | accessControl.grantRole(admins[i], ADMIN_ROLE_ID); 30 | } 31 | accessControl.transferOwnership(owner); 32 | return accessControl; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ABIs/LensFees.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "treasuryAddress", 7 | "type": "address", 8 | "internalType": "address" 9 | }, 10 | { 11 | "name": "treasuryFeeBps", 12 | "type": "uint16", 13 | "internalType": "uint16" 14 | } 15 | ], 16 | "stateMutability": "nonpayable" 17 | }, 18 | { 19 | "type": "function", 20 | "name": "getLensFeesData", 21 | "inputs": [], 22 | "outputs": [ 23 | { 24 | "name": "", 25 | "type": "tuple", 26 | "internalType": "struct LensFeesData", 27 | "components": [ 28 | { 29 | "name": "treasuryAddress", 30 | "type": "address", 31 | "internalType": "address" 32 | }, 33 | { 34 | "name": "treasuryFeeBps", 35 | "type": "uint16", 36 | "internalType": "uint16" 37 | } 38 | ] 39 | } 40 | ], 41 | "stateMutability": "view" 42 | }, 43 | { 44 | "type": "function", 45 | "name": "getTreasuryAddress", 46 | "inputs": [], 47 | "outputs": [ 48 | { 49 | "name": "", 50 | "type": "address", 51 | "internalType": "address" 52 | } 53 | ], 54 | "stateMutability": "view" 55 | }, 56 | { 57 | "type": "function", 58 | "name": "getTreasuryFeeBps", 59 | "inputs": [], 60 | "outputs": [ 61 | { 62 | "name": "", 63 | "type": "uint16", 64 | "internalType": "uint16" 65 | } 66 | ], 67 | "stateMutability": "view" 68 | } 69 | ] -------------------------------------------------------------------------------- /contracts/extensions/factories/AppFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "contracts/core/interfaces/IAccessControl.sol"; 6 | import {AppInitialProperties, App} from "contracts/extensions/primitives/app/App.sol"; 7 | import {KeyValue} from "contracts/core/types/Types.sol"; 8 | import {BeaconProxy} from "contracts/core/upgradeability/BeaconProxy.sol"; 9 | import {ProxyAdmin} from "contracts/core/upgradeability/ProxyAdmin.sol"; 10 | 11 | contract AppFactory { 12 | event Lens_AppFactory_Deployment(address indexed app, string metadataURI, KeyValue[] extraData); 13 | 14 | address internal immutable _beacon; 15 | address internal immutable _lock; 16 | 17 | constructor(address beacon, address lock) { 18 | _beacon = beacon; 19 | _lock = lock; 20 | } 21 | 22 | function deployApp( 23 | string memory metadataURI, 24 | bool sourceStampVerificationEnabled, 25 | IAccessControl accessControl, 26 | address proxyAdminOwner, 27 | AppInitialProperties calldata initialProperties, 28 | KeyValue[] calldata extraData 29 | ) external returns (address) { 30 | address proxyAdmin = address(new ProxyAdmin(proxyAdminOwner, _lock)); 31 | App app = App(address(new BeaconProxy(proxyAdmin, _beacon))); 32 | app.initialize(metadataURI, sourceStampVerificationEnabled, accessControl, initialProperties, extraData); 33 | emit Lens_AppFactory_Deployment(address(app), metadataURI, extraData); 34 | return address(app); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /deploy/upgradeActions.ts: -------------------------------------------------------------------------------- 1 | import { deployActionImplsAndUpgrade } from './deployActions'; 2 | import { generateEnvFile, loadAddressBook } from './lensUtils'; 3 | import { getWallet } from './utils'; 4 | 5 | async function deploy() { 6 | const regularDeployerPk = process.env.WALLET_PRIVATE_KEY; 7 | if (!regularDeployerPk) { 8 | throw new Error('WALLET_PRIVATE_KEY not found in environment variables'); 9 | } 10 | const regularDeployerPkBalance = await getWallet(regularDeployerPk).getBalance(); 11 | console.log('Regular deployer balance:', regularDeployerPkBalance.toString()); 12 | 13 | const proxyAdminPk = process.env.PROXY_ADMIN_PRIVATE_KEY; 14 | if (!proxyAdminPk) { 15 | throw new Error('PROXY_ADMIN_PRIVATE_KEY not found in environment variables'); 16 | } 17 | const proxyAdminPkBalance = await getWallet(proxyAdminPk).getBalance(); 18 | console.log('Proxy admin balance:', proxyAdminPkBalance.toString()); 19 | 20 | const addressBook = loadAddressBook(); 21 | const actionHubAddress = addressBook['ActionHub'].address; 22 | 23 | if (!actionHubAddress) { 24 | throw new Error('ActionHub address not found in the address book'); 25 | } else { 26 | console.log( 27 | `ActionHub address being used for new action implementations: ${actionHubAddress}\n\n` 28 | ); 29 | } 30 | 31 | await deployActionImplsAndUpgrade(actionHubAddress, getWallet(proxyAdminPk)); 32 | generateEnvFile(); 33 | } 34 | 35 | if (require.main === module) { 36 | deploy() 37 | .then(() => process.exit(0)) 38 | .catch((error) => { 39 | console.error(error); 40 | process.exit(1); 41 | }); 42 | } 43 | 44 | export default deploy; 45 | -------------------------------------------------------------------------------- /contracts/core/upgradeability/Lock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {ILock} from "contracts/core/interfaces/ILock.sol"; 6 | import {Ownable} from "contracts/core/access/Ownable.sol"; 7 | import {EventEmitterEarly} from "contracts/migration/EventEmitterEarly.sol"; 8 | 9 | contract Lock is Ownable, ILock, EventEmitterEarly { 10 | event Lens_Lock_LockStatusSet(bool indexed locked); 11 | event Lens_Lock_LockStatusSet(address indexed target, bool indexed locked); 12 | 13 | bool internal _areAllAddressesUnlocked; 14 | mapping(address => bool) internal _isAddressUnlocked; 15 | 16 | constructor(address owner, bool locked) Ownable() { 17 | _transferOwnership(owner); 18 | _setLockStatus(locked); 19 | } 20 | 21 | function isLocked() external view override returns (bool) { 22 | if (_areAllAddressesUnlocked) { 23 | return false; 24 | } else { 25 | return !_isAddressUnlocked[msg.sender]; 26 | } 27 | } 28 | 29 | function setLockStatus(bool locked) external onlyOwner { 30 | _setLockStatus(locked); 31 | } 32 | 33 | // Only to unlock specific addresses before the global lock is released. 34 | function setLockStatusForAddress(address target, bool locked) external onlyOwner { 35 | _isAddressUnlocked[target] = !locked; 36 | emit Lens_Lock_LockStatusSet(target, locked); 37 | } 38 | 39 | function _setLockStatus(bool locked) internal { 40 | _areAllAddressesUnlocked = !locked; 41 | emit Lens_Lock_LockStatusSet(locked); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /deploy/verify.ts: -------------------------------------------------------------------------------- 1 | // function deployAccount( 2 | // address owner, 0xB2b033701F9FbcF51ce3e4866C6605aCE3a4f3C7 3 | // string calldata metadataURI, "https://devnet.irys.xyz/GLzhFVr9nAQ7svCzJds2TNiJXeCBf3NVgT82XZuBUsch" 4 | // address[] calldata accountManagers, [0x00A58BA275E6BFC004E2bf9be121a15a2c543e71] 5 | // AccountManagerPermissions[] calldata accountManagersPermissions, [(true, false, false, true)] 6 | // SourceStamp calldata sourceStamp (0x0000000000000000000000000000000000000000, 0, 0, 0x) 7 | // ) external returns (address) { 8 | 9 | import { emptySourceStamp } from './deployAux'; 10 | import { verifyDeployedContract } from './utils'; 11 | import * as hre from 'hardhat'; 12 | 13 | const owner = '0xB2b033701F9FbcF51ce3e4866C6605aCE3a4f3C7'; 14 | const metadataURI = 'https://devnet.irys.xyz/GLzhFVr9nAQ7svCzJds2TNiJXeCBf3NVgT82XZuBUsch'; 15 | const accountManagers = ['0x00A58BA275E6BFC004E2bf9be121a15a2c543e71']; 16 | const accountManagersPermissions = [ 17 | { 18 | canExecuteTransactions: true, 19 | canTransferTokens: false, 20 | canTransferNative: false, 21 | canSetMetadataURI: true, 22 | }, 23 | ]; 24 | const sourceStamp = emptySourceStamp; 25 | 26 | export default async function () { 27 | const deployedAddress = '0x58F05d2d64e168d491C30dDDe58585784d411A91'; 28 | const deployedArtifact = await hre.artifacts.readArtifact('Account'); 29 | 30 | await verifyDeployedContract({ 31 | address: deployedAddress, 32 | artifact: deployedArtifact, 33 | constructorArguments: [ 34 | owner, 35 | metadataURI, 36 | accountManagers, 37 | accountManagersPermissions, 38 | sourceStamp, 39 | ], 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /ABIs/AccessControlled.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "getAccessControl", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address", 10 | "internalType": "contract IAccessControl" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "setAccessControl", 18 | "inputs": [ 19 | { 20 | "name": "newAccessControl", 21 | "type": "address", 22 | "internalType": "contract IAccessControl" 23 | } 24 | ], 25 | "outputs": [], 26 | "stateMutability": "nonpayable" 27 | }, 28 | { 29 | "type": "event", 30 | "name": "Lens_AccessControlAdded", 31 | "inputs": [ 32 | { 33 | "name": "accessControl", 34 | "type": "address", 35 | "indexed": true, 36 | "internalType": "address" 37 | }, 38 | { 39 | "name": "accessControlType", 40 | "type": "bytes32", 41 | "indexed": true, 42 | "internalType": "bytes32" 43 | } 44 | ], 45 | "anonymous": false 46 | }, 47 | { 48 | "type": "event", 49 | "name": "Lens_AccessControlUpdated", 50 | "inputs": [ 51 | { 52 | "name": "accessControl", 53 | "type": "address", 54 | "indexed": true, 55 | "internalType": "address" 56 | }, 57 | { 58 | "name": "accessControlType", 59 | "type": "bytes32", 60 | "indexed": true, 61 | "internalType": "bytes32" 62 | } 63 | ], 64 | "anonymous": false 65 | }, 66 | { 67 | "type": "error", 68 | "name": "AccessDenied", 69 | "inputs": [] 70 | } 71 | ] -------------------------------------------------------------------------------- /hashed-constants-standard.js: -------------------------------------------------------------------------------- 1 | //////////////////////////////////// Permissions //////////////////////////////////// 2 | 3 | // Definition: 4 | keccak256("lens.permission.{permissionName}"); 5 | 6 | // Examples: 7 | keccak256("lens.permission.SkipPayments"); 8 | keccak256("lens.permission.ChangeRules"); 9 | keccak256("lens.permission.BanMembers"); 10 | 11 | //////////////////////////////////// Custom Params //////////////////////////////////// 12 | 13 | // Definition: 14 | keccak256("lens.param.{paramName}"); 15 | 16 | // Examples: 17 | keccak256("lens.param.accessControl"); 18 | keccak256("lens.param.sourceStamp"); 19 | 20 | //////////////////////////////////// Extra data //////////////////////////////////// 21 | 22 | // Definition: 23 | keccak256("lens.data.{extraDataKey}"); 24 | 25 | // Examples: 26 | keccak256("lens.data.groupFeed"); 27 | keccak256("lens.data.source"); 28 | 29 | //////////////////////////////////// Roles //////////////////////////////////// 30 | 31 | // Definition: 32 | keccak256("lens.role.{roleId}"); 33 | 34 | // Examples: 35 | keccak256("lens.role.Owner"); 36 | keccak256("lens.role.Admin"); 37 | 38 | //////////////////////////////////// Storage //////////////////////////////////// 39 | 40 | // Definition: 41 | keccak256("lens.storage.{contractType}.{storedObject}"); 42 | 43 | // Examples: 44 | keccak256("lens.storage.AccessControl.roles"); 45 | 46 | //////////////////////////////////// Contract Type //////////////////////////////////// 47 | 48 | // Definition: 49 | keccak256("lens.contract.{contractType}[.{subType}]"); 50 | 51 | // Examples: 52 | keccak256("lens.contract.AccessControl.OwnerAdminOnlyAccessControl"); 53 | keccak256("lens.contract.AccessControl.RoleBasedAccessControl"); 54 | -------------------------------------------------------------------------------- /ABIs/TransparentUpgradeableProxy.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "_logic", 7 | "type": "address", 8 | "internalType": "address" 9 | }, 10 | { 11 | "name": "admin_", 12 | "type": "address", 13 | "internalType": "address" 14 | }, 15 | { 16 | "name": "_data", 17 | "type": "bytes", 18 | "internalType": "bytes" 19 | } 20 | ], 21 | "stateMutability": "payable" 22 | }, 23 | { 24 | "type": "fallback", 25 | "stateMutability": "payable" 26 | }, 27 | { 28 | "type": "receive", 29 | "stateMutability": "payable" 30 | }, 31 | { 32 | "type": "event", 33 | "name": "AdminChanged", 34 | "inputs": [ 35 | { 36 | "name": "previousAdmin", 37 | "type": "address", 38 | "indexed": false, 39 | "internalType": "address" 40 | }, 41 | { 42 | "name": "newAdmin", 43 | "type": "address", 44 | "indexed": false, 45 | "internalType": "address" 46 | } 47 | ], 48 | "anonymous": false 49 | }, 50 | { 51 | "type": "event", 52 | "name": "BeaconUpgraded", 53 | "inputs": [ 54 | { 55 | "name": "beacon", 56 | "type": "address", 57 | "indexed": true, 58 | "internalType": "address" 59 | } 60 | ], 61 | "anonymous": false 62 | }, 63 | { 64 | "type": "event", 65 | "name": "Upgraded", 66 | "inputs": [ 67 | { 68 | "name": "implementation", 69 | "type": "address", 70 | "indexed": true, 71 | "internalType": "address" 72 | } 73 | ], 74 | "anonymous": false 75 | } 76 | ] -------------------------------------------------------------------------------- /contracts/extensions/factories/FeedFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "contracts/core/interfaces/IAccessControl.sol"; 6 | import {Feed} from "contracts/core/primitives/feed/Feed.sol"; 7 | import {RuleChange, KeyValue} from "contracts/core/types/Types.sol"; 8 | import {BeaconProxy} from "contracts/core/upgradeability/BeaconProxy.sol"; 9 | import {ProxyAdmin} from "contracts/core/upgradeability/ProxyAdmin.sol"; 10 | import {PrimitiveFactory} from "contracts/extensions/factories/PrimitiveFactory.sol"; 11 | 12 | contract FeedFactory is PrimitiveFactory { 13 | event Lens_FeedFactory_Deployment(address indexed feed, string metadataURI); 14 | 15 | constructor(address primitiveBeacon, address proxyAdminLock, address lensFactory) 16 | PrimitiveFactory(primitiveBeacon, proxyAdminLock, lensFactory) 17 | {} 18 | 19 | function deployFeed( 20 | string memory metadataURI, 21 | IAccessControl accessControl, 22 | address proxyAdminOwner, 23 | RuleChange[] calldata ruleChanges, 24 | KeyValue[] calldata extraData 25 | ) external onlyLensFactory returns (address) { 26 | address proxyAdmin = address(new ProxyAdmin(proxyAdminOwner, PROXY_ADMIN_LOCK)); 27 | Feed feed = Feed(address(new BeaconProxy(proxyAdmin, PRIMITIVE_BEACON))); 28 | feed.initialize(metadataURI, TEMPORARY_ACCESS_CONTROL); 29 | feed.changeFeedRules(ruleChanges); 30 | feed.setExtraData(extraData); 31 | feed.setAccessControl(accessControl); 32 | emit Lens_FeedFactory_Deployment(address(feed), metadataURI); 33 | return address(feed); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ABIs/IERC20Permit.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "DOMAIN_SEPARATOR", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "bytes32", 10 | "internalType": "bytes32" 11 | } 12 | ], 13 | "stateMutability": "view" 14 | }, 15 | { 16 | "type": "function", 17 | "name": "nonces", 18 | "inputs": [ 19 | { 20 | "name": "owner", 21 | "type": "address", 22 | "internalType": "address" 23 | } 24 | ], 25 | "outputs": [ 26 | { 27 | "name": "", 28 | "type": "uint256", 29 | "internalType": "uint256" 30 | } 31 | ], 32 | "stateMutability": "view" 33 | }, 34 | { 35 | "type": "function", 36 | "name": "permit", 37 | "inputs": [ 38 | { 39 | "name": "owner", 40 | "type": "address", 41 | "internalType": "address" 42 | }, 43 | { 44 | "name": "spender", 45 | "type": "address", 46 | "internalType": "address" 47 | }, 48 | { 49 | "name": "value", 50 | "type": "uint256", 51 | "internalType": "uint256" 52 | }, 53 | { 54 | "name": "deadline", 55 | "type": "uint256", 56 | "internalType": "uint256" 57 | }, 58 | { 59 | "name": "v", 60 | "type": "uint8", 61 | "internalType": "uint8" 62 | }, 63 | { 64 | "name": "r", 65 | "type": "bytes32", 66 | "internalType": "bytes32" 67 | }, 68 | { 69 | "name": "s", 70 | "type": "bytes32", 71 | "internalType": "bytes32" 72 | } 73 | ], 74 | "outputs": [], 75 | "stateMutability": "nonpayable" 76 | } 77 | ] -------------------------------------------------------------------------------- /contracts/extensions/factories/GraphFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "contracts/core/interfaces/IAccessControl.sol"; 6 | import {Graph} from "contracts/core/primitives/graph/Graph.sol"; 7 | import {RuleChange, KeyValue} from "contracts/core/types/Types.sol"; 8 | import {BeaconProxy} from "contracts/core/upgradeability/BeaconProxy.sol"; 9 | import {ProxyAdmin} from "contracts/core/upgradeability/ProxyAdmin.sol"; 10 | import {PrimitiveFactory} from "contracts/extensions/factories/PrimitiveFactory.sol"; 11 | 12 | contract GraphFactory is PrimitiveFactory { 13 | event Lens_GraphFactory_Deployment(address indexed graph, string metadataURI); 14 | 15 | constructor(address primitiveBeacon, address proxyAdminLock, address lensFactory) 16 | PrimitiveFactory(primitiveBeacon, proxyAdminLock, lensFactory) 17 | {} 18 | 19 | function deployGraph( 20 | string memory metadataURI, 21 | IAccessControl accessControl, 22 | address proxyAdminOwner, 23 | RuleChange[] calldata ruleChanges, 24 | KeyValue[] calldata extraData 25 | ) external onlyLensFactory returns (address) { 26 | address proxyAdmin = address(new ProxyAdmin(proxyAdminOwner, PROXY_ADMIN_LOCK)); 27 | Graph graph = Graph(address(new BeaconProxy(proxyAdmin, PRIMITIVE_BEACON))); 28 | graph.initialize(metadataURI, TEMPORARY_ACCESS_CONTROL); 29 | graph.changeGraphRules(ruleChanges); 30 | graph.setExtraData(extraData); 31 | graph.setAccessControl(accessControl); 32 | emit Lens_GraphFactory_Deployment(address(graph), metadataURI); 33 | return address(graph); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/core/base/EntityExtraDataBased.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.26; 3 | 4 | import {KeyValue} from "contracts/core/types/Types.sol"; 5 | import {ExtraStorageBased} from "contracts/core/base/ExtraStorageBased.sol"; 6 | 7 | abstract contract EntityExtraDataBased is ExtraStorageBased { 8 | function _emitEntityExtraDataAddedEvent(uint256 entityId, KeyValue memory extraDataAdded) internal virtual; 9 | 10 | function _emitEntityExtraDataUpdatedEvent(uint256 entityId, KeyValue memory extraDataUpdated) internal virtual; 11 | 12 | function _emitEntityExtraDataRemovedEvent(uint256 entityId, KeyValue memory extraDataRemoved) internal virtual; 13 | 14 | function _setEntityExtraData(uint256 entityId, KeyValue[] memory extraDataToSet) internal virtual { 15 | for (uint256 i = 0; i < extraDataToSet.length; i++) { 16 | bool hadAValueSetBefore = _setEntityExtraStorage_Account(entityId, extraDataToSet[i]); 17 | bool isNewValueEmpty = extraDataToSet[i].value.length == 0; 18 | if (hadAValueSetBefore) { 19 | if (isNewValueEmpty) { 20 | _emitEntityExtraDataRemovedEvent(entityId, extraDataToSet[i]); 21 | } else { 22 | _emitEntityExtraDataUpdatedEvent(entityId, extraDataToSet[i]); 23 | } 24 | } else if (!isNewValueEmpty) { 25 | _emitEntityExtraDataAddedEvent(entityId, extraDataToSet[i]); 26 | } 27 | } 28 | } 29 | 30 | function _getEntityExtraData(address addressScope, uint256 entityId, bytes32 key) 31 | internal 32 | view 33 | returns (bytes memory) 34 | { 35 | return _getEntityExtraStorage_Account(addressScope, entityId, key); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/upgradeability/Initializable.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import "forge-std/Test.sol"; 6 | import {Initializable} from "@core/upgradeability/Initializable.sol"; 7 | import {Errors} from "@core/types/Errors.sol"; 8 | 9 | contract InitializableContract is Initializable { 10 | function initialize() public initializer { 11 | return; 12 | } 13 | } 14 | 15 | contract InitializableTest is Test { 16 | /// @custom:keccak lens.storage.Initializable 17 | bytes32 constant STORAGE__INITIALIZABLE = 0xbd2c04feebbff2d29fe1b04edf9a1d94ba7a836bad797bdd99c9e722e172cdd0; 18 | 19 | bytes32 constant TRUE = bytes32(uint256(1)); 20 | bytes32 constant FALSE = bytes32(uint256(0)); 21 | 22 | InitializableContract initializable; 23 | 24 | function setUp() public virtual { 25 | initializable = new InitializableContract(); 26 | } 27 | 28 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 29 | 30 | function test_Initialize() public { 31 | assertEq(vm.load(address(initializable), STORAGE__INITIALIZABLE), FALSE); 32 | 33 | initializable.initialize(); 34 | 35 | assertEq(vm.load(address(initializable), STORAGE__INITIALIZABLE), TRUE); 36 | } 37 | 38 | function test_Cannot_InitializeTwice() public { 39 | assertEq(vm.load(address(initializable), STORAGE__INITIALIZABLE), FALSE); 40 | 41 | initializable.initialize(); 42 | 43 | assertEq(vm.load(address(initializable), STORAGE__INITIALIZABLE), TRUE); 44 | 45 | vm.expectRevert(Errors.AlreadyInitialized.selector); 46 | initializable.initialize(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /deploy/deployActions.ts: -------------------------------------------------------------------------------- 1 | import { ContractRunner, ethers } from 'ethers'; 2 | import { 3 | deployLensContractAsProxy, 4 | ContractType, 5 | ContractInfo, 6 | deployImplAndUpgradeTransparentProxy, 7 | } from './lensUtils'; 8 | 9 | function getContracts(actionHub: string): ContractInfo[] { 10 | return [ 11 | { 12 | contractName: 'TippingAccountAction', 13 | contractType: ContractType.Action, 14 | constructorArguments: [actionHub], 15 | }, 16 | { 17 | contractName: 'TippingPostAction', 18 | contractType: ContractType.Action, 19 | constructorArguments: [actionHub], 20 | }, 21 | { 22 | contractName: 'SimpleCollectAction', 23 | contractType: ContractType.Action, 24 | constructorArguments: [actionHub], 25 | }, 26 | ]; 27 | } 28 | 29 | export async function deployActions(actionHub: string, actionsOwner: string): Promise { 30 | const metadataURI = ''; 31 | const contracts = getContracts(actionHub); 32 | 33 | const initializerABI = ['function initialize(address owner, string memory metadataURI) external']; 34 | const initializerInterface = new ethers.Interface(initializerABI); 35 | const initializeEncodedCall = initializerInterface.encodeFunctionData('initialize', [ 36 | actionsOwner, 37 | metadataURI, 38 | ]); 39 | 40 | for (const contract of contracts) { 41 | await deployLensContractAsProxy(contract, actionsOwner, initializeEncodedCall); 42 | } 43 | } 44 | 45 | export async function deployActionImplsAndUpgrade( 46 | actionHub: string, 47 | proxyAdminWallet: ContractRunner 48 | ): Promise { 49 | const contracts = getContracts(actionHub); 50 | 51 | for (const contract of contracts) { 52 | await deployImplAndUpgradeTransparentProxy(proxyAdminWallet, contract); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /contracts/core/types/Errors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | library Errors { 6 | error AccessDenied(); 7 | error AllAnyOfRulesReverted(); 8 | error AlreadyExists(); 9 | error AlreadyInitialized(); 10 | error AlreadyProcessed(); 11 | error AutoUpgradeEnabled(); 12 | error Banned(); 13 | error Blocked(); 14 | error CannotFollowAgain(); 15 | error CannotHaveRules(); 16 | error CannotStartWithThat(); 17 | error ConfigureCallReverted(); 18 | error Disabled(); 19 | error DoesNotExist(); 20 | error DuplicatedValue(); 21 | error Expired(); 22 | error FailedToTransferNative(); 23 | error Immutable(); 24 | error InsufficientAllowance(); 25 | error InvalidConfigSalt(); 26 | error InvalidMsgSender(); 27 | error InvalidParameter(); 28 | error InvalidSignature(); 29 | error InvalidSourceStampOriginalMsgSender(); 30 | error LimitReached(); 31 | error Locked(); 32 | error NonceUsed(); 33 | error NotAContract(); 34 | error NotAllowed(); 35 | error NotAMember(); 36 | error NotEnough(); 37 | error NotEnoughBalance(); 38 | error NotFollowing(); 39 | error NotFound(); 40 | error NotImplemented(); 41 | error RedundantStateChange(); 42 | error RequiredRuleReverted(); 43 | error RuleNotConfigured(); 44 | error SelectorEnabledForDifferentRuleType(); 45 | error ActionOnSelf(); 46 | error SingleAnyOfRule(); 47 | error UnexpectedContractImpl(); 48 | error UnexpectedValue(); 49 | error UnsupportedSelector(); 50 | error UnsupportedOperation(); 51 | error Untrusted(); 52 | error UsernameAssigned(); 53 | error TransferFailed(); 54 | error WrongSigner(); 55 | } 56 | -------------------------------------------------------------------------------- /contracts/extensions/factories/GroupFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "contracts/core/interfaces/IAccessControl.sol"; 6 | import {Group} from "contracts/core/primitives/group/Group.sol"; 7 | import {RuleChange, RuleProcessingParams, KeyValue} from "contracts/core/types/Types.sol"; 8 | import {BeaconProxy} from "contracts/core/upgradeability/BeaconProxy.sol"; 9 | import {ProxyAdmin} from "contracts/core/upgradeability/ProxyAdmin.sol"; 10 | import {PrimitiveFactory} from "contracts/extensions/factories/PrimitiveFactory.sol"; 11 | 12 | contract GroupFactory is PrimitiveFactory { 13 | event Lens_GroupFactory_Deployment(address indexed group, string metadataURI); 14 | 15 | constructor(address primitiveBeacon, address proxyAdminLock, address lensFactory) 16 | PrimitiveFactory(primitiveBeacon, proxyAdminLock, lensFactory) 17 | {} 18 | 19 | function deployGroup( 20 | string memory metadataURI, 21 | IAccessControl accessControl, 22 | address proxyAdminOwner, 23 | RuleChange[] calldata ruleChanges, 24 | KeyValue[] calldata extraData, 25 | address foundingMember 26 | ) external onlyLensFactory returns (address) { 27 | address proxyAdmin = address(new ProxyAdmin(proxyAdminOwner, PROXY_ADMIN_LOCK)); 28 | Group group = Group(address(new BeaconProxy(proxyAdmin, PRIMITIVE_BEACON))); 29 | group.initialize(metadataURI, TEMPORARY_ACCESS_CONTROL, foundingMember); 30 | group.changeGroupRules(ruleChanges); 31 | group.setExtraData(extraData); 32 | group.setAccessControl(accessControl); 33 | emit Lens_GroupFactory_Deployment(address(group), metadataURI); 34 | return address(group); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/core/interfaces/IRoleBasedAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {IAccessControl} from "contracts/core/interfaces/IAccessControl.sol"; 6 | 7 | enum Access { 8 | UNDEFINED, 9 | GRANTED, 10 | DENIED 11 | } 12 | 13 | struct Role { 14 | address account; 15 | uint256 roleId; 16 | } 17 | 18 | interface IRoleBasedAccessControl is IAccessControl { 19 | event Lens_AccessControl_RoleGranted(address indexed account, uint256 indexed roleId); 20 | event Lens_AccessControl_RoleRevoked(address indexed account, uint256 indexed roleId); 21 | 22 | event Lens_AccessControl_AccessAdded( 23 | uint256 indexed roleId, address indexed contractAddress, uint256 indexed permissionId, bool granted 24 | ); 25 | event Lens_AccessControl_AccessUpdated( 26 | uint256 indexed roleId, address indexed contractAddress, uint256 indexed permissionId, bool granted 27 | ); 28 | event Lens_AccessControl_AccessRemoved( 29 | uint256 indexed roleId, address indexed contractAddress, uint256 indexed permissionId 30 | ); 31 | 32 | // Role functions 33 | function grantRole(address account, uint256 roleId) external; 34 | 35 | function revokeRole(address account, uint256 roleId) external; 36 | 37 | function grantRoles(Role[] calldata roles) external; 38 | 39 | function revokeRoles(Role[] calldata roles) external; 40 | 41 | function hasRole(address account, uint256 roleId) external view returns (bool); 42 | 43 | // Access functions 44 | function setAccess(uint256 roleId, address contractAddress, uint256 permissionId, Access access) external; 45 | 46 | function getAccess(uint256 roleId, address contractAddress, uint256 permissionId) external view returns (Access); 47 | } 48 | -------------------------------------------------------------------------------- /test/mocks/MockFeed.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Post} from "contracts/core/interfaces/IFeed.sol"; 6 | 7 | contract MockFeed { 8 | address internal _postAuthor; 9 | bool internal _postExists = true; 10 | string internal _contentURI = "someContentURI"; 11 | uint80 internal _creationTimestamp = uint80(block.timestamp); 12 | 13 | function setPostAuthor(uint256, /* postId */ address author) external { 14 | _postAuthor = author; 15 | } 16 | 17 | function getPostAuthor(uint256 /* postId */ ) external view returns (address) { 18 | return _postAuthor; 19 | } 20 | 21 | function setPostExists(bool exists) external { 22 | _postExists = exists; 23 | } 24 | 25 | function postExists(uint256 /* postId */ ) external view returns (bool) { 26 | return _postExists; 27 | } 28 | 29 | function setContentURI(string memory contentURI) external { 30 | _contentURI = contentURI; 31 | } 32 | 33 | function getContentURI(uint256 /* postId */ ) external view returns (string memory) { 34 | return _contentURI; 35 | } 36 | 37 | function getPost(uint256 postId) external view returns (Post memory) { 38 | return Post({ 39 | author: _postAuthor, 40 | authorPostSequentialId: 1, 41 | postSequentialId: 1, 42 | contentURI: _contentURI, 43 | rootPostId: postId, 44 | repostedPostId: 0, 45 | quotedPostId: 0, 46 | repliedPostId: 0, 47 | creationTimestamp: _creationTimestamp, 48 | creationSource: address(0), 49 | lastUpdatedTimestamp: _creationTimestamp, 50 | lastUpdateSource: address(0), 51 | isDeleted: false 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /deploy/deploySingleContract.ts: -------------------------------------------------------------------------------- 1 | import { ContractType, ContractInfo, deployLensContract } from './lensUtils'; 2 | 3 | async function deploy() { 4 | //////////////// SETUP ///////////////// 5 | const contractToDeploy: ContractInfo = 6 | { 7 | name: 'LensFactoryImpl', 8 | contractName: 'LensFactory', 9 | contractType: ContractType.Implementation, 10 | constructorArguments: [ 11 | { 12 | accessControlFactory: '0x5eb740362F17815Ae67EBcA6420Cbb350f714C3E', 13 | accountFactory: '0xE55C2154d1766a9C6319dBD989C89867b0457358', 14 | appFactory: '0xc650f3CcfF7801F5e95a99B99AAbD2f6319d38ed', 15 | groupFactory: '0xEF51808f8a2399282CDd156E897473b282998a29', 16 | feedFactory: '0xb8169FB0FaB6a699854fd4fD2457b990988E1372', 17 | graphFactory: '0x7cbB07bD2E80A27c59Ed707B79024cC5e54dEaF0', 18 | namespaceFactory: '0xb69CBb69041a30216e2fe13E9700b32761b859C3' 19 | }, 20 | { 21 | accountBlockingRule:'0xf3de16e99679243E36BB449CADEA247Cf61450e1', 22 | groupGatedFeedRule: '0xbDE71d01eC6d6c49b2bcc9067EcA352a17D25A91', 23 | usernameSimpleCharsetRule: '0x1dB51f49DE4D266B2ab7D62656510083e0AACe44', 24 | banMemberGroupRule: '0xd12E1aD028d550F85F2a8d9130C46dB77A6A0a41', 25 | addRemovePidGroupRule: '0x90d39577A9a6C76ED8a5b2B908a7053ef5430194', 26 | usernameReservedNamespaceRule: '0x9a8b0e3344f5ca5f6fc9FcEb8fF543FDeF5eb2b9' 27 | } 28 | ], 29 | }; 30 | //////////////////////////////////////// 31 | 32 | const deployedImplementation = await deployLensContract( 33 | contractToDeploy, 34 | true 35 | ); 36 | 37 | console.log( 38 | `${contractToDeploy.contractName} deployed at ${deployedImplementation.address}` 39 | ); 40 | console.table(deployedImplementation); 41 | } 42 | 43 | if (require.main === module) { 44 | deploy() 45 | .then(() => process.exit(0)) 46 | .catch((error) => { 47 | console.error(error); 48 | process.exit(1); 49 | }); 50 | } 51 | 52 | export default deploy; 53 | -------------------------------------------------------------------------------- /contracts/extensions/factories/AccountFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // Copyright (C) 2024 Lens Labs. All Rights Reserved. 3 | pragma solidity ^0.8.26; 4 | 5 | import {Account, AccountManagerPermissions} from "contracts/extensions/account/Account.sol"; 6 | import {KeyValue, SourceStamp} from "contracts/core/types/Types.sol"; 7 | import {BeaconProxy} from "contracts/core/upgradeability/BeaconProxy.sol"; 8 | import {ProxyAdmin} from "contracts/core/upgradeability/ProxyAdmin.sol"; 9 | 10 | contract AccountFactory { 11 | event Lens_Account_Created( 12 | address indexed account, 13 | address indexed owner, 14 | string metadataURI, 15 | address[] accountManagers, 16 | AccountManagerPermissions[] accountManagersPermissions, 17 | address indexed source, 18 | KeyValue[] extraData 19 | ); 20 | 21 | address internal immutable _beacon; 22 | address internal immutable _lock; 23 | 24 | constructor(address beacon, address lock) { 25 | _beacon = beacon; 26 | _lock = lock; 27 | } 28 | 29 | function deployAccount( 30 | address owner, 31 | string calldata metadataURI, 32 | address[] calldata accountManagers, 33 | AccountManagerPermissions[] calldata accountManagersPermissions, 34 | SourceStamp calldata sourceStamp, 35 | KeyValue[] calldata extraData 36 | ) external returns (address) { 37 | address proxyAdmin = address(new ProxyAdmin(owner, _lock)); // Owner of Proxy Admin same as owner of Account 38 | Account account = Account(payable(new BeaconProxy(proxyAdmin, _beacon))); 39 | account.initialize(owner, metadataURI, accountManagers, accountManagersPermissions, sourceStamp, extraData); 40 | emit Lens_Account_Created( 41 | address(account), 42 | owner, 43 | metadataURI, 44 | accountManagers, 45 | accountManagersPermissions, 46 | sourceStamp.source, 47 | extraData 48 | ); 49 | return payable(account); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /deploy/deployProxyStuff.ts: -------------------------------------------------------------------------------- 1 | import { 2 | deployLensContract, 3 | ContractType, 4 | ContractInfo, 5 | loadContractAddressFromAddressBook, 6 | } from './lensUtils'; 7 | 8 | export async function deployLock(lockType: string, lockOwner: string): Promise { 9 | const lock = await deployLensContract({ 10 | name: lockType, 11 | contractName: 'Lock', 12 | contractType: ContractType.Aux, 13 | constructorArguments: [lockOwner, true], 14 | }); 15 | } 16 | 17 | export async function deployBeacons(beaconOwner: string): Promise { 18 | const contracts: ContractInfo[] = [ 19 | { 20 | name: 'AppBeacon', 21 | contractName: 'Beacon', 22 | contractType: ContractType.Beacon, 23 | constructorArguments: [beaconOwner, 1, loadContractAddressFromAddressBook('AppImpl')], 24 | }, 25 | { 26 | name: 'AccountBeacon', 27 | contractName: 'Beacon', 28 | contractType: ContractType.Beacon, 29 | constructorArguments: [beaconOwner, 1, loadContractAddressFromAddressBook('AccountImpl')], 30 | }, 31 | { 32 | name: 'FeedBeacon', 33 | contractName: 'Beacon', 34 | contractType: ContractType.Beacon, 35 | constructorArguments: [beaconOwner, 1, loadContractAddressFromAddressBook('FeedImpl')], 36 | }, 37 | { 38 | name: 'GraphBeacon', 39 | contractName: 'Beacon', 40 | contractType: ContractType.Beacon, 41 | constructorArguments: [beaconOwner, 1, loadContractAddressFromAddressBook('GraphImpl')], 42 | }, 43 | { 44 | name: 'GroupBeacon', 45 | contractName: 'Beacon', 46 | contractType: ContractType.Beacon, 47 | constructorArguments: [beaconOwner, 1, loadContractAddressFromAddressBook('GroupImpl')], 48 | }, 49 | { 50 | name: 'NamespaceBeacon', 51 | contractName: 'Beacon', 52 | contractType: ContractType.Beacon, 53 | constructorArguments: [beaconOwner, 1, loadContractAddressFromAddressBook('NamespaceImpl')], 54 | }, 55 | ]; 56 | 57 | for (const contract of contracts) { 58 | await deployLensContract(contract); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___w.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHciLCJkZXNjcmlwdGlvbiI6ImxlbnMgLSBAdyIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG14bGJuTThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kxTWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSStkend2ZEhOd1lXNCtQQzkwWlhoMFBqd3ZjM1puUGc9PSIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJsZW5ndGgiLCJ2YWx1ZSI6IjEifV19 -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___ww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3IiwiZGVzY3JpcHRpb24iOiJsZW5zIC0gQHd3IiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQbXhsYm5NOEwzUnpjR0Z1UGp3dmRHVjRkRDQ4ZEdWNGRDQjRiV3c2YzNCaFkyVTlJbkJ5WlhObGNuWmxJaUJtYVd4c1BTSWpNa015UkRNd0lpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSTFNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJZ2MzUjViR1U5SW1acGJHdzZJekpqTW1Rek1EdG1hV3hzT21OdmJHOXlLR1JwYzNCc1lYa3RjRE1nTGpFM01qVWdMakUzTmpVZ0xqRTRPRElwTzJacGJHd3RiM0JoWTJsMGVUb3hJajQ4ZEhOd1lXNGdlRDBpTXpJaUlIazlJalEyT0NJK2QzYzhMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OEwzTjJaejQ9IiwiYXR0cmlidXRlcyI6W3sidHJhaXRfdHlwZSI6Imxlbmd0aCIsInZhbHVlIjoiMiJ9XX0= -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___www.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3dyIsImRlc2NyaXB0aW9uIjoibGVucyAtIEB3d3ciLCJpbWFnZSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJMU1USWlJR2hsYVdkb2REMGlOVEV5SWlCbWFXeHNQU0p1YjI1bElpQjJhV1YzUW05NFBTSXdJREFnTlRFeUlEVXhNaUkrUEhCaGRHZ2dabWxzYkQwaUkyWm1aaUlnWkQwaVRUQWdNR2cxTVRKMk5URXlTREI2SWlCemRIbHNaVDBpWm1sc2JEb2pabVptTzJacGJHd3RiM0JoWTJsMGVUb3hJaTgrUEhCaGRHZ2dabWxzYkMxeWRXeGxQU0psZG1WdWIyUmtJaUJqYkdsd0xYSjFiR1U5SW1WMlpXNXZaR1FpSUdROUlrMDVNUzQyT1RZZ05UVXVNalU1WXpJdU56ZzFMVEl1TlRjM0lEWXVORE01TFRRdU1UYzFJREV3TGpVeExUUXVNVGMxSURrdU1EUTRMakF3TWlBeE5pNHpOemNnTnk0ek5DQXhOaTR6TnpjZ01UWXVNemt6SURBZ055NDRNek10Tnk0M05TQXhOQzQxTXpFdE9TNDJPRGdnTVRZdU1EYzBMVGt1TURZZ055NHlNVFV0TWpBdU9EWTBJREV4TGpRek5pMHpNeTQyTURRZ01URXVORE0yY3kweU5DNDFOQzAwTGpJeE9DMHpNeTQyTURNdE1URXVORE0yWXkweExqa3lOeTB4TGpVME5TMDVMalk0T1MwNExqSTFOUzA1TGpZNE9TMHhOaTR3TnpZZ01DMDVMakExTkNBM0xqTXpNeTB4Tmk0ek9UTWdNVFl1TXpjekxURTJMak01TXlBMExqQTNOQ0F3SURjdU56TWdNUzQxT1RnZ01UQXVOVEUxSURRdU1UYzFiQzR5T0RjdExqRTBNME0xT1M0NE1TQTBOaTQyTmpZZ05qWXVOamdnTkRBZ056VXVNamtnTkRCek1UVXVORGdnTmk0Mk5qWWdNVFl1TVRFNElERTFMakV4Tkd3dU1qZzNMakUwTTNvaUlHWnBiR3c5SWlNeVF6SkVNekFpSUdacGJHd3RiM0JoWTJsMGVUMGlMak1pSUhOMGVXeGxQU0ptYVd4c09pTXlZekprTXpBN1ptbHNiRHBqYjJ4dmNpaGthWE53YkdGNUxYQXpJQzR4TnpJMUlDNHhOelkxSUM0eE9EZ3lLVHRtYVd4c0xXOXdZV05wZEhrNkxqTWlMejQ4ZEdWNGRDQm1hV3hzUFNJak1rTXlSRE13SWlCbWFXeHNMVzl3WVdOcGRIazlJaTQxSWlCbWIyNTBMV1poYldsc2VUMGljMkZ1Y3kxelpYSnBaaUlnWm05dWRDMXphWHBsUFNJek1pSWdabTl1ZEMxM1pXbG5hSFE5SWpZd01DSWdiR1YwZEdWeUxYTndZV05wYm1jOUlqQmxiU0krUEhSemNHRnVJSGc5SWpNeUlpQjVQU0kwTURBaVBteGxibk04TDNSemNHRnVQand2ZEdWNGRENDhkR1Y0ZENCNGJXdzZjM0JoWTJVOUluQnlaWE5sY25abElpQm1hV3hzUFNJak1rTXlSRE13SWlCbWIyNTBMV1poYldsc2VUMGljMkZ1Y3kxelpYSnBaaUlnWm05dWRDMXphWHBsUFNJMU1pSWdabTl1ZEMxM1pXbG5hSFE5SWpZd01DSWdiR1YwZEdWeUxYTndZV05wYm1jOUlqQmxiU0lnYzNSNWJHVTlJbVpwYkd3Nkl6SmpNbVF6TUR0bWFXeHNPbU52Ykc5eUtHUnBjM0JzWVhrdGNETWdMakUzTWpVZ0xqRTNOalVnTGpFNE9ESXBPMlpwYkd3dGIzQmhZMmwwZVRveElqNDhkSE53WVc0Z2VEMGlNeklpSUhrOUlqUTJPQ0krZDNkM1BDOTBjM0JoYmo0OEwzUmxlSFErUEM5emRtYysiLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiIzIn1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___lens.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQGxlbnMiLCJkZXNjcmlwdGlvbiI6ImxlbnMgLSBAbGVucyIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG14bGJuTThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kxTWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSStiR1Z1Y3p3dmRITndZVzQrUEM5MFpYaDBQand2YzNablBnPT0iLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiI0In1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___stani.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHN0YW5pIiwiZGVzY3JpcHRpb24iOiJsZW5zIC0gQHN0YW5pIiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQbXhsYm5NOEwzUnpjR0Z1UGp3dmRHVjRkRDQ4ZEdWNGRDQjRiV3c2YzNCaFkyVTlJbkJ5WlhObGNuWmxJaUJtYVd4c1BTSWpNa015UkRNd0lpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSTFNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJZ2MzUjViR1U5SW1acGJHdzZJekpqTW1Rek1EdG1hV3hzT21OdmJHOXlLR1JwYzNCc1lYa3RjRE1nTGpFM01qVWdMakUzTmpVZ0xqRTRPRElwTzJacGJHd3RiM0JoWTJsMGVUb3hJajQ4ZEhOd1lXNGdlRDBpTXpJaUlIazlJalEyT0NJK2MzUmhibWs4TDNSemNHRnVQand2ZEdWNGRENDhMM04yWno0PSIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJsZW5ndGgiLCJ2YWx1ZSI6IjUifV19 -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3ciLCJkZXNjcmlwdGlvbiI6ImxlbnMgLSBAd3d3dyIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG14bGJuTThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kxTWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSStkM2QzZHp3dmRITndZVzQrUEM5MFpYaDBQand2YzNablBnPT0iLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiI0In1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3IiwiZGVzY3JpcHRpb24iOiJsZW5zIC0gQHd3d3d3IiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQbXhsYm5NOEwzUnpjR0Z1UGp3dmRHVjRkRDQ4ZEdWNGRDQjRiV3c2YzNCaFkyVTlJbkJ5WlhObGNuWmxJaUJtYVd4c1BTSWpNa015UkRNd0lpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSTFNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJZ2MzUjViR1U5SW1acGJHdzZJekpqTW1Rek1EdG1hV3hzT21OdmJHOXlLR1JwYzNCc1lYa3RjRE1nTGpFM01qVWdMakUzTmpVZ0xqRTRPRElwTzJacGJHd3RiM0JoWTJsMGVUb3hJajQ4ZEhOd1lXNGdlRDBpTXpJaUlIazlJalEyT0NJK2QzZDNkM2M4TDNSemNHRnVQand2ZEdWNGRENDhMM04yWno0PSIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJsZW5ndGgiLCJ2YWx1ZSI6IjUifV19 -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3dyIsImRlc2NyaXB0aW9uIjoibGVucyAtIEB3d3d3d3ciLCJpbWFnZSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJMU1USWlJR2hsYVdkb2REMGlOVEV5SWlCbWFXeHNQU0p1YjI1bElpQjJhV1YzUW05NFBTSXdJREFnTlRFeUlEVXhNaUkrUEhCaGRHZ2dabWxzYkQwaUkyWm1aaUlnWkQwaVRUQWdNR2cxTVRKMk5URXlTREI2SWlCemRIbHNaVDBpWm1sc2JEb2pabVptTzJacGJHd3RiM0JoWTJsMGVUb3hJaTgrUEhCaGRHZ2dabWxzYkMxeWRXeGxQU0psZG1WdWIyUmtJaUJqYkdsd0xYSjFiR1U5SW1WMlpXNXZaR1FpSUdROUlrMDVNUzQyT1RZZ05UVXVNalU1WXpJdU56ZzFMVEl1TlRjM0lEWXVORE01TFRRdU1UYzFJREV3TGpVeExUUXVNVGMxSURrdU1EUTRMakF3TWlBeE5pNHpOemNnTnk0ek5DQXhOaTR6TnpjZ01UWXVNemt6SURBZ055NDRNek10Tnk0M05TQXhOQzQxTXpFdE9TNDJPRGdnTVRZdU1EYzBMVGt1TURZZ055NHlNVFV0TWpBdU9EWTBJREV4TGpRek5pMHpNeTQyTURRZ01URXVORE0yY3kweU5DNDFOQzAwTGpJeE9DMHpNeTQyTURNdE1URXVORE0yWXkweExqa3lOeTB4TGpVME5TMDVMalk0T1MwNExqSTFOUzA1TGpZNE9TMHhOaTR3TnpZZ01DMDVMakExTkNBM0xqTXpNeTB4Tmk0ek9UTWdNVFl1TXpjekxURTJMak01TXlBMExqQTNOQ0F3SURjdU56TWdNUzQxT1RnZ01UQXVOVEUxSURRdU1UYzFiQzR5T0RjdExqRTBNME0xT1M0NE1TQTBOaTQyTmpZZ05qWXVOamdnTkRBZ056VXVNamtnTkRCek1UVXVORGdnTmk0Mk5qWWdNVFl1TVRFNElERTFMakV4Tkd3dU1qZzNMakUwTTNvaUlHWnBiR3c5SWlNeVF6SkVNekFpSUdacGJHd3RiM0JoWTJsMGVUMGlMak1pSUhOMGVXeGxQU0ptYVd4c09pTXlZekprTXpBN1ptbHNiRHBqYjJ4dmNpaGthWE53YkdGNUxYQXpJQzR4TnpJMUlDNHhOelkxSUM0eE9EZ3lLVHRtYVd4c0xXOXdZV05wZEhrNkxqTWlMejQ4ZEdWNGRDQm1hV3hzUFNJak1rTXlSRE13SWlCbWFXeHNMVzl3WVdOcGRIazlJaTQxSWlCbWIyNTBMV1poYldsc2VUMGljMkZ1Y3kxelpYSnBaaUlnWm05dWRDMXphWHBsUFNJek1pSWdabTl1ZEMxM1pXbG5hSFE5SWpZd01DSWdiR1YwZEdWeUxYTndZV05wYm1jOUlqQmxiU0krUEhSemNHRnVJSGc5SWpNeUlpQjVQU0kwTURBaVBteGxibk04TDNSemNHRnVQand2ZEdWNGRENDhkR1Y0ZENCNGJXdzZjM0JoWTJVOUluQnlaWE5sY25abElpQm1hV3hzUFNJak1rTXlSRE13SWlCbWIyNTBMV1poYldsc2VUMGljMkZ1Y3kxelpYSnBaaUlnWm05dWRDMXphWHBsUFNJMU1pSWdabTl1ZEMxM1pXbG5hSFE5SWpZd01DSWdiR1YwZEdWeUxYTndZV05wYm1jOUlqQmxiU0lnYzNSNWJHVTlJbVpwYkd3Nkl6SmpNbVF6TUR0bWFXeHNPbU52Ykc5eUtHUnBjM0JzWVhrdGNETWdMakUzTWpVZ0xqRTNOalVnTGpFNE9ESXBPMlpwYkd3dGIzQmhZMmwwZVRveElqNDhkSE53WVc0Z2VEMGlNeklpSUhrOUlqUTJPQ0krZDNkM2QzZDNQQzkwYzNCaGJqNDhMM1JsZUhRK1BDOXpkbWMrIiwiYXR0cmlidXRlcyI6W3sidHJhaXRfdHlwZSI6Imxlbmd0aCIsInZhbHVlIjoiNiJ9XX0= -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/orb___jordan.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQGpvcmRhbiIsImRlc2NyaXB0aW9uIjoib3JiIC0gQGpvcmRhbiIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG05eVlqd3ZkSE53WVc0K1BDOTBaWGgwUGp4MFpYaDBJSGh0YkRwemNHRmpaVDBpY0hKbGMyVnlkbVVpSUdacGJHdzlJaU15UXpKRU16QWlJR1p2Ym5RdFptRnRhV3g1UFNKellXNXpMWE5sY21sbUlpQm1iMjUwTFhOcGVtVTlJalV5SWlCbWIyNTBMWGRsYVdkb2REMGlOakF3SWlCc1pYUjBaWEl0YzNCaFkybHVaejBpTUdWdElpQnpkSGxzWlQwaVptbHNiRG9qTW1NeVpETXdPMlpwYkd3NlkyOXNiM0lvWkdsemNHeGhlUzF3TXlBdU1UY3lOU0F1TVRjMk5TQXVNVGc0TWlrN1ptbHNiQzF2Y0dGamFYUjVPakVpUGp4MGMzQmhiaUI0UFNJek1pSWdlVDBpTkRZNElqNXFiM0prWVc0OEwzUnpjR0Z1UGp3dmRHVjRkRDQ4TDNOMlp6ND0iLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiI2In1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___satoshi.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHNhdG9zaGkiLCJkZXNjcmlwdGlvbiI6ImxlbnMgLSBAc2F0b3NoaSIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG14bGJuTThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kxTWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSStjMkYwYjNOb2FUd3ZkSE53WVc0K1BDOTBaWGgwUGp3dmMzWm5QZz09IiwiYXR0cmlidXRlcyI6W3sidHJhaXRfdHlwZSI6Imxlbmd0aCIsInZhbHVlIjoiNyJ9XX0= -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3d3ciLCJkZXNjcmlwdGlvbiI6ImxlbnMgLSBAd3d3d3d3dyIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG14bGJuTThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kxTWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSStkM2QzZDNkM2R6d3ZkSE53WVc0K1BDOTBaWGgwUGp3dmMzWm5QZz09IiwiYXR0cmlidXRlcyI6W3sidHJhaXRfdHlwZSI6Imxlbmd0aCIsInZhbHVlIjoiNyJ9XX0= -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3d3d3IiwiZGVzY3JpcHRpb24iOiJsZW5zIC0gQHd3d3d3d3d3IiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQbXhsYm5NOEwzUnpjR0Z1UGp3dmRHVjRkRDQ4ZEdWNGRDQjRiV3c2YzNCaFkyVTlJbkJ5WlhObGNuWmxJaUJtYVd4c1BTSWpNa015UkRNd0lpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSTFNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJZ2MzUjViR1U5SW1acGJHdzZJekpqTW1Rek1EdG1hV3hzT21OdmJHOXlLR1JwYzNCc1lYa3RjRE1nTGpFM01qVWdMakUzTmpVZ0xqRTRPRElwTzJacGJHd3RiM0JoWTJsMGVUb3hJajQ4ZEhOd1lXNGdlRDBpTXpJaUlIazlJalEyT0NJK2QzZDNkM2QzZDNjOEwzUnpjR0Z1UGp3dmRHVjRkRDQ4TDNOMlp6ND0iLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiI4In1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3d3d3dyIsImRlc2NyaXB0aW9uIjoibGVucyAtIEB3d3d3d3d3d3ciLCJpbWFnZSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJMU1USWlJR2hsYVdkb2REMGlOVEV5SWlCbWFXeHNQU0p1YjI1bElpQjJhV1YzUW05NFBTSXdJREFnTlRFeUlEVXhNaUkrUEhCaGRHZ2dabWxzYkQwaUkyWm1aaUlnWkQwaVRUQWdNR2cxTVRKMk5URXlTREI2SWlCemRIbHNaVDBpWm1sc2JEb2pabVptTzJacGJHd3RiM0JoWTJsMGVUb3hJaTgrUEhCaGRHZ2dabWxzYkMxeWRXeGxQU0psZG1WdWIyUmtJaUJqYkdsd0xYSjFiR1U5SW1WMlpXNXZaR1FpSUdROUlrMDVNUzQyT1RZZ05UVXVNalU1WXpJdU56ZzFMVEl1TlRjM0lEWXVORE01TFRRdU1UYzFJREV3TGpVeExUUXVNVGMxSURrdU1EUTRMakF3TWlBeE5pNHpOemNnTnk0ek5DQXhOaTR6TnpjZ01UWXVNemt6SURBZ055NDRNek10Tnk0M05TQXhOQzQxTXpFdE9TNDJPRGdnTVRZdU1EYzBMVGt1TURZZ055NHlNVFV0TWpBdU9EWTBJREV4TGpRek5pMHpNeTQyTURRZ01URXVORE0yY3kweU5DNDFOQzAwTGpJeE9DMHpNeTQyTURNdE1URXVORE0yWXkweExqa3lOeTB4TGpVME5TMDVMalk0T1MwNExqSTFOUzA1TGpZNE9TMHhOaTR3TnpZZ01DMDVMakExTkNBM0xqTXpNeTB4Tmk0ek9UTWdNVFl1TXpjekxURTJMak01TXlBMExqQTNOQ0F3SURjdU56TWdNUzQxT1RnZ01UQXVOVEUxSURRdU1UYzFiQzR5T0RjdExqRTBNME0xT1M0NE1TQTBOaTQyTmpZZ05qWXVOamdnTkRBZ056VXVNamtnTkRCek1UVXVORGdnTmk0Mk5qWWdNVFl1TVRFNElERTFMakV4Tkd3dU1qZzNMakUwTTNvaUlHWnBiR3c5SWlNeVF6SkVNekFpSUdacGJHd3RiM0JoWTJsMGVUMGlMak1pSUhOMGVXeGxQU0ptYVd4c09pTXlZekprTXpBN1ptbHNiRHBqYjJ4dmNpaGthWE53YkdGNUxYQXpJQzR4TnpJMUlDNHhOelkxSUM0eE9EZ3lLVHRtYVd4c0xXOXdZV05wZEhrNkxqTWlMejQ4ZEdWNGRDQm1hV3hzUFNJak1rTXlSRE13SWlCbWFXeHNMVzl3WVdOcGRIazlJaTQxSWlCbWIyNTBMV1poYldsc2VUMGljMkZ1Y3kxelpYSnBaaUlnWm05dWRDMXphWHBsUFNJek1pSWdabTl1ZEMxM1pXbG5hSFE5SWpZd01DSWdiR1YwZEdWeUxYTndZV05wYm1jOUlqQmxiU0krUEhSemNHRnVJSGc5SWpNeUlpQjVQU0kwTURBaVBteGxibk04TDNSemNHRnVQand2ZEdWNGRENDhkR1Y0ZENCNGJXdzZjM0JoWTJVOUluQnlaWE5sY25abElpQm1hV3hzUFNJak1rTXlSRE13SWlCbWIyNTBMV1poYldsc2VUMGljMkZ1Y3kxelpYSnBaaUlnWm05dWRDMXphWHBsUFNJMU1pSWdabTl1ZEMxM1pXbG5hSFE5SWpZd01DSWdiR1YwZEdWeUxYTndZV05wYm1jOUlqQmxiU0lnYzNSNWJHVTlJbVpwYkd3Nkl6SmpNbVF6TUR0bWFXeHNPbU52Ykc5eUtHUnBjM0JzWVhrdGNETWdMakUzTWpVZ0xqRTNOalVnTGpFNE9ESXBPMlpwYkd3dGIzQmhZMmwwZVRveElqNDhkSE53WVc0Z2VEMGlNeklpSUhrOUlqUTJPQ0krZDNkM2QzZDNkM2QzUEM5MGMzQmhiajQ4TDNSbGVIUStQQzl6ZG1jKyIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJsZW5ndGgiLCJ2YWx1ZSI6IjkifV19 -------------------------------------------------------------------------------- /contracts/core/base/MetadataBased.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.26; 3 | 4 | import {IMetadataBased} from "contracts/core/interfaces/IMetadataBased.sol"; 5 | import {Errors} from "contracts/core/types/Errors.sol"; 6 | 7 | abstract contract MetadataBased is IMetadataBased { 8 | // source = address(0) => Global MetadataURI 9 | // source = any other address => MetadataURI set by specific source 10 | struct MetadataURIStorage { 11 | mapping(address => string) metadataURI; 12 | } 13 | 14 | /// @custom:keccak lens.storage.metadataURI 15 | bytes32 constant STORAGE__METADATA_URI = 0x1e3609457e69da9dd0dabac13fb8ca9b651f93d6966199d652f92264e8b2ea10; 16 | 17 | function $metadataStorage() internal pure returns (MetadataURIStorage storage _storage) { 18 | assembly { 19 | _storage.slot := STORAGE__METADATA_URI 20 | } 21 | } 22 | 23 | function setMetadataURI(string memory metadataURI) external override { 24 | _beforeMetadataURIUpdate(metadataURI); 25 | // solc-ignore-next-line unreachable 26 | _setMetadataURI(metadataURI); 27 | } 28 | 29 | function _setMetadataURI(string memory metadataURI) internal { 30 | $metadataStorage().metadataURI[address(0)] = metadataURI; 31 | _emitMetadataURISet(metadataURI, address(0)); 32 | } 33 | 34 | function _setMetadataURI(string memory metadataURI, address source) internal { 35 | $metadataStorage().metadataURI[source] = metadataURI; 36 | _emitMetadataURISet(metadataURI, source); 37 | } 38 | 39 | function _beforeMetadataURIUpdate(string memory /* metadataURI */ ) internal virtual { 40 | revert Errors.NotImplemented(); 41 | } 42 | 43 | function _emitMetadataURISet(string memory, /* metadataURI */ address /* source */ ) internal virtual; 44 | 45 | function getMetadataURI() external view override returns (string memory) { 46 | return $metadataStorage().metadataURI[address(0)]; 47 | } 48 | 49 | function getMetadataURI(address source) external view returns (string memory) { 50 | return $metadataStorage().metadataURI[source]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/orb___customer23.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQGN1c3RvbWVyMjMiLCJkZXNjcmlwdGlvbiI6Im9yYiAtIEBjdXN0b21lcjIzIiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQbTl5WWp3dmRITndZVzQrUEM5MFpYaDBQangwWlhoMElIaHRiRHB6Y0dGalpUMGljSEpsYzJWeWRtVWlJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnZiblF0Wm1GdGFXeDVQU0p6WVc1ekxYTmxjbWxtSWlCbWIyNTBMWE5wZW1VOUlqVXlJaUJtYjI1MExYZGxhV2RvZEQwaU5qQXdJaUJzWlhSMFpYSXRjM0JoWTJsdVp6MGlNR1Z0SWlCemRIbHNaVDBpWm1sc2JEb2pNbU15WkRNd08yWnBiR3c2WTI5c2IzSW9aR2x6Y0d4aGVTMXdNeUF1TVRjeU5TQXVNVGMyTlNBdU1UZzRNaWs3Wm1sc2JDMXZjR0ZqYVhSNU9qRWlQangwYzNCaGJpQjRQU0l6TWlJZ2VUMGlORFk0SWo1amRYTjBiMjFsY2pJelBDOTBjM0JoYmo0OEwzUmxlSFErUEM5emRtYysiLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiIxMCJ9XX0= -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwwwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3d3d3d3ciLCJkZXNjcmlwdGlvbiI6ImxlbnMgLSBAd3d3d3d3d3d3dyIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG14bGJuTThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kxTWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSStkM2QzZDNkM2QzZDNkend2ZEhOd1lXNCtQQzkwWlhoMFBqd3ZjM1puUGc9PSIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJsZW5ndGgiLCJ2YWx1ZSI6IjEwIn1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwwwwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3d3d3d3d3IiwiZGVzY3JpcHRpb24iOiJsZW5zIC0gQHd3d3d3d3d3d3d3IiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQbXhsYm5NOEwzUnpjR0Z1UGp3dmRHVjRkRDQ4ZEdWNGRDQjRiV3c2YzNCaFkyVTlJbkJ5WlhObGNuWmxJaUJtYVd4c1BTSWpNa015UkRNd0lpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSTFNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJZ2MzUjViR1U5SW1acGJHdzZJekpqTW1Rek1EdG1hV3hzT21OdmJHOXlLR1JwYzNCc1lYa3RjRE1nTGpFM01qVWdMakUzTmpVZ0xqRTRPRElwTzJacGJHd3RiM0JoWTJsMGVUb3hJajQ4ZEhOd1lXNGdlRDBpTXpJaUlIazlJalEyT0NJK2QzZDNkM2QzZDNkM2QzYzhMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OEwzTjJaejQ9IiwiYXR0cmlidXRlcyI6W3sidHJhaXRfdHlwZSI6Imxlbmd0aCIsInZhbHVlIjoiMTEifV19 -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/CAPITALIZATION___TEST2.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHRlc3QyIiwiZGVzY3JpcHRpb24iOiJDQVBJVEFMSVpBVElPTiAtIEB0ZXN0MiIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG1OaGNHbDBZV3hwZW1GMGFXOXVQQzkwYzNCaGJqNDhMM1JsZUhRK1BIUmxlSFFnZUcxc09uTndZV05sUFNKd2NtVnpaWEoyWlNJZ1ptbHNiRDBpSXpKRE1rUXpNQ0lnWm05dWRDMW1ZVzFwYkhrOUluTmhibk10YzJWeWFXWWlJR1p2Ym5RdGMybDZaVDBpTlRJaUlHWnZiblF0ZDJWcFoyaDBQU0kyTURBaUlHeGxkSFJsY2kxemNHRmphVzVuUFNJd1pXMGlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TVNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME5qZ2lQblJsYzNReVBDOTBjM0JoYmo0OEwzUmxlSFErUEM5emRtYysiLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiI1In1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwwwwwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3d3d3d3d3dyIsImRlc2NyaXB0aW9uIjoibGVucyAtIEB3d3d3d3d3d3d3d3ciLCJpbWFnZSI6ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJMU1USWlJR2hsYVdkb2REMGlOVEV5SWlCbWFXeHNQU0p1YjI1bElpQjJhV1YzUW05NFBTSXdJREFnTlRFeUlEVXhNaUkrUEhCaGRHZ2dabWxzYkQwaUkyWm1aaUlnWkQwaVRUQWdNR2cxTVRKMk5URXlTREI2SWlCemRIbHNaVDBpWm1sc2JEb2pabVptTzJacGJHd3RiM0JoWTJsMGVUb3hJaTgrUEhCaGRHZ2dabWxzYkMxeWRXeGxQU0psZG1WdWIyUmtJaUJqYkdsd0xYSjFiR1U5SW1WMlpXNXZaR1FpSUdROUlrMDVNUzQyT1RZZ05UVXVNalU1WXpJdU56ZzFMVEl1TlRjM0lEWXVORE01TFRRdU1UYzFJREV3TGpVeExUUXVNVGMxSURrdU1EUTRMakF3TWlBeE5pNHpOemNnTnk0ek5DQXhOaTR6TnpjZ01UWXVNemt6SURBZ055NDRNek10Tnk0M05TQXhOQzQxTXpFdE9TNDJPRGdnTVRZdU1EYzBMVGt1TURZZ055NHlNVFV0TWpBdU9EWTBJREV4TGpRek5pMHpNeTQyTURRZ01URXVORE0yY3kweU5DNDFOQzAwTGpJeE9DMHpNeTQyTURNdE1URXVORE0yWXkweExqa3lOeTB4TGpVME5TMDVMalk0T1MwNExqSTFOUzA1TGpZNE9TMHhOaTR3TnpZZ01DMDVMakExTkNBM0xqTXpNeTB4Tmk0ek9UTWdNVFl1TXpjekxURTJMak01TXlBMExqQTNOQ0F3SURjdU56TWdNUzQxT1RnZ01UQXVOVEUxSURRdU1UYzFiQzR5T0RjdExqRTBNME0xT1M0NE1TQTBOaTQyTmpZZ05qWXVOamdnTkRBZ056VXVNamtnTkRCek1UVXVORGdnTmk0Mk5qWWdNVFl1TVRFNElERTFMakV4Tkd3dU1qZzNMakUwTTNvaUlHWnBiR3c5SWlNeVF6SkVNekFpSUdacGJHd3RiM0JoWTJsMGVUMGlMak1pSUhOMGVXeGxQU0ptYVd4c09pTXlZekprTXpBN1ptbHNiRHBqYjJ4dmNpaGthWE53YkdGNUxYQXpJQzR4TnpJMUlDNHhOelkxSUM0eE9EZ3lLVHRtYVd4c0xXOXdZV05wZEhrNkxqTWlMejQ4ZEdWNGRDQm1hV3hzUFNJak1rTXlSRE13SWlCbWFXeHNMVzl3WVdOcGRIazlJaTQxSWlCbWIyNTBMV1poYldsc2VUMGljMkZ1Y3kxelpYSnBaaUlnWm05dWRDMXphWHBsUFNJek1pSWdabTl1ZEMxM1pXbG5hSFE5SWpZd01DSWdiR1YwZEdWeUxYTndZV05wYm1jOUlqQmxiU0krUEhSemNHRnVJSGc5SWpNeUlpQjVQU0kwTURBaVBteGxibk04TDNSemNHRnVQand2ZEdWNGRENDhkR1Y0ZENCNGJXdzZjM0JoWTJVOUluQnlaWE5sY25abElpQm1hV3hzUFNJak1rTXlSRE13SWlCbWIyNTBMV1poYldsc2VUMGljMkZ1Y3kxelpYSnBaaUlnWm05dWRDMXphWHBsUFNJME9DSWdabTl1ZEMxM1pXbG5hSFE5SWpZd01DSWdiR1YwZEdWeUxYTndZV05wYm1jOUlqQmxiU0lnYzNSNWJHVTlJbVpwYkd3Nkl6SmpNbVF6TUR0bWFXeHNPbU52Ykc5eUtHUnBjM0JzWVhrdGNETWdMakUzTWpVZ0xqRTNOalVnTGpFNE9ESXBPMlpwYkd3dGIzQmhZMmwwZVRveElqNDhkSE53WVc0Z2VEMGlNeklpSUhrOUlqUTJPQ0krZDNkM2QzZDNkM2QzZDNkM1BDOTBjM0JoYmo0OEwzUmxlSFErUEM5emRtYysiLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiIxMiJ9XX0= -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwwwwwwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3d3d3d3d3d3ciLCJkZXNjcmlwdGlvbiI6ImxlbnMgLSBAd3d3d3d3d3d3d3d3dyIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG14bGJuTThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kwTkNJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSStkM2QzZDNkM2QzZDNkM2QzZHp3dmRITndZVzQrUEM5MFpYaDBQand2YzNablBnPT0iLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoibGVuZ3RoIiwidmFsdWUiOiIxMyJ9XX0= -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/UpperC4se___c0nVERSiOn.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQGMwbnZlcnNpb24iLCJkZXNjcmlwdGlvbiI6IlVwcGVyQzRzZSAtIEBjMG52ZXJzaW9uIiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQblZ3Y0dWeVl6UnpaVHd2ZEhOd1lXNCtQQzkwWlhoMFBqeDBaWGgwSUhodGJEcHpjR0ZqWlQwaWNISmxjMlZ5ZG1VaUlHWnBiR3c5SWlNeVF6SkVNekFpSUdadmJuUXRabUZ0YVd4NVBTSnpZVzV6TFhObGNtbG1JaUJtYjI1MExYTnBlbVU5SWpVeUlpQm1iMjUwTFhkbGFXZG9kRDBpTmpBd0lpQnNaWFIwWlhJdGMzQmhZMmx1WnowaU1HVnRJaUJ6ZEhsc1pUMGlabWxzYkRvak1tTXlaRE13TzJacGJHdzZZMjlzYjNJb1pHbHpjR3hoZVMxd015QXVNVGN5TlNBdU1UYzJOU0F1TVRnNE1pazdabWxzYkMxdmNHRmphWFI1T2pFaVBqeDBjM0JoYmlCNFBTSXpNaUlnZVQwaU5EWTRJajVqTUc1MlpYSnphVzl1UEM5MGMzQmhiajQ4TDNSbGVIUStQQzl6ZG1jKyIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJsZW5ndGgiLCJ2YWx1ZSI6IjEwIn1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___donosonaumczuk.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQGRvbm9zb25hdW1jenVrIiwiZGVzY3JpcHRpb24iOiJsZW5zIC0gQGRvbm9zb25hdW1jenVrIiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQbXhsYm5NOEwzUnpjR0Z1UGp3dmRHVjRkRDQ4ZEdWNGRDQjRiV3c2YzNCaFkyVTlJbkJ5WlhObGNuWmxJaUJtYVd4c1BTSWpNa015UkRNd0lpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSTBNU0lnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJZ2MzUjViR1U5SW1acGJHdzZJekpqTW1Rek1EdG1hV3hzT21OdmJHOXlLR1JwYzNCc1lYa3RjRE1nTGpFM01qVWdMakUzTmpVZ0xqRTRPRElwTzJacGJHd3RiM0JoWTJsMGVUb3hJajQ4ZEhOd1lXNGdlRDBpTXpJaUlIazlJalEyT0NJK1pHOXViM052Ym1GMWJXTjZkV3M4TDNSemNHRnVQand2ZEdWNGRENDhMM04yWno0PSIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJsZW5ndGgiLCJ2YWx1ZSI6IjE0In1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/lens___wwwwwwwwwwwwww.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHd3d3d3d3d3d3d3d3d3IiwiZGVzY3JpcHRpb24iOiJsZW5zIC0gQHd3d3d3d3d3d3d3d3d3IiwiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSTFNVElpSUdobGFXZG9kRDBpTlRFeUlpQm1hV3hzUFNKdWIyNWxJaUIyYVdWM1FtOTRQU0l3SURBZ05URXlJRFV4TWlJK1BIQmhkR2dnWm1sc2JEMGlJMlptWmlJZ1pEMGlUVEFnTUdnMU1USjJOVEV5U0RCNklpQnpkSGxzWlQwaVptbHNiRG9qWm1abU8yWnBiR3d0YjNCaFkybDBlVG94SWk4K1BIQmhkR2dnWm1sc2JDMXlkV3hsUFNKbGRtVnViMlJrSWlCamJHbHdMWEoxYkdVOUltVjJaVzV2WkdRaUlHUTlJazA1TVM0Mk9UWWdOVFV1TWpVNVl6SXVOemcxTFRJdU5UYzNJRFl1TkRNNUxUUXVNVGMxSURFd0xqVXhMVFF1TVRjMUlEa3VNRFE0TGpBd01pQXhOaTR6TnpjZ055NHpOQ0F4Tmk0ek56Y2dNVFl1TXpreklEQWdOeTQ0TXpNdE55NDNOU0F4TkM0MU16RXRPUzQyT0RnZ01UWXVNRGMwTFRrdU1EWWdOeTR5TVRVdE1qQXVPRFkwSURFeExqUXpOaTB6TXk0Mk1EUWdNVEV1TkRNMmN5MHlOQzQxTkMwMExqSXhPQzB6TXk0Mk1ETXRNVEV1TkRNMll5MHhMamt5TnkweExqVTBOUzA1TGpZNE9TMDRMakkxTlMwNUxqWTRPUzB4Tmk0d056WWdNQzA1TGpBMU5DQTNMak16TXkweE5pNHpPVE1nTVRZdU16Y3pMVEUyTGpNNU15QTBMakEzTkNBd0lEY3VOek1nTVM0MU9UZ2dNVEF1TlRFMUlEUXVNVGMxYkM0eU9EY3RMakUwTTBNMU9TNDRNU0EwTmk0Mk5qWWdOall1TmpnZ05EQWdOelV1TWprZ05EQnpNVFV1TkRnZ05pNDJOallnTVRZdU1URTRJREUxTGpFeE5Hd3VNamczTGpFME0zb2lJR1pwYkd3OUlpTXlRekpFTXpBaUlHWnBiR3d0YjNCaFkybDBlVDBpTGpNaUlITjBlV3hsUFNKbWFXeHNPaU15WXpKa016QTdabWxzYkRwamIyeHZjaWhrYVhOd2JHRjVMWEF6SUM0eE56STFJQzR4TnpZMUlDNHhPRGd5S1R0bWFXeHNMVzl3WVdOcGRIazZMak1pTHo0OGRHVjRkQ0JtYVd4c1BTSWpNa015UkRNd0lpQm1hV3hzTFc5d1lXTnBkSGs5SWk0MUlpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSXpNaUlnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJK1BIUnpjR0Z1SUhnOUlqTXlJaUI1UFNJME1EQWlQbXhsYm5NOEwzUnpjR0Z1UGp3dmRHVjRkRDQ4ZEdWNGRDQjRiV3c2YzNCaFkyVTlJbkJ5WlhObGNuWmxJaUJtYVd4c1BTSWpNa015UkRNd0lpQm1iMjUwTFdaaGJXbHNlVDBpYzJGdWN5MXpaWEpwWmlJZ1ptOXVkQzF6YVhwbFBTSTBNU0lnWm05dWRDMTNaV2xuYUhROUlqWXdNQ0lnYkdWMGRHVnlMWE53WVdOcGJtYzlJakJsYlNJZ2MzUjViR1U5SW1acGJHdzZJekpqTW1Rek1EdG1hV3hzT21OdmJHOXlLR1JwYzNCc1lYa3RjRE1nTGpFM01qVWdMakUzTmpVZ0xqRTRPRElwTzJacGJHd3RiM0JoWTJsMGVUb3hJajQ4ZEhOd1lXNGdlRDBpTXpJaUlIazlJalEyT0NJK2QzZDNkM2QzZDNkM2QzZDNkM2M4TDNSemNHRnVQand2ZEdWNGRENDhMM04yWno0PSIsImF0dHJpYnV0ZXMiOlt7InRyYWl0X3R5cGUiOiJsZW5ndGgiLCJ2YWx1ZSI6IjE0In1dfQ== -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/somelongerapp___im_alan.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQGltX2FsYW4iLCJkZXNjcmlwdGlvbiI6InNvbWVsb25nZXJhcHAgLSBAaW1fYWxhbiIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG5OdmJXVnNiMjVuWlhKaGNIQThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kxTWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSSthVzFmWVd4aGJqd3ZkSE53WVc0K1BDOTBaWGgwUGp3dmMzWm5QZz09IiwiYXR0cmlidXRlcyI6W3sidHJhaXRfdHlwZSI6Imxlbmd0aCIsInZhbHVlIjoiNyJ9XX0= -------------------------------------------------------------------------------- /test/token-uri/expected-svgs/somelongerapp___vitalik.base64: -------------------------------------------------------------------------------- 1 | data:application/json;base64,eyJuYW1lIjoiQHZpdGFsaWsiLCJkZXNjcmlwdGlvbiI6InNvbWVsb25nZXJhcHAgLSBAdml0YWxpayIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0kxTVRJaUlHaGxhV2RvZEQwaU5URXlJaUJtYVd4c1BTSnViMjVsSWlCMmFXVjNRbTk0UFNJd0lEQWdOVEV5SURVeE1pSStQSEJoZEdnZ1ptbHNiRDBpSTJabVppSWdaRDBpVFRBZ01HZzFNVEoyTlRFeVNEQjZJaUJ6ZEhsc1pUMGlabWxzYkRvalptWm1PMlpwYkd3dGIzQmhZMmwwZVRveElpOCtQSEJoZEdnZ1ptbHNiQzF5ZFd4bFBTSmxkbVZ1YjJSa0lpQmpiR2x3TFhKMWJHVTlJbVYyWlc1dlpHUWlJR1E5SWswNU1TNDJPVFlnTlRVdU1qVTVZekl1TnpnMUxUSXVOVGMzSURZdU5ETTVMVFF1TVRjMUlERXdMalV4TFRRdU1UYzFJRGt1TURRNExqQXdNaUF4Tmk0ek56Y2dOeTR6TkNBeE5pNHpOemNnTVRZdU16a3pJREFnTnk0NE16TXROeTQzTlNBeE5DNDFNekV0T1M0Mk9EZ2dNVFl1TURjMExUa3VNRFlnTnk0eU1UVXRNakF1T0RZMElERXhMalF6Tmkwek15NDJNRFFnTVRFdU5ETTJjeTB5TkM0MU5DMDBMakl4T0Mwek15NDJNRE10TVRFdU5ETTJZeTB4TGpreU55MHhMalUwTlMwNUxqWTRPUzA0TGpJMU5TMDVMalk0T1MweE5pNHdOellnTUMwNUxqQTFOQ0EzTGpNek15MHhOaTR6T1RNZ01UWXVNemN6TFRFMkxqTTVNeUEwTGpBM05DQXdJRGN1TnpNZ01TNDFPVGdnTVRBdU5URTFJRFF1TVRjMWJDNHlPRGN0TGpFME0wTTFPUzQ0TVNBME5pNDJOallnTmpZdU5qZ2dOREFnTnpVdU1qa2dOREJ6TVRVdU5EZ2dOaTQyTmpZZ01UWXVNVEU0SURFMUxqRXhOR3d1TWpnM0xqRTBNM29pSUdacGJHdzlJaU15UXpKRU16QWlJR1pwYkd3dGIzQmhZMmwwZVQwaUxqTWlJSE4wZVd4bFBTSm1hV3hzT2lNeVl6SmtNekE3Wm1sc2JEcGpiMnh2Y2loa2FYTndiR0Y1TFhBeklDNHhOekkxSUM0eE56WTFJQzR4T0RneUtUdG1hV3hzTFc5d1lXTnBkSGs2TGpNaUx6NDhkR1Y0ZENCbWFXeHNQU0lqTWtNeVJETXdJaUJtYVd4c0xXOXdZV05wZEhrOUlpNDFJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0l6TWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSStQSFJ6Y0dGdUlIZzlJak15SWlCNVBTSTBNREFpUG5OdmJXVnNiMjVuWlhKaGNIQThMM1J6Y0dGdVBqd3ZkR1Y0ZEQ0OGRHVjRkQ0I0Yld3NmMzQmhZMlU5SW5CeVpYTmxjblpsSWlCbWFXeHNQU0lqTWtNeVJETXdJaUJtYjI1MExXWmhiV2xzZVQwaWMyRnVjeTF6WlhKcFppSWdabTl1ZEMxemFYcGxQU0kxTWlJZ1ptOXVkQzEzWldsbmFIUTlJall3TUNJZ2JHVjBkR1Z5TFhOd1lXTnBibWM5SWpCbGJTSWdjM1I1YkdVOUltWnBiR3c2SXpKak1tUXpNRHRtYVd4c09tTnZiRzl5S0dScGMzQnNZWGt0Y0RNZ0xqRTNNalVnTGpFM05qVWdMakU0T0RJcE8yWnBiR3d0YjNCaFkybDBlVG94SWo0OGRITndZVzRnZUQwaU16SWlJSGs5SWpRMk9DSStkbWwwWVd4cGF6d3ZkSE53WVc0K1BDOTBaWGgwUGp3dmMzWm5QZz09IiwiYXR0cmlidXRlcyI6W3sidHJhaXRfdHlwZSI6Imxlbmd0aCIsInZhbHVlIjoiNyJ9XX0= --------------------------------------------------------------------------------