epistery 1.5.2 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"id":"5a987fca94856a824565c891c6bdbcbd","_format":"hh-sol-build-info-1","solcVersion":"0.8.27","solcLongVersion":"0.8.27+commit.40a35a09","input":{"language":"Solidity","sources":{"contracts/IAddressNaming.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title IAddressNaming\n * @notice Canonical interface for resolving identity names from addresses.\n *\n * Names belong to the address itself, not to any (address, list) join.\n * Roles (\"owner\", \"admin\", \"read\", ...) remain on whitelist / ACL entries;\n * names do not. The same address may carry different per-list handles in\n * legacy WhitelistEntry.name fields, but its identity name lives behind\n * this interface.\n *\n * Implementations may be multi-tenant or single-tenant:\n * - Multi-tenant (e.g. epistery Agent.sol): the read accepts an\n * ownerAddress scope; writes are keyed by msg.sender.\n * - Single-tenant (e.g. epistery-host DomainAgent.sol): the read's\n * ownerAddress argument is accepted on the signature for ABI\n * compatibility but ignored; writes are admin-gated.\n *\n * Off-chain consumers (e.g. epistery's Utils.ResolveAddressName,\n * Utils.SetAddressName) hold against this interface, so they work\n * uniformly against either implementation without branching on\n * contract type.\n */\ninterface IAddressNaming {\n /**\n * @notice Set the human-readable name for an address.\n * @param addr The address to name\n * @param name The name string; empty string clears\n */\n function setAddressName(address addr, string memory name) external;\n\n /**\n * @notice Resolve an address to its name.\n * @param ownerAddress The naming-scope owner; ignored by single-tenant\n * implementations, used by multi-tenant ones\n * @param addr The address to resolve\n * @return The name, or empty string if unset\n */\n function getAddressName(address ownerAddress, address addr) external view returns (string memory);\n}\n"},"contracts/ICreditAccount.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title ICreditAccount\n * @notice Canonical interface for credit-bearing user accounts.\n *\n * Signatures match Steven's `UniversalTeamRegistryV4` on Polygon mainnet\n * (`0x83B25fDD25516057AaaAf8027464C8bbb2f91d5B`) so that contract can\n * declare conformance without renaming any existing methods.\n *\n * Implementations decide the conversion between native token (POL/ETH)\n * and credits. UniversalTeamRegistryV4 uses 1 POL = 1,000,000 credits.\n *\n * The user-facing surface is deposit + read. Implementation-private\n * operations (funding specific child contracts like Secrets or KeyVaults,\n * setting rate tables, etc.) are NOT part of this interface — they're\n * authorization-gated internals.\n *\n * Epistery does not yet implement credit accounting. This interface is\n * declared on the epistery side as the cross-system spec; epistery\n * contracts may adopt it later, or epistery-host may delegate credit\n * operations to rootz-v6's deployed Registry.\n */\ninterface ICreditAccount {\n /**\n * @notice Emitted when credits are added to an account.\n * @param user The user whose balance increased\n * @param amount The credit amount added\n */\n event CreditsDeposited(address indexed user, uint256 amount);\n\n /**\n * @notice Deposit credits for `user`, paying with native token.\n * The native token amount is `msg.value`; implementations convert\n * to credit units according to their rate table.\n * @param user The account to credit\n * @param amount The credit amount to deposit\n */\n function depositCredits(address user, uint256 amount) external payable;\n\n /**\n * @notice Read the current credit balance for `user`.\n * @param user The account to read\n * @return The current credit balance\n */\n function getUserCredits(address user) external view returns (uint256);\n}\n"},"contracts/IUserRegistry.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./IAddressNaming.sol\";\n\n/**\n * @title IUserRegistry\n * @notice Canonical interface for \"I am a user registry\" — the kind of\n * service that knows which addresses belong to people on this system.\n *\n * Carries IAddressNaming as a base because name resolution is a user-registry\n * responsibility. Adds a single membership predicate; implementations decide\n * what \"registered\" means in their domain:\n *\n * - epistery Agent.sol / DomainAgent.sol: addresses listed on any\n * whitelist / ACL under this contract.\n * - rootz-v6 UniversalTeamRegistryV4: addresses with a credit account,\n * authorized factory, or team membership.\n * - rootz-v6 IdentityContractV3: addresses authorized as rivets on this\n * identity.\n *\n * Off-chain code that wants to ask \"does this system know this address?\"\n * holds against this interface and gets a uniform answer.\n */\ninterface IUserRegistry is IAddressNaming {\n /**\n * @notice True if this registry has a record of the address.\n * \"Record\" is implementation-defined — membership, identity,\n * credit account, rivet registration, etc.\n * @param addr The address to check\n * @return True if the address is known to this registry\n */\n function isRegistered(address addr) external view returns (bool);\n}\n"}},"settings":{"optimizer":{"enabled":true,"runs":200},"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}}}},"output":{"sources":{"contracts/IAddressNaming.sol":{"ast":{"absolutePath":"contracts/IAddressNaming.sol","exportedSymbols":{"IAddressNaming":[21]},"id":22,"license":"MIT","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity","^","0.8",".0"],"nodeType":"PragmaDirective","src":"32:23:0"},{"abstract":false,"baseContracts":[],"canonicalName":"IAddressNaming","contractDependencies":[],"contractKind":"interface","documentation":{"id":2,"nodeType":"StructuredDocumentation","src":"57:1023:0","text":" @title IAddressNaming\n @notice Canonical interface for resolving identity names from addresses.\n Names belong to the address itself, not to any (address, list) join.\n Roles (\"owner\", \"admin\", \"read\", ...) remain on whitelist / ACL entries;\n names do not. The same address may carry different per-list handles in\n legacy WhitelistEntry.name fields, but its identity name lives behind\n this interface.\n Implementations may be multi-tenant or single-tenant:\n - Multi-tenant (e.g. epistery Agent.sol): the read accepts an\n ownerAddress scope; writes are keyed by msg.sender.\n - Single-tenant (e.g. epistery-host DomainAgent.sol): the read's\n ownerAddress argument is accepted on the signature for ABI\n compatibility but ignored; writes are admin-gated.\n Off-chain consumers (e.g. epistery's Utils.ResolveAddressName,\n Utils.SetAddressName) hold against this interface, so they work\n uniformly against either implementation without branching on\n contract type."},"fullyImplemented":false,"id":21,"linearizedBaseContracts":[21],"name":"IAddressNaming","nameLocation":"1091:14:0","nodeType":"ContractDefinition","nodes":[{"documentation":{"id":3,"nodeType":"StructuredDocumentation","src":"1112:165:0","text":" @notice Set the human-readable name for an address.\n @param addr The address to name\n @param name The name string; empty string clears"},"functionSelector":"3037c5ad","id":10,"implemented":false,"kind":"function","modifiers":[],"name":"setAddressName","nameLocation":"1291:14:0","nodeType":"FunctionDefinition","parameters":{"id":8,"nodeType":"ParameterList","parameters":[{"constant":false,"id":5,"mutability":"mutable","name":"addr","nameLocation":"1314:4:0","nodeType":"VariableDeclaration","scope":10,"src":"1306:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":4,"name":"address","nodeType":"ElementaryTypeName","src":"1306:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":7,"mutability":"mutable","name":"name","nameLocation":"1334:4:0","nodeType":"VariableDeclaration","scope":10,"src":"1320:18:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":6,"name":"string","nodeType":"ElementaryTypeName","src":"1320:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"1305:34:0"},"returnParameters":{"id":9,"nodeType":"ParameterList","parameters":[],"src":"1348:0:0"},"scope":21,"src":"1282:67:0","stateMutability":"nonpayable","virtual":false,"visibility":"external"},{"documentation":{"id":11,"nodeType":"StructuredDocumentation","src":"1355:283:0","text":" @notice Resolve an address to its name.\n @param ownerAddress The naming-scope owner; ignored by single-tenant\n implementations, used by multi-tenant ones\n @param addr The address to resolve\n @return The name, or empty string if unset"},"functionSelector":"b715bb74","id":20,"implemented":false,"kind":"function","modifiers":[],"name":"getAddressName","nameLocation":"1652:14:0","nodeType":"FunctionDefinition","parameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":13,"mutability":"mutable","name":"ownerAddress","nameLocation":"1675:12:0","nodeType":"VariableDeclaration","scope":20,"src":"1667:20:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":12,"name":"address","nodeType":"ElementaryTypeName","src":"1667:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":15,"mutability":"mutable","name":"addr","nameLocation":"1697:4:0","nodeType":"VariableDeclaration","scope":20,"src":"1689:12:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":14,"name":"address","nodeType":"ElementaryTypeName","src":"1689:7:0","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"1666:36:0"},"returnParameters":{"id":19,"nodeType":"ParameterList","parameters":[{"constant":false,"id":18,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":20,"src":"1726:13:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":17,"name":"string","nodeType":"ElementaryTypeName","src":"1726:6:0","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"1725:15:0"},"scope":21,"src":"1643:98:0","stateMutability":"view","virtual":false,"visibility":"external"}],"scope":22,"src":"1081:662:0","usedErrors":[],"usedEvents":[]}],"src":"32:1712:0"},"id":0},"contracts/ICreditAccount.sol":{"ast":{"absolutePath":"contracts/ICreditAccount.sol","exportedSymbols":{"ICreditAccount":[48]},"id":49,"license":"MIT","nodeType":"SourceUnit","nodes":[{"id":23,"literals":["solidity","^","0.8",".0"],"nodeType":"PragmaDirective","src":"32:23:1"},{"abstract":false,"baseContracts":[],"canonicalName":"ICreditAccount","contractDependencies":[],"contractKind":"interface","documentation":{"id":24,"nodeType":"StructuredDocumentation","src":"57:968:1","text":" @title ICreditAccount\n @notice Canonical interface for credit-bearing user accounts.\n Signatures match Steven's `UniversalTeamRegistryV4` on Polygon mainnet\n (`0x83B25fDD25516057AaaAf8027464C8bbb2f91d5B`) so that contract can\n declare conformance without renaming any existing methods.\n Implementations decide the conversion between native token (POL/ETH)\n and credits. UniversalTeamRegistryV4 uses 1 POL = 1,000,000 credits.\n The user-facing surface is deposit + read. Implementation-private\n operations (funding specific child contracts like Secrets or KeyVaults,\n setting rate tables, etc.) are NOT part of this interface — they're\n authorization-gated internals.\n Epistery does not yet implement credit accounting. This interface is\n declared on the epistery side as the cross-system spec; epistery\n contracts may adopt it later, or epistery-host may delegate credit\n operations to rootz-v6's deployed Registry."},"fullyImplemented":false,"id":48,"linearizedBaseContracts":[48],"name":"ICreditAccount","nameLocation":"1036:14:1","nodeType":"ContractDefinition","nodes":[{"anonymous":false,"documentation":{"id":25,"nodeType":"StructuredDocumentation","src":"1057:169:1","text":" @notice Emitted when credits are added to an account.\n @param user The user whose balance increased\n @param amount The credit amount added"},"eventSelector":"39a4ec3b6d79868398fdc7602609ac7bba0e4a84df0ec6ff2007fdf0691431c7","id":31,"name":"CreditsDeposited","nameLocation":"1237:16:1","nodeType":"EventDefinition","parameters":{"id":30,"nodeType":"ParameterList","parameters":[{"constant":false,"id":27,"indexed":true,"mutability":"mutable","name":"user","nameLocation":"1270:4:1","nodeType":"VariableDeclaration","scope":31,"src":"1254:20:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":26,"name":"address","nodeType":"ElementaryTypeName","src":"1254:7:1","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":29,"indexed":false,"mutability":"mutable","name":"amount","nameLocation":"1284:6:1","nodeType":"VariableDeclaration","scope":31,"src":"1276:14:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":28,"name":"uint256","nodeType":"ElementaryTypeName","src":"1276:7:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1253:38:1"},"src":"1231:61:1"},{"documentation":{"id":32,"nodeType":"StructuredDocumentation","src":"1298:296:1","text":" @notice Deposit credits for `user`, paying with native token.\n The native token amount is `msg.value`; implementations convert\n to credit units according to their rate table.\n @param user The account to credit\n @param amount The credit amount to deposit"},"functionSelector":"1409f160","id":39,"implemented":false,"kind":"function","modifiers":[],"name":"depositCredits","nameLocation":"1608:14:1","nodeType":"FunctionDefinition","parameters":{"id":37,"nodeType":"ParameterList","parameters":[{"constant":false,"id":34,"mutability":"mutable","name":"user","nameLocation":"1631:4:1","nodeType":"VariableDeclaration","scope":39,"src":"1623:12:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":33,"name":"address","nodeType":"ElementaryTypeName","src":"1623:7:1","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"},{"constant":false,"id":36,"mutability":"mutable","name":"amount","nameLocation":"1645:6:1","nodeType":"VariableDeclaration","scope":39,"src":"1637:14:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":35,"name":"uint256","nodeType":"ElementaryTypeName","src":"1637:7:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1622:30:1"},"returnParameters":{"id":38,"nodeType":"ParameterList","parameters":[],"src":"1669:0:1"},"scope":48,"src":"1599:71:1","stateMutability":"payable","virtual":false,"visibility":"external"},{"documentation":{"id":40,"nodeType":"StructuredDocumentation","src":"1676:151:1","text":" @notice Read the current credit balance for `user`.\n @param user The account to read\n @return The current credit balance"},"functionSelector":"81b26dda","id":47,"implemented":false,"kind":"function","modifiers":[],"name":"getUserCredits","nameLocation":"1841:14:1","nodeType":"FunctionDefinition","parameters":{"id":43,"nodeType":"ParameterList","parameters":[{"constant":false,"id":42,"mutability":"mutable","name":"user","nameLocation":"1864:4:1","nodeType":"VariableDeclaration","scope":47,"src":"1856:12:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":41,"name":"address","nodeType":"ElementaryTypeName","src":"1856:7:1","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"1855:14:1"},"returnParameters":{"id":46,"nodeType":"ParameterList","parameters":[{"constant":false,"id":45,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":47,"src":"1893:7:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":44,"name":"uint256","nodeType":"ElementaryTypeName","src":"1893:7:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"1892:9:1"},"scope":48,"src":"1832:70:1","stateMutability":"view","virtual":false,"visibility":"external"}],"scope":49,"src":"1026:878:1","usedErrors":[],"usedEvents":[31]}],"src":"32:1873:1"},"id":1},"contracts/IUserRegistry.sol":{"ast":{"absolutePath":"contracts/IUserRegistry.sol","exportedSymbols":{"IAddressNaming":[21],"IUserRegistry":[63]},"id":64,"license":"MIT","nodeType":"SourceUnit","nodes":[{"id":50,"literals":["solidity","^","0.8",".0"],"nodeType":"PragmaDirective","src":"32:23:2"},{"absolutePath":"contracts/IAddressNaming.sol","file":"./IAddressNaming.sol","id":51,"nameLocation":"-1:-1:-1","nodeType":"ImportDirective","scope":64,"sourceUnit":22,"src":"57:30:2","symbolAliases":[],"unitAlias":""},{"abstract":false,"baseContracts":[{"baseName":{"id":53,"name":"IAddressNaming","nameLocations":["962:14:2"],"nodeType":"IdentifierPath","referencedDeclaration":21,"src":"962:14:2"},"id":54,"nodeType":"InheritanceSpecifier","src":"962:14:2"}],"canonicalName":"IUserRegistry","contractDependencies":[],"contractKind":"interface","documentation":{"id":52,"nodeType":"StructuredDocumentation","src":"89:845:2","text":" @title IUserRegistry\n @notice Canonical interface for \"I am a user registry\" — the kind of\n service that knows which addresses belong to people on this system.\n Carries IAddressNaming as a base because name resolution is a user-registry\n responsibility. Adds a single membership predicate; implementations decide\n what \"registered\" means in their domain:\n - epistery Agent.sol / DomainAgent.sol: addresses listed on any\n whitelist / ACL under this contract.\n - rootz-v6 UniversalTeamRegistryV4: addresses with a credit account,\n authorized factory, or team membership.\n - rootz-v6 IdentityContractV3: addresses authorized as rivets on this\n identity.\n Off-chain code that wants to ask \"does this system know this address?\"\n holds against this interface and gets a uniform answer."},"fullyImplemented":false,"id":63,"linearizedBaseContracts":[63,21],"name":"IUserRegistry","nameLocation":"945:13:2","nodeType":"ContractDefinition","nodes":[{"documentation":{"id":55,"nodeType":"StructuredDocumentation","src":"983:294:2","text":" @notice True if this registry has a record of the address.\n \"Record\" is implementation-defined — membership, identity,\n credit account, rivet registration, etc.\n @param addr The address to check\n @return True if the address is known to this registry"},"functionSelector":"c3c5a547","id":62,"implemented":false,"kind":"function","modifiers":[],"name":"isRegistered","nameLocation":"1291:12:2","nodeType":"FunctionDefinition","parameters":{"id":58,"nodeType":"ParameterList","parameters":[{"constant":false,"id":57,"mutability":"mutable","name":"addr","nameLocation":"1312:4:2","nodeType":"VariableDeclaration","scope":62,"src":"1304:12:2","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"},"typeName":{"id":56,"name":"address","nodeType":"ElementaryTypeName","src":"1304:7:2","stateMutability":"nonpayable","typeDescriptions":{"typeIdentifier":"t_address","typeString":"address"}},"visibility":"internal"}],"src":"1303:14:2"},"returnParameters":{"id":61,"nodeType":"ParameterList","parameters":[{"constant":false,"id":60,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":62,"src":"1341:4:2","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":59,"name":"bool","nodeType":"ElementaryTypeName","src":"1341:4:2","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"src":"1340:6:2"},"scope":63,"src":"1282:65:2","stateMutability":"view","virtual":false,"visibility":"external"}],"scope":64,"src":"935:414:2","usedErrors":[],"usedEvents":[]}],"src":"32:1318:2"},"id":2}},"contracts":{"contracts/IAddressNaming.sol":{"IAddressNaming":{"abi":[{"inputs":[{"internalType":"address","name":"ownerAddress","type":"address"},{"internalType":"address","name":"addr","type":"address"}],"name":"getAddressName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"string","name":"name","type":"string"}],"name":"setAddressName","outputs":[],"stateMutability":"nonpayable","type":"function"}],"evm":{"bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"deployedBytecode":{"functionDebugData":{},"generatedSources":[],"immutableReferences":{},"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"methodIdentifiers":{"getAddressName(address,address)":"b715bb74","setAddressName(address,string)":"3037c5ad"}},"metadata":"{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ownerAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getAddressName\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"setAddressName\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"getAddressName(address,address)\":{\"params\":{\"addr\":\"The address to resolve\",\"ownerAddress\":\"The naming-scope owner; ignored by single-tenant implementations, used by multi-tenant ones\"},\"returns\":{\"_0\":\"The name, or empty string if unset\"}},\"setAddressName(address,string)\":{\"params\":{\"addr\":\"The address to name\",\"name\":\"The name string; empty string clears\"}}},\"title\":\"IAddressNaming\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getAddressName(address,address)\":{\"notice\":\"Resolve an address to its name.\"},\"setAddressName(address,string)\":{\"notice\":\"Set the human-readable name for an address.\"}},\"notice\":\"Canonical interface for resolving identity names from addresses. Names belong to the address itself, not to any (address, list) join. Roles (\\\"owner\\\", \\\"admin\\\", \\\"read\\\", ...) remain on whitelist / ACL entries; names do not. The same address may carry different per-list handles in legacy WhitelistEntry.name fields, but its identity name lives behind this interface. Implementations may be multi-tenant or single-tenant: - Multi-tenant (e.g. epistery Agent.sol): the read accepts an ownerAddress scope; writes are keyed by msg.sender. - Single-tenant (e.g. epistery-host DomainAgent.sol): the read's ownerAddress argument is accepted on the signature for ABI compatibility but ignored; writes are admin-gated. Off-chain consumers (e.g. epistery's Utils.ResolveAddressName, Utils.SetAddressName) hold against this interface, so they work uniformly against either implementation without branching on contract type.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/IAddressNaming.sol\":\"IAddressNaming\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/IAddressNaming.sol\":{\"keccak256\":\"0xf8b93ac3414bd15df108ea437af325845e6c2328a079a7488d034e098326e5eb\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7d82ba022d574b496e5e059997eca06b2e6d9dffcc568863b0eefa4b0d5bdd0e\",\"dweb:/ipfs/QmbT4Dg9ky58kew75JHwruwMW61cPhZuF2dARa8StKaELb\"]}},\"version\":1}"}},"contracts/ICreditAccount.sol":{"ICreditAccount":{"abi":[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreditsDeposited","type":"event"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositCredits","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserCredits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],"evm":{"bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"deployedBytecode":{"functionDebugData":{},"generatedSources":[],"immutableReferences":{},"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"methodIdentifiers":{"depositCredits(address,uint256)":"1409f160","getUserCredits(address)":"81b26dda"}},"metadata":"{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"CreditsDeposited\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"depositCredits\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"getUserCredits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"CreditsDeposited(address,uint256)\":{\"params\":{\"amount\":\"The credit amount added\",\"user\":\"The user whose balance increased\"}}},\"kind\":\"dev\",\"methods\":{\"depositCredits(address,uint256)\":{\"params\":{\"amount\":\"The credit amount to deposit\",\"user\":\"The account to credit\"}},\"getUserCredits(address)\":{\"params\":{\"user\":\"The account to read\"},\"returns\":{\"_0\":\"The current credit balance\"}}},\"title\":\"ICreditAccount\",\"version\":1},\"userdoc\":{\"events\":{\"CreditsDeposited(address,uint256)\":{\"notice\":\"Emitted when credits are added to an account.\"}},\"kind\":\"user\",\"methods\":{\"depositCredits(address,uint256)\":{\"notice\":\"Deposit credits for `user`, paying with native token. The native token amount is `msg.value`; implementations convert to credit units according to their rate table.\"},\"getUserCredits(address)\":{\"notice\":\"Read the current credit balance for `user`.\"}},\"notice\":\"Canonical interface for credit-bearing user accounts. Signatures match Steven's `UniversalTeamRegistryV4` on Polygon mainnet (`0x83B25fDD25516057AaaAf8027464C8bbb2f91d5B`) so that contract can declare conformance without renaming any existing methods. Implementations decide the conversion between native token (POL/ETH) and credits. UniversalTeamRegistryV4 uses 1 POL = 1,000,000 credits. The user-facing surface is deposit + read. Implementation-private operations (funding specific child contracts like Secrets or KeyVaults, setting rate tables, etc.) are NOT part of this interface \\u2014 they're authorization-gated internals. Epistery does not yet implement credit accounting. This interface is declared on the epistery side as the cross-system spec; epistery contracts may adopt it later, or epistery-host may delegate credit operations to rootz-v6's deployed Registry.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/ICreditAccount.sol\":\"ICreditAccount\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/ICreditAccount.sol\":{\"keccak256\":\"0x106c366de017885ff2f9e2ecf3c6822a7972cde95c28f19dab0132ab62b9ef4a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://89157a024cd18708980f20c2eabf0259451a3ff5a92ec067c7d45cca31315e67\",\"dweb:/ipfs/QmcNPfVFXqpnHWq4i9byjFzpZSJ3WJJfQgh7qfB1dp8G82\"]}},\"version\":1}"}},"contracts/IUserRegistry.sol":{"IUserRegistry":{"abi":[{"inputs":[{"internalType":"address","name":"ownerAddress","type":"address"},{"internalType":"address","name":"addr","type":"address"}],"name":"getAddressName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isRegistered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"string","name":"name","type":"string"}],"name":"setAddressName","outputs":[],"stateMutability":"nonpayable","type":"function"}],"evm":{"bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"deployedBytecode":{"functionDebugData":{},"generatedSources":[],"immutableReferences":{},"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"methodIdentifiers":{"getAddressName(address,address)":"b715bb74","isRegistered(address)":"c3c5a547","setAddressName(address,string)":"3037c5ad"}},"metadata":"{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ownerAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getAddressName\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"isRegistered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"setAddressName\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"getAddressName(address,address)\":{\"params\":{\"addr\":\"The address to resolve\",\"ownerAddress\":\"The naming-scope owner; ignored by single-tenant implementations, used by multi-tenant ones\"},\"returns\":{\"_0\":\"The name, or empty string if unset\"}},\"isRegistered(address)\":{\"params\":{\"addr\":\"The address to check\"},\"returns\":{\"_0\":\"True if the address is known to this registry\"}},\"setAddressName(address,string)\":{\"params\":{\"addr\":\"The address to name\",\"name\":\"The name string; empty string clears\"}}},\"title\":\"IUserRegistry\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getAddressName(address,address)\":{\"notice\":\"Resolve an address to its name.\"},\"isRegistered(address)\":{\"notice\":\"True if this registry has a record of the address. \\\"Record\\\" is implementation-defined \\u2014 membership, identity, credit account, rivet registration, etc.\"},\"setAddressName(address,string)\":{\"notice\":\"Set the human-readable name for an address.\"}},\"notice\":\"Canonical interface for \\\"I am a user registry\\\" \\u2014 the kind of service that knows which addresses belong to people on this system. Carries IAddressNaming as a base because name resolution is a user-registry responsibility. Adds a single membership predicate; implementations decide what \\\"registered\\\" means in their domain: - epistery Agent.sol / DomainAgent.sol: addresses listed on any whitelist / ACL under this contract. - rootz-v6 UniversalTeamRegistryV4: addresses with a credit account, authorized factory, or team membership. - rootz-v6 IdentityContractV3: addresses authorized as rivets on this identity. Off-chain code that wants to ask \\\"does this system know this address?\\\" holds against this interface and gets a uniform answer.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/IUserRegistry.sol\":\"IUserRegistry\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/IAddressNaming.sol\":{\"keccak256\":\"0xf8b93ac3414bd15df108ea437af325845e6c2328a079a7488d034e098326e5eb\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7d82ba022d574b496e5e059997eca06b2e6d9dffcc568863b0eefa4b0d5bdd0e\",\"dweb:/ipfs/QmbT4Dg9ky58kew75JHwruwMW61cPhZuF2dARa8StKaELb\"]},\"contracts/IUserRegistry.sol\":{\"keccak256\":\"0x7d56fa638d36ef669871b65c07f8342b8bd09f5a138ddd5cff64e1090d49f806\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://76c9d11f44bc0c2bbccdecd25f8f025ee6a8ae25190f6d88b1ac31251edac1c1\",\"dweb:/ipfs/QmRJvZ1PmJW346FqA48aU8XBR8LhDFBMt6mUDGGPU73pmo\"]}},\"version\":1}"}}}}}
@@ -0,0 +1,4 @@
1
+ {
2
+ "_format": "hh-sol-dbg-1",
3
+ "buildInfo": "../../build-info/5a987fca94856a824565c891c6bdbcbd.json"
4
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "ICreditAccount",
4
+ "sourceName": "contracts/ICreditAccount.sol",
5
+ "abi": [
6
+ {
7
+ "anonymous": false,
8
+ "inputs": [
9
+ {
10
+ "indexed": true,
11
+ "internalType": "address",
12
+ "name": "user",
13
+ "type": "address"
14
+ },
15
+ {
16
+ "indexed": false,
17
+ "internalType": "uint256",
18
+ "name": "amount",
19
+ "type": "uint256"
20
+ }
21
+ ],
22
+ "name": "CreditsDeposited",
23
+ "type": "event"
24
+ },
25
+ {
26
+ "inputs": [
27
+ {
28
+ "internalType": "address",
29
+ "name": "user",
30
+ "type": "address"
31
+ },
32
+ {
33
+ "internalType": "uint256",
34
+ "name": "amount",
35
+ "type": "uint256"
36
+ }
37
+ ],
38
+ "name": "depositCredits",
39
+ "outputs": [],
40
+ "stateMutability": "payable",
41
+ "type": "function"
42
+ },
43
+ {
44
+ "inputs": [
45
+ {
46
+ "internalType": "address",
47
+ "name": "user",
48
+ "type": "address"
49
+ }
50
+ ],
51
+ "name": "getUserCredits",
52
+ "outputs": [
53
+ {
54
+ "internalType": "uint256",
55
+ "name": "",
56
+ "type": "uint256"
57
+ }
58
+ ],
59
+ "stateMutability": "view",
60
+ "type": "function"
61
+ }
62
+ ],
63
+ "bytecode": "0x",
64
+ "deployedBytecode": "0x",
65
+ "linkReferences": {},
66
+ "deployedLinkReferences": {}
67
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "_format": "hh-sol-dbg-1",
3
+ "buildInfo": "../../build-info/5a987fca94856a824565c891c6bdbcbd.json"
4
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "IUserRegistry",
4
+ "sourceName": "contracts/IUserRegistry.sol",
5
+ "abi": [
6
+ {
7
+ "inputs": [
8
+ {
9
+ "internalType": "address",
10
+ "name": "ownerAddress",
11
+ "type": "address"
12
+ },
13
+ {
14
+ "internalType": "address",
15
+ "name": "addr",
16
+ "type": "address"
17
+ }
18
+ ],
19
+ "name": "getAddressName",
20
+ "outputs": [
21
+ {
22
+ "internalType": "string",
23
+ "name": "",
24
+ "type": "string"
25
+ }
26
+ ],
27
+ "stateMutability": "view",
28
+ "type": "function"
29
+ },
30
+ {
31
+ "inputs": [
32
+ {
33
+ "internalType": "address",
34
+ "name": "addr",
35
+ "type": "address"
36
+ }
37
+ ],
38
+ "name": "isRegistered",
39
+ "outputs": [
40
+ {
41
+ "internalType": "bool",
42
+ "name": "",
43
+ "type": "bool"
44
+ }
45
+ ],
46
+ "stateMutability": "view",
47
+ "type": "function"
48
+ },
49
+ {
50
+ "inputs": [
51
+ {
52
+ "internalType": "address",
53
+ "name": "addr",
54
+ "type": "address"
55
+ },
56
+ {
57
+ "internalType": "string",
58
+ "name": "name",
59
+ "type": "string"
60
+ }
61
+ ],
62
+ "name": "setAddressName",
63
+ "outputs": [],
64
+ "stateMutability": "nonpayable",
65
+ "type": "function"
66
+ }
67
+ ],
68
+ "bytecode": "0x",
69
+ "deployedBytecode": "0x",
70
+ "linkReferences": {},
71
+ "deployedLinkReferences": {}
72
+ }
@@ -0,0 +1,48 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ /**
5
+ * @title ICreditAccount
6
+ * @notice Canonical interface for credit-bearing user accounts.
7
+ *
8
+ * Signatures match Steven's `UniversalTeamRegistryV4` on Polygon mainnet
9
+ * (`0x83B25fDD25516057AaaAf8027464C8bbb2f91d5B`) so that contract can
10
+ * declare conformance without renaming any existing methods.
11
+ *
12
+ * Implementations decide the conversion between native token (POL/ETH)
13
+ * and credits. UniversalTeamRegistryV4 uses 1 POL = 1,000,000 credits.
14
+ *
15
+ * The user-facing surface is deposit + read. Implementation-private
16
+ * operations (funding specific child contracts like Secrets or KeyVaults,
17
+ * setting rate tables, etc.) are NOT part of this interface — they're
18
+ * authorization-gated internals.
19
+ *
20
+ * Epistery does not yet implement credit accounting. This interface is
21
+ * declared on the epistery side as the cross-system spec; epistery
22
+ * contracts may adopt it later, or epistery-host may delegate credit
23
+ * operations to rootz-v6's deployed Registry.
24
+ */
25
+ interface ICreditAccount {
26
+ /**
27
+ * @notice Emitted when credits are added to an account.
28
+ * @param user The user whose balance increased
29
+ * @param amount The credit amount added
30
+ */
31
+ event CreditsDeposited(address indexed user, uint256 amount);
32
+
33
+ /**
34
+ * @notice Deposit credits for `user`, paying with native token.
35
+ * The native token amount is `msg.value`; implementations convert
36
+ * to credit units according to their rate table.
37
+ * @param user The account to credit
38
+ * @param amount The credit amount to deposit
39
+ */
40
+ function depositCredits(address user, uint256 amount) external payable;
41
+
42
+ /**
43
+ * @notice Read the current credit balance for `user`.
44
+ * @param user The account to read
45
+ * @return The current credit balance
46
+ */
47
+ function getUserCredits(address user) external view returns (uint256);
48
+ }
@@ -0,0 +1,34 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ import "./IAddressNaming.sol";
5
+
6
+ /**
7
+ * @title IUserRegistry
8
+ * @notice Canonical interface for "I am a user registry" — the kind of
9
+ * service that knows which addresses belong to people on this system.
10
+ *
11
+ * Carries IAddressNaming as a base because name resolution is a user-registry
12
+ * responsibility. Adds a single membership predicate; implementations decide
13
+ * what "registered" means in their domain:
14
+ *
15
+ * - epistery Agent.sol / DomainAgent.sol: addresses listed on any
16
+ * whitelist / ACL under this contract.
17
+ * - rootz-v6 UniversalTeamRegistryV4: addresses with a credit account,
18
+ * authorized factory, or team membership.
19
+ * - rootz-v6 IdentityContractV3: addresses authorized as rivets on this
20
+ * identity.
21
+ *
22
+ * Off-chain code that wants to ask "does this system know this address?"
23
+ * holds against this interface and gets a uniform answer.
24
+ */
25
+ interface IUserRegistry is IAddressNaming {
26
+ /**
27
+ * @notice True if this registry has a record of the address.
28
+ * "Record" is implementation-defined — membership, identity,
29
+ * credit account, rivet registration, etc.
30
+ * @param addr The address to check
31
+ * @return True if the address is known to this registry
32
+ */
33
+ function isRegistered(address addr) external view returns (bool);
34
+ }
@@ -1,57 +1,62 @@
1
1
  # Identity Naming: Decoupling Names from Roles and Lists
2
2
 
3
- **Status:** Introduced in `epistery` agent contract v3.2.0 (2026-05-13).
3
+ **Status:** Introduced 2026-05-13 as the first interface in the cross-system epistery contract spec — `IAddressNaming` (`contracts/IAddressNaming.sol`). The epistery package exports the interface; the deployed contracts in each repo (`epistery-host/contracts/DomainAgent.sol`, `rootz-v6/contracts/UniversalTeamRegistryV4.sol`, …) opt in by declaring `is IAddressNaming` and bumping their own VERSION.
4
4
 
5
5
  ## Principle
6
6
 
7
- The human-readable name of an address is a property of the **address itself**, not of any (address, list) join. Roles ("owner", "admin", "read", ...) belong on whitelist entries; names do not.
7
+ The human-readable name of an address is a property of the **address itself**, not of any (address, list) join. Roles ("owner", "admin", "read", ) belong on whitelist entries; names do not.
8
8
 
9
9
  ### Why this matters
10
10
 
11
- Earlier versions used `WhitelistEntry.name` as the identity-name source. That had two failure modes:
11
+ Earlier versions used `WhitelistEntry.name` as the identity-name source. Two failure modes:
12
12
 
13
13
  1. The same address on multiple lists could carry different names — the resolver returned whichever it walked first.
14
14
  2. Privileged addresses (the contract sponsor / domain host) often surface in portal UIs as `(auto) Owner` or `(auto) Host` rows with role-label strings in the name slot, marked uneditable. Those addresses became "stranded" — the system couldn't recognize them under the user's real name.
15
15
 
16
- The fix is to give names their own primitive: a `addressNames[owner][addr] name` mapping, separate from whitelists.
16
+ The fix: give names their own primitive, separate from whitelists, exposed through a shared interface so every contract that needs identity naming implements it the same way.
17
17
 
18
- ## Contract surface (agent.sol v3.2.0)
18
+ ## The interface (`contracts/IAddressNaming.sol`)
19
19
 
20
20
  ```solidity
21
- // Storage
22
- mapping(address => mapping(address => string)) private addressNames;
21
+ interface IAddressNaming {
22
+ function setAddressName(address addr, string memory name) external;
23
+ function getAddressName(address ownerAddress, address addr) external view returns (string memory);
24
+ }
25
+ ```
23
26
 
24
- // Event
25
- event AddressNameSet(address indexed owner, address indexed addr, string name);
27
+ Pass an empty string to `setAddressName` to clear a name. Names are limited only by gas / storage cost; epistery clients clamp to 128 chars at the route layer.
26
28
 
27
- // Writes (caller's scope: msg.sender is the naming owner — typically the domain wallet)
28
- function setAddressName(address addr, string memory name) external;
29
+ ### How implementations differ
29
30
 
30
- // Reads (specify the owner-scope explicitly)
31
- function getAddressName(address ownerAddress, address addr) external view returns (string memory);
32
- ```
31
+ The interface accommodates two tenancy models:
33
32
 
34
- Pass an empty string to `setAddressName` to clear a name. Names are limited only by gas / storage cost; epistery clients clamp to 128 chars at the route layer.
33
+ | Implementation | Tenancy | Storage | `ownerAddress` arg on read |
34
+ |---|---|---|---|
35
+ | `epistery/contracts/agent.sol` (archetype) | Multi-tenant | `addressNames[msg.sender][addr] → name` | Used to select the naming scope |
36
+ | `epistery-host/contracts/DomainAgent.sol` | Single-tenant (per domain) | `addressNames[addr] → name` | Accepted on the signature for ABI parity; ignored |
37
+ | `rootz-v6/.../UniversalTeamRegistryV4.sol` | (when adopted) | per Steven's design | per Steven's design |
38
+
39
+ `epistery/contracts/agent.sol` is the **archetype** — a reference implementation, not a deployed contract. The actually-deployed contracts are forks (`DomainAgent.sol`, `UniversalTeamRegistryV4.sol`) that adopt the interface independently.
35
40
 
36
41
  ### What did NOT change
37
42
 
38
- `WhitelistEntry { addr, name, role, meta }` keeps all four fields. The `name` field on the join is now reinterpreted as a **per-list handle / role-label slot** — it may evolve into a useful per-list display field, but it is no longer the identity name source. The resolver does not read it.
43
+ `WhitelistEntry { addr, name, role, meta }` (and the `ACLEntry` equivalent in `DomainAgent.sol`) keeps all four fields. The per-entry `name` slot is now reinterpreted as a **per-list handle / role-label slot** — it may evolve into a useful per-list display field, but it is no longer the identity name source. The resolver does not read it.
39
44
 
40
- Existing data in `WhitelistEntry.name` is preserved on chain; the only change is that nothing in the auth path consults it for identity.
45
+ Existing data in those `name` fields is preserved on chain; the only change is that nothing in the auth path consults it for identity.
41
46
 
42
- ### Older contracts
47
+ ### Contracts that haven't adopted yet
43
48
 
44
- Agent contracts deployed before v3.2.0 do not have `getAddressName`. The TypeScript resolver swallows the resulting RPC error and returns `undefined`. Domains running older contracts continue to function they just have no addresses with resolved names until the contract is upgraded.
49
+ For contracts deployed before `is IAddressNaming` was declared on them, the `getAddressName` call reverts. The TypeScript resolver swallows the resulting RPC error and returns `undefined` — the request still goes through, just without a resolved name. Domains running pre-adoption contracts continue to function; they just have no addresses with names until the contract is upgraded.
45
50
 
46
51
  ## TypeScript surface
47
52
 
48
53
  ### `Utils` static methods (`src/utils/Utils.ts`)
49
54
 
50
55
  ```ts
51
- // Read — one RPC call, returns undefined if unset or on older contracts
56
+ // Read — one RPC call, returns undefined if unset or on pre-adoption contracts
52
57
  Utils.ResolveAddressName(
53
58
  wallet: Wallet,
54
- ownerAddress: string, // typically domain wallet address
59
+ ownerAddress: string, // the naming-scope owner (typically the domain wallet)
55
60
  addressToCheck: string,
56
61
  contractAddress?: string,
57
62
  ): Promise<string | undefined>;
@@ -65,6 +70,8 @@ Utils.SetAddressName(
65
70
  ): Promise<any>;
66
71
  ```
67
72
 
73
+ Both work uniformly against any contract that declares `is IAddressNaming`. The off-chain code holds against the interface signature, not against any particular contract type.
74
+
68
75
  ### `EpisteryAttach` (instance methods on `index.mjs`)
69
76
 
70
77
  ```js
@@ -88,7 +95,7 @@ Mounted under the whitelist router (`routes/whitelist/index.mjs`):
88
95
 
89
96
  `setName` validates `address` (0x-prefixed 40 hex), accepts any string for `name` (empty string clears), and clamps name length to 128 chars.
90
97
 
91
- `resolveName` reads the on-chain mapping under the current domain's scope.
98
+ `resolveName` reads from whichever `IAddressNaming` contract this domain is configured with.
92
99
 
93
100
  ## Auth middleware
94
101
 
@@ -103,30 +110,28 @@ Downstream consumers can read the resolved name from:
103
110
 
104
111
  The client-side `ClientWalletInfo` interface (`src/utils/types.ts`) has `name?: string` for type completeness.
105
112
 
106
- ## Migration
113
+ ## Adoption — per fork
107
114
 
108
- For a domain running an existing agent contract:
115
+ The interface ships in the epistery npm package. Each deployed-contract fork adopts on its own cadence:
109
116
 
110
- 1. **Recompile + redeploy** the v3.2.0 agent contract for that domain (`npx hardhat compile`, then `npm run deploy:agent` against your target network). Existing `WhitelistEntry` rows are not in the new contract — redeployment is a fresh state unless you carry data over manually.
111
-
112
- 2. **Bootstrap names** after redeploy. For each address that should have a name (you, your collaborators, named services), call:
113
-
114
- ```bash
115
- curl -X POST https://<your-domain>/.well-known/epistery/whitelist/setName \
116
- -H "Cookie: _epistery=<your admin session>" \
117
- -H "Content-Type: application/json" \
118
- -d '{"address":"0xe75Fc5...", "name":"michael"}'
119
- ```
120
-
121
- Or programmatically:
117
+ ### For `epistery-host/contracts/DomainAgent.sol`
122
118
 
119
+ 1. `import "epistery/contracts/IAddressNaming.sol";` and declare `is IAddressNaming` on the contract (already done as of v1.4.1).
120
+ 2. Add `mapping(address => string) private addressNames;` and implement `setAddressName` / `getAddressName` (already done).
121
+ 3. Recompile, redeploy, update `~/.epistery/{domain}/config.ini` to point at the new contract address (the deploy flow in `index.mjs` writes this; the migration helper in `utils/DomainChain.mjs` lifts old `WhitelistEntry.name` values into the new mapping).
122
+ 4. Bootstrap names admin-side:
123
123
  ```js
124
124
  await epistery.setAddressName("0xe75Fc5...", "michael");
125
125
  ```
126
126
 
127
- 3. **No client changes required** for existing flows — the resolver runs in middleware and surfaces the name wherever `req.episteryClient` is consumed.
127
+ ### For `rootz-v6/.../UniversalTeamRegistryV4.sol`
128
+
129
+ Steven's call when he wants to. Adoption is:
130
+ 1. `import "epistery/contracts/IAddressNaming.sol";` and declare `is IAddressNaming`.
131
+ 2. Add the `addressNames` mapping + the two methods. Choose the tenancy model that matches his contract's existing scope (single- or multi-tenant on `msg.sender`).
132
+ 3. Redeploy (or use his existing upgrade path).
128
133
 
129
- If you want to avoid a redeploy, you can defer: the system continues to function as it did, `req.episteryClient.name` just stays undefined for everyone. The new methods become available the moment the contract is upgraded.
134
+ Off-chain consumers (epistery's `Utils.ResolveAddressName`, anyone using the resolver via the npm package) keep working against either side without code changes.
130
135
 
131
136
  ## Related changes in the same release
132
137
 
@@ -139,4 +144,6 @@ The motivating bug: a user whose desktop is the contract sponsor was being rende
139
144
 
140
145
  The structural cause: `WhitelistEntry.name` was overloading two distinct concepts — a per-list role/handle label, and the user's identity name. The same address on different lists could legitimately have different per-list handles, but the user has *one* name. Putting that name on the join was wrong.
141
146
 
142
- The fix is the minimum architectural correction: pull the name off the join, put it on the address, scoped by the domain that's doing the naming. Roles stay where they are. Old data stays where it is. The resolver gets simpler (one RPC call), and privileged-address rendering becomes the portal UI's concern, not the data model's.
147
+ The fix is the minimum architectural correction: pull the name off the join, put it on the address, scoped by the domain that's doing the naming. Roles stay where they are. Old data stays where it is. The resolver gets simpler (one RPC call), and privileged-address rendering becomes the portal UI's concern, not the data model's.
148
+
149
+ The cross-system mechanism is the interface (`IAddressNaming`) — every contract that wants to be a naming source declares conformance. The interface lives in epistery; the implementations live in the forks. No code is shared, no contract inherits from another; alignment is by signature, not by source.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epistery",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Epistery brings blockchain capabilities to mundane web tasks like engagement metrics, authentication and commerce of all sorts.",
5
5
  "author": "Rootz Corp.",
6
6
  "license": "MIT",
@@ -563,17 +563,19 @@ export default function whitelistRoutes(epistery) {
563
563
  });
564
564
 
565
565
  // Resolve address name endpoint — public read of the on-chain mapping.
566
+ // Returns name: null for any unset / unconfigured case (domains without
567
+ // an agent contract, addresses without a name set). Opportunistic — no
568
+ // log noise on the common no-name path.
566
569
  router.get("/resolveName", async (req, res) => {
570
+ const address = req.query.address;
571
+ if (!address || !/^0x[a-fA-F0-9]{40}$/.test(address)) {
572
+ return res.status(400).json({ error: "Invalid address parameter" });
573
+ }
567
574
  try {
568
- const address = req.query.address;
569
- if (!address || !/^0x[a-fA-F0-9]{40}$/.test(address)) {
570
- return res.status(400).json({ error: "Invalid address parameter" });
571
- }
572
575
  const name = await epistery.resolveName(address);
573
576
  res.json({ address, name: name || null });
574
- } catch (error) {
575
- console.error("[whitelist] Resolve name error:", error);
576
- res.status(500).json({ error: error.message });
577
+ } catch {
578
+ res.json({ address, name: null });
577
579
  }
578
580
  });
579
581