From 21ea26c55e0d96ecec1e5b1b66576775f06abf46 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Sat, 6 Jan 2024 23:51:44 +0700 Subject: [PATCH 01/84] feat: prototype attesters in registry --- src/Registry.sol | 2 +- src/base/QueryAttester.sol | 298 +++++++++++++++++++++++++++++++++++++ test/Query.t.sol | 13 ++ 3 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/base/QueryAttester.sol diff --git a/src/Registry.sol b/src/Registry.sol index 9665f208..0aef54b3 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -14,7 +14,7 @@ import { ResolverUID, ResolverRecord, ModuleRecord -} from "./base/Query.sol"; +} from "./base/QueryAttester.sol"; /** * @author zeroknots diff --git a/src/base/QueryAttester.sol b/src/base/QueryAttester.sol new file mode 100644 index 00000000..759546ca --- /dev/null +++ b/src/base/QueryAttester.sol @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { IQuery } from "../interface/IQuery.sol"; +import { + AttestationRecord, + SchemaUID, + SchemaRecord, + AttestationResolve, + Attestation, + ResolverUID, + ResolverRecord, + ModuleRecord +} from "./Attestation.sol"; + +import { ZERO_TIMESTAMP } from "../Common.sol"; + +/** + * @title Query + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + * Implements EIP-7484 to query attestations stored in the registry. + * @dev This contract is abstract and provides utility functions to query attestations. + */ +abstract contract Query is IQuery { + struct Attesters { + uint8 attesterCount; + uint8 threshold; + address attester; + } + + mapping(address account => Attesters) _attesters; + mapping(address account => mapping(address attester => address linkedAttester)) _linkedAttesters; + + function setAttester(uint8 threshold, address[] calldata attesters) external { + uint256 attestersLength = attesters.length; + + Attesters storage _att = _attesters[msg.sender]; + _att.attesterCount = uint8(attestersLength); + _att.threshold = threshold; + + _attesters[msg.sender].attester = attesters[0]; + + attestersLength--; + for (uint256 i; i < attestersLength; i++) { + _linkedAttesters[msg.sender][attesters[i]] = attesters[i + 1]; + } + } + + function _getAttesters( + address linkedAttester, + uint256 length + ) + internal + view + returns (address[] memory attesters) + { + attesters = new address[](length); + attesters[0] = linkedAttester; + + for (uint256 i = 1; i < length; i++) { + linkedAttester = _linkedAttesters[msg.sender][linkedAttester]; + attesters[i] = linkedAttester; + } + } + + function check(address module) external view { + Attesters memory _att = _attesters[msg.sender]; + if (_att.attesterCount == 0 || _att.threshold == 0) { + revert(); + } else if (_att.attesterCount == 1) { + check(module, _att.attester); + } else if (_att.attesterCount > 1) { + address[] memory attesters = _getAttesters(_att.attester, _att.attesterCount); + checkN(module, attesters, _att.threshold); + } + } + + /** + * @inheritdoc IQuery + */ + + function check( + address module, + address attester + ) + public + view + override(IQuery) + returns (uint256 attestedAt) + { + AttestationRecord storage attestation = _getAttestation(module, attester); + + // attestedAt = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestedAt := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (attestedAt == ZERO_TIMESTAMP) { + revert AttestationNotFound(); + } + + if (expirationTime != ZERO_TIMESTAMP) { + if (block.timestamp > expirationTime) { + revert AttestationNotFound(); + } + } + + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(attestation.attester); + } + } + + /** + * @inheritdoc IQuery + */ + function checkN( + address module, + address[] memory attesters, + uint256 threshold + ) + public + view + override(IQuery) + returns (uint256[] memory attestedAtArray) + { + uint256 attestersLength = attesters.length; + if (attestersLength < threshold || threshold == 0) { + threshold = attestersLength; + } + + uint256 timeNow = block.timestamp; + attestedAtArray = new uint256[](attestersLength); + + for (uint256 i; i < attestersLength; ++i) { + AttestationRecord storage attestation = + _getAttestation({ moduleAddress: module, attester: attesters[i] }); + + uint256 attestationTime; // = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestationTime := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(attestation.attester); + } + + if (expirationTime != ZERO_TIMESTAMP) { + if (timeNow > expirationTime) { + revert AttestationNotFound(); + } + } + + attestedAtArray[i] = attestationTime; + + if (attestationTime == ZERO_TIMESTAMP) continue; + if (threshold != 0) --threshold; + } + if (threshold == 0) return attestedAtArray; + revert InsufficientAttestations(); + } + + /** + * @inheritdoc IQuery + */ + function checkNUnsafe( + address module, + address[] calldata attesters, + uint256 threshold + ) + external + view + returns (uint256[] memory attestedAtArray) + { + uint256 attestersLength = attesters.length; + if (attestersLength < threshold || threshold == 0) { + threshold = attestersLength; + } + + uint256 timeNow = block.timestamp; + attestedAtArray = new uint256[](attestersLength); + + for (uint256 i; i < attestersLength; ++i) { + AttestationRecord storage attestation = + _getAttestation({ moduleAddress: module, attester: attesters[i] }); + + uint256 attestationTime; // = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestationTime := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (revocationTime != ZERO_TIMESTAMP) { + attestedAtArray[i] = 0; + continue; + } + + attestedAtArray[i] = attestationTime; + + if (expirationTime != ZERO_TIMESTAMP) { + if (timeNow > expirationTime) { + attestedAtArray[i] = 0; + continue; + } + } + + if (attestationTime == ZERO_TIMESTAMP) continue; + if (threshold != 0) --threshold; + } + if (threshold == 0) return attestedAtArray; + revert InsufficientAttestations(); + } + + /** + * @inheritdoc IQuery + */ + function findAttestation( + address module, + address attesters + ) + public + view + override(IQuery) + returns (AttestationRecord memory attestation) + { + attestation = _getAttestation(module, attesters); + } + + /** + * @inheritdoc IQuery + */ + function findAttestations( + address module, + address[] memory attesters + ) + external + view + override(IQuery) + returns (AttestationRecord[] memory attestations) + { + uint256 attesterssLength = attesters.length; + attestations = new AttestationRecord[](attesterssLength); + for (uint256 i; i < attesterssLength; ++i) { + attestations[i] = findAttestation(module, attesters[i]); + } + } + + /** + * @notice Internal function to retrieve an attestation record. + * + * @dev This is a virtual function and is meant to be overridden in derived contracts. + * + * @param moduleAddress The address of the module for which the attestation is retrieved. + * @param attester The address of the attester whose record is being retrieved. + * + * @return Attestation record associated with the given module and attester. + */ + + function _getAttestation( + address moduleAddress, + address attester + ) + internal + view + virtual + returns (AttestationRecord storage); +} diff --git a/test/Query.t.sol b/test/Query.t.sol index 06b2bdd9..563ac559 100644 --- a/test/Query.t.sol +++ b/test/Query.t.sol @@ -22,6 +22,19 @@ contract QueryTest is AttestationTest { instance.registry.check(defaultModule1, attester); } + function testCheckAttestationInternal() public { + testAttest(); + + address[] memory attesters = new address[](1); + attesters[0] = attester; + instance.registry.setAttester(1, attesters); + uint256 gass = gasleft(); + instance.registry.check(defaultModule1); + gass = gass - gasleft(); + + console2.log("gas used", gass); + } + function testCheckAttestation__RevertWhen__AttestationNotExistent() public { vm.expectRevert(IQuery.AttestationNotFound.selector); instance.registry.check(defaultModule1, attester); From 8c654a93b8b5dc3677858fd1b1dfe42b57f1c6b1 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Sun, 7 Jan 2024 00:00:34 +0700 Subject: [PATCH 02/84] fix: check on threshold length --- src/base/QueryAttester.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/base/QueryAttester.sol b/src/base/QueryAttester.sol index 759546ca..2239df7d 100644 --- a/src/base/QueryAttester.sol +++ b/src/base/QueryAttester.sol @@ -35,6 +35,9 @@ abstract contract Query is IQuery { uint256 attestersLength = attesters.length; Attesters storage _att = _attesters[msg.sender]; + if (threshold > attestersLength) { + threshold = uint8(attestersLength); + } _att.attesterCount = uint8(attestersLength); _att.threshold = threshold; From 73784522add0b2fe64632c4afeb4ea0575019658 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Sun, 7 Jan 2024 00:01:15 +0700 Subject: [PATCH 03/84] feat: check for account --- src/base/QueryAttester.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/base/QueryAttester.sol b/src/base/QueryAttester.sol index 2239df7d..b052ef96 100644 --- a/src/base/QueryAttester.sol +++ b/src/base/QueryAttester.sol @@ -78,6 +78,18 @@ abstract contract Query is IQuery { } } + function checkForAccount(address account, address module) external view { + Attesters memory _att = _attesters[account]; + if (_att.attesterCount == 0 || _att.threshold == 0) { + revert(); + } else if (_att.attesterCount == 1) { + check(module, _att.attester); + } else if (_att.attesterCount > 1) { + address[] memory attesters = _getAttesters(_att.attester, _att.attesterCount); + checkN(module, attesters, _att.threshold); + } + } + /** * @inheritdoc IQuery */ From 0911751e0ee19508bc4bdf97e52328468deb8f92 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Sun, 7 Jan 2024 00:01:57 +0700 Subject: [PATCH 04/84] chore: rename --- src/base/QueryAttester.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/QueryAttester.sol b/src/base/QueryAttester.sol index b052ef96..1ce03c46 100644 --- a/src/base/QueryAttester.sol +++ b/src/base/QueryAttester.sol @@ -78,7 +78,7 @@ abstract contract Query is IQuery { } } - function checkForAccount(address account, address module) external view { + function checkOnBehalf(address account, address module) external view { Attesters memory _att = _attesters[account]; if (_att.attesterCount == 0 || _att.threshold == 0) { revert(); From 79eca98ba930584e40dfd65364c91b7d0f182477 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 6 Feb 2024 08:32:40 +0700 Subject: [PATCH 05/84] spacing --- .gas-snapshot | 116 ------------------ foundry.toml | 4 +- script/DeployRegistry.s.sol | 2 +- src/base/Attestation.sol | 8 +- src/base/Module.sol | 1 - src/base/Query.sol | 2 - src/base/QueryAttester.sol | 55 ++++++--- src/external/ResolverBase.sol | 1 - .../MultiAttesterRegistryIntegration.sol | 1 - .../examples/SimpleRegistryIntegration.sol | 1 - .../examples/SimpleRegistryStorageSlot.sol | 1 - src/interface/ISchema.sol | 1 - test/Attestation.t.sol | 40 ++---- test/AttestationDelegation.t.sol | 52 ++------ test/resolvers/TokenizedResolver.t.sol | 5 +- test/utils/BaseUtils.sol | 2 +- 16 files changed, 67 insertions(+), 225 deletions(-) delete mode 100644 .gas-snapshot diff --git a/.gas-snapshot b/.gas-snapshot deleted file mode 100644 index 248f2fce..00000000 --- a/.gas-snapshot +++ /dev/null @@ -1,116 +0,0 @@ -AttestationDelegationTest:testAttest() (gas: 236648) -AttestationDelegationTest:testAttest__RevertWhen_InvalidSignature() (gas: 81170) -AttestationDelegationTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 73233) -AttestationDelegationTest:testAttest__RevertWhen__InvalidSchema() (gas: 70222) -AttestationDelegationTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 104060) -AttestationDelegationTest:testAttest__RevertWhen__ZeroImplementation() (gas: 73470) -AttestationDelegationTest:testAttest__With__LargeAttestation() (gas: 441774) -AttestationDelegationTest:testMultiAttest() (gas: 578947) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 265063) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidLength__DataLength() (gas: 20162) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidLength__SignatureLength() (gas: 206475) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 261042) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidSignature() (gas: 235207) -AttestationDelegationTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 296139) -AttestationDelegationTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 263236) -AttestationDelegationTest:testMultiRevoke() (gas: 633003) -AttestationDelegationTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558562) -AttestationDelegationTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 209073) -AttestationDelegationTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539260) -AttestationDelegationTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541378) -AttestationDelegationTest:testRevoke() (gas: 273156) -AttestationDelegationTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 291040) -AttestationDelegationTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 75469) -AttestationDelegationTest:testRevoke__RevertWhen__InvalidSchema() (gas: 265566) -AttestationDelegationTest:testRevoke__RevertWhen__InvalidSignature() (gas: 248016) -AttestationResolveTest:testResolveAttestation() (gas: 17596) -AttestationResolveTest:testResolveAttestation__RevertWhen__InsufficientValue() (gas: 20228) -AttestationResolveTest:testResolveAttestation__RevertWhen__InvalidAttestation() (gas: 20780) -AttestationResolveTest:testResolveAttestation__RevertWhen__InvalidRevocation() (gas: 20779) -AttestationResolveTest:testResolveAttestation__RevertWhen__ResolverNotPayableAndValue() (gas: 20158) -AttestationResolveTest:testResolveAttestation__RevertWhen__ZeroResolverAndValue() (gas: 16986) -AttestationResolveTest:testResolveAttestation__WithValue() (gas: 59730) -AttestationTest:testAttest() (gas: 193660) -AttestationTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 32875) -AttestationTest:testAttest__RevertWhen__InvalidSchema() (gas: 29936) -AttestationTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 63769) -AttestationTest:testAttest__RevertWhen__ZeroImplementation() (gas: 33023) -AttestationTest:testAttest__With__LargeAttestation() (gas: 395795) -AttestationTest:testMultiAttest() (gas: 513538) -AttestationTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 200172) -AttestationTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 196284) -AttestationTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 231345) -AttestationTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 229241) -AttestationTest:testMultiRevoke() (gas: 551479) -AttestationTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558396) -AttestationTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 208979) -AttestationTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539188) -AttestationTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541269) -AttestationTest:testRevoke() (gas: 213801) -AttestationTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 227300) -AttestationTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 40523) -AttestationTest:testRevoke__RevertWhen__InvalidSchema() (gas: 211825) -AttestationTest:testRevoke__RevertWhen__NotOriginalAttester() (gas: 210649) -DeployRegistryTest:testRun() (gas: 4410056) -EIP712VerifierTest:testGetAttestationDigest() (gas: 20149) -EIP712VerifierTest:testGetNonce() (gas: 48032) -EIP712VerifierTest:testGetRevocationDigest() (gas: 18470) -EIP712VerifierTest:testVerifyAttest() (gas: 45158) -EIP712VerifierTest:testVerifyAttest__RevertWhen__InvalidSignature() (gas: 39033) -EIP712VerifierTest:testVerifyRevoke() (gas: 43400) -MockRegistryTest:testCheck() (gas: 5721) -MockRegistryTest:testCheckN() (gas: 7714) -MockRegistryTest:testCheckNUnsafe() (gas: 7779) -ModuleTest:testCreate3() (gas: 185945) -ModuleTest:testDeployNoArgs() (gas: 205604) -ModuleTest:testDeployWithArgs() (gas: 229119) -ModuleTest:testExternalFactory() (gas: 345659) -ModuleTest:testNonexistingModule__ShouldRevert() (gas: 78682) -ModuleTest:testReRegisterModule__ShouldRevert() (gas: 214236) -QueryTest:testAttest() (gas: 193660) -QueryTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 32942) -QueryTest:testAttest__RevertWhen__InvalidSchema() (gas: 30003) -QueryTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 63814) -QueryTest:testAttest__RevertWhen__ZeroImplementation() (gas: 33112) -QueryTest:testAttest__With__LargeAttestation() (gas: 395862) -QueryTest:testCheckAttestation() (gas: 195317) -QueryTest:testCheckAttestation__RevertWhen__AttestationNotExistent() (gas: 13120) -QueryTest:testCheckAttestation__RevertWhen__Expired() (gas: 241325) -QueryTest:testCheckAttestation__RevertWhen__Revoked() (gas: 215496) -QueryTest:testCheckNAttestation() (gas: 405642) -QueryTest:testCheckNAttestationUnsafe() (gas: 410171) -QueryTest:testCheckNAttestationUnsafe__Expired() (gas: 410898) -QueryTest:testCheckNAttestationUnsafe__RevertWhen__ThresholdNotMet() (gas: 211730) -QueryTest:testCheckNAttestationUnsafe__Revoked() (gas: 426993) -QueryTest:testCheckNAttestation__RevertWhen__Expired() (gas: 479262) -QueryTest:testCheckNAttestation__RevertWhen__Revoked() (gas: 445299) -QueryTest:testCheckNAttestation__RevertWhen__ThresholdNotMet() (gas: 204623) -QueryTest:testFindAttestation() (gas: 197162) -QueryTest:testFindAttestations() (gas: 410019) -QueryTest:testMultiAttest() (gas: 513560) -QueryTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 200194) -QueryTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 196284) -QueryTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 231389) -QueryTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 229241) -QueryTest:testMultiRevoke() (gas: 551657) -QueryTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558418) -QueryTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 209001) -QueryTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539188) -QueryTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541269) -QueryTest:testRevoke() (gas: 213934) -QueryTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 227499) -QueryTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 40545) -QueryTest:testRevoke__RevertWhen__InvalidSchema() (gas: 211847) -QueryTest:testRevoke__RevertWhen__NotOriginalAttester() (gas: 210649) -RegistryGasComparisonTest:testGasCheck() (gas: 24415) -RegistryGasComparisonTest:testGasCheckN__Given__ThreeAttesters() (gas: 52861) -RegistryGasComparisonTest:testGasCheckN__Given__TwoAttesters() (gas: 39735) -SchemaTest:testRegisterResolver() (gas: 57007) -SchemaTest:testRegisterResolver__RevertWhen__AlreadyExists() (gas: 62681) -SchemaTest:testRegisterResolver__RevertWhen__InvalidResolver() (gas: 11707) -SchemaTest:testRegisterSchema() (gas: 60515) -SchemaTest:testRegisterSchema__RevertWhen__AlreadyExists() (gas: 63133) -SchemaTest:testSetResolver() (gas: 63593) -SimpleRegistryIntegrationTest:testGasRegistryCheck() (gas: 200478) -TokenizedResolverTest:testTokenizedResolver() (gas: 510680) -ValueResolverTest:testValueResolver() (gas: 490959) \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 44e8e3df..1a276d6e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,5 @@ [profile.default] -solc_version = "0.8.22" +solc_version = "0.8.24" src = 'src' out = 'out' libs = ['lib'] @@ -14,4 +14,4 @@ number_underscore="thousands" [invariant] # fail_on_revert = true runs = 1200 -depth = 20 \ No newline at end of file +depth = 20 diff --git a/script/DeployRegistry.s.sol b/script/DeployRegistry.s.sol index d17aac33..097f6ac1 100644 --- a/script/DeployRegistry.s.sol +++ b/script/DeployRegistry.s.sol @@ -22,7 +22,7 @@ contract DeployRegistryScript is Script, RegistryTestTools { RegistryInstance memory instance = _setupInstance({ name: "Registry", salt: salt }); // Set up default resolver - DebugResolver debugResolver = new DebugResolver{salt: salt}(address(instance.registry)); + DebugResolver debugResolver = new DebugResolver{ salt: salt }(address(instance.registry)); instance.registry.registerResolver(IResolver(address(debugResolver))); vm.stopBroadcast(); diff --git a/src/base/Attestation.sol b/src/base/Attestation.sol index 053f7910..ed894fd6 100644 --- a/src/base/Attestation.sol +++ b/src/base/Attestation.sol @@ -221,9 +221,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // caching current time as it will be used in the for loop // for loop will run and save the return values in these two arrays - AttestationRecord[] memory attestationRecords = new AttestationRecord[]( - length - ); + AttestationRecord[] memory attestationRecords = new AttestationRecord[](length); // msg.values used for resolver uint256[] memory values = new uint256[](length); @@ -418,9 +416,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // caching length uint256 length = revocationRequestDatas.length; - AttestationRecord[] memory attestationRecords = new AttestationRecord[]( - length - ); + AttestationRecord[] memory attestationRecords = new AttestationRecord[](length); uint256[] memory values = new uint256[](length); for (uint256 i; i < length; ++i) { diff --git a/src/base/Module.sol b/src/base/Module.sol index ba1521f7..6031dec8 100644 --- a/src/base/Module.sol +++ b/src/base/Module.sol @@ -253,7 +253,6 @@ abstract contract Module is IModule, ReentrancyGuard { * * @return moduleRecord The module record associated with the given address. */ - function getModule(address moduleAddress) public view returns (ModuleRecord memory) { return _getModule(moduleAddress); } diff --git a/src/base/Query.sol b/src/base/Query.sol index 898875fe..e060182b 100644 --- a/src/base/Query.sol +++ b/src/base/Query.sol @@ -25,7 +25,6 @@ abstract contract Query is IQuery { /** * @inheritdoc IQuery */ - function check( address module, address attester @@ -233,7 +232,6 @@ abstract contract Query is IQuery { * * @return Attestation record associated with the given module and attester. */ - function _getAttestation( address moduleAddress, address attester diff --git a/src/base/QueryAttester.sol b/src/base/QueryAttester.sol index 1ce03c46..73cde182 100644 --- a/src/base/QueryAttester.sol +++ b/src/base/QueryAttester.sol @@ -26,10 +26,10 @@ abstract contract Query is IQuery { uint8 attesterCount; uint8 threshold; address attester; + mapping(address attester => address linkedAttester) linkedAttesters; } mapping(address account => Attesters) _attesters; - mapping(address account => mapping(address attester => address linkedAttester)) _linkedAttesters; function setAttester(uint8 threshold, address[] calldata attesters) external { uint256 attestersLength = attesters.length; @@ -45,11 +45,12 @@ abstract contract Query is IQuery { attestersLength--; for (uint256 i; i < attestersLength; i++) { - _linkedAttesters[msg.sender][attesters[i]] = attesters[i + 1]; + _att.linkedAttesters[attesters[i]] = attesters[i + 1]; } } function _getAttesters( + Attesters storage attesterStorage, address linkedAttester, uint256 length ) @@ -57,43 +58,62 @@ abstract contract Query is IQuery { view returns (address[] memory attesters) { + // this function is used by check(), length is always > 0. Trying to be as gas efficient as possible. attesters = new address[](length); - attesters[0] = linkedAttester; + attesters[0] = linkedAttester; // the first attester for (uint256 i = 1; i < length; i++) { - linkedAttester = _linkedAttesters[msg.sender][linkedAttester]; + // loop over the linked list, add entries to array, + // use read out attester value as the next value to query the linked list + linkedAttester = attesterStorage.linkedAttesters[linkedAttester]; attesters[i] = linkedAttester; } } function check(address module) external view { - Attesters memory _att = _attesters[msg.sender]; - if (_att.attesterCount == 0 || _att.threshold == 0) { + Attesters storage _att = _attesters[msg.sender]; + uint256 threshold = _att.threshold; + uint256 attesterCount = _att.attesterCount; + address attester0 = _att.attester; + + // if there is no attester or threshold, the user never configured any attesters. This is a revert. + if (attesterCount == 0 || threshold == 0) { revert(); - } else if (_att.attesterCount == 1) { - check(module, _att.attester); - } else if (_att.attesterCount > 1) { - address[] memory attesters = _getAttesters(_att.attester, _att.attesterCount); - checkN(module, attesters, _att.threshold); + } else if (attesterCount == 1) { + check({ module: module, attester: attester0 }); + } else if (attesterCount > 1) { + address[] memory attesters = _getAttesters({ + attesterStorage: _att, + linkedAttester: attester0, + length: attesterCount + }); + checkN({ module: module, attesters: attesters, threshold: threshold }); } } function checkOnBehalf(address account, address module) external view { - Attesters memory _att = _attesters[account]; - if (_att.attesterCount == 0 || _att.threshold == 0) { + Attesters storage _att = _attesters[account]; + uint256 threshold = _att.threshold; + uint256 attesterCount = _att.attesterCount; + address attester0 = _att.attester; + // if there is no attester or threshold, the user never configured any attesters. This is a revert. + if (attesterCount == 0 || threshold == 0) { revert(); } else if (_att.attesterCount == 1) { - check(module, _att.attester); + check({ module: module, attester: attester0 }); } else if (_att.attesterCount > 1) { - address[] memory attesters = _getAttesters(_att.attester, _att.attesterCount); - checkN(module, attesters, _att.threshold); + address[] memory attesters = _getAttesters({ + attesterStorage: _att, + linkedAttester: attester0, + length: attesterCount + }); + checkN({ module: module, attesters: attesters, threshold: threshold }); } } /** * @inheritdoc IQuery */ - function check( address module, address attester @@ -301,7 +321,6 @@ abstract contract Query is IQuery { * * @return Attestation record associated with the given module and attester. */ - function _getAttestation( address moduleAddress, address attester diff --git a/src/external/ResolverBase.sol b/src/external/ResolverBase.sol index 511dae1e..e6f456c4 100644 --- a/src/external/ResolverBase.sol +++ b/src/external/ResolverBase.sol @@ -84,7 +84,6 @@ abstract contract ResolverBase is IResolver { /** * @inheritdoc IResolver */ - function multiAttest( AttestationRecord[] calldata attestations, uint256[] calldata values diff --git a/src/integrations/examples/MultiAttesterRegistryIntegration.sol b/src/integrations/examples/MultiAttesterRegistryIntegration.sol index 21fa3188..73d5fb67 100644 --- a/src/integrations/examples/MultiAttesterRegistryIntegration.sol +++ b/src/integrations/examples/MultiAttesterRegistryIntegration.sol @@ -37,7 +37,6 @@ abstract contract RegistryIntegration { * @param _contract The address of the contract to be checked in the registry * @return validCheck the registry returned a boolean if the attestations with selected threshold was valid */ - function _checkRegistry(address _contract) internal view returns (bool validCheck) { registry.checkN(_contract, trustedAttesters, threshold); return true; diff --git a/src/integrations/examples/SimpleRegistryIntegration.sol b/src/integrations/examples/SimpleRegistryIntegration.sol index 9cec4cdc..48a3e29d 100644 --- a/src/integrations/examples/SimpleRegistryIntegration.sol +++ b/src/integrations/examples/SimpleRegistryIntegration.sol @@ -35,7 +35,6 @@ abstract contract RegistryIntegration { * @param _contract The address of the contract to be checked in the registry * @return listedAt The timestamp at which the contract was listed (0 if never listed) */ - function _checkRegistry(address _contract) internal view returns (uint256 listedAt) { return registry.check(_contract, trustedAttester); } diff --git a/src/integrations/examples/SimpleRegistryStorageSlot.sol b/src/integrations/examples/SimpleRegistryStorageSlot.sol index 8c049b4c..52cd2ce9 100644 --- a/src/integrations/examples/SimpleRegistryStorageSlot.sol +++ b/src/integrations/examples/SimpleRegistryStorageSlot.sol @@ -49,7 +49,6 @@ abstract contract RegistryIntegrationStorageSlot { * @param _contract The address of the contract to be checked in the registry * @return listedAt The timestamp at which the contract was listed (0 if never listed) */ - function _checkRegistry(address _contract) internal view returns (uint256 listedAt) { RegistryIntegrationStorage.Storage storage s = RegistryIntegrationStorage.store(); return s.registry.check(_contract, s.trustedAttester); diff --git a/src/interface/ISchema.sol b/src/interface/ISchema.sol index fd8fb1d1..91ba4a16 100644 --- a/src/interface/ISchema.sol +++ b/src/interface/ISchema.sol @@ -57,7 +57,6 @@ interface ISchema { * * @return uid The unique ID (ResolverUID) associated with the registered resolver. */ - function registerResolver(IResolver _resolver) external returns (ResolverUID); /** diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 5225d29a..0551cc2a 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -146,9 +146,7 @@ contract AttestationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -179,9 +177,7 @@ contract AttestationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -214,9 +210,7 @@ contract AttestationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -248,9 +242,7 @@ contract AttestationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -283,9 +275,7 @@ contract AttestationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -368,9 +358,7 @@ contract AttestationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -405,9 +393,7 @@ contract AttestationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -432,9 +418,7 @@ contract AttestationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -463,9 +447,7 @@ contract AttestationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -495,9 +477,7 @@ contract AttestationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; diff --git a/test/AttestationDelegation.t.sol b/test/AttestationDelegation.t.sol index 5c1704b8..9fe44457 100644 --- a/test/AttestationDelegation.t.sol +++ b/test/AttestationDelegation.t.sol @@ -167,9 +167,7 @@ contract AttestationDelegationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -206,9 +204,7 @@ contract AttestationDelegationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -247,9 +243,7 @@ contract AttestationDelegationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -287,9 +281,7 @@ contract AttestationDelegationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -309,9 +301,7 @@ contract AttestationDelegationTest is BaseTest { } function testMultiAttest__RevertWhen__InvalidLength__DataLength() public { - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 0 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](0); bytes[] memory sigs = new bytes[](0); @@ -347,9 +337,7 @@ contract AttestationDelegationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -387,9 +375,7 @@ contract AttestationDelegationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -429,9 +415,7 @@ contract AttestationDelegationTest is BaseTest { value: 0 }); - AttestationRequestData[] memory attArray = new AttestationRequestData[]( - 2 - ); + AttestationRequestData[] memory attArray = new AttestationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -560,9 +544,7 @@ contract AttestationDelegationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; bytes[] memory sigs = instance.signRevocation(defaultSchema1, auth1k, attArray); @@ -609,9 +591,7 @@ contract AttestationDelegationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -636,9 +616,7 @@ contract AttestationDelegationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -667,9 +645,7 @@ contract AttestationDelegationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; @@ -699,9 +675,7 @@ contract AttestationDelegationTest is BaseTest { RevocationRequestData memory attData2 = RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); - RevocationRequestData[] memory attArray = new RevocationRequestData[]( - 2 - ); + RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; attArray[1] = attData2; diff --git a/test/resolvers/TokenizedResolver.t.sol b/test/resolvers/TokenizedResolver.t.sol index d92af7f2..758417d9 100644 --- a/test/resolvers/TokenizedResolver.t.sol +++ b/test/resolvers/TokenizedResolver.t.sol @@ -16,10 +16,7 @@ contract TokenizedResolverTest is BaseTest { function setUp() public override { super.setUp(); token = new MockERC20("test", "test", 8); - resolver = new TokenizedResolver( - address(instance.registry), - address(token) - ); + resolver = new TokenizedResolver(address(instance.registry), address(token)); validator = new SimpleValidator(); token.mint(address(this), 10_000); diff --git a/test/utils/BaseUtils.sol b/test/utils/BaseUtils.sol index 5768c1a4..f0c3a745 100644 --- a/test/utils/BaseUtils.sol +++ b/test/utils/BaseUtils.sol @@ -301,7 +301,7 @@ contract RegistryTestTools { { RegistryInstance memory instance; - Registry registry = new Registry{salt: salt}(); + Registry registry = new Registry{ salt: salt }(); instance = RegistryInstance(registry, name); return instance; From f5eafdd9d03e06143c8438aa9fd6d00f83dec30a Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 6 Feb 2024 08:43:29 +0700 Subject: [PATCH 06/84] chore: cleaned dependencies --- .gitmodules | 20 -- .solhint.json | 19 + AuditChecklist.md | 167 +++++++++ lib/solmate | 1 - package.json | 61 ++++ pnpm-lock.yaml | 743 +++++++++++++++++++++++++++++++++++++++ remappings.txt | 7 +- src/base/Attestation.sol | 5 - 8 files changed, 993 insertions(+), 30 deletions(-) delete mode 100644 .gitmodules create mode 100644 .solhint.json create mode 100644 AuditChecklist.md delete mode 160000 lib/solmate create mode 100644 package.json create mode 100644 pnpm-lock.yaml diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9450bc88..00000000 --- a/.gitmodules +++ /dev/null @@ -1,20 +0,0 @@ -[submodule "lib/forge-std"] - path = lib/forge-std - url = https://github.com/foundry-rs/forge-std - branch = v1.5.2 -[submodule "lib/solmate"] - path = lib/solmate - url = https://github.com/transmissions11/solmate -[submodule "lib/hashi"] - path = lib/hashi - url = https://github.com/gnosis/hashi -[submodule "lib/Solidity-RLP"] - path = lib/Solidity-RLP - url = https://github.com/hamdiallam/Solidity-RLP - branch = v2.0.8 -[submodule "lib/solady"] - path = lib/solady - url = https://github.com/Vectorized/solady -[submodule "lib/openzeppelin-contracts"] - path = lib/openzeppelin-contracts - url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/.solhint.json b/.solhint.json new file mode 100644 index 00000000..f24c4323 --- /dev/null +++ b/.solhint.json @@ -0,0 +1,19 @@ +{ + "extends": "solhint:recommended", + "rules": { + "avoid-low-level-calls": "off", + "code-complexity": ["error", 9], + "compiler-version": ["error", ">=0.8.0"], + "contract-name-camelcase": "off", + "const-name-snakecase": "off", + "custom-errors": "off", + "func-name-mixedcase": "off", + "func-visibility": ["error", { "ignoreConstructors": true }], + "max-line-length": ["error", 123], + "named-parameters-mapping": "warn", + "no-empty-blocks": "off", + "not-rely-on-time": "off", + "one-contract-per-file": "off", + "var-name-mixedcase": "off" + } +} diff --git a/AuditChecklist.md b/AuditChecklist.md new file mode 100644 index 00000000..ad598e11 --- /dev/null +++ b/AuditChecklist.md @@ -0,0 +1,167 @@ +# Audit Check List + +### General Review Approach: + +## Variables + +- `V1` - Can it be `internal`? +- `V2` - Can it be `constant`? +- `V3` - Can it be `immutable`? +- `V4` - Is its visibility set? (SWC-108) +- `V5` - Is the purpose of the variable and other important information documented using natspec? +- `V6` - Can it be packed with an adjacent storage variable? +- `V7` - Can it be packed in a struct with more than 1 other variable? +- `V8` - Use full 256 bit types unless packing with other variables. +- `V9` - If it's a public array, is a separate function provided to return the full array? +- `V10` - Only use `private` to intentionally prevent child contracts from accessing the variable, prefer `internal` for flexibility. + +## Structs + +- `S1` - Is a struct necessary? Can the variable be packed raw in storage? +- `S2` - Are its fields packed together (if possible)? +- `S3` - Is the purpose of the struct and all fields documented using natspec? + +## Functions + +- `F1` - Can it be `external`? +- `F2` - Should it be `internal`? +- `F3` - Should it be `payable`? +- `F4` - Can it be combined with another similar function? +- `F5` - Validate all parameters are within safe bounds, even if the function can only be called by a trusted users. +- `F6` - Is the checks before effects pattern followed? (SWC-107) +- `F7` - Check for front-running possibilities, such as the approve function. (SWC-114) +- `F8` - Is insufficient gas griefing possible? (SWC-126) +- `F9` - Are the correct modifiers applied, such as `onlyOwner`/`requiresAuth`? +- `F10` - Are return values always assigned? +- `F11` - Write down and test invariants about state before a function can run correctly. +- `F12` - Write down and test invariants about the return or any changes to state after a function has run. +- `F13` - Take care when naming functions, because people will assume behavior based on the name. +- `F14` - If a function is intentionally unsafe (to save gas, etc), use an unwieldy name to draw attention to its risk. +- `F15` - Are all arguments, return values, side effects and other information documented using natspec? +- `F16` - If the function allows operating on another user in the system, do not assume `msg.sender` is the user being operated on. +- `F17` - If the function requires the contract be in an uninitialized state, check an explicit `initialized` variable. Do not use `owner == address(0)` or other similar checks as substitutes. +- `F18` - Only use `private` to intentionally prevent child contracts from calling the function, prefer `internal` for flexibility. +- `F19` - Use `virtual` if there are legitimate (and safe) instances where a child contract may wish to override the function's behavior. + +## Modifiers + +- `M1` - Are no storage updates made (except in a reentrancy lock)? +- `M2` - Are external calls avoided? +- `M3` - Is the purpose of the modifier and other important information documented using natspec? + +## Code + +- `C1` - Using SafeMath or 0.8 checked math? (SWC-101) +- `C2` - Are any storage slots read multiple times? +- `C3` - Are any unbounded loops/arrays used that can cause DoS? (SWC-128) +- `C4` - Use `block.timestamp` only for long intervals. (SWC-116) +- `C5` - Don't use block.number for elapsed time. (SWC-116) +- `C7` - Avoid delegatecall wherever possible, especially to external (even if trusted) contracts. (SWC-112) +- `C8` - Do not update the length of an array while iterating over it. +- `C9` - Don't use `blockhash()`, etc for randomness. (SWC-120) +- `C10` - Are signatures protected against replay with a nonce and `block.chainid` (SWC-121) +- `C11` - Ensure all signatures use EIP-712. (SWC-117 SWC-122) +- `C12` - Output of `abi.encodePacked()` shouldn't be hashed if using >2 dynamic types. Prefer using `abi.encode()` in general. (SWC-133) +- `C13` - Careful with assembly, don't use any arbitrary data. (SWC-127) +- `C14` - Don't assume a specific ETH balance. (SWC-132) +- `C15` - Avoid insufficient gas griefing. (SWC-126) +- `C16` - Private data isn't private. (SWC-136) +- `C17` - Updating a struct/array in memory won't modify it in storage. +- `C18` - Never shadow state variables. (SWC-119) +- `C19` - Do not mutate function parameters. +- `C20` - Is calculating a value on the fly cheaper than storing it? +- `C21` - Are all state variables read from the correct contract (master vs. clone)? +- `C22` - Are comparison operators used correctly (`>`, `<`, `>=`, `<=`), especially to prevent off-by-one errors? +- `C23` - Are logical operators used correctly (`==`, `!=`, `&&`, `||`, `!`), especially to prevent off-by-one errors? +- `C24` - Always multiply before dividing, unless the multiplication could overflow. +- `C25` - Are magic numbers replaced by a constant with an intuitive name? +- `C26` - If the recipient of ETH had a fallback function that reverted, could it cause DoS? (SWC-113) +- `C27` - Use SafeERC20 or check return values safely. +- `C28` - Don't use `msg.value` in a loop. +- `C29` - Don't use `msg.value` if recursive delegatecalls are possible (like if the contract inherits `Multicall`/`Batchable`). +- `C30` - Don't assume `msg.sender` is always a relevant user. +- `C31` - Don't use `assert()` unless for fuzzing or formal verification. (SWC-110) +- `C32` - Don't use `tx.origin` for authorization. (SWC-115) +- `C33` - Don't use `address.transfer()` or `address.send()`. Use `.call.value(...)("")` instead. (SWC-134) +- `C34` - When using low-level calls, ensure the contract exists before calling. +- `C35` - When calling a function with many parameters, use the named argument syntax. +- `C36` - Do not use assembly for create2. Prefer the modern salted contract creation syntax. +- `C37` - Do not use assembly to access chainid or contract code/size/hash. Prefer the modern Solidity syntax. +- `C38` - Use the `delete` keyword when setting a variable to a zero value (`0`, `false`, `""`, etc). +- `C39` - Comment the "why" as much as possible. +- `C40` - Comment the "what" if using obscure syntax or writing unconventional code. +- `C41` - Comment explanations + example inputs/outputs next to complex and fixed point math. +- `C42` - Comment explanations wherever optimizations are done, along with an estimate of much gas they save. +- `C43` - Comment explanations wherever certain optimizations are purposely avoided, along with an estimate of much gas they would/wouldn't save if implemented. +- `C44` - Use `unchecked` blocks where overflow/underflow is impossible, or where an overflow/underflow is unrealistic on human timescales (counters, etc). Comment explanations wherever `unchecked` is used, along with an estimate of how much gas it saves (if relevant). +- `C45` - Do not depend on Solidity's arithmetic operator precedence rules. In addition to the use of parentheses to override default operator precedence, parentheses should also be used to emphasise it. +- `C46` - Expressions passed to logical/comparison operators (`&&`/`||`/`>=`/`==`/etc) should not have side-effects. +- `C47` - Wherever arithmetic operations are performed that could result in precision loss, ensure it benefits the right actors in the system, and document it with comments. +- `C48` - Document the reason why a reentrancy lock is necessary whenever it's used with an inline or `@dev` natspec comment. +- `C49` - When fuzzing functions that only operate on specific numerical ranges use modulo to tighten the fuzzer's inputs (such as `x = x % 10000 + 1` to restrict from 1 to 10,000). +- `C50` - Use ternary expressions to simplify branching logic wherever possible. +- `C51` - When operating on more than one address, ask yourself what happens if they're the same. + +## External Calls + +- `X1` - Is an external contract call actually needed? +- `X2` - If there is an error, could it cause DoS? Like `balanceOf()` reverting. (SWC-113) +- `X3` - Would it be harmful if the call reentered into the current function? +- `X4` - Would it be harmful if the call reentered into another function? +- `X5` - Is the result checked and errors dealt with? (SWC-104) +- `X6` - What if it uses all the gas provided? +- `X7` - Could it cause an out-of-gas in the calling contract if it returns a massive amount of data? +- `X8` - If you are calling a particular function, do not assume that `success` implies that the function exists (phantom functions). + +## Static Calls + +- `S1` - Is an external contract call actually needed? +- `S2` - Is it actually marked as view in the interface? +- `S3` - If there is an error, could it cause DoS? Like `balanceOf()` reverting. (SWC-113) +- `S4` - If the call entered an infinite loop, could it cause DoS? + +## Events + +- `E1` - Should any fields be indexed? +- `E2` - Is the creator of the relevant action included as an indexed field? +- `E3` - Do not index dynamic types like strings or bytes. +- `E4` - Is when the event emitted and all fields documented using natspec? +- `E5` - Are all users/ids that are operated on in functions that emit the event stored as indexed fields? +- `E6` - Avoid function calls and evaluation of expressions within event arguments. Their order of evaluation is unpredictable. + +## Contract + +- `T1` - Use an SPDX license identifier. +- `T2` - Are events emitted for every storage mutating function? +- `T3` - Check for correct inheritance, keep it simple and linear. (SWC-125) +- `T4` - Use a `receive() external payable` function if the contract should accept transferred ETH. +- `T5` - Write down and test invariants about relationships between stored state. +- `T6` - Is the purpose of the contract and how it interacts with others documented using natspec? +- `T7` - The contract should be marked `abstract` if another contract must inherit it to unlock its full functionality. +- `T8` - Emit an appropriate event for any non-immutable variable set in the constructor that emits an event when mutated elsewhere. +- `T9` - Avoid over-inheritance as it masks complexity and encourages over-abstraction. +- `T10` - Always use the named import syntax to explicitly declare which contracts are being imported from another file. +- `T11` - Group imports by their folder/package. Separate groups with an empty line. Groups of external dependencies should come first, then mock/testing contracts (if relevant), and finally local imports. +- `T12` - Summarize the purpose and functionality of the contract with a `@notice` natspec comment. Document how the contract interacts with other contracts inside/outside the project in a `@dev` natspec comment. + +## Project + +- `P1` - Use the right license (you must use GPL if you depend on GPL code, etc). +- `P2` - Unit test everything. +- `P3` - Fuzz test as much as possible. +- `P4` - Use symbolic execution where possible. +- `P5` - Run Slither/Solhint and review all findings. + +## DeFi + +- `D1` - Check your assumptions about what other contracts do and return. +- `D2` - Don't mix internal accounting with actual balances. +- `D3` - Don't use spot price from an AMM as an oracle. +- `D4` - Do not trade on AMMs without receiving a price target off-chain or via an oracle. +- `D5` - Use sanity checks to prevent oracle/price manipulation. +- `D6` - Watch out for rebasing tokens. If they are unsupported, ensure that property is documented. +- `D7` - Watch out for ERC-777 tokens. Even a token you trust could preform reentrancy if it's an ERC-777. +- `D8` - Watch out for fee-on-transfer tokens. If they are unsupported, ensure that property is documented. +- `D9` - Watch out for tokens that use too many or too few decimals. Ensure the max and min supported values are documented. +- `D10` - Be careful of relying on the raw token balance of a contract to determine earnings. Contracts which provide a way to recover assets sent directly to them can mess up share price functions that rely on the raw Ether or token balances of an address. +- `D11` - If your contract is a target for token approvals, do not make arbitrary calls from user input. diff --git a/lib/solmate b/lib/solmate deleted file mode 160000 index 1b3adf67..00000000 --- a/lib/solmate +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1b3adf677e7e383cc684b5d5bd441da86bf4bf1c diff --git a/package.json b/package.json new file mode 100644 index 00000000..db876f74 --- /dev/null +++ b/package.json @@ -0,0 +1,61 @@ +{ + "name": "@rhinestone/registry", + "description": "Rhinestone Registry", + "license": "GPL-3.0", + "version": "0.3.1", + "author": { + "name": "zeroknots.eth", + "url": "https://rhinestone.wtf" + }, + "bugs": { + "url": "https://github.com/rhinestonewtf/registry/issues" + }, + "dependencies": { + "@openzeppelin/contracts": "5.0.1" + }, + "devDependencies": { + "ds-test": "github:dapphub/ds-test", + "forge-std": "github:foundry-rs/forge-std", + "prettier": "^2.8.8", + "solmate": "github:transmissions11/solmate", + "solady": "github:vectorized/solady", + "solhint": "^4.1.1" + }, + "files": [ + "artifacts", + "src", + "test/utils", + "CHANGELOG.md", + "LICENSE-GPL.md" + ], + "homepage": "https://github.com/rhinestonewtf/registry/#readme", + "keywords": [ + "blockchain", + "ethereum", + "foundry", + "smart-contracts", + "solidity", + "web3" + ], + "publishConfig": { + "access": "public" + }, + "repository": "github.com/rhinestonewtf/registry", + "scripts": { + "build": "forge build", + "build:optimized": "FOUNDRY_PROFILE=optimized forge build", + "build:smt": "FOUNDRY_PROFILE=smt forge build", + "clean": "rm -rf artifacts broadcast cache docs out out-optimized out-svg", + "gas:report": "forge test --gas-report --mp \"./test/integration/**/*.sol\" --nmt \"test(Fuzz)?_RevertWhen_\\w{1,}?\"", + "gas:snapshot": "forge snapshot --mp \"./test/integration/**/*.sol\" --nmt \"test(Fuzz)?_RevertWhen_\\w{1,}?\"", + "gas:snapshot:optimized": "pnpm run build:optimized && FOUNDRY_PROFILE=test-optimized forge snapshot --mp \"./test/integration/**/*.sol\" --nmt \"test(Fork)?(Fuzz)?_RevertWhen_\\w{1,}?\"", + "lint": "pnpm run lint:sol && bun run prettier:check", + "lint:sol": "forge fmt --check && pnpm solhint \"{script,src,test}/**/*.sol\"", + "prepack": "pnpm install", + "prettier:check": "prettier --check \"**/*.{json,md,svg,yml}\"", + "prettier:write": "prettier --write \"**/*.{json,md,svg,yml}\"", + "test": "forge test", + "test:lite": "FOUNDRY_PROFILE=lite forge test", + "test:optimized": "pnpm run build:optimized && FOUNDRY_PROFILE=test-optimized forge test" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..5affa9bb --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,743 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@openzeppelin/contracts': + specifier: 5.0.1 + version: 5.0.1 + +devDependencies: + ds-test: + specifier: github:dapphub/ds-test + version: github.com/dapphub/ds-test/e282159d5170298eb2455a6c05280ab5a73a4ef0 + forge-std: + specifier: github:foundry-rs/forge-std + version: github.com/foundry-rs/forge-std/4513bc2063f23c57bee6558799584b518d387a39 + prettier: + specifier: ^2.8.8 + version: 2.8.8 + solady: + specifier: github:vectorized/solady + version: github.com/vectorized/solady/330d9aceb8086f2ac85bb5dbfb9991642ae944e6 + solhint: + specifier: ^4.1.1 + version: 4.1.1 + solmate: + specifier: github:transmissions11/solmate + version: github.com/transmissions11/solmate/c892309933b25c03d32b1b0d674df7ae292ba925 + +packages: + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@openzeppelin/contracts@5.0.1: + resolution: {integrity: sha512-yQJaT5HDp9hYOOp4jTYxMsR02gdFZFXhewX5HW9Jo4fsqSVqqyIO/xTHdWDaKX5a3pv1txmf076Lziz+sO7L1w==} + dev: false + + /@pnpm/config.env-replace@1.1.0: + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + dev: true + + /@pnpm/network.ca-file@1.0.2: + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + dependencies: + graceful-fs: 4.2.10 + dev: true + + /@pnpm/npm-conf@2.2.2: + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + engines: {node: '>=12'} + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + dev: true + + /@sindresorhus/is@5.6.0: + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + dev: true + + /@solidity-parser/parser@0.16.2: + resolution: {integrity: sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==} + dependencies: + antlr4ts: 0.5.0-alpha.4 + dev: true + + /@szmarczak/http-timer@5.0.1: + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + dependencies: + defer-to-connect: 2.0.1 + dev: true + + /@types/http-cache-semantics@4.0.4: + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /antlr4@4.13.1: + resolution: {integrity: sha512-kiXTspaRYvnIArgE97z5YVVf/cDVQABr3abFRR6mE7yesLMkgu4ujuyV/sgxafQ8wgve0DJQUJ38Z8tkgA2izA==} + engines: {node: '>=16'} + dev: true + + /antlr4ts@0.5.0-alpha.4: + resolution: {integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==} + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /ast-parents@0.0.1: + resolution: {integrity: sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==} + dev: true + + /astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + dev: true + + /cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + dependencies: + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + mimic-response: 4.0.0 + normalize-url: 8.0.0 + responselike: 3.0.0 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + + /cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + dev: true + + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: true + + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: true + + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: true + + /got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + dev: true + + /graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: true + + /http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + dev: true + + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} + dependencies: + package-json: 8.1.1 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: true + + /mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /normalize-url@8.0.0: + resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==} + engines: {node: '>=14.16'} + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + dev: true + + /package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} + dependencies: + got: 12.6.1 + registry-auth-token: 5.0.2 + registry-url: 6.0.1 + semver: 7.6.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.23.5 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: true + + /registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} + dependencies: + '@pnpm/npm-conf': 2.2.2 + dev: true + + /registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + dependencies: + rc: 1.2.8 + dev: true + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + dependencies: + lowercase-keys: 3.0.0 + dev: true + + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /solhint@4.1.1: + resolution: {integrity: sha512-7G4iF8H5hKHc0tR+/uyZesSKtfppFIMvPSW+Ku6MSL25oVRuyFeqNhOsXHfkex64wYJyXs4fe+pvhB069I19Tw==} + hasBin: true + dependencies: + '@solidity-parser/parser': 0.16.2 + ajv: 6.12.6 + antlr4: 4.13.1 + ast-parents: 0.0.1 + chalk: 4.1.2 + commander: 10.0.1 + cosmiconfig: 8.3.6 + fast-diff: 1.3.0 + glob: 8.1.0 + ignore: 5.3.1 + js-yaml: 4.1.0 + latest-version: 7.0.0 + lodash: 4.17.21 + pluralize: 8.0.0 + semver: 7.6.0 + strip-ansi: 6.0.1 + table: 6.8.1 + text-table: 0.2.0 + optionalDependencies: + prettier: 2.8.8 + transitivePeerDependencies: + - typescript + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /table@6.8.1: + resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} + engines: {node: '>=10.0.0'} + dependencies: + ajv: 8.12.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + github.com/dapphub/ds-test/e282159d5170298eb2455a6c05280ab5a73a4ef0: + resolution: {tarball: https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0} + name: ds-test + version: 1.0.0 + dev: true + + github.com/foundry-rs/forge-std/4513bc2063f23c57bee6558799584b518d387a39: + resolution: {tarball: https://codeload.github.com/foundry-rs/forge-std/tar.gz/4513bc2063f23c57bee6558799584b518d387a39} + name: forge-std + version: 1.7.6 + dev: true + + github.com/transmissions11/solmate/c892309933b25c03d32b1b0d674df7ae292ba925: + resolution: {tarball: https://codeload.github.com/transmissions11/solmate/tar.gz/c892309933b25c03d32b1b0d674df7ae292ba925} + name: solmate + version: 6.2.0 + dev: true + + github.com/vectorized/solady/330d9aceb8086f2ac85bb5dbfb9991642ae944e6: + resolution: {tarball: https://codeload.github.com/vectorized/solady/tar.gz/330d9aceb8086f2ac85bb5dbfb9991642ae944e6} + name: solady + version: 0.0.167 + dev: true diff --git a/remappings.txt b/remappings.txt index ade16dae..8e0148ac 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,3 @@ -@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ -checknsignatures/=lib/checknsignatures/src/ -solmate/=lib/solmate/src/ -solady/=lib/solady/src/ +@openzeppelin/=node_modules/@openzeppelin/ +solmate/=node_modules/solmate/src/ +solady/=node_modules/solady/src/ diff --git a/src/base/Attestation.sol b/src/base/Attestation.sol index ed894fd6..23d27182 100644 --- a/src/base/Attestation.sol +++ b/src/base/Attestation.sol @@ -43,11 +43,6 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua mapping(address module => mapping(address attester => AttestationRecord attestation)) internal _moduleToAttesterToAttestations; - /** - * @notice Constructs a new Attestation contract instance. - */ - constructor() { } - /*////////////////////////////////////////////////////////////// ATTEST //////////////////////////////////////////////////////////////*/ From 4e3e784f4a5286c2aa4d94079b127b6addf3d3d0 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 6 Feb 2024 10:02:42 +0700 Subject: [PATCH 07/84] fix: imports --- .solhintignore | 6 +++++ lib/Solidity-RLP | 1 - lib/forge-std | 1 - lib/hashi | 1 - lib/openzeppelin-contracts | 1 - lib/solady | 1 - remappings.txt | 2 ++ src/Common.sol | 10 +++---- src/DataTypes.sol | 4 +-- src/Registry.sol | 11 ++++---- src/base/Attestation.sol | 20 +++++++------- src/base/AttestationDelegation.sol | 23 ++++------------ src/base/AttestationResolve.sol | 15 ++++------- src/base/EIP712Verifier.sol | 7 +---- src/base/Module.sol | 17 +++++------- src/base/Query.sol | 14 +++------- src/base/QueryAttester.sol | 20 +++++--------- src/external/ISchemaValidator.sol | 2 +- src/external/ResolverBase.sol | 8 +++--- src/external/examples/ValueResolver.sol | 4 +-- src/interface/IAttestation.sol | 12 +++------ src/interface/IQuery.sol | 1 + src/lib/ModuleDeploymentLib.sol | 13 ++++++--- test/Attestation.t.sol | 15 +++-------- test/AttestationDelegation.t.sol | 5 ++-- test/AttestationResolve.t.sol | 25 +++++++++-------- test/utils/BaseTest.t.sol | 1 + test/utils/ERC1271Attester.sol | 3 ++- utils/coverage.sh | 3 --- utils/flatten.sh | 11 -------- utils/pyrometer.sh | 4 --- utils/rename.sh | 15 ----------- utils/run_script.sh | 23 ---------------- utils/run_script_local.sh | 36 ------------------------- 34 files changed, 101 insertions(+), 234 deletions(-) create mode 100644 .solhintignore delete mode 160000 lib/Solidity-RLP delete mode 160000 lib/forge-std delete mode 160000 lib/hashi delete mode 160000 lib/openzeppelin-contracts delete mode 160000 lib/solady delete mode 100755 utils/coverage.sh delete mode 100755 utils/flatten.sh delete mode 100755 utils/pyrometer.sh delete mode 100755 utils/rename.sh delete mode 100755 utils/run_script.sh delete mode 100755 utils/run_script_local.sh diff --git a/.solhintignore b/.solhintignore new file mode 100644 index 00000000..f096d995 --- /dev/null +++ b/.solhintignore @@ -0,0 +1,6 @@ +node_modules/ +test/ +src/integrations/MockRegistry.sol +src/integrations/examples/ +src/external/examples/ +script/ diff --git a/lib/Solidity-RLP b/lib/Solidity-RLP deleted file mode 160000 index 0212f8e7..00000000 --- a/lib/Solidity-RLP +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0212f8e754471da67fc5387df7855f47f944f925 diff --git a/lib/forge-std b/lib/forge-std deleted file mode 160000 index 2b58ecbc..00000000 --- a/lib/forge-std +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b58ecbcf3dfde7a75959dc7b4eb3d0670278de6 diff --git a/lib/hashi b/lib/hashi deleted file mode 160000 index f249a0b0..00000000 --- a/lib/hashi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f249a0b0fcf5ffc81deadbb6cb162b06c62478ef diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts deleted file mode 160000 index 932fddf6..00000000 --- a/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 932fddf69a699a9a80fd2396fd1a2ab91cdda123 diff --git a/lib/solady b/lib/solady deleted file mode 160000 index 2225f7f5..00000000 --- a/lib/solady +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2225f7f5e538dcb06030c51560eeaafe4f9d4022 diff --git a/remappings.txt b/remappings.txt index 8e0148ac..e40312c4 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,5 @@ @openzeppelin/=node_modules/@openzeppelin/ solmate/=node_modules/solmate/src/ solady/=node_modules/solady/src/ +forge-std/=node_modules/forge-std/src/ +ds-test/=node_modules/ds-test/src/ diff --git a/src/Common.sol b/src/Common.sol index d39815d6..2ede3e8f 100644 --- a/src/Common.sol +++ b/src/Common.sol @@ -26,14 +26,10 @@ function _time() view returns (uint48) { /** * @dev Returns whether an address is a contract. - * @param account The address to check. + * @param addr The address to check. * * @return true if `account` is a contract, false otherwise. */ -function _isContract(address account) view returns (bool) { - uint256 size; - assembly { - size := extcodesize(account) - } - return size > 0; +function _isContract(address addr) view returns (bool) { + return addr.code.length > 0; } diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 1f0a3686..bc05e86c 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -85,7 +85,7 @@ struct MultiAttestationRequest { struct MultiDelegatedAttestationRequest { SchemaUID schemaUID; // The unique identifier of the schema. AttestationRequestData[] data; // The arguments of the attestation requests. - bytes[] signatures; // The signatures data. Please note that the signatures are assumed to be signed with increasing nonces. + bytes[] signatures; // The signatures data. signatures are assumed to be signed with increasing nonces. address attester; // The attesting account. } @@ -135,7 +135,7 @@ struct MultiDelegatedRevocationRequest { SchemaUID schemaUID; // The unique identifier of the schema. RevocationRequestData[] data; // The arguments of the revocation requests. address revoker; // The revoking account. - bytes[] signatures; // The signatures data. Please note that the signatures are assumed to be signed with increasing nonces. + bytes[] signatures; // The signatures data. signatures are assumed to be signed with increasing nonces. } /*////////////////////////////////////////////////////////////// diff --git a/src/Registry.sol b/src/Registry.sol index 0aef54b3..6772e3f5 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -3,18 +3,17 @@ pragma solidity ^0.8.19; import { Schema } from "./base/Schema.sol"; import { AttestationDelegation } from "./base/AttestationDelegation.sol"; +import { Attestation, AttestationResolve } from "./base/Attestation.sol"; import { Module } from "./base/Module.sol"; +import { Query } from "./base/QueryAttester.sol"; import { - Query, + AttestationRecord, SchemaUID, SchemaRecord, - AttestationResolve, - Attestation, - AttestationRecord, - ResolverUID, ResolverRecord, + ResolverUID, ModuleRecord -} from "./base/QueryAttester.sol"; +} from "./DataTypes.sol"; /** * @author zeroknots diff --git a/src/base/Attestation.sol b/src/base/Attestation.sol index 23d27182..89e4e5cc 100644 --- a/src/base/Attestation.sol +++ b/src/base/Attestation.sol @@ -5,29 +5,31 @@ import { ReentrancyGuard } from "solmate/utils/ReentrancyGuard.sol"; import { IAttestation, - AttestationRecord, AttestationRequest, MultiAttestationRequest, RevocationRequest, MultiRevocationRequest, - AttestationLib, - ResolverRecord, - MultiDelegatedAttestationRequest + AttestationLib } from "../interface/IAttestation.sol"; import { SchemaUID, ResolverUID, SchemaRecord, ISchemaValidator } from "./Schema.sol"; -import { ModuleRecord, AttestationRequestData, RevocationRequestData } from "./Module.sol"; +import { ModuleRecord } from "./Module.sol"; import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; import { ZERO_ADDRESS, AccessDenied, NotFound, ZERO_TIMESTAMP, - InvalidLength, InvalidSchema, _time } from "../Common.sol"; -import { AttestationDataRef, writeAttestationData } from "../DataTypes.sol"; +import { + AttestationDataRef, + AttestationRecord, + AttestationRequestData, + RevocationRequestData, + writeAttestationData +} from "../DataTypes.sol"; import { AttestationResolve } from "./AttestationResolve.sol"; /** @@ -300,7 +302,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua */ function _writeAttestation( SchemaUID schemaUID, - ResolverUID resolverUID, + ResolverUID resolverUID, // TODO: why isnt this used AttestationRequestData calldata attestationRequestData, address attester ) @@ -315,7 +317,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua ) { revert InvalidExpirationTime(); } - // caching module address. gas bad + // caching module address. address module = attestationRequestData.subject; ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); diff --git a/src/base/AttestationDelegation.sol b/src/base/AttestationDelegation.sol index 8137b5c9..bb56ce88 100644 --- a/src/base/AttestationDelegation.sol +++ b/src/base/AttestationDelegation.sol @@ -12,18 +12,9 @@ import { ModuleRecord, ResolverUID, AttestationRecord, - RevocationRequestData, - SchemaRecord + RevocationRequestData } from "../DataTypes.sol"; -import { - ZERO_ADDRESS, - AccessDenied, - NotFound, - ZERO_TIMESTAMP, - InvalidLength, - InvalidSchema, - _time -} from "../Common.sol"; +import { InvalidLength } from "../Common.sol"; /** * @title AttestationDelegation @@ -32,11 +23,6 @@ import { * @author rhinestone | zeroknots.eth, Konrad Kopp(@kopy-kat) */ abstract contract AttestationDelegation is IAttestation, Attestation { - /** - * @dev Initializes the contract with a name and version for the attestation. - */ - constructor() { } - /*////////////////////////////////////////////////////////////// ATTEST //////////////////////////////////////////////////////////////*/ @@ -94,9 +80,10 @@ abstract contract AttestationDelegation is IAttestation, Attestation { // Batched Revocations can only be done for a single resolver. See IAttestation.sol ModuleRecord memory moduleRecord = _getModule({ moduleAddress: multiDelegatedRequests[0].data[0].subject }); + // TODO: // I think it would be much better to move this into the for loop so we can iterate over the requests. - // Its possible that the MultiAttestationRequests is attesting different modules, that thus have different resolvers - // gas bad + // Its possible that the MultiAttestationRequests is attesting different modules, + // that thus have different resolvers gas bad for (uint256 i; i < length; ++i) { // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there diff --git a/src/base/AttestationResolve.sol b/src/base/AttestationResolve.sol index cf02e091..93809331 100644 --- a/src/base/AttestationResolve.sol +++ b/src/base/AttestationResolve.sol @@ -3,19 +3,13 @@ pragma solidity ^0.8.19; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import { - IAttestation, - ResolverUID, - AttestationRecord, - SchemaUID, - SchemaRecord, - ModuleRecord, - ResolverRecord, - IResolver -} from "../interface/IAttestation.sol"; +import { IAttestation, SchemaUID } from "../interface/IAttestation.sol"; +import { ResolverUID } from "./Schema.sol"; import { EIP712Verifier } from "./EIP712Verifier.sol"; import { ZERO_ADDRESS } from "../Common.sol"; +import { AttestationRecord, SchemaRecord, ModuleRecord, ResolverRecord } from "../DataTypes.sol"; +import { IResolver } from "../external/IResolver.sol"; /** * @title AttestationResolve @@ -102,6 +96,7 @@ abstract contract AttestationResolve is IAttestation, EIP712Verifier { * * @return Returns the total sent ETH amount. */ + // solhint-disable-next-line code-complexity function _resolveAttestations( ResolverUID resolverUID, AttestationRecord[] memory attestationRecords, diff --git a/src/base/EIP712Verifier.sol b/src/base/EIP712Verifier.sol index c1e2362f..fc2891c1 100644 --- a/src/base/EIP712Verifier.sol +++ b/src/base/EIP712Verifier.sol @@ -28,12 +28,7 @@ abstract contract EIP712Verifier is EIP712 { keccak256("RevocationRequestData(address,address,uint256)"); // Replay protection nonces. - mapping(address => uint256) private _nonces; - - /** - * @dev Creates a new EIP712Verifier instance. - */ - constructor() { } + mapping(address account => uint256 nonce) private _nonces; function _domainNameAndVersion() internal diff --git a/src/base/Module.sol b/src/base/Module.sol index 6031dec8..4c56c114 100644 --- a/src/base/Module.sol +++ b/src/base/Module.sol @@ -7,17 +7,10 @@ import { CREATE3 } from "solady/utils/CREATE3.sol"; import { IModule } from "../interface/IModule.sol"; import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; -import { Schema } from "./Schema.sol"; import { IResolver } from "../external/IResolver.sol"; import { InvalidResolver, _isContract, ZERO_ADDRESS } from "../Common.sol"; -import { - ResolverRecord, - ModuleRecord, - ResolverUID, - AttestationRequestData, - RevocationRequestData -} from "../DataTypes.sol"; +import { ResolverRecord, ModuleRecord, ResolverUID } from "../DataTypes.sol"; /** * @title Module @@ -27,10 +20,12 @@ import { * * @dev The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract * that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, - * sender address, deploy parameters hash, and additional metadata are stored in a struct and mapped to the module's address in + * sender address, deploy parameters hash, and additional metadata are stored in + * a struct and mapped to the module's address in * the `_modules` mapping for easy access and management. * - * @dev In conclusion, the Module is a central part of a system to manage, deploy, and interact with a set of smart contracts + * @dev In conclusion, the Module is a central part of a system to manage, + * deploy, and interact with a set of smart contracts * in a structured and controlled manner. * * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) @@ -39,7 +34,7 @@ abstract contract Module is IModule, ReentrancyGuard { using ModuleDeploymentLib for bytes; using ModuleDeploymentLib for address; - mapping(address moduleAddress => ModuleRecord) private _modules; + mapping(address moduleAddress => ModuleRecord modueRecord) private _modules; /** * @inheritdoc IModule diff --git a/src/base/Query.sol b/src/base/Query.sol index e060182b..57f526d0 100644 --- a/src/base/Query.sol +++ b/src/base/Query.sol @@ -2,18 +2,9 @@ pragma solidity ^0.8.19; import { IQuery } from "../interface/IQuery.sol"; -import { - AttestationRecord, - SchemaUID, - SchemaRecord, - AttestationResolve, - Attestation, - ResolverUID, - ResolverRecord, - ModuleRecord -} from "./Attestation.sol"; import { ZERO_TIMESTAMP } from "../Common.sol"; +import { AttestationRecord } from "../DataTypes.sol"; /** * @title Query @@ -42,6 +33,7 @@ abstract contract Query is IQuery { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload(attestation.slot) @@ -98,6 +90,7 @@ abstract contract Query is IQuery { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload(attestation.slot) @@ -157,6 +150,7 @@ abstract contract Query is IQuery { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload(attestation.slot) diff --git a/src/base/QueryAttester.sol b/src/base/QueryAttester.sol index 73cde182..0dbfa81c 100644 --- a/src/base/QueryAttester.sol +++ b/src/base/QueryAttester.sol @@ -2,18 +2,9 @@ pragma solidity ^0.8.19; import { IQuery } from "../interface/IQuery.sol"; -import { - AttestationRecord, - SchemaUID, - SchemaRecord, - AttestationResolve, - Attestation, - ResolverUID, - ResolverRecord, - ModuleRecord -} from "./Attestation.sol"; import { ZERO_TIMESTAMP } from "../Common.sol"; +import { AttestationRecord } from "../DataTypes.sol"; /** * @title Query @@ -29,7 +20,7 @@ abstract contract Query is IQuery { mapping(address attester => address linkedAttester) linkedAttesters; } - mapping(address account => Attesters) _attesters; + mapping(address account => Attesters attesters) internal _attesters; function setAttester(uint8 threshold, address[] calldata attesters) external { uint256 attestersLength = attesters.length; @@ -78,7 +69,7 @@ abstract contract Query is IQuery { // if there is no attester or threshold, the user never configured any attesters. This is a revert. if (attesterCount == 0 || threshold == 0) { - revert(); + revert NoAttesterSet(); } else if (attesterCount == 1) { check({ module: module, attester: attester0 }); } else if (attesterCount > 1) { @@ -98,7 +89,7 @@ abstract contract Query is IQuery { address attester0 = _att.attester; // if there is no attester or threshold, the user never configured any attesters. This is a revert. if (attesterCount == 0 || threshold == 0) { - revert(); + revert NoAttesterSet(); } else if (_att.attesterCount == 1) { check({ module: module, attester: attester0 }); } else if (_att.attesterCount > 1) { @@ -131,6 +122,7 @@ abstract contract Query is IQuery { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload(attestation.slot) @@ -187,6 +179,7 @@ abstract contract Query is IQuery { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload(attestation.slot) @@ -246,6 +239,7 @@ abstract contract Query is IQuery { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload(attestation.slot) diff --git a/src/external/ISchemaValidator.sol b/src/external/ISchemaValidator.sol index 7607ed74..f0419540 100644 --- a/src/external/ISchemaValidator.sol +++ b/src/external/ISchemaValidator.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { AttestationRequestData, ModuleRecord } from "../DataTypes.sol"; +import { AttestationRequestData } from "../DataTypes.sol"; import { IERC165 } from "forge-std/interfaces/IERC165.sol"; /** diff --git a/src/external/ResolverBase.sol b/src/external/ResolverBase.sol index e6f456c4..19a077cc 100644 --- a/src/external/ResolverBase.sol +++ b/src/external/ResolverBase.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { AccessDenied, ZERO_TIMESTAMP, NotFound, ZERO_ADDRESS } from "../Common.sol"; +import { AccessDenied, ZERO_ADDRESS } from "../Common.sol"; import { AttestationRecord, ModuleRecord } from "../DataTypes.sol"; import { IResolver } from "./IResolver.sol"; @@ -19,7 +19,7 @@ abstract contract ResolverBase is IResolver { string public constant VERSION = "0.1"; // The global Rhinestone Registry contract. - address internal immutable _rs; + address internal immutable REGISTRY; /** * @dev Creates a new resolver. @@ -30,7 +30,7 @@ abstract contract ResolverBase is IResolver { if (rs == ZERO_ADDRESS) { revert InvalidRS(); } - _rs = rs; + REGISTRY = rs; } /** @@ -225,7 +225,7 @@ abstract contract ResolverBase is IResolver { * @dev Ensures that only the RS contract can make this call. */ function _onlyRSRegistry() private view { - if (msg.sender != _rs) { + if (msg.sender != REGISTRY) { revert AccessDenied(); } } diff --git a/src/external/examples/ValueResolver.sol b/src/external/examples/ValueResolver.sol index d5cc110f..0d1191ab 100644 --- a/src/external/examples/ValueResolver.sol +++ b/src/external/examples/ValueResolver.sol @@ -9,7 +9,7 @@ import { ResolverBase, AttestationRecord, ModuleRecord } from "../ResolverBase.s * @notice A resolver for value (ETH) attestations. */ contract ValueResolver is ResolverBase { - uint256 immutable fee = 10; + uint256 public immutable FEE = 10; constructor(address rs) ResolverBase(rs) { } @@ -22,7 +22,7 @@ contract ValueResolver is ResolverBase { override returns (bool) { - return msg.value > fee; + return msg.value > FEE; } function onRevoke( diff --git a/src/interface/IAttestation.sol b/src/interface/IAttestation.sol index 625954a7..228e3205 100644 --- a/src/interface/IAttestation.sol +++ b/src/interface/IAttestation.sol @@ -5,16 +5,10 @@ import { SchemaUID, AttestationDataRef, AttestationRequest, - AttestationRecord, - SchemaRecord, MultiAttestationRequest, - ResolverRecord, - ModuleRecord, - IResolver, DelegatedAttestationRequest, MultiDelegatedAttestationRequest, RevocationRequest, - ResolverUID, DelegatedRevocationRequest, MultiDelegatedRevocationRequest, MultiRevocationRequest @@ -102,7 +96,8 @@ interface IAttestation { /** * @notice Handles a single delegated attestation request * - * @dev The function verifies the attestation, wraps the data in an array and forwards it to the _multiAttest() function + * @dev The function verifies the attestation, + * wraps the data in an array and forwards it to the _multiAttest() function * * @param delegatedRequest A delegated attestation request */ @@ -167,7 +162,8 @@ interface IAttestation { library AttestationLib { /** * @dev Generates a unique salt for an attestation using the provided attester and module addresses. - * The salt is generated using a keccak256 hash of the module address, attester address, current timestamp, and chain ID. + * The salt is generated using a keccak256 hash of the module address, + * attester address, current timestamp, and chain ID. * This salt will be used for SSTORE2 * * @param attester Address of the entity making the attestation. diff --git a/src/interface/IQuery.sol b/src/interface/IQuery.sol index 52c91b29..39aa9591 100644 --- a/src/interface/IQuery.sol +++ b/src/interface/IQuery.sol @@ -11,6 +11,7 @@ import { IERC7484 } from "./IERC7484.sol"; * @author zeroknots */ interface IQuery is IERC7484 { + error NoAttesterSet(); error RevokedAttestation(address attester); error AttestationNotFound(); error InsufficientAttestations(); diff --git a/src/lib/ModuleDeploymentLib.sol b/src/lib/ModuleDeploymentLib.sol index 7637c61f..162fdb9c 100644 --- a/src/lib/ModuleDeploymentLib.sol +++ b/src/lib/ModuleDeploymentLib.sol @@ -15,6 +15,7 @@ library ModuleDeploymentLib { * @return hash The hash of the contract code. */ function codeHash(address contractAddr) internal view returns (bytes32 hash) { + // solhint-disable-next-line no-inline-assembly assembly { if iszero(extcodesize(contractAddr)) { revert(0, 0) } hash := extcodehash(contractAddr) @@ -26,7 +27,8 @@ library ModuleDeploymentLib { * @dev This method uses the CREATE2 opcode to deploy a new contract with a deterministic address. * * @param createCode The creationCode for the contract. - * @param params The parameters for creating the contract. If the contract has a constructor, this MUST be provided. Function will fail if params are abi.encodePacked in createCode. + * @param params The parameters for creating the contract. If the contract has a constructor, + * this MUST be provided. Function will fail if params are abi.encodePacked in createCode. * @param salt The salt for creating the contract. * * @return moduleAddress The address of the deployed contract. @@ -47,6 +49,9 @@ library ModuleDeploymentLib { // if params were abi.encodePacked in createCode, this will revert initCodeHash = keccak256(initCode); + // TODO: can we use a lib for this? + + // solhint-disable-next-line no-inline-assembly assembly { moduleAddress := create2(value, add(initCode, 0x20), mload(initCode), salt) // If the contract was not created successfully, the transaction is reverted. @@ -61,9 +66,11 @@ library ModuleDeploymentLib { * @dev This function uses the formula specified in EIP-1014 (https://eips.ethereum.org/EIPS/eip-1014). * * @param _code The contract code that would be deployed. - * @param _salt A salt used for the address calculation. This must be the same salt that would be passed to the CREATE2 opcode. + * @param _salt A salt used for the address calculation. + * This must be the same salt that would be passed to the CREATE2 opcode. * - * @return The address that the contract would be deployed at if the CREATE2 opcode was called with the specified _code and _salt. + * @return The address that the contract would be deployed + * at if the CREATE2 opcode was called with the specified _code and _salt. */ function calcAddress(bytes memory _code, bytes32 _salt) internal view returns (address) { bytes32 hash = diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 0551cc2a..fbe5bad9 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -2,15 +2,7 @@ pragma solidity ^0.8.19; import { Test } from "forge-std/Test.sol"; -import { - Attestation, - MultiDelegatedAttestationRequest, - AttestationRecord, - IAttestation, - InvalidSchema, - NotFound, - AccessDenied -} from "../src/base/Attestation.sol"; +import { IAttestation, InvalidSchema, NotFound, AccessDenied } from "../src/base/Attestation.sol"; import { ERC1271Attester, EXPECTED_SIGNATURE } from "./utils/ERC1271Attester.sol"; @@ -20,15 +12,14 @@ import { RegistryInstance, console2, AttestationRequestData, - DelegatedAttestationRequest, MockModuleWithArgs, - ResolverUID, - IResolver, SchemaUID, ISchemaValidator } from "./utils/BaseTest.t.sol"; import { + AttestationRecord, + MultiDelegatedAttestationRequest, MultiAttestationRequest, MultiRevocationRequest, RevocationRequestData, diff --git a/test/AttestationDelegation.t.sol b/test/AttestationDelegation.t.sol index 9fe44457..96be30ec 100644 --- a/test/AttestationDelegation.t.sol +++ b/test/AttestationDelegation.t.sol @@ -4,8 +4,6 @@ pragma solidity ^0.8.19; import { Test } from "forge-std/Test.sol"; import { Attestation, - MultiDelegatedAttestationRequest, - AttestationRecord, IAttestation, InvalidSchema, NotFound, @@ -23,13 +21,14 @@ import { DelegatedAttestationRequest, MockModuleWithArgs, ResolverUID, - IResolver, SchemaUID, ISchemaValidator } from "./utils/BaseTest.t.sol"; import { + AttestationRecord, MultiAttestationRequest, + MultiDelegatedAttestationRequest, MultiRevocationRequest, RevocationRequestData, DelegatedRevocationRequest, diff --git a/test/AttestationResolve.t.sol b/test/AttestationResolve.t.sol index 1c2557fa..bb3b2028 100644 --- a/test/AttestationResolve.t.sol +++ b/test/AttestationResolve.t.sol @@ -2,16 +2,7 @@ pragma solidity ^0.8.19; import { BaseTest } from "./utils/BaseTest.t.sol"; -import { - AttestationResolve, - ResolverUID, - AttestationRecord, - SchemaUID, - SchemaRecord, - ResolverRecord, - ModuleRecord, - IResolver -} from "../src/base/AttestationResolve.sol"; +import { AttestationResolve, ResolverUID, SchemaUID } from "../src/base/AttestationResolve.sol"; import { AttestationRequest, MultiAttestationRequest, @@ -24,8 +15,20 @@ import { IAttestation } from "../src/interface/IAttestation.sol"; import { ISchemaValidator } from "../src/interface/ISchema.sol"; +import { IResolver } from "../src/external/IResolver.sol"; import { ResolverBase } from "../src/external/ResolverBase.sol"; -import { AttestationDataRef } from "../src/DataTypes.sol"; +import { + AttestationRecord, + AttestationDataRef, + MultiAttestationRequest, + MultiDelegatedAttestationRequest, + MultiRevocationRequest, + DelegatedRevocationRequest, + MultiDelegatedRevocationRequest, + SchemaRecord, + ResolverRecord, + ModuleRecord +} from "../src/DataTypes.sol"; contract AttestationResolveInstance is AttestationResolve { function resolveAttestation( diff --git a/test/utils/BaseTest.t.sol b/test/utils/BaseTest.t.sol index 6aa39577..b6fef707 100644 --- a/test/utils/BaseTest.t.sol +++ b/test/utils/BaseTest.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "./BaseUtils.sol"; +import "src/DataTypes.sol"; import "../../src/external/IResolver.sol"; import "../../src/external/examples/DebugResolver.sol"; diff --git a/test/utils/ERC1271Attester.sol b/test/utils/ERC1271Attester.sol index 8b4dfb73..b11d2ab9 100644 --- a/test/utils/ERC1271Attester.sol +++ b/test/utils/ERC1271Attester.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.0; -import "@openzeppelin/contracts/interfaces/IERC1271.sol"; +import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; +// solhint-disable-next-line max-line-length bytes constant EXPECTED_SIGNATURE = hex"ddeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefeadbeef"; diff --git a/utils/coverage.sh b/utils/coverage.sh deleted file mode 100755 index 47fc31bd..00000000 --- a/utils/coverage.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/zsh -forge coverage --report lcov && ekhtml lcov.info --branch-coverage --output-dir coverage - diff --git a/utils/flatten.sh b/utils/flatten.sh deleted file mode 100755 index 9a696791..00000000 --- a/utils/flatten.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -# Read the contract name -echo Which contract do you want to flatten \(eg Greeter\)? -read contract - -# Remove an existing flattened contracts -rm -rf flattened.sol - -# FLATTEN -forge flatten ./src/${contract}.sol > flattened.sol diff --git a/utils/pyrometer.sh b/utils/pyrometer.sh deleted file mode 100755 index de6f1343..00000000 --- a/utils/pyrometer.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -./utils/flatten.sh -pyrometer flatten.sol diff --git a/utils/rename.sh b/utils/rename.sh deleted file mode 100755 index cde43a46..00000000 --- a/utils/rename.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -# Make sed command compatible in both Mac and Linux environments -# Reference: https://stackoverflow.com/a/38595160/8696958 -sedi () { - sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@" -} - -# Read the new repo name -echo Enter your new repo name: -read repo - -# Rename instances of "femplate" to the new repo name in README.md -sedi 's/femplate/'${repo}'/g' 'README.md' -sedi 's/.'${repo}'..https:\/\/github.com\/abigger87\/'${repo}'./[femplate](https:\/\/github.com\/abigger87\/femplate)/g' 'README.md' diff --git a/utils/run_script.sh b/utils/run_script.sh deleted file mode 100755 index 35bc705b..00000000 --- a/utils/run_script.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -# Read the RPC URL -source .env - -# Read script -echo Which script do you want to run? -read script - -# Read script arguments -echo Enter script arguments, or press enter if none: -read -ra args - -# Run the script -echo Running Script: $script... - -# Run the script with interactive inputs -forge script $script \ - --rpc-url $RPC_URL \ - --broadcast \ - -vvvv \ - --private-key $DEPLOYER_KEY \ - $args diff --git a/utils/run_script_local.sh b/utils/run_script_local.sh deleted file mode 100755 index 6c026190..00000000 --- a/utils/run_script_local.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash - -# Read the RPC URL -source .env - -## Fork Mainnet -echo Please wait a few seconds for anvil to fork mainnet and run locally... -anvil --fork-url $RPC_URL & - -# Wait for anvil to fork -sleep 5 - -# Read script -echo Which script do you want to run? -read script - -# Read script arguments -echo Enter script arguments, or press enter if none: -read -ra args - -# Run the script -echo Running Script: $script... - -# We specify the anvil url as http://localhost:8545 -# We need to specify the sender for our local anvil node -forge script $script \ - --fork-url http://localhost:8545 \ - --broadcast \ - -vvvv \ - --sender 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 \ - --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ - $args - -# Once finished, we want to kill our anvil instance running in the background -trap "exit" INT TERM -trap "kill 0" EXIT From 48ae9a206f199904d552c3b10090a30244defb53 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 6 Feb 2024 10:24:34 +0700 Subject: [PATCH 08/84] fix: removing resolverUID was not used in the attestation record --- src/base/Attestation.sol | 4 ---- src/base/AttestationDelegation.sol | 1 - 2 files changed, 5 deletions(-) diff --git a/src/base/Attestation.sol b/src/base/Attestation.sol index 89e4e5cc..5bc59e22 100644 --- a/src/base/Attestation.sol +++ b/src/base/Attestation.sol @@ -63,7 +63,6 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // write attestations to registry storge (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ schemaUID: request.schemaUID, - resolverUID: resolverUID, attestationRequestData: requestData, attester: msg.sender }); @@ -227,7 +226,6 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua for (uint256 i; i < length; ++i) { (attestationRecords[i], values[i]) = _writeAttestation({ schemaUID: schemaUID, - resolverUID: resolverUID, attestationRequestData: attestationRequestDatas[i], attester: attester }); @@ -293,7 +291,6 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua * is writted to the EVM with SSTORE2 to allow for large attestations without spending a lot of gas * * @param schemaUID The unique identifier of the schema being attested to. - * @param resolverUID The unique identifier of the resolver for the module. * @param attestationRequestData The data for the attestation request. * @param attester The address of the entity making the attestation. * @@ -302,7 +299,6 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua */ function _writeAttestation( SchemaUID schemaUID, - ResolverUID resolverUID, // TODO: why isnt this used AttestationRequestData calldata attestationRequestData, address attester ) diff --git a/src/base/AttestationDelegation.sol b/src/base/AttestationDelegation.sol index bb56ce88..7b23a44d 100644 --- a/src/base/AttestationDelegation.sol +++ b/src/base/AttestationDelegation.sol @@ -46,7 +46,6 @@ abstract contract AttestationDelegation is IAttestation, Attestation { (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ schemaUID: delegatedRequest.schemaUID, - resolverUID: resolverUID, attestationRequestData: attestationRequestData, attester: delegatedRequest.attester }); From 070ae03dcfb9a3d8d43c4f90e5cb2c7261687c30 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 6 Feb 2024 10:50:58 +0700 Subject: [PATCH 09/84] chore: gas --- .gas-snapshot | 117 +++++++++++++++++++++++++++++ src/base/Attestation.sol | 8 +- src/base/AttestationDelegation.sol | 6 +- src/base/EIP712Verifier.sol | 15 +++- src/base/QueryAttester.sol | 2 +- test/EIP712Verifier.t.sol | 4 +- 6 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 .gas-snapshot diff --git a/.gas-snapshot b/.gas-snapshot new file mode 100644 index 00000000..6f525ff0 --- /dev/null +++ b/.gas-snapshot @@ -0,0 +1,117 @@ +AttestationDelegationTest:testAttest() (gas: 236573) +AttestationDelegationTest:testAttest__RevertWhen_InvalidSignature() (gas: 81176) +AttestationDelegationTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 73165) +AttestationDelegationTest:testAttest__RevertWhen__InvalidSchema() (gas: 70136) +AttestationDelegationTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 103950) +AttestationDelegationTest:testAttest__RevertWhen__ZeroImplementation() (gas: 73402) +AttestationDelegationTest:testAttest__With__LargeAttestation() (gas: 441727) +AttestationDelegationTest:testMultiAttest() (gas: 578905) +AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 265016) +AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidLength__DataLength() (gas: 20031) +AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidLength__SignatureLength() (gas: 206425) +AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 260976) +AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidSignature() (gas: 235201) +AttestationDelegationTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 296050) +AttestationDelegationTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 263189) +AttestationDelegationTest:testMultiRevoke() (gas: 633071) +AttestationDelegationTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558481) +AttestationDelegationTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 208998) +AttestationDelegationTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539188) +AttestationDelegationTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541281) +AttestationDelegationTest:testRevoke() (gas: 273143) +AttestationDelegationTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 291032) +AttestationDelegationTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 75399) +AttestationDelegationTest:testRevoke__RevertWhen__InvalidSchema() (gas: 265512) +AttestationDelegationTest:testRevoke__RevertWhen__InvalidSignature() (gas: 247924) +AttestationResolveTest:testResolveAttestation() (gas: 17618) +AttestationResolveTest:testResolveAttestation__RevertWhen__InsufficientValue() (gas: 20250) +AttestationResolveTest:testResolveAttestation__RevertWhen__InvalidAttestation() (gas: 20692) +AttestationResolveTest:testResolveAttestation__RevertWhen__InvalidRevocation() (gas: 20801) +AttestationResolveTest:testResolveAttestation__RevertWhen__ResolverNotPayableAndValue() (gas: 20180) +AttestationResolveTest:testResolveAttestation__RevertWhen__ZeroResolverAndValue() (gas: 16986) +AttestationResolveTest:testResolveAttestation__WithValue() (gas: 59663) +AttestationTest:testAttest() (gas: 193605) +AttestationTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 32738) +AttestationTest:testAttest__RevertWhen__InvalidSchema() (gas: 29891) +AttestationTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 63679) +AttestationTest:testAttest__RevertWhen__ZeroImplementation() (gas: 32975) +AttestationTest:testAttest__With__LargeAttestation() (gas: 395769) +AttestationTest:testMultiAttest() (gas: 513511) +AttestationTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 200140) +AttestationTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 196255) +AttestationTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 231271) +AttestationTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 229167) +AttestationTest:testMultiRevoke() (gas: 551445) +AttestationTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558315) +AttestationTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 208926) +AttestationTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539116) +AttestationTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541194) +AttestationTest:testRevoke() (gas: 213722) +AttestationTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 227327) +AttestationTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 40476) +AttestationTest:testRevoke__RevertWhen__InvalidSchema() (gas: 211792) +AttestationTest:testRevoke__RevertWhen__NotOriginalAttester() (gas: 210546) +DeployRegistryTest:testRun() (gas: 4643338) +EIP712VerifierTest:testGetAttestationDigest() (gas: 20134) +EIP712VerifierTest:testGetNonce() (gas: 48075) +EIP712VerifierTest:testGetRevocationDigest() (gas: 18477) +EIP712VerifierTest:testVerifyAttest() (gas: 45223) +EIP712VerifierTest:testVerifyAttest__RevertWhen__InvalidSignature() (gas: 39132) +EIP712VerifierTest:testVerifyRevoke() (gas: 43469) +MockRegistryTest:testCheck() (gas: 5721) +MockRegistryTest:testCheckN() (gas: 7736) +MockRegistryTest:testCheckNUnsafe() (gas: 7779) +ModuleTest:testCreate3() (gas: 186050) +ModuleTest:testDeployNoArgs() (gas: 205553) +ModuleTest:testDeployWithArgs() (gas: 229068) +ModuleTest:testExternalFactory() (gas: 345786) +ModuleTest:testNonexistingModule__ShouldRevert() (gas: 78699) +ModuleTest:testReRegisterModule__ShouldRevert() (gas: 214229) +QueryTest:testAttest() (gas: 193605) +QueryTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 32872) +QueryTest:testAttest__RevertWhen__InvalidSchema() (gas: 29936) +QueryTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 63813) +QueryTest:testAttest__RevertWhen__ZeroImplementation() (gas: 33042) +QueryTest:testAttest__With__LargeAttestation() (gas: 395836) +QueryTest:testCheckAttestation() (gas: 195284) +QueryTest:testCheckAttestationInternal() (gas: 220632) +QueryTest:testCheckAttestation__RevertWhen__AttestationNotExistent() (gas: 13252) +QueryTest:testCheckAttestation__RevertWhen__Expired() (gas: 241272) +QueryTest:testCheckAttestation__RevertWhen__Revoked() (gas: 215460) +QueryTest:testCheckNAttestation() (gas: 405649) +QueryTest:testCheckNAttestationUnsafe() (gas: 410051) +QueryTest:testCheckNAttestationUnsafe__Expired() (gas: 410778) +QueryTest:testCheckNAttestationUnsafe__RevertWhen__ThresholdNotMet() (gas: 211697) +QueryTest:testCheckNAttestationUnsafe__Revoked() (gas: 426892) +QueryTest:testCheckNAttestation__RevertWhen__Expired() (gas: 479196) +QueryTest:testCheckNAttestation__RevertWhen__Revoked() (gas: 445326) +QueryTest:testCheckNAttestation__RevertWhen__ThresholdNotMet() (gas: 204685) +QueryTest:testFindAttestation() (gas: 197129) +QueryTest:testFindAttestations() (gas: 409961) +QueryTest:testMultiAttest() (gas: 513555) +QueryTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 200162) +QueryTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 196277) +QueryTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 231337) +QueryTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 229189) +QueryTest:testMultiRevoke() (gas: 551645) +QueryTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558359) +QueryTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 209036) +QueryTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539116) +QueryTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541216) +QueryTest:testRevoke() (gas: 213920) +QueryTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 227504) +QueryTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 40564) +QueryTest:testRevoke__RevertWhen__InvalidSchema() (gas: 211814) +QueryTest:testRevoke__RevertWhen__NotOriginalAttester() (gas: 210613) +RegistryGasComparisonTest:testGasCheck() (gas: 24495) +RegistryGasComparisonTest:testGasCheckN__Given__ThreeAttesters() (gas: 52969) +RegistryGasComparisonTest:testGasCheckN__Given__TwoAttesters() (gas: 39930) +SchemaTest:testRegisterResolver() (gas: 57051) +SchemaTest:testRegisterResolver__RevertWhen__AlreadyExists() (gas: 62725) +SchemaTest:testRegisterResolver__RevertWhen__InvalidResolver() (gas: 11729) +SchemaTest:testRegisterSchema() (gas: 60492) +SchemaTest:testRegisterSchema__RevertWhen__AlreadyExists() (gas: 63043) +SchemaTest:testSetResolver() (gas: 63614) +SimpleRegistryIntegrationTest:testGasRegistryCheck() (gas: 200467) +TokenizedResolverTest:testTokenizedResolver() (gas: 510624) +ValueResolverTest:testValueResolver() (gas: 490772) \ No newline at end of file diff --git a/src/base/Attestation.sol b/src/base/Attestation.sol index 5bc59e22..128414ed 100644 --- a/src/base/Attestation.sol +++ b/src/base/Attestation.sol @@ -52,13 +52,13 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua /** * @inheritdoc IAttestation */ + // @audit is ReentrancyGuard necessary? function attest(AttestationRequest calldata request) external payable nonReentrant { AttestationRequestData calldata requestData = request.data; ModuleRecord storage moduleRecord = _getModule({ moduleAddress: request.data.subject }); - ResolverUID resolverUID = moduleRecord.resolverUID; - verifyAttestationData(request.schemaUID, requestData); + verifyAttestationData({ schemaUID: request.schemaUID, requestData: requestData }); // write attestations to registry storge (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ @@ -69,7 +69,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // trigger the resolver procedure _resolveAttestation({ - resolverUID: resolverUID, + resolverUID: moduleRecord.resolverUID, attestationRecord: attestationRecord, value: value, isRevocation: false, @@ -257,7 +257,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // if validator is set, call the validator if (address(validator) != ZERO_ADDRESS) { // revert if ISchemaValidator returns false - if (!schema.validator.validateSchema(requestData)) { + if (!validator.validateSchema(requestData)) { revert InvalidAttestation(); } } diff --git a/src/base/AttestationDelegation.sol b/src/base/AttestationDelegation.sol index 7b23a44d..8e230ac3 100644 --- a/src/base/AttestationDelegation.sol +++ b/src/base/AttestationDelegation.sol @@ -35,11 +35,13 @@ abstract contract AttestationDelegation is IAttestation, Attestation { payable nonReentrant { - _verifyAttest(delegatedRequest); + _verifyAttestCalldata(delegatedRequest); + // Get attestationRequestData calldata pointer AttestationRequestData calldata attestationRequestData = delegatedRequest.data; + // @audit could this be address(0), what happens if there is no module Record ModuleRecord storage moduleRecord = - _getModule({ moduleAddress: delegatedRequest.data.subject }); + _getModule({ moduleAddress: attestationRequestData.subject }); ResolverUID resolverUID = moduleRecord.resolverUID; verifyAttestationData(delegatedRequest.schemaUID, attestationRequestData); diff --git a/src/base/EIP712Verifier.sol b/src/base/EIP712Verifier.sol index fc2891c1..35c864ec 100644 --- a/src/base/EIP712Verifier.sol +++ b/src/base/EIP712Verifier.sol @@ -154,10 +154,21 @@ abstract contract EIP712Verifier is EIP712 { * @param request The arguments of the delegated attestation request. */ function _verifyAttest(DelegatedAttestationRequest memory request) internal { - AttestationRequestData memory data = request.data; + uint256 nonce = _newNonce(request.attester); + bytes32 digest = _attestationDigest(request.data, request.schemaUID, nonce); + bool valid = + SignatureCheckerLib.isValidSignatureNow(request.attester, digest, request.signature); + if (!valid) revert InvalidSignature(); + } + /** + * @dev Verifies delegated attestation request. + * + * @param request The arguments of the delegated attestation request. + */ + function _verifyAttestCalldata(DelegatedAttestationRequest calldata request) internal { uint256 nonce = _newNonce(request.attester); - bytes32 digest = _attestationDigest(data, request.schemaUID, nonce); + bytes32 digest = _attestationDigest(request.data, request.schemaUID, nonce); bool valid = SignatureCheckerLib.isValidSignatureNow(request.attester, digest, request.signature); if (!valid) revert InvalidSignature(); diff --git a/src/base/QueryAttester.sol b/src/base/QueryAttester.sol index 0dbfa81c..fbb4a654 100644 --- a/src/base/QueryAttester.sol +++ b/src/base/QueryAttester.sol @@ -22,7 +22,7 @@ abstract contract Query is IQuery { mapping(address account => Attesters attesters) internal _attesters; - function setAttester(uint8 threshold, address[] calldata attesters) external { + function setAttester(uint8 threshold, address[] calldata attesters) external payable { uint256 attestersLength = attesters.length; Attesters storage _att = _attesters[msg.sender]; diff --git a/test/EIP712Verifier.t.sol b/test/EIP712Verifier.t.sol index cf4cd602..8e17c0d9 100644 --- a/test/EIP712Verifier.t.sol +++ b/test/EIP712Verifier.t.sol @@ -23,11 +23,11 @@ struct SampleAttestation { } contract EIP712VerifierInstance is EIP712Verifier { - function verifyAttest(DelegatedAttestationRequest memory request) public { + function verifyAttest(DelegatedAttestationRequest calldata request) public { _verifyAttest(request); } - function verifyRevoke(DelegatedRevocationRequest memory request) public { + function verifyRevoke(DelegatedRevocationRequest calldata request) public { _verifyRevoke(request); } From 14cd03ff5ed8502b8f3c4c5d37d1d4c075de3e82 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 6 Feb 2024 12:00:14 +0700 Subject: [PATCH 10/84] chore: name refactor --- CHANGELOG.md | 2 +- LICENSE | 2 +- docs/Attestation.md | 14 +-- lcov.info | 20 ++-- src/DataTypes.sol | 22 ++-- src/base/Attestation.sol | 46 ++++---- src/base/AttestationDelegation.sol | 92 ++++++++------- src/base/AttestationResolve.sol | 8 +- src/base/EIP712Verifier.sol | 38 ++++--- src/base/Module.sol | 10 +- src/interface/IAttestation.sol | 42 +++---- src/interface/IRegistry.sol | 24 ++-- test/Attestation.t.sol | 54 ++++----- test/AttestationDelegation.t.sol | 150 ++++++++++++------------- test/AttestationResolve.t.sol | 40 +++---- test/EIP712Verifier.t.sol | 30 ++--- test/Query.t.sol | 22 ++-- test/gas/RegistryGasComparison.t.sol | 6 +- test/resolvers/TokenizedResolver.t.sol | 2 +- test/resolvers/ValueResolver.t.sol | 4 +- test/utils/BaseUtils.sol | 24 ++-- 21 files changed, 328 insertions(+), 324 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 926fcaf3..946d53cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ The format is based on [Common Changelog](https://common-changelog.org/). ### Added -- Non-delegated attestations can now be made without a signature +- Non-signed attestations can now be made without a signature - Full ERC1271 support - SSTORE2 to store attestation data diff --git a/LICENSE b/LICENSE index 53d1f3d0..f67e5993 100644 --- a/LICENSE +++ b/LICENSE @@ -447,7 +447,7 @@ covered work, you indicate your acceptance of this License to do so. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible +propagate that work, moduleAddr to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an diff --git a/docs/Attestation.md b/docs/Attestation.md index 4379d2d2..2abb0408 100644 --- a/docs/Attestation.md +++ b/docs/Attestation.md @@ -13,7 +13,7 @@ data is `abi.encode()` according to a defined schema. The data is not stored in ```solidity struct AttestationRequestData { - address subject; // The subject of the attestation. + address moduleAddr; // The moduleAddr of the attestation. uint48 expirationTime; // The time when the attestation expires (Unix timestamp). uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors. bytes data; // Custom attestation data. @@ -27,7 +27,7 @@ AttestationRecord stored in the registry contract storage ```solidity struct AttestationRecord { SchemaUID schemaUID; // The unique identifier of the schema. - address subject; // The recipient of the attestation i.e. module + address moduleAddr; // The recipient of the attestation i.e. module address attester; // The attester/sender of the attestation. uint48 time; // The time when the attestation was created (Unix timestamp). uint48 expirationTime; // The time when the attestation expires (Unix timestamp). @@ -60,7 +60,7 @@ Attestations can not be edited. Should attestation data change, the old attestat ## Delegated Attestations -All Attestations leveraged within the Registry are designated as "delegated". +All Attestations leveraged within the Registry are designated as "signed". Such Attestations empower an entity to sign an attestation while enabling another entity to bear the transaction cost. With these attestations, the actual Attester and the one relaying the Attestation can be separate entities, thus accommodating a variety of use cases. @@ -71,9 +71,9 @@ This becomes particularly beneficial when: ```solidity /** - * @dev A struct representing the full arguments of the full delegated attestation request. + * @dev A struct representing the full arguments of the full signed attestation request. */ -struct DelegatedAttestationRequest { +struct SignedAttestationRequest { SchemaUID schemaUID; // The unique identifier of the schema. AttestationRequestData data; // The arguments of the attestation request. bytes signature; // The signature data. @@ -83,5 +83,5 @@ struct DelegatedAttestationRequest { ### ERC1271 Support -The Registry attestation process supports the ERC1271 standard, which allows smart contracts to implement a standard interface for contract ownership. This is particularly useful for smart account modules that are owned by a smart contract. The Registry supports the ERC1271 standard for delegated attestations. -Should the attester in the `DelegatedAttestationRequest` be a contract, a ERC1271 validation call is made. +The Registry attestation process supports the ERC1271 standard, which allows smart contracts to implement a standard interface for contract ownership. This is particularly useful for smart account modules that are owned by a smart contract. The Registry supports the ERC1271 standard for signed attestations. +Should the attester in the `SignedAttestationRequest` be a contract, a ERC1271 validation call is made. diff --git a/lcov.info b/lcov.info index eda957f6..ee45d336 100644 --- a/lcov.info +++ b/lcov.info @@ -217,8 +217,8 @@ DA:523,8 DA:524,8 DA:526,8 DA:529,8 -FN:544,RSAttestation._resolveAttestation -FNDA:46,RSAttestation._resolveAttestation +FN:544,RSAttestation._requireExternalResolveAttestation +FNDA:46,RSAttestation._requireExternalResolveAttestation DA:555,46 DA:556,46 BRDA:556,23,0,- @@ -253,8 +253,8 @@ BRDA:588,30,0,- BRDA:588,30,1,- DA:589,0 DA:592,0 -FN:607,RSAttestation._resolveAttestations -FNDA:49,RSAttestation._resolveAttestations +FN:607,RSAttestation._requireExternalResolveAttestations +FNDA:49,RSAttestation._requireExternalResolveAttestations DA:618,49 DA:619,49 BRDA:619,31,0,- @@ -379,8 +379,8 @@ BRDA:70,1,1,2 DA:73,2 DA:74,2 DA:76,2 -FN:79,RSModule._register -FNDA:12,RSModule._register +FN:79,RSModule._storeModuleRecord +FNDA:12,RSModule._storeModuleRecord DA:90,12 BRDA:90,2,0,- BRDA:90,2,1,12 @@ -443,8 +443,8 @@ DA:113,0 DA:114,0 DA:115,0 DA:116,0 -FN:120,RSQuery._verifyAttestation -FNDA:0,RSQuery._verifyAttestation +FN:120,RSQuery._requireValidAttestSignatureation +FNDA:0,RSQuery._requireValidAttestSignatureation DA:121,0 DA:122,0 DA:123,0 @@ -532,8 +532,8 @@ DA:116,0 FN:119,EIP712Verifier._attestationDigest FNDA:113,EIP712Verifier._attestationDigest DA:128,113 -FN:149,EIP712Verifier._verifyAttest -FNDA:58,EIP712Verifier._verifyAttest +FN:149,EIP712Verifier._requireValidAttestSignature +FNDA:58,EIP712Verifier._requireValidAttestSignature DA:150,58 DA:151,58 DA:153,58 diff --git a/src/DataTypes.sol b/src/DataTypes.sol index bc05e86c..60ace52e 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -15,7 +15,7 @@ struct AttestationRecord { uint48 expirationTime; // The time when the attestation expires (Unix timestamp). uint48 revocationTime; // The time when the attestation was revoked (Unix timestamp). SchemaUID schemaUID; // The unique identifier of the schema. - address subject; // The implementation address of the module that is being attested. + address moduleAddr; // The implementation address of the module that is being attested. address attester; // The attesting account. AttestationDataRef dataPointer; // SSTORE2 pointer to the attestation data. } @@ -47,7 +47,7 @@ struct ResolverRecord { * @dev A struct representing the arguments of the attestation request. */ struct AttestationRequestData { - address subject; // The subject of the attestation. + address moduleAddr; // The moduleAddr of the attestation. uint48 expirationTime; // The time when the attestation expires (Unix timestamp). uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors. bytes data; // Custom attestation data. @@ -62,9 +62,9 @@ struct AttestationRequest { } /** - * @dev A struct representing the full arguments of the full delegated attestation request. + * @dev A struct representing the full arguments of the full signed attestation request. */ -struct DelegatedAttestationRequest { +struct SignedAttestationRequest { SchemaUID schemaUID; // The unique identifier of the schema. AttestationRequestData data; // The arguments of the attestation request. address attester; // The attesting account. @@ -80,9 +80,9 @@ struct MultiAttestationRequest { } /** - * @dev A struct representing the full arguments of the delegated multi attestation request. + * @dev A struct representing the full arguments of the signed multi attestation request. */ -struct MultiDelegatedAttestationRequest { +struct MultiSignedAttestationRequest { SchemaUID schemaUID; // The unique identifier of the schema. AttestationRequestData[] data; // The arguments of the attestation requests. bytes[] signatures; // The signatures data. signatures are assumed to be signed with increasing nonces. @@ -97,7 +97,7 @@ struct MultiDelegatedAttestationRequest { * @dev A struct representing the arguments of the revocation request. */ struct RevocationRequestData { - address subject; // The module address. + address moduleAddr; // The module address. address attester; // The attesting account. uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors. } @@ -111,9 +111,9 @@ struct RevocationRequest { } /** - * @dev A struct representing the arguments of the full delegated revocation request. + * @dev A struct representing the arguments of the full signed revocation request. */ -struct DelegatedRevocationRequest { +struct SignedRevocationRequest { SchemaUID schemaUID; // The unique identifier of the schema. RevocationRequestData data; // The arguments of the revocation request. address revoker; // The revoking account. @@ -129,9 +129,9 @@ struct MultiRevocationRequest { } /** - * @dev A struct representing the full arguments of the delegated multi revocation request. + * @dev A struct representing the full arguments of the signed multi revocation request. */ -struct MultiDelegatedRevocationRequest { +struct MultiSignedRevocationRequest { SchemaUID schemaUID; // The unique identifier of the schema. RevocationRequestData[] data; // The arguments of the revocation requests. address revoker; // The revoking account. diff --git a/src/base/Attestation.sol b/src/base/Attestation.sol index 128414ed..dd952026 100644 --- a/src/base/Attestation.sol +++ b/src/base/Attestation.sol @@ -56,9 +56,10 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua function attest(AttestationRequest calldata request) external payable nonReentrant { AttestationRequestData calldata requestData = request.data; - ModuleRecord storage moduleRecord = _getModule({ moduleAddress: request.data.subject }); + ModuleRecord storage moduleRecord = _getModule({ moduleAddress: request.data.moduleAddr }); - verifyAttestationData({ schemaUID: request.schemaUID, requestData: requestData }); + // check if schema exists and is valid. This will revert if validtor returns false + _requireSchemaCheck({ schemaUID: request.schemaUID, requestData: requestData }); // write attestations to registry storge (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ @@ -68,7 +69,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua }); // trigger the resolver procedure - _resolveAttestation({ + _requireExternalResolveAttestation({ resolverUID: moduleRecord.resolverUID, attestationRecord: attestationRecord, value: value, @@ -91,7 +92,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // Batched Revocations can only be done for a single resolver. See IAttestation.sol ModuleRecord storage moduleRecord = - _getModule({ moduleAddress: multiRequests[0].data[0].subject }); + _getModule({ moduleAddress: multiRequests[0].data[0].moduleAddr }); for (uint256 i; i < length; ++i) { // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there @@ -126,7 +127,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua * @inheritdoc IAttestation */ function revoke(RevocationRequest calldata request) external payable nonReentrant { - ModuleRecord memory moduleRecord = _getModule({ moduleAddress: request.data.subject }); + ModuleRecord memory moduleRecord = _getModule({ moduleAddress: request.data.moduleAddr }); SchemaRecord storage schema = _getSchema({ schemaUID: request.schemaUID }); if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); @@ -134,7 +135,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua AttestationRecord memory attestationRecord = _revoke({ request: request.data, revoker: msg.sender }); - _resolveAttestation({ + _requireExternalResolveAttestation({ resolverUID: moduleRecord.resolverUID, attestationRecord: attestationRecord, value: 0, @@ -160,7 +161,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // Batched Revocations can only be done for a single resolver. See IAttestation.sol ModuleRecord memory moduleRecord = - _getModule({ moduleAddress: multiRequests[0].data[0].subject }); + _getModule({ moduleAddress: multiRequests[0].data[0].moduleAddr }); uint256 requestsLength = multiRequests.length; // should cache length @@ -210,7 +211,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua internal returns (uint256 usedValue) { - verifyAttestationData(schemaUID, attestationRequestDatas); + _requireSchemaCheck(schemaUID, attestationRequestDatas); // caching length uint256 length = attestationRequestDatas.length; @@ -232,7 +233,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua } // trigger the resolver procedure - usedValue = _resolveAttestations({ + usedValue = _requireExternalResolveAttestations({ resolverUID: resolverUID, attestationRecords: attestationRecords, values: values, @@ -242,7 +243,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua }); } - function verifyAttestationData( + function _requireSchemaCheck( SchemaUID schemaUID, AttestationRequestData calldata requestData ) @@ -255,15 +256,13 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // validate Schema ISchemaValidator validator = schema.validator; // if validator is set, call the validator - if (address(validator) != ZERO_ADDRESS) { + if (address(validator) != ZERO_ADDRESS && validator.validateSchema(requestData) == false) { // revert if ISchemaValidator returns false - if (!validator.validateSchema(requestData)) { - revert InvalidAttestation(); - } + revert InvalidAttestation(); } } - function verifyAttestationData( + function _requireSchemaCheck( SchemaUID schemaUID, AttestationRequestData[] calldata requestDatas ) @@ -276,11 +275,8 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // validate Schema ISchemaValidator validator = schema.validator; // if validator is set, call the validator - if (address(validator) != ZERO_ADDRESS) { - // revert if ISchemaValidator returns false - if (!schema.validator.validateSchema(requestDatas)) { - revert InvalidAttestation(); - } + if (address(validator) != ZERO_ADDRESS && validator.validateSchema(requestDatas) == false) { + revert InvalidAttestation(); } } @@ -314,7 +310,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua revert InvalidExpirationTime(); } // caching module address. - address module = attestationRequestData.subject; + address module = attestationRequestData.moduleAddr; ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); // Ensure that attestation is for module that was registered. @@ -332,7 +328,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // write attestationdata with SSTORE2 to EVM, and prepare return value attestationRecord = AttestationRecord({ schemaUID: schemaUID, - subject: module, + moduleAddr: module, attester: attester, time: timeNow, expirationTime: attestationRequestData.expirationTime, @@ -355,7 +351,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua returns (AttestationRecord memory) { AttestationRecord storage attestation = - _moduleToAttesterToAttestations[request.subject][request.attester]; + _moduleToAttesterToAttestations[request.moduleAddr][request.attester]; // Ensure that we aren't attempting to revoke a non-existing attestation. if (AttestationDataRef.unwrap(attestation.dataPointer) == ZERO_ADDRESS) { @@ -374,7 +370,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua attestation.revocationTime = _time(); emit Revoked({ - subject: attestation.subject, + moduleAddr: attestation.moduleAddr, revoker: revoker, schema: attestation.schemaUID }); @@ -419,7 +415,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua values[i] = revocationRequests.value; } - return _resolveAttestations({ + return _requireExternalResolveAttestations({ resolverUID: resolverUID, attestationRecords: attestationRecords, values: values, diff --git a/src/base/AttestationDelegation.sol b/src/base/AttestationDelegation.sol index 8e230ac3..480d2007 100644 --- a/src/base/AttestationDelegation.sol +++ b/src/base/AttestationDelegation.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.19; import { IAttestation } from "../interface/IAttestation.sol"; import { Attestation } from "./Attestation.sol"; import { - DelegatedAttestationRequest, - MultiDelegatedAttestationRequest, - DelegatedRevocationRequest, - MultiDelegatedRevocationRequest, + SignedAttestationRequest, + MultiSignedAttestationRequest, + SignedRevocationRequest, + MultiSignedRevocationRequest, AttestationRequestData, ModuleRecord, ResolverUID, @@ -18,7 +18,7 @@ import { InvalidLength } from "../Common.sol"; /** * @title AttestationDelegation - * @dev This contract provides a delegated approach to attesting and revoking attestations. + * @dev This contract provides a signed approach to attesting and revoking attestations. * The contract extends both IAttestation and Attestation. * @author rhinestone | zeroknots.eth, Konrad Kopp(@kopy-kat) */ @@ -30,29 +30,34 @@ abstract contract AttestationDelegation is IAttestation, Attestation { /** * @inheritdoc IAttestation */ - function attest(DelegatedAttestationRequest calldata delegatedRequest) + function attest(SignedAttestationRequest calldata signedRequest) external payable nonReentrant { - _verifyAttestCalldata(delegatedRequest); - // Get attestationRequestData calldata pointer - AttestationRequestData calldata attestationRequestData = delegatedRequest.data; + AttestationRequestData calldata attestationRequestData = signedRequest.data; + // check signature. this will revert if signedRequest.attester != signer + _requireValidAttestSignatureCalldata(signedRequest); + // check if schema exists and is valid. This will revert if validtor returns false + _requireSchemaCheck(signedRequest.schemaUID, attestationRequestData); + // @audit could this be address(0), what happens if there is no module Record ModuleRecord storage moduleRecord = - _getModule({ moduleAddress: attestationRequestData.subject }); + _getModule({ moduleAddress: attestationRequestData.moduleAddr }); ResolverUID resolverUID = moduleRecord.resolverUID; - verifyAttestationData(delegatedRequest.schemaUID, attestationRequestData); - + // store attestation record (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ - schemaUID: delegatedRequest.schemaUID, + schemaUID: signedRequest.schemaUID, attestationRequestData: attestationRequestData, - attester: delegatedRequest.attester + attester: signedRequest.attester }); - _resolveAttestation({ + // if a external resolver is configured for the resolver UID, + // this will call the external resolver contract to validate the attestationrequest + // should the external resolver return false, this will revert + _requireExternalResolveAttestation({ resolverUID: resolverUID, attestationRecord: attestationRecord, value: value, @@ -65,12 +70,14 @@ abstract contract AttestationDelegation is IAttestation, Attestation { /** * @inheritdoc IAttestation */ - function multiAttest(MultiDelegatedAttestationRequest[] calldata multiDelegatedRequests) + function multiAttest(MultiSignedAttestationRequest[] calldata multiSignedRequests) external payable nonReentrant { - uint256 length = multiDelegatedRequests.length; + // check if schema exists and is valid. This will revert if validtor returns false + _requireSchemaCheck({ schemaUID: multiSignedRequests.schemaUID, requestDatas: multiSignedRequests.data }); + uint256 length = multiSignedRequests.length; // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless @@ -80,7 +87,7 @@ abstract contract AttestationDelegation is IAttestation, Attestation { // Batched Revocations can only be done for a single resolver. See IAttestation.sol ModuleRecord memory moduleRecord = - _getModule({ moduleAddress: multiDelegatedRequests[0].data[0].subject }); + _getModule({ moduleAddress: multiSignedRequests[0].data[0].moduleAddr }); // TODO: // I think it would be much better to move this into the for loop so we can iterate over the requests. // Its possible that the MultiAttestationRequests is attesting different modules, @@ -95,34 +102,33 @@ abstract contract AttestationDelegation is IAttestation, Attestation { last = i == length - 1; } - MultiDelegatedAttestationRequest calldata multiDelegatedRequest = - multiDelegatedRequests[i]; - AttestationRequestData[] calldata attestationRequestDatas = multiDelegatedRequest.data; + MultiSignedAttestationRequest calldata multiSignedRequest = multiSignedRequests[i]; + AttestationRequestData[] calldata attestationRequestDatas = multiSignedRequest.data; uint256 dataLength = attestationRequestDatas.length; // Ensure that no inputs are missing. - if (dataLength != multiDelegatedRequest.signatures.length) { + if (dataLength != multiSignedRequest.signatures.length) { revert InvalidLength(); } // Verify signatures. Note that the signatures are assumed to be signed with increasing nonces. for (uint256 j; j < dataLength; ++j) { - _verifyAttest( - DelegatedAttestationRequest({ - schemaUID: multiDelegatedRequest.schemaUID, + _requireValidAttestSignature( + SignedAttestationRequest({ + schemaUID: multiSignedRequest.schemaUID, data: attestationRequestDatas[j], - signature: multiDelegatedRequest.signatures[j], - attester: multiDelegatedRequest.attester + signature: multiSignedRequest.signatures[j], + attester: multiSignedRequest.attester }) ); } // Process the current batch of attestations. uint256 usedValue = _multiAttest({ - schemaUID: multiDelegatedRequest.schemaUID, + schemaUID: multiSignedRequest.schemaUID, resolverUID: moduleRecord.resolverUID, attestationRequestDatas: attestationRequestDatas, - attester: multiDelegatedRequest.attester, + attester: multiSignedRequest.attester, availableValue: availableValue, isLastAttestation: last }); @@ -139,13 +145,13 @@ abstract contract AttestationDelegation is IAttestation, Attestation { /** * @inheritdoc IAttestation */ - function revoke(DelegatedRevocationRequest calldata request) external payable nonReentrant { + function revoke(SignedRevocationRequest calldata request) external payable nonReentrant { _verifyRevoke(request); RevocationRequestData[] memory data = new RevocationRequestData[](1); data[0] = request.data; - ModuleRecord memory moduleRecord = _getModule({ moduleAddress: request.data.subject }); + ModuleRecord memory moduleRecord = _getModule({ moduleAddress: request.data.moduleAddr }); _multiRevoke({ schemaUID: request.schemaUID, @@ -160,7 +166,7 @@ abstract contract AttestationDelegation is IAttestation, Attestation { /** * @inheritdoc IAttestation */ - function multiRevoke(MultiDelegatedRevocationRequest[] calldata multiDelegatedRequests) + function multiRevoke(MultiSignedRevocationRequest[] calldata multiSignedRequests) external payable nonReentrant @@ -170,11 +176,11 @@ abstract contract AttestationDelegation is IAttestation, Attestation { // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be // possible to send too much ETH anyway. uint256 availableValue = msg.value; - uint256 length = multiDelegatedRequests.length; + uint256 length = multiSignedRequests.length; // Batched Revocations can only be done for a single resolver. See IAttestation.sol ModuleRecord memory moduleRecord = - _getModule({ moduleAddress: multiDelegatedRequests[0].data[0].subject }); + _getModule({ moduleAddress: multiSignedRequests[0].data[0].moduleAddr }); for (uint256 i; i < length; ++i) { // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there @@ -185,33 +191,33 @@ abstract contract AttestationDelegation is IAttestation, Attestation { last = i == length - 1; } - MultiDelegatedRevocationRequest memory multiDelegatedRequest = multiDelegatedRequests[i]; - RevocationRequestData[] memory revocationRequestDatas = multiDelegatedRequest.data; + MultiSignedRevocationRequest memory multiSignedRequest = multiSignedRequests[i]; + RevocationRequestData[] memory revocationRequestDatas = multiSignedRequest.data; uint256 dataLength = revocationRequestDatas.length; // Ensure that no inputs are missing. - if (dataLength == 0 || dataLength != multiDelegatedRequest.signatures.length) { + if (dataLength == 0 || dataLength != multiSignedRequest.signatures.length) { revert InvalidLength(); } // Verify EIP712 signatures. Please note that the signatures are assumed to be signed with increasing nonces. for (uint256 j; j < dataLength; ++j) { _verifyRevoke( - DelegatedRevocationRequest({ - schemaUID: multiDelegatedRequest.schemaUID, + SignedRevocationRequest({ + schemaUID: multiSignedRequest.schemaUID, data: revocationRequestDatas[j], - signature: multiDelegatedRequest.signatures[j], - revoker: multiDelegatedRequest.revoker + signature: multiSignedRequest.signatures[j], + revoker: multiSignedRequest.revoker }) ); } // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. availableValue -= _multiRevoke({ - schemaUID: multiDelegatedRequest.schemaUID, + schemaUID: multiSignedRequest.schemaUID, resolverUID: moduleRecord.resolverUID, revocationRequestDatas: revocationRequestDatas, - revoker: multiDelegatedRequest.revoker, + revoker: multiSignedRequest.revoker, availableValue: availableValue, isLastRevocation: last }); diff --git a/src/base/AttestationResolve.sol b/src/base/AttestationResolve.sol index 93809331..39e8a983 100644 --- a/src/base/AttestationResolve.sol +++ b/src/base/AttestationResolve.sol @@ -13,7 +13,7 @@ import { IResolver } from "../external/IResolver.sol"; /** * @title AttestationResolve - * @dev This contract provides functions to resolve non-delegated attestations and revocations. + * @dev This contract provides functions to resolve non-signed attestations and revocations. * @author rhinestone | zeroknots.eth, Konrad Kopp(@kopy-kat) */ abstract contract AttestationResolve is IAttestation, EIP712Verifier { @@ -31,7 +31,7 @@ abstract contract AttestationResolve is IAttestation, EIP712Verifier { * * @return Returns the total sent ETH amount. */ - function _resolveAttestation( + function _requireExternalResolveAttestation( ResolverUID resolverUID, AttestationRecord memory attestationRecord, uint256 value, @@ -97,7 +97,7 @@ abstract contract AttestationResolve is IAttestation, EIP712Verifier { * @return Returns the total sent ETH amount. */ // solhint-disable-next-line code-complexity - function _resolveAttestations( + function _requireExternalResolveAttestations( ResolverUID resolverUID, AttestationRecord[] memory attestationRecords, uint256[] memory values, @@ -110,7 +110,7 @@ abstract contract AttestationResolve is IAttestation, EIP712Verifier { { uint256 length = attestationRecords.length; if (length == 1) { - return _resolveAttestation({ + return _requireExternalResolveAttestation({ resolverUID: resolverUID, attestationRecord: attestationRecords[0], value: values[0], diff --git a/src/base/EIP712Verifier.sol b/src/base/EIP712Verifier.sol index 35c864ec..aaeae7f1 100644 --- a/src/base/EIP712Verifier.sol +++ b/src/base/EIP712Verifier.sol @@ -8,9 +8,9 @@ import { InvalidSignature } from "../Common.sol"; import { AttestationRequestData, SchemaUID, - DelegatedAttestationRequest, + SignedAttestationRequest, RevocationRequestData, - DelegatedRevocationRequest + SignedRevocationRequest } from "../DataTypes.sol"; /** @@ -139,7 +139,7 @@ abstract contract EIP712Verifier is EIP712 { ATTEST_TYPEHASH, block.chainid, schemaUID, - data.subject, + data.moduleAddr, data.expirationTime, keccak256(data.data), nonce @@ -149,11 +149,11 @@ abstract contract EIP712Verifier is EIP712 { } /** - * @dev Verifies delegated attestation request. + * @dev Verifies signed attestation request. * - * @param request The arguments of the delegated attestation request. + * @param request The arguments of the signed attestation request. */ - function _verifyAttest(DelegatedAttestationRequest memory request) internal { + function _requireValidAttestSignature(SignedAttestationRequest memory request) internal { uint256 nonce = _newNonce(request.attester); bytes32 digest = _attestationDigest(request.data, request.schemaUID, nonce); bool valid = @@ -162,11 +162,13 @@ abstract contract EIP712Verifier is EIP712 { } /** - * @dev Verifies delegated attestation request. + * @dev Verifies signed attestation request. * - * @param request The arguments of the delegated attestation request. + * @param request The arguments of the signed attestation request. */ - function _verifyAttestCalldata(DelegatedAttestationRequest calldata request) internal { + function _requireValidAttestSignatureCalldata(SignedAttestationRequest calldata request) + internal + { uint256 nonce = _newNonce(request.attester); bytes32 digest = _attestationDigest(request.data, request.schemaUID, nonce); bool valid = @@ -205,7 +207,7 @@ abstract contract EIP712Verifier is EIP712 { returns (bytes32 digest) { uint256 nonce = getNonce(revoker) + 1; - digest = _revocationDigest(schemaUID, revData.subject, revData.attester, nonce); + digest = _revocationDigest(schemaUID, revData.moduleAddr, revData.attester, nonce); } /** @@ -225,20 +227,20 @@ abstract contract EIP712Verifier is EIP712 { view returns (bytes32 digest) { - digest = _revocationDigest(schemaUID, revData.subject, revData.attester, nonce); + digest = _revocationDigest(schemaUID, revData.moduleAddr, revData.attester, nonce); } /** * @dev Gets the revocation digest * @param schemaUID The UID of the schema. - * @param subject The address of the subject. + * @param moduleAddr The address of the moduleAddr. * @param nonce The nonce of the attestation request. * * @return digest The revocation digest. */ function _revocationDigest( SchemaUID schemaUID, - address subject, + address moduleAddr, address attester, uint256 nonce ) @@ -248,21 +250,21 @@ abstract contract EIP712Verifier is EIP712 { { digest = _hashTypedData( keccak256( - abi.encode(REVOKE_TYPEHASH, block.chainid, schemaUID, subject, attester, nonce) + abi.encode(REVOKE_TYPEHASH, block.chainid, schemaUID, moduleAddr, attester, nonce) ) ); } /** - * @dev Verifies delegated revocation request. + * @dev Verifies signed revocation request. * - * @param request The arguments of the delegated revocation request. + * @param request The arguments of the signed revocation request. */ - function _verifyRevoke(DelegatedRevocationRequest memory request) internal { + function _verifyRevoke(SignedRevocationRequest memory request) internal { RevocationRequestData memory data = request.data; uint256 nonce = _newNonce(request.revoker); - bytes32 digest = _revocationDigest(request.schemaUID, data.subject, data.attester, nonce); + bytes32 digest = _revocationDigest(request.schemaUID, data.moduleAddr, data.attester, nonce); bool valid = SignatureCheckerLib.isValidSignatureNow(request.revoker, digest, request.signature); if (!valid) revert InvalidSignature(); diff --git a/src/base/Module.sol b/src/base/Module.sol index 4c56c114..0dc7ea35 100644 --- a/src/base/Module.sol +++ b/src/base/Module.sol @@ -56,7 +56,7 @@ abstract contract Module is IModule, ReentrancyGuard { (moduleAddr,,) = code.deploy(deployParams, salt, msg.value); - _register({ + _storeModuleRecord({ moduleAddress: moduleAddr, sender: msg.sender, resolver: resolver, @@ -86,7 +86,7 @@ abstract contract Module is IModule, ReentrancyGuard { bytes32 senderSalt = keccak256(abi.encodePacked(salt, msg.sender)); moduleAddr = CREATE3.deploy(senderSalt, creationCode, msg.value); - _register({ + _storeModuleRecord({ moduleAddress: moduleAddr, sender: msg.sender, resolver: resolver, @@ -118,7 +118,7 @@ abstract contract Module is IModule, ReentrancyGuard { if (moduleAddr == ZERO_ADDRESS) revert InvalidDeployment(); if (_isContract(moduleAddr) != true) revert InvalidDeployment(); - _register({ + _storeModuleRecord({ moduleAddress: moduleAddr, sender: msg.sender, resolver: resolver, @@ -141,7 +141,7 @@ abstract contract Module is IModule, ReentrancyGuard { ResolverRecord memory resolver = getResolver(resolverUID); if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); - _register({ + _storeModuleRecord({ moduleAddress: moduleAddress, sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function resolver: resolver, @@ -161,7 +161,7 @@ abstract contract Module is IModule, ReentrancyGuard { * @param resolverUID Unique ID of the resolver. * @param metadata Data associated with the module. */ - function _register( + function _storeModuleRecord( address moduleAddress, address sender, ResolverRecord memory resolver, diff --git a/src/interface/IAttestation.sol b/src/interface/IAttestation.sol index 228e3205..ec9ba9c0 100644 --- a/src/interface/IAttestation.sol +++ b/src/interface/IAttestation.sol @@ -6,11 +6,11 @@ import { AttestationDataRef, AttestationRequest, MultiAttestationRequest, - DelegatedAttestationRequest, - MultiDelegatedAttestationRequest, + SignedAttestationRequest, + MultiSignedAttestationRequest, RevocationRequest, - DelegatedRevocationRequest, - MultiDelegatedRevocationRequest, + SignedRevocationRequest, + MultiSignedRevocationRequest, MultiRevocationRequest } from "../DataTypes.sol"; @@ -39,12 +39,12 @@ interface IAttestation { /** * @dev Emitted when an attestation has been made. * - * @param subject The subject of the attestation. + * @param moduleAddr The moduleAddr of the attestation. * @param attester The attesting account. * @param schema The UID of the schema. */ event Attested( - address indexed subject, + address indexed moduleAddr, address indexed attester, SchemaUID schema, AttestationDataRef indexed dataPointer @@ -53,11 +53,11 @@ interface IAttestation { /** * @dev Emitted when an attestation has been revoked. * - * @param subject The subject of the attestation. + * @param moduleAddr The moduleAddr of the attestation. * @param revoker The attesting account. * @param schema The UID of the schema. */ - event Revoked(address indexed subject, address indexed revoker, SchemaUID indexed schema); + event Revoked(address indexed moduleAddr, address indexed revoker, SchemaUID indexed schema); /** * @dev Emitted when a data has been timestamped. @@ -94,26 +94,26 @@ interface IAttestation { function multiAttest(MultiAttestationRequest[] calldata multiRequests) external payable; /** - * @notice Handles a single delegated attestation request + * @notice Handles a single signed attestation request * * @dev The function verifies the attestation, * wraps the data in an array and forwards it to the _multiAttest() function * - * @param delegatedRequest A delegated attestation request + * @param signedRequest A signed attestation request */ - function attest(DelegatedAttestationRequest calldata delegatedRequest) external payable; + function attest(SignedAttestationRequest calldata signedRequest) external payable; /** - * @notice Function to handle multiple delegated attestation requests + * @notice Function to handle multiple signed attestation requests * * @dev It iterates over the attestation requests and processes them. It collects the returned UIDs into a list. * @dev Although the registry supports batched attestations, the function only allows * batched Attestations for a single resolver. * If you want to attest to multiple resolvers, you need to call the function multiple times. * - * @param multiDelegatedRequests An array of multiple delegated attestation requests + * @param multiSignedRequests An array of multiple signed attestation requests */ - function multiAttest(MultiDelegatedAttestationRequest[] calldata multiDelegatedRequests) + function multiAttest(MultiSignedAttestationRequest[] calldata multiSignedRequests) external payable; @@ -124,25 +124,25 @@ interface IAttestation { */ function revoke(RevocationRequest calldata request) external payable; /** - * @notice Handles a single delegated revocation request + * @notice Handles a single signed revocation request * * @dev The function verifies the revocation, prepares data for the _multiRevoke() function and revokes the requestZ * - * @param request A delegated revocation request + * @param request A signed revocation request */ - function revoke(DelegatedRevocationRequest calldata request) external payable; + function revoke(SignedRevocationRequest calldata request) external payable; /** - * @notice Handles multiple delegated revocation requests + * @notice Handles multiple signed revocation requests * - * @dev The function iterates over the multiDelegatedRequests array, verifies each revocation and revokes the request + * @dev The function iterates over the multiSignedRequests array, verifies each revocation and revokes the request * @dev Although the registry supports batched revocations, the function only allows * batched Attestations for a single resolver. * If you want to attest to multiple resolvers, you need to call the function multiple times. * - * @param multiDelegatedRequests An array of multiple delegated revocation requests + * @param multiSignedRequests An array of multiple signed revocation requests */ - function multiRevoke(MultiDelegatedRevocationRequest[] calldata multiDelegatedRequests) + function multiRevoke(MultiSignedRevocationRequest[] calldata multiSignedRequests) external payable; diff --git a/src/interface/IRegistry.sol b/src/interface/IRegistry.sol index fe9cfc94..23687250 100644 --- a/src/interface/IRegistry.sol +++ b/src/interface/IRegistry.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; interface IRegistry { event Attested( - address indexed subject, + address indexed moduleAddr, address indexed attester, bytes32 schema, address indexed dataPointer @@ -15,7 +15,7 @@ interface IRegistry { ); event ModuleRegistration(address indexed implementation, bytes32 resolver); event NewSchemaResolver(bytes32 indexed uid, address resolver); - event Revoked(address indexed subject, address indexed attester, bytes32 indexed schema); + event Revoked(address indexed moduleAddr, address indexed attester, bytes32 indexed schema); event RevokedOffchain(address indexed revoker, bytes32 indexed data, uint64 indexed timestamp); event SchemaRegistered(bytes32 indexed uid, address registerer); event SchemaResolverRegistered(bytes32 indexed uid, address registerer); @@ -23,7 +23,7 @@ interface IRegistry { struct AttestationRecord { bytes32 schemaUID; - address subject; + address moduleAddr; address attester; uint48 time; uint48 expirationTime; @@ -37,20 +37,20 @@ interface IRegistry { } struct AttestationRequestData { - address subject; + address moduleAddr; uint48 expirationTime; uint256 value; bytes data; } - struct DelegatedAttestationRequest { + struct SignedAttestationRequest { bytes32 schemaUID; AttestationRequestData data; bytes signature; address attester; } - struct DelegatedRevocationRequest { + struct SignedRevocationRequest { bytes32 schemaUID; RevocationRequestData data; bytes signature; @@ -69,14 +69,14 @@ interface IRegistry { AttestationRequestData[] data; } - struct MultiDelegatedAttestationRequest { + struct MultiSignedAttestationRequest { bytes32 schemaUID; AttestationRequestData[] data; bytes[] signatures; address attester; } - struct MultiDelegatedRevocationRequest { + struct MultiSignedRevocationRequest { bytes32 schemaUID; RevocationRequestData[] data; bytes[] signatures; @@ -99,7 +99,7 @@ interface IRegistry { } struct RevocationRequestData { - address subject; + address moduleAddr; address attester; uint256 value; } @@ -110,7 +110,7 @@ interface IRegistry { string schema; } - function attest(DelegatedAttestationRequest memory delegatedRequest) external payable; + function attest(SignedAttestationRequest memory signedRequest) external payable; function attest(AttestationRequest memory request) external payable; function check( address module, @@ -214,12 +214,12 @@ interface IRegistry { returns (bytes32 digest); function getRevokeTypeHash() external pure returns (bytes32); function getSchema(bytes32 uid) external view returns (SchemaRecord memory); - function multiAttest(MultiDelegatedAttestationRequest[] memory multiDelegatedRequests) + function multiAttest(MultiSignedAttestationRequest[] memory multiSignedRequests) external payable; function multiAttest(MultiAttestationRequest[] memory multiRequests) external payable; function multiRevoke(MultiRevocationRequest[] memory multiRequests) external payable; - function multiRevoke(MultiDelegatedRevocationRequest[] memory multiDelegatedRequests) + function multiRevoke(MultiSignedRevocationRequest[] memory multiSignedRequests) external payable; function register(bytes32 resolverUID, address moduleAddress, bytes memory data) external; diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index fbe5bad9..12bde519 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -19,7 +19,7 @@ import { import { AttestationRecord, - MultiDelegatedAttestationRequest, + MultiSignedAttestationRequest, MultiAttestationRequest, MultiRevocationRequest, RevocationRequestData, @@ -49,7 +49,7 @@ contract AttestationTest is BaseTest { function testAttest__RevertWhen__InvalidExpirationTime() public { AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(1), data: abi.encode(true), value: 0 @@ -61,7 +61,7 @@ contract AttestationTest is BaseTest { function testAttest__RevertWhen__ZeroImplementation() public { AttestationRequestData memory attData = AttestationRequestData({ - subject: address(0x69), + moduleAddr: address(0x69), expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -73,7 +73,7 @@ contract AttestationTest is BaseTest { function testAttest__RevertWhen__InvalidSchema() public { AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -86,7 +86,7 @@ contract AttestationTest is BaseTest { function testAttest__RevertWhen__ValidatorSaysInvalidAttestation() public { SchemaUID schemaId = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -109,7 +109,7 @@ contract AttestationTest is BaseTest { console2.log(data.length); AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: data, value: 0 @@ -124,14 +124,14 @@ contract AttestationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -155,14 +155,14 @@ contract AttestationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -188,14 +188,14 @@ contract AttestationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -220,14 +220,14 @@ contract AttestationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(1), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -253,14 +253,14 @@ contract AttestationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: address(0x69), + moduleAddr: address(0x69), expirationTime: uint48(0), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -310,7 +310,7 @@ contract AttestationTest is BaseTest { instance.mockAttestation(defaultSchema1, defaultModule1); RevocationRequestData memory revoke = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequest memory req = RevocationRequest({ schemaUID: defaultSchema1, data: revoke }); @@ -344,10 +344,10 @@ contract AttestationTest is BaseTest { instance.mockAttestation(defaultSchema1, anotherModule); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; @@ -379,10 +379,10 @@ contract AttestationTest is BaseTest { instance.mockAttestation(defaultSchema1, anotherModule); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; @@ -404,10 +404,10 @@ contract AttestationTest is BaseTest { ); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; @@ -433,10 +433,10 @@ contract AttestationTest is BaseTest { instance.mockAttestation(defaultSchema1, anotherModule); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; @@ -463,10 +463,10 @@ contract AttestationTest is BaseTest { instance.mockAttestation(defaultSchema1, anotherModule); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; diff --git a/test/AttestationDelegation.t.sol b/test/AttestationDelegation.t.sol index 96be30ec..32e46782 100644 --- a/test/AttestationDelegation.t.sol +++ b/test/AttestationDelegation.t.sol @@ -18,7 +18,7 @@ import { RegistryInstance, console2, AttestationRequestData, - DelegatedAttestationRequest, + SignedAttestationRequest, MockModuleWithArgs, ResolverUID, SchemaUID, @@ -28,11 +28,11 @@ import { import { AttestationRecord, MultiAttestationRequest, - MultiDelegatedAttestationRequest, + MultiSignedAttestationRequest, MultiRevocationRequest, RevocationRequestData, - DelegatedRevocationRequest, - MultiDelegatedRevocationRequest + SignedRevocationRequest, + MultiSignedRevocationRequest } from "../src/DataTypes.sol"; struct SampleAttestation { @@ -53,69 +53,69 @@ contract AttestationDelegationTest is BaseTest { } function testAttest() public { - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); } function testAttest__RevertWhen__InvalidExpirationTime() public { AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(1), data: abi.encode(true), value: 0 }); vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidExpirationTime.selector)); - instance.newDelegatedAttestation(defaultSchema1, auth1k, attData); + instance.newSignedAttestation(defaultSchema1, auth1k, attData); } function testAttest__RevertWhen__ZeroImplementation() public { AttestationRequestData memory attData = AttestationRequestData({ - subject: address(0x69), + moduleAddr: address(0x69), expirationTime: uint48(0), data: abi.encode(true), value: 0 }); vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.newDelegatedAttestation(defaultSchema1, auth1k, attData); + instance.newSignedAttestation(defaultSchema1, auth1k, attData); } function testAttest__RevertWhen__InvalidSchema() public { AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.newDelegatedAttestation(SchemaUID.wrap(0), auth1k, attData); + instance.newSignedAttestation(SchemaUID.wrap(0), auth1k, attData); } function testAttest__RevertWhen__ValidatorSaysInvalidAttestation() public { SchemaUID schemaUID = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.newDelegatedAttestation(schemaUID, auth1k, attData); + instance.newSignedAttestation(schemaUID, auth1k, attData); } function testAttest__RevertWhen_InvalidSignature() public { SchemaUID schemaUID = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); bytes memory signature = ""; - DelegatedAttestationRequest memory req = DelegatedAttestationRequest({ + SignedAttestationRequest memory req = SignedAttestationRequest({ schemaUID: schemaUID, data: attData, signature: signature, @@ -138,13 +138,13 @@ contract AttestationDelegationTest is BaseTest { console2.log(data.length); AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: data, value: 0 }); - instance.newDelegatedAttestation(defaultSchema1, auth1k, attData); + instance.newSignedAttestation(defaultSchema1, auth1k, attData); } function testMultiAttest() public { @@ -153,14 +153,14 @@ contract AttestationDelegationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -172,8 +172,8 @@ contract AttestationDelegationTest is BaseTest { bytes[] memory sigs = instance.signAttestation(defaultSchema1, auth1k, attArray); - MultiDelegatedAttestationRequest[] memory reqs = new MultiDelegatedAttestationRequest[](1); - MultiDelegatedAttestationRequest memory req1 = MultiDelegatedAttestationRequest({ + MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); + MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ schemaUID: defaultSchema1, data: attArray, signatures: sigs, @@ -190,14 +190,14 @@ contract AttestationDelegationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -209,8 +209,8 @@ contract AttestationDelegationTest is BaseTest { bytes[] memory sigs = instance.signAttestation(SchemaUID.wrap(0), auth1k, attArray); - MultiDelegatedAttestationRequest[] memory reqs = new MultiDelegatedAttestationRequest[](1); - MultiDelegatedAttestationRequest memory req1 = MultiDelegatedAttestationRequest({ + MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); + MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ schemaUID: SchemaUID.wrap(0), data: attArray, signatures: sigs, @@ -229,14 +229,14 @@ contract AttestationDelegationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(0), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -248,8 +248,8 @@ contract AttestationDelegationTest is BaseTest { bytes[] memory sigs = instance.signAttestation(schemaUID, auth1k, attArray); - MultiDelegatedAttestationRequest[] memory reqs = new MultiDelegatedAttestationRequest[](1); - MultiDelegatedAttestationRequest memory req1 = MultiDelegatedAttestationRequest({ + MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); + MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ schemaUID: schemaUID, data: attArray, signatures: sigs, @@ -267,14 +267,14 @@ contract AttestationDelegationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(1), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -286,8 +286,8 @@ contract AttestationDelegationTest is BaseTest { bytes[] memory sigs = instance.signAttestation(defaultSchema1, auth1k, attArray); - MultiDelegatedAttestationRequest[] memory reqs = new MultiDelegatedAttestationRequest[](1); - MultiDelegatedAttestationRequest memory req1 = MultiDelegatedAttestationRequest({ + MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); + MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ schemaUID: defaultSchema1, data: attArray, signatures: sigs, @@ -304,8 +304,8 @@ contract AttestationDelegationTest is BaseTest { bytes[] memory sigs = new bytes[](0); - MultiDelegatedAttestationRequest[] memory reqs = new MultiDelegatedAttestationRequest[](1); - MultiDelegatedAttestationRequest memory req1 = MultiDelegatedAttestationRequest({ + MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); + MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ schemaUID: defaultSchema1, data: attArray, signatures: sigs, @@ -323,14 +323,14 @@ contract AttestationDelegationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(1), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -342,8 +342,8 @@ contract AttestationDelegationTest is BaseTest { bytes[] memory sigs = new bytes[](0); - MultiDelegatedAttestationRequest[] memory reqs = new MultiDelegatedAttestationRequest[](1); - MultiDelegatedAttestationRequest memory req1 = MultiDelegatedAttestationRequest({ + MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); + MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ schemaUID: defaultSchema1, data: attArray, signatures: sigs, @@ -361,14 +361,14 @@ contract AttestationDelegationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(1), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -382,8 +382,8 @@ contract AttestationDelegationTest is BaseTest { sigs[0] = ""; sigs[1] = ""; - MultiDelegatedAttestationRequest[] memory reqs = new MultiDelegatedAttestationRequest[](1); - MultiDelegatedAttestationRequest memory req1 = MultiDelegatedAttestationRequest({ + MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); + MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ schemaUID: defaultSchema1, data: attArray, signatures: sigs, @@ -401,14 +401,14 @@ contract AttestationDelegationTest is BaseTest { ); AttestationRequestData memory attData1 = AttestationRequestData({ - subject: address(0x69), + moduleAddr: address(0x69), expirationTime: uint48(0), data: abi.encode(true), value: 0 }); AttestationRequestData memory attData2 = AttestationRequestData({ - subject: anotherModule, + moduleAddr: anotherModule, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -420,8 +420,8 @@ contract AttestationDelegationTest is BaseTest { bytes[] memory sigs = instance.signAttestation(defaultSchema1, auth1k, attArray); - MultiDelegatedAttestationRequest[] memory reqs = new MultiDelegatedAttestationRequest[](1); - MultiDelegatedAttestationRequest memory req1 = MultiDelegatedAttestationRequest({ + MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); + MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ schemaUID: defaultSchema1, data: attArray, signatures: sigs, @@ -434,8 +434,8 @@ contract AttestationDelegationTest is BaseTest { } function testRevoke() public { - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); - instance.delegatedRevokeAttestation(defaultModule1, defaultSchema1, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.signedRevokeAttestation(defaultModule1, defaultSchema1, auth1k); AttestationRecord memory attestation = instance.registry.findAttestation(defaultModule1, vm.addr(auth1k)); @@ -444,12 +444,12 @@ contract AttestationDelegationTest is BaseTest { function testRevoke__RevertWhen__InvalidSignature() public { address attester = vm.addr(auth1k); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - DelegatedRevocationRequest memory req = DelegatedRevocationRequest({ + SignedRevocationRequest memory req = SignedRevocationRequest({ schemaUID: defaultSchema1, data: attData1, revoker: attester, @@ -464,11 +464,11 @@ contract AttestationDelegationTest is BaseTest { address attester = vm.addr(auth1k); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); bytes memory sig = instance.signRevocation(defaultSchema1, auth1k, attData1); - DelegatedRevocationRequest memory req = DelegatedRevocationRequest({ + SignedRevocationRequest memory req = SignedRevocationRequest({ schemaUID: defaultSchema1, data: attData1, revoker: attester, @@ -481,14 +481,14 @@ contract AttestationDelegationTest is BaseTest { function testRevoke__RevertWhen__InvalidSchema() public { address attester = vm.addr(auth1k); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); bytes memory sig = instance.signRevocation(SchemaUID.wrap(0), auth1k, attData1); - DelegatedRevocationRequest memory req = DelegatedRevocationRequest({ + SignedRevocationRequest memory req = SignedRevocationRequest({ schemaUID: SchemaUID.wrap(0), data: attData1, revoker: attester, @@ -500,15 +500,15 @@ contract AttestationDelegationTest is BaseTest { } function testRevoke__RevertWhen__AlreadyRevoked() public { - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); address attester = vm.addr(auth1k); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); bytes memory sig = instance.signRevocation(defaultSchema1, auth1k, attData1); - DelegatedRevocationRequest memory req = DelegatedRevocationRequest({ + SignedRevocationRequest memory req = SignedRevocationRequest({ schemaUID: defaultSchema1, data: attData1, revoker: attester, @@ -518,7 +518,7 @@ contract AttestationDelegationTest is BaseTest { bytes memory sig2 = instance.signRevocation(defaultSchema1, auth1k, attData1); - DelegatedRevocationRequest memory req2 = DelegatedRevocationRequest({ + SignedRevocationRequest memory req2 = SignedRevocationRequest({ schemaUID: defaultSchema1, data: attData1, revoker: attester, @@ -534,14 +534,14 @@ contract AttestationDelegationTest is BaseTest { defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) ); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); - instance.mockDelegatedAttestation(defaultSchema1, anotherModule, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.mockSignedAttestation(defaultSchema1, anotherModule, auth1k); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; @@ -555,8 +555,8 @@ contract AttestationDelegationTest is BaseTest { // bytes[] memory sigs = instance.signRevocation(defaultSchema1, auth1k, attArray); - MultiDelegatedRevocationRequest[] memory reqs = new MultiDelegatedRevocationRequest[](1); - MultiDelegatedRevocationRequest memory req1 = MultiDelegatedRevocationRequest({ + MultiSignedRevocationRequest[] memory reqs = new MultiSignedRevocationRequest[](1); + MultiSignedRevocationRequest memory req1 = MultiSignedRevocationRequest({ schemaUID: defaultSchema1, data: attArray, revoker: attester, @@ -585,10 +585,10 @@ contract AttestationDelegationTest is BaseTest { instance.mockAttestation(defaultSchema1, anotherModule); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; @@ -610,10 +610,10 @@ contract AttestationDelegationTest is BaseTest { ); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; @@ -639,10 +639,10 @@ contract AttestationDelegationTest is BaseTest { instance.mockAttestation(defaultSchema1, anotherModule); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; @@ -669,10 +669,10 @@ contract AttestationDelegationTest is BaseTest { instance.mockAttestation(defaultSchema1, anotherModule); RevocationRequestData memory attData1 = - RevocationRequestData({ subject: defaultModule1, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); RevocationRequestData memory attData2 = - RevocationRequestData({ subject: anotherModule, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); RevocationRequestData[] memory attArray = new RevocationRequestData[](2); attArray[0] = attData1; diff --git a/test/AttestationResolve.t.sol b/test/AttestationResolve.t.sol index bb3b2028..7c41fe33 100644 --- a/test/AttestationResolve.t.sol +++ b/test/AttestationResolve.t.sol @@ -6,11 +6,11 @@ import { AttestationResolve, ResolverUID, SchemaUID } from "../src/base/Attestat import { AttestationRequest, MultiAttestationRequest, - DelegatedAttestationRequest, - MultiDelegatedAttestationRequest, + SignedAttestationRequest, + MultiSignedAttestationRequest, RevocationRequest, - DelegatedRevocationRequest, - MultiDelegatedRevocationRequest, + SignedRevocationRequest, + MultiSignedRevocationRequest, MultiRevocationRequest, IAttestation } from "../src/interface/IAttestation.sol"; @@ -21,10 +21,10 @@ import { AttestationRecord, AttestationDataRef, MultiAttestationRequest, - MultiDelegatedAttestationRequest, + MultiSignedAttestationRequest, MultiRevocationRequest, - DelegatedRevocationRequest, - MultiDelegatedRevocationRequest, + SignedRevocationRequest, + MultiSignedRevocationRequest, SchemaRecord, ResolverRecord, ModuleRecord @@ -42,7 +42,7 @@ contract AttestationResolveInstance is AttestationResolve { public returns (uint256) { - return _resolveAttestation( + return _requireExternalResolveAttestation( resolverUID, attestationRecord, value, isRevocation, availableValue, isLastAttestation ); } @@ -58,7 +58,7 @@ contract AttestationResolveInstance is AttestationResolve { public returns (uint256) { - return _resolveAttestations( + return _requireExternalResolveAttestations( resolverUID, attestationRecords, values, isRevocation, availableValue, isLast ); } @@ -102,14 +102,14 @@ contract AttestationResolveInstance is AttestationResolve { // Required by IAttestation function attest(AttestationRequest calldata request) external payable { } function multiAttest(MultiAttestationRequest[] calldata multiRequests) external payable { } - function attest(DelegatedAttestationRequest calldata delegatedRequest) external payable { } - function multiAttest(MultiDelegatedAttestationRequest[] calldata multiDelegatedRequests) + function attest(SignedAttestationRequest calldata signedRequest) external payable { } + function multiAttest(MultiSignedAttestationRequest[] calldata multiSignedRequests) external payable { } function revoke(RevocationRequest calldata request) external payable { } - function revoke(DelegatedRevocationRequest calldata request) external payable { } - function multiRevoke(MultiDelegatedRevocationRequest[] calldata multiDelegatedRequests) + function revoke(SignedRevocationRequest calldata request) external payable { } + function multiRevoke(MultiSignedRevocationRequest[] calldata multiSignedRequests) external payable { } @@ -244,7 +244,7 @@ contract AttestationResolveTest is BaseTest { function testResolveAttestation() public { AttestationRecord memory attestationRecord = AttestationRecord({ schemaUID: defaultSchema1, - subject: address(this), + moduleAddr: address(this), attester: address(this), time: uint48(0), expirationTime: uint48(0), @@ -265,7 +265,7 @@ contract AttestationResolveTest is BaseTest { function testResolveAttestation__WithValue() public { AttestationRecord memory attestationRecord = AttestationRecord({ schemaUID: defaultSchema1, - subject: address(this), + moduleAddr: address(this), attester: address(this), time: uint48(0), expirationTime: uint48(0), @@ -292,7 +292,7 @@ contract AttestationResolveTest is BaseTest { function testResolveAttestation__RevertWhen__ZeroResolverAndValue() public { AttestationRecord memory attestationRecord = AttestationRecord({ schemaUID: defaultSchema1, - subject: address(this), + moduleAddr: address(this), attester: address(this), time: uint48(0), expirationTime: uint48(0), @@ -315,7 +315,7 @@ contract AttestationResolveTest is BaseTest { function testResolveAttestation__RevertWhen__ResolverNotPayableAndValue() public { AttestationRecord memory attestationRecord = AttestationRecord({ schemaUID: defaultSchema1, - subject: address(this), + moduleAddr: address(this), attester: address(this), time: uint48(0), expirationTime: uint48(0), @@ -338,7 +338,7 @@ contract AttestationResolveTest is BaseTest { function testResolveAttestation__RevertWhen__InsufficientValue() public { AttestationRecord memory attestationRecord = AttestationRecord({ schemaUID: defaultSchema1, - subject: address(this), + moduleAddr: address(this), attester: address(this), time: uint48(0), expirationTime: uint48(0), @@ -361,7 +361,7 @@ contract AttestationResolveTest is BaseTest { function testResolveAttestation__RevertWhen__InvalidRevocation() public { AttestationRecord memory attestationRecord = AttestationRecord({ schemaUID: defaultSchema1, - subject: address(this), + moduleAddr: address(this), attester: address(this), time: uint48(0), expirationTime: uint48(0), @@ -383,7 +383,7 @@ contract AttestationResolveTest is BaseTest { function testResolveAttestation__RevertWhen__InvalidAttestation() public { AttestationRecord memory attestationRecord = AttestationRecord({ schemaUID: defaultSchema1, - subject: address(this), + moduleAddr: address(this), attester: address(this), time: uint48(0), expirationTime: uint48(0), diff --git a/test/EIP712Verifier.t.sol b/test/EIP712Verifier.t.sol index 8e17c0d9..ffbdca31 100644 --- a/test/EIP712Verifier.t.sol +++ b/test/EIP712Verifier.t.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.19; import { BaseTest, RegistryTestLib, RegistryInstance } from "./utils/BaseTest.t.sol"; import { EIP712Verifier, - DelegatedAttestationRequest, - DelegatedRevocationRequest, + SignedAttestationRequest, + SignedRevocationRequest, SchemaUID, AttestationRequestData, RevocationRequestData, @@ -23,11 +23,11 @@ struct SampleAttestation { } contract EIP712VerifierInstance is EIP712Verifier { - function verifyAttest(DelegatedAttestationRequest calldata request) public { - _verifyAttest(request); + function verifyAttest(SignedAttestationRequest calldata request) public { + _requireValidAttestSignature(request); } - function verifyRevoke(DelegatedRevocationRequest calldata request) public { + function verifyRevoke(SignedRevocationRequest calldata request) public { _verifyRevoke(request); } @@ -66,7 +66,7 @@ contract EIP712VerifierTest is BaseTest { uint256 nonce = verifier.getNonce(account) + 1; AttestationRequestData memory attData = AttestationRequestData({ - subject: address(0), + moduleAddr: address(0), expirationTime: uint48(0), value: 0, data: "" @@ -82,7 +82,7 @@ contract EIP712VerifierTest is BaseTest { verifier.getAttestTypeHash(), block.chainid, schemaUID, - attData.subject, + attData.moduleAddr, attData.expirationTime, keccak256(attData.data), nonce @@ -96,7 +96,7 @@ contract EIP712VerifierTest is BaseTest { function testVerifyAttest() public { SchemaUID schemaUID = SchemaUID.wrap(0); AttestationRequestData memory attData = AttestationRequestData({ - subject: address(0x69), + moduleAddr: address(0x69), expirationTime: uint48(0), value: 0, data: abi.encode(true) @@ -107,7 +107,7 @@ contract EIP712VerifierTest is BaseTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(auth1k, digest); bytes memory signature = abi.encodePacked(r, s, v); - DelegatedAttestationRequest memory request = DelegatedAttestationRequest({ + SignedAttestationRequest memory request = SignedAttestationRequest({ schemaUID: schemaUID, data: attData, attester: vm.addr(auth1k), @@ -120,12 +120,12 @@ contract EIP712VerifierTest is BaseTest { function testVerifyAttest__RevertWhen__InvalidSignature() public { address attester = makeAddr("attester"); AttestationRequestData memory attData = AttestationRequestData({ - subject: address(0), + moduleAddr: address(0), expirationTime: uint48(0), value: 0, data: "" }); - DelegatedAttestationRequest memory request = DelegatedAttestationRequest({ + SignedAttestationRequest memory request = SignedAttestationRequest({ schemaUID: SchemaUID.wrap(0), data: attData, attester: attester, @@ -142,7 +142,7 @@ contract EIP712VerifierTest is BaseTest { uint256 nonce = verifier.getNonce(account) + 1; RevocationRequestData memory revData = - RevocationRequestData({ subject: address(0), attester: account, value: 0 }); + RevocationRequestData({ moduleAddr: address(0), attester: account, value: 0 }); bytes32 digest1 = verifier.getRevocationDigest(revData, schemaUID, account); bytes32 digest2 = verifier.getRevocationDigest(revData, schemaUID, nonce); @@ -154,7 +154,7 @@ contract EIP712VerifierTest is BaseTest { verifier.getRevokeTypeHash(), block.chainid, schemaUID, - revData.subject, + revData.moduleAddr, account, nonce ) @@ -168,7 +168,7 @@ contract EIP712VerifierTest is BaseTest { address revoker = vm.addr(auth1k); SchemaUID schemaUID = SchemaUID.wrap(0); RevocationRequestData memory revData = - RevocationRequestData({ subject: address(0), attester: revoker, value: 0 }); + RevocationRequestData({ moduleAddr: address(0), attester: revoker, value: 0 }); uint256 nonce = verifier.getNonce(vm.addr(auth1k)) + 1; bytes32 digest = @@ -176,7 +176,7 @@ contract EIP712VerifierTest is BaseTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(auth1k, digest); bytes memory signature = abi.encodePacked(r, s, v); - DelegatedRevocationRequest memory request = DelegatedRevocationRequest({ + SignedRevocationRequest memory request = SignedRevocationRequest({ schemaUID: schemaUID, data: revData, revoker: revoker, diff --git a/test/Query.t.sol b/test/Query.t.sol index 563ac559..a6ce0641 100644 --- a/test/Query.t.sol +++ b/test/Query.t.sol @@ -42,7 +42,7 @@ contract QueryTest is AttestationTest { function testCheckAttestation__RevertWhen__Expired() public { vm.warp(100); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); vm.warp(200); vm.expectRevert(IQuery.AttestationNotFound.selector); instance.registry.check(defaultModule1, attester); @@ -57,7 +57,7 @@ contract QueryTest is AttestationTest { function testCheckNAttestation() public { testAttest(); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth2k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); address[] memory attesters = new address[](2); attesters[0] = attester; attesters[1] = vm.addr(auth2k); @@ -77,14 +77,14 @@ contract QueryTest is AttestationTest { function testCheckNAttestation__RevertWhen__Expired() public { vm.warp(100); testAttest(); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth2k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); AttestationRequestData memory attData = AttestationRequestData({ - subject: defaultModule1, + moduleAddr: defaultModule1, expirationTime: uint48(101), data: abi.encode(false), value: 0 }); - instance.newDelegatedAttestation(defaultSchema1, auth2k, attData); + instance.newSignedAttestation(defaultSchema1, auth2k, attData); vm.warp(200); address[] memory attesters = new address[](2); attesters[0] = attester; @@ -96,8 +96,8 @@ contract QueryTest is AttestationTest { function testCheckNAttestation__RevertWhen__Revoked() public { testAttest(); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth2k); - instance.delegatedRevokeAttestation(defaultModule1, defaultSchema1, auth2k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); + instance.signedRevokeAttestation(defaultModule1, defaultSchema1, auth2k); address[] memory attesters = new address[](2); attesters[0] = vm.addr(auth1k); attesters[1] = vm.addr(auth2k); @@ -108,7 +108,7 @@ contract QueryTest is AttestationTest { function testCheckNAttestationUnsafe() public { testAttest(); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth2k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); address[] memory attesters = new address[](2); attesters[0] = vm.addr(auth1k); attesters[1] = vm.addr(auth2k); @@ -128,7 +128,7 @@ contract QueryTest is AttestationTest { function testCheckNAttestationUnsafe__Expired() public { vm.warp(100); testAttest(); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); vm.warp(200); address[] memory attesters = new address[](2); attesters[0] = vm.addr(auth1k); @@ -139,7 +139,7 @@ contract QueryTest is AttestationTest { function testCheckNAttestationUnsafe__Revoked() public { testAttest(); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth1k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); instance.revokeAttestation(defaultModule1, defaultSchema1, address(this)); address[] memory attesters = new address[](2); attesters[0] = vm.addr(auth1k); @@ -157,7 +157,7 @@ contract QueryTest is AttestationTest { function testFindAttestations() public { testAttest(); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, auth2k); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); address[] memory attesters = new address[](2); attesters[0] = attester; diff --git a/test/gas/RegistryGasComparison.t.sol b/test/gas/RegistryGasComparison.t.sol index eaeeaa45..de208522 100644 --- a/test/gas/RegistryGasComparison.t.sol +++ b/test/gas/RegistryGasComparison.t.sol @@ -58,9 +58,9 @@ contract RegistryGasComparisonTest is BaseTest { (address _thirdAttester, uint256 thirdKey) = makeAddrAndKey("thirdAttester"); thirdAttester = _thirdAttester; - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, firstKey); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, secondKey); - instance.mockDelegatedAttestation(defaultSchema1, defaultModule1, thirdKey); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, firstKey); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, secondKey); + instance.mockSignedAttestation(defaultSchema1, defaultModule1, thirdKey); } function testGasCheck() public { diff --git a/test/resolvers/TokenizedResolver.t.sol b/test/resolvers/TokenizedResolver.t.sol index 758417d9..97b2ebb2 100644 --- a/test/resolvers/TokenizedResolver.t.sol +++ b/test/resolvers/TokenizedResolver.t.sol @@ -32,7 +32,7 @@ contract TokenizedResolverTest is BaseTest { ); AttestationRequestData memory attData = AttestationRequestData({ - subject: module, + moduleAddr: module, expirationTime: uint48(0), data: abi.encode(true), value: 0 diff --git a/test/resolvers/ValueResolver.t.sol b/test/resolvers/ValueResolver.t.sol index fad9f4c5..ddb81818 100644 --- a/test/resolvers/ValueResolver.t.sol +++ b/test/resolvers/ValueResolver.t.sol @@ -24,14 +24,14 @@ contract ValueResolverTest is BaseTest { ); AttestationRequestData memory attData = AttestationRequestData({ - subject: module, + moduleAddr: module, expirationTime: uint48(0), data: abi.encode(true), value: 1 ether }); bytes memory signature = RegistryTestLib.signAttestation(instance, schema, auth1k, attData); - DelegatedAttestationRequest memory req = DelegatedAttestationRequest({ + SignedAttestationRequest memory req = SignedAttestationRequest({ schemaUID: schema, data: attData, signature: signature, diff --git a/test/utils/BaseUtils.sol b/test/utils/BaseUtils.sol index f0c3a745..bc496770 100644 --- a/test/utils/BaseUtils.sol +++ b/test/utils/BaseUtils.sol @@ -9,8 +9,8 @@ import "../../src/Registry.sol"; import { AttestationRequestData, RevocationRequestData, - DelegatedAttestationRequest, - DelegatedRevocationRequest + SignedAttestationRequest, + SignedRevocationRequest } from "../../src/base/AttestationDelegation.sol"; import { ISchemaValidator, IResolver } from "../../src/interface/ISchema.sol"; import { AttestationRequest, RevocationRequest } from "../../src/DataTypes.sol"; @@ -36,7 +36,7 @@ library RegistryTestLib { public { AttestationRequestData memory attData = AttestationRequestData({ - subject: moduleAddr, + moduleAddr: moduleAddr, expirationTime: uint48(0), data: abi.encode(true), value: 0 @@ -44,7 +44,7 @@ library RegistryTestLib { newAttestation(instance, schemaUID, attData); } - function mockDelegatedAttestation( + function mockSignedAttestation( RegistryInstance memory instance, SchemaUID schemaUID, address moduleAddr, @@ -53,12 +53,12 @@ library RegistryTestLib { public { AttestationRequestData memory attData = AttestationRequestData({ - subject: moduleAddr, + moduleAddr: moduleAddr, expirationTime: uint48(0x42424242), data: abi.encode(true), value: 0 }); - newDelegatedAttestation(instance, schemaUID, authKey, attData); + newSignedAttestation(instance, schemaUID, authKey, attData); } // function mockAttestation( @@ -82,7 +82,7 @@ library RegistryTestLib { instance.registry.attest(req); } - function newDelegatedAttestation( + function newSignedAttestation( RegistryInstance memory instance, SchemaUID schemaUID, uint256 attesterKey, @@ -91,7 +91,7 @@ library RegistryTestLib { public { bytes memory signature = signAttestation(instance, schemaUID, attesterKey, attData); - DelegatedAttestationRequest memory req = DelegatedAttestationRequest({ + SignedAttestationRequest memory req = SignedAttestationRequest({ schemaUID: schemaUID, data: attData, signature: signature, @@ -165,13 +165,13 @@ library RegistryTestLib { public { RevocationRequestData memory revoke = - RevocationRequestData({ subject: module, attester: attester, value: 0 }); + RevocationRequestData({ moduleAddr: module, attester: attester, value: 0 }); RevocationRequest memory req = RevocationRequest({ schemaUID: schemaUID, data: revoke }); instance.registry.revoke(req); } - function delegatedRevokeAttestation( + function signedRevokeAttestation( RegistryInstance memory instance, address module, SchemaUID schemaUID, @@ -180,11 +180,11 @@ library RegistryTestLib { public { RevocationRequestData memory revoke = - RevocationRequestData({ subject: module, attester: getAddr(attesterPk), value: 0 }); + RevocationRequestData({ moduleAddr: module, attester: getAddr(attesterPk), value: 0 }); bytes memory signature = signRevocation(instance, schemaUID, attesterPk, revoke); - DelegatedRevocationRequest memory req = DelegatedRevocationRequest({ + SignedRevocationRequest memory req = SignedRevocationRequest({ schemaUID: schemaUID, data: revoke, signature: signature, From d4fa989c1afbe7d41516639cfdce959147340826 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 6 Feb 2024 17:25:35 +0700 Subject: [PATCH 11/84] foo --- AuditChecklist.md | 4 + src/DataTypes.sol | 78 +----------- src/base/AttestationDelegation.sol | 5 +- src/base/AttestationSimple.sol | 183 +++++++++++++++++++++++++++++ src/base/EIP712Verifier.sol | 1 + src/lib/AttestationLib.sol | 35 ++++++ src/lib/StubLib.sol | 73 ++++++++++++ src/simple/Attestation.sol | 159 +++++++++++++++++++++++++ src/simple/ResolverManager.sol | 53 +++++++++ src/simple/SchemaManager.sol | 31 +++++ src/simple/SignedAttestation.sol | 55 +++++++++ 11 files changed, 600 insertions(+), 77 deletions(-) create mode 100644 src/base/AttestationSimple.sol create mode 100644 src/lib/AttestationLib.sol create mode 100644 src/lib/StubLib.sol create mode 100644 src/simple/Attestation.sol create mode 100644 src/simple/ResolverManager.sol create mode 100644 src/simple/SchemaManager.sol create mode 100644 src/simple/SignedAttestation.sol diff --git a/AuditChecklist.md b/AuditChecklist.md index ad598e11..968bdcfe 100644 --- a/AuditChecklist.md +++ b/AuditChecklist.md @@ -165,3 +165,7 @@ - `D9` - Watch out for tokens that use too many or too few decimals. Ensure the max and min supported values are documented. - `D10` - Be careful of relying on the raw token balance of a contract to determine earnings. Contracts which provide a way to recover assets sent directly to them can mess up share price functions that rely on the raw Ether or token balances of an address. - `D11` - If your contract is a target for token approvals, do not make arbitrary calls from user input. + +# TODO: + +nonce iteration in signature should not be looped sstore diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 60ace52e..358c5aa0 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -23,7 +23,6 @@ struct AttestationRecord { // Struct that represents Module artefact. struct ModuleRecord { ResolverUID resolverUID; // The unique identifier of the resolver. - address implementation; // The deployed contract address address sender; // The address of the sender who deployed the contract bytes metadata; // Additional data related to the contract deployment } @@ -49,46 +48,9 @@ struct ResolverRecord { struct AttestationRequestData { address moduleAddr; // The moduleAddr of the attestation. uint48 expirationTime; // The time when the attestation expires (Unix timestamp). - uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors. bytes data; // Custom attestation data. } -/** - * @dev A struct representing the full arguments of the attestation request. - */ -struct AttestationRequest { - SchemaUID schemaUID; // The unique identifier of the schema. - AttestationRequestData data; // The arguments of the attestation request. -} - -/** - * @dev A struct representing the full arguments of the full signed attestation request. - */ -struct SignedAttestationRequest { - SchemaUID schemaUID; // The unique identifier of the schema. - AttestationRequestData data; // The arguments of the attestation request. - address attester; // The attesting account. - bytes signature; // The signature data. -} - -/** - * @dev A struct representing the full arguments of the multi attestation request. - */ -struct MultiAttestationRequest { - SchemaUID schemaUID; // The unique identifier of the schema. - AttestationRequestData[] data; // The arguments of the attestation request. -} - -/** - * @dev A struct representing the full arguments of the signed multi attestation request. - */ -struct MultiSignedAttestationRequest { - SchemaUID schemaUID; // The unique identifier of the schema. - AttestationRequestData[] data; // The arguments of the attestation requests. - bytes[] signatures; // The signatures data. signatures are assumed to be signed with increasing nonces. - address attester; // The attesting account. -} - /*////////////////////////////////////////////////////////////// Revocation Requests //////////////////////////////////////////////////////////////*/ @@ -98,44 +60,6 @@ struct MultiSignedAttestationRequest { */ struct RevocationRequestData { address moduleAddr; // The module address. - address attester; // The attesting account. - uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors. -} - -/** - * @dev A struct representing the full arguments of the revocation request. - */ -struct RevocationRequest { - SchemaUID schemaUID; // The unique identifier of the schema. - RevocationRequestData data; // The arguments of the revocation request. -} - -/** - * @dev A struct representing the arguments of the full signed revocation request. - */ -struct SignedRevocationRequest { - SchemaUID schemaUID; // The unique identifier of the schema. - RevocationRequestData data; // The arguments of the revocation request. - address revoker; // The revoking account. - bytes signature; // The signature data. -} - -/** - * @dev A struct representing the full arguments of the multi revocation request. - */ -struct MultiRevocationRequest { - SchemaUID schemaUID; // The unique identifier of the schema. - RevocationRequestData[] data; // The arguments of the revocation request. -} - -/** - * @dev A struct representing the full arguments of the signed multi revocation request. - */ -struct MultiSignedRevocationRequest { - SchemaUID schemaUID; // The unique identifier of the schema. - RevocationRequestData[] data; // The arguments of the revocation requests. - address revoker; // The revoking account. - bytes[] signatures; // The signatures data. signatures are assumed to be signed with increasing nonces. } /*////////////////////////////////////////////////////////////// @@ -159,6 +83,8 @@ function schemaNotEq(SchemaUID uid1, SchemaUID uid) pure returns (bool) { //--------------------- ResolverUID -----------------------------| type ResolverUID is bytes32; +ResolverUID constant RESOLVER_UID_ZERO = ResolverUID.wrap(bytes32(0)); + using { resolverEq as == } for ResolverUID global; using { resolverNotEq as != } for ResolverUID global; diff --git a/src/base/AttestationDelegation.sol b/src/base/AttestationDelegation.sol index 480d2007..a944df07 100644 --- a/src/base/AttestationDelegation.sol +++ b/src/base/AttestationDelegation.sol @@ -76,7 +76,10 @@ abstract contract AttestationDelegation is IAttestation, Attestation { nonReentrant { // check if schema exists and is valid. This will revert if validtor returns false - _requireSchemaCheck({ schemaUID: multiSignedRequests.schemaUID, requestDatas: multiSignedRequests.data }); + _requireSchemaCheck({ + schemaUID: multiSignedRequests.schemaUID, + requestDatas: multiSignedRequests.data + }); uint256 length = multiSignedRequests.length; // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting diff --git a/src/base/AttestationSimple.sol b/src/base/AttestationSimple.sol new file mode 100644 index 00000000..da5ebf15 --- /dev/null +++ b/src/base/AttestationSimple.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { ReentrancyGuard } from "solmate/utils/ReentrancyGuard.sol"; + +import { + IAttestation, + AttestationRequest, + MultiAttestationRequest, + RevocationRequest, + MultiRevocationRequest, + AttestationLib +} from "../interface/IAttestation.sol"; +import { SchemaUID, ResolverUID, SchemaRecord, ISchemaValidator } from "./Schema.sol"; +import { ModuleRecord } from "./Module.sol"; +import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; +import { + ZERO_ADDRESS, + AccessDenied, + NotFound, + ZERO_TIMESTAMP, + InvalidSchema, + _time +} from "../Common.sol"; + +import { + AttestationDataRef, + AttestationRecord, + AttestationRequestData, + RevocationRequestData, + writeAttestationData +} from "../DataTypes.sol"; +import { AttestationResolve } from "./AttestationResolve.sol"; + +contract Attestation { + using ModuleDeploymentLib for address; + + // Mapping of module addresses to attester addresses to their attestation records. + mapping(address module => mapping(address attester => AttestationRecord attestation)) internal + _moduleToAttesterToAttestations; + + function attest(SchemaUID schemaUID, AttestationRequestData calldata request) external { + _attestAndCheckExternal(msg.sender, schemaUID, request); + } + + function attest(SchemaUID schemaUID, AttestationRequestData[] calldata requests) external { + _attestAndCheckExternal(msg.sender, schemaUID, requests); + } + + function _attestAndCheckExternal( + address attester, + SchemaUID schemaUID, + AttestationRequestData calldata request + ) + internal + { + AttestationRecord memory record = _storeAttestation({ + schemaUID: schemaUID, + attester: attester, + attestationRequestData: request + }); + + // check if schema exists and is valid. This will revert if validtor returns false + _requireSchemaCheck({ schemaUID: schemaUID, record: record }); + + // trigger the resolver procedure + _requireExternalResolveAttestation({ resolverUID: moduleRecord.resolverUID, record: record }); + } + + function _attestAndCheckExternal( + address attester, + SchemaUID schemaUID, + AttestationRequestData[] calldata requests + ) + internal + { + uint256 length = requests.length; + AttestationRecord[] memory attestationRecords = new AttestationRecord[](length); + + for (uint256 i; i < length; i++) { + attestationRecord[i] = _storeAttestation({ + schemaUID: schemaUID, + attester: attester, + attestationRequestData: requests[i] + }); + } + + // check if schema exists and is valid. This will revert if validtor returns false + _requireSchemaCheck({ schemaUID: schemaUID, records: attestationRecords }); + + // trigger the resolver procedure + _requireExternalResolveAttestation({ + resolverUID: moduleRecord.resolverUID, + records: attestationRecords + }); + } + + function _storeAttestation( + SchemaUID schemaUID, + address attester, + AttestationRequestData calldata attestationRequestData + ) + internal + returns (AttestationRecord memory record) + { + AttestationRecord storage recordStorage = _moduleToAttesterToAttestations[module][attester]; + uint48 timeNow = _time(); + // Ensure that either no expiration time was set or that it was set in the future. + if ( + attestationRequestData.expirationTime != ZERO_TIMESTAMP + && attestationRequestData.expirationTime <= timeNow + ) { + revert InvalidExpirationTime(); + } + // caching module address. + address module = attestationRequestData.moduleAddr; + ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); + + // Ensure that attestation is for module that was registered. + if (moduleRecord.resolverUID != RESOLVER_UID_ZERO) { + revert ModuleNotRegistered(); + } + + // get salt used for SSTORE2 to avoid collisions during CREATE2 + bytes32 attestationSalt = AttestationLib.attestationSalt(attester, module); + AttestationDataRef sstore2Pointer = writeAttestationData({ + attestationData: attestationRequestData.data, + salt: attestationSalt + }); + + // SSTORE attestation on registry storage + record = AttestationRecord({ + schemaUID: schemaUID, + moduleAddr: module, + attester: attester, + time: timeNow, + expirationTime: attestationRequestData.expirationTime, + revocationTime: uint48(ZERO_TIMESTAMP), + dataPointer: sstore2Pointer + }); + recordStorage = record; + + emit Attested(module, attester, schemaUID, sstore2Pointer); + } + + function _requireSchemaCheck( + SchemaUID schemaUID, + AttestationRecord memory record + ) + internal + view + { + // only run this function if the selected schemaUID exists + SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); + if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); + // validate Schema + ISchemaValidator validator = schema.validator; + // if validator is set, call the validator + if (address(validator) != ZERO_ADDRESS && validator.validateSchema(record) == false) { + // revert if ISchemaValidator returns false + revert InvalidAttestation(); + } + } + + function _requireSchemaCheck( + SchemaUID schemaUID, + AttestationRecord[] memory records + ) + internal + view + { + // only run this function if the selected schemaUID exists + SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); + if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); + // validate Schema + ISchemaValidator validator = schema.validator; + // if validator is set, call the validator + if (address(validator) != ZERO_ADDRESS && validator.validateSchema(records) == false) { + // revert if ISchemaValidator returns false + revert InvalidAttestation(); + } + } +} diff --git a/src/base/EIP712Verifier.sol b/src/base/EIP712Verifier.sol index aaeae7f1..bfcede3b 100644 --- a/src/base/EIP712Verifier.sol +++ b/src/base/EIP712Verifier.sol @@ -184,6 +184,7 @@ abstract contract EIP712Verifier is EIP712 { * @return nonce The new nonce. */ function _newNonce(address account) private returns (uint256 nonce) { + // TODO: gas bad. this will be iterated in a for loop unchecked { nonce = ++_nonces[account]; } diff --git a/src/lib/AttestationLib.sol b/src/lib/AttestationLib.sol new file mode 100644 index 00000000..310fedb5 --- /dev/null +++ b/src/lib/AttestationLib.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { EIP712 } from "solady/utils/EIP712.sol"; +import { AttestationRequestData } from "../DataTypes.sol"; + +// The hash of the data type used to relay calls to the attest function. It's the value of +bytes32 constant ATTEST_TYPEHASH = keccak256("AttestationRequestData(address,uint48,uint256,bytes)"); + +// The hash of the data type used to relay calls to the revoke function. It's the value of +bytes32 constant REVOKE_TYPEHASH = keccak256("RevocationRequestData(address,address,uint256)"); + +library AttestationLib { + function digest( + AttestationRequestData calldata data, + uint256 nonce + ) + internal + returns (bytes32 digest) + { + digest = _hashTypedData( + keccak256( + abi.encodePacked( + ATTEST_TYPEHASH, + block.chainid, + data.requester, + data.timestamp, + data.expiration, + data.schema, + nonce + ) + ) + ); + } +} diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol new file mode 100644 index 00000000..88785497 --- /dev/null +++ b/src/lib/StubLib.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { AttestationRecord, ResolverRecord } from "../DataTypes.sol"; + +library StubLib { + function requireExternalSchemaValidation( + AttestationRecord memory attestationRecord, + SchemaRecord storage schema + ) + internal + view + { + // only run this function if the selected schemaUID exists + if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); + // validate Schema + ISchemaValidator validator = schema.validator; + // if validator is set, call the validator + if (address(validator) != ZERO_ADDRESS && validator.validateSchema(requestData) == false) { + // revert if ISchemaValidator returns false + revert InvalidAttestation(); + } + } + + function _requireSchemaCheck( + AttestationRecord[] memory attestationRecords, + SchemaRecord storage schema + ) + internal + view + { + // only run this function if the selected schemaUID exists + if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); + // validate Schema + ISchemaValidator validator = schema.validator; + // if validator is set, call the validator + if ( + address(validator) != ZERO_ADDRESS + && validator.validateSchema(attestationRecords) == false + ) { + revert InvalidAttestation(); + } + } + + function requireExternalResolverCheck( + AttestationRecord memory attestationRecord, + ResolverRecord storage resolver + ) + internal + { + IResolver resolverContract = resolver.resolver; + + if (address(resolverContract) != ZERO_ADDRESS) return; + if (resolverContract.resolveAttestation(attestationRecord) == false) { + revert InvalidAttestation(); + } + } + + function requireExternalResolver( + AttestationRecord[] memory attestationRecords, + ResolverRecord storage resolver + ) + internal + { + IResolver resolverContract = resolver.resolver; + + if (address(resolverContract) != ZERO_ADDRESS) return; + + if (resolverContract.resolveAttestation(attestationRecords) == false) { + revert InvalidAttestation(); + } + } +} diff --git a/src/simple/Attestation.sol b/src/simple/Attestation.sol new file mode 100644 index 00000000..3aa3b29b --- /dev/null +++ b/src/simple/Attestation.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { AttestationRecord } from "../DataTypes.sol"; +import { ResolverManager } from "./ResolverManager.sol"; +import { SchemaManager } from "./SchemaManager.sol"; +import { StubLib } from "../lib/StubLib.sol"; + +contract Attestation is ResolverManager, SchemaManager { + using StubLib for AttestationRecord; + using StubLib for AttestationRecord[]; + + error DifferentResolvers(); + + mapping(address module => mapping(address attester => AttestationRecord attestation)) internal + _moduleToAttesterToAttestations; + + function attest(SchemaUID schemaUID, AttestationRequestData calldata request) external { + _attest(msg.sender, schemaUID, request); + } + + function attest(SchemaUID schemaUID, AttestationRequestData[] calldata requests) external { + _attest(msg.sender, schemaUID, requests); + } + + function revoke(address module) external { } + + function _revoke(address attester, address module) internal { + _storeRevocation(attester, module); + } + + function _attest( + address attester, + SchemaUID schemaUID, + AttestationRequestData calldata request + ) + internal + { + (AttestationRecord memory record, ResolverUID resolverUID) = _storeAttestation({ + schemaUID: schemaUID, + attester: attester, + attestationRequestData: request + }); + + record.requireExternalSchemaValidation({ schema: schema[schemaUID] }); + record.requireExternalResolverCheck({ resolver: resolver[resolverUID] }); + } + + function _attest( + address attester, + SchemaUID schemaUID, + AttestationRequestData[] calldata requests + ) + internal + { + uint256 length = requests.length; + AttetationRecord[] memory records = new AttestationRecord[](length); + // loop will check that the batched attestation is made ONLY for the same resolver + // @dev if you want to use different resolvers, make different attestation calls + ResolverUID resolverUID; + for (uint256 i; i < length; i++) { + ResolverUID resolverUID_cache; + (records[i], resolverUID_cache) = _storeAttestation({ + schemaUID: schemaUID, + attester: attester, + attestationRequestData: requests[i] + }); + // cache the first resolverUID and compare it to the rest + if (i == 0) resolverUID = resolverUID_cache; + else if (resolverUID_cache != resolverUID) revert DiffernetResolvers(); + } + + records.requireExternalSchemaValidation({ schema: schema[schemaUID] }); + records.requireExternalResolverCheck({ resolver: resolver[resolverUID] }); + } + + function _storeAttestation( + SchemaUID schemaUID, + address attester, + AttestationRequestData calldata attestationRequestData + ) + internal + returns (AttestationRecord memory record, ResolverUID resolverUID) + { + AttestationRecord storage record = _moduleToAttesterToAttestations[module][attester]; + uint48 timeNow = _time(); + // Ensure that either no expiration time was set or that it was set in the future. + if ( + attestationRequestData.expirationTime != ZERO_TIMESTAMP + && attestationRequestData.expirationTime <= timeNow + ) { + revert InvalidExpirationTime(); + } + // caching module address. + address module = attestationRequestData.moduleAddr; + ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); + + // Ensure that attestation is for module that was registered. + if (moduleRecord.implementation == ZERO_ADDRESS) { + revert InvalidAttestation(); + } + resolverUID = moduleRecord.resolverUID; + + // get salt used for SSTORE2 to avoid collisions during CREATE2 + bytes32 attestationSalt = AttestationLib.attestationSalt(attester, module); + AttestationDataRef sstore2Pointer = writeAttestationData({ + attestationData: attestationRequestData.data, + salt: attestationSalt + }); + + // SSTORE attestation on registry storage + record = AttestationRecord({ + schemaUID: schemaUID, + moduleAddr: module, + attester: attester, + time: timeNow, + expirationTime: attestationRequestData.expirationTime, + revocationTime: uint48(ZERO_TIMESTAMP), + dataPointer: sstore2Pointer + }); + + emit Attested(module, attester, schemaUID, sstore2Pointer); + } + + function _storeRevocation( + address attester, + address moduleAddr + ) + internal + returns (AttestationRecord memory attestationRecord, ResolverUID resolverUID) + { + AttestationRecord storage attestation = + _moduleToAttesterToAttestations[moduleAddr][attester]; + + // Ensure that we aren't attempting to revoke a non-existing attestation. + if (AttestationDataRef.unwrap(attestation.dataPointer) == ZERO_ADDRESS) { + revert NotFound(); + } + + // Allow only original attesters to revoke their attestations. + if (attestation.attester != revoker) { + revert AccessDenied(); + } + + // Ensure that we aren't trying to revoke the same attestation twice. + if (attestation.revocationTime != ZERO_TIMESTAMP) { + revert AlreadyRevoked(); + } + + // set revocation time to NOW + attestation.revocationTime = _time(); + emit Revoked({ + moduleAddr: attestation.moduleAddr, + revoker: revoker, + schema: attestation.schemaUID + }); + resolverUID = _getModule({ moduleAddress: moduleAddr }).resolverUID; + } +} diff --git a/src/simple/ResolverManager.sol b/src/simple/ResolverManager.sol new file mode 100644 index 00000000..394e9034 --- /dev/null +++ b/src/simple/ResolverManager.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; + +abstract contract ResolverManager { + mapping(ResolverUID uid => ResolverRecord resolver) public resolvers; + + /** + * @dev Modifier to require that the caller is the owner of a resolver + * + * @param uid The UID of the resolver. + */ + modifier onlyResolverOwner(ResolverUID uid) { + if (resolvers[uid].resolverOwner != msg.sender) { + revert AccessDenied(); + } + _; + } + + /** + * @inheritdoc IExternalResolver + */ + function registerResolver(IResolver _resolver) external returns (ResolverUID uid) { + if (address(_resolver) == ZERO_ADDRESS) revert InvalidResolver(); + + // build a ResolverRecord from the input + ResolverRecord memory resolver = + ResolverRecord({ resolver: _resolver, resolverOwner: msg.sender }); + + // Computing a unique ID for the schema using its properties + uid = resolver.getUID(); + + // Checking if a schema with this UID already exists -> resolver can never be ZERO_ADDRESS + if (address(resolvers[uid].resolver) != ZERO_ADDRESS) { + revert AlreadyExists(); + } + + // Storing schema in the _schemas mapping + resolvers[uid] = resolver; + + emit SchemaResolverRegistered(uid, msg.sender); + } + + /** + * @inheritdoc IExternalResolver + */ + function setResolver(ResolverUID uid, IResolver resolver) external onlyResolverOwner(uid) { + ResolverRecord storage referrer = resolvers[uid]; + referrer.resolver = resolver; + emit NewSchemaResolver(uid, address(resolver)); + } +} diff --git a/src/simple/SchemaManager.sol b/src/simple/SchemaManager.sol new file mode 100644 index 00000000..e923911a --- /dev/null +++ b/src/simple/SchemaManager.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; + +abstract contract SchemaManager { + // The global mapping between schema records and their IDs. + mapping(SchemaUID uid => SchemaRecord schemaRecord) internal schemas; + + function registerSchema( + string calldata schema, + ISchemaValidator validator // OPTIONAL + ) + external + returns (SchemaUID uid) + { + // TODO: ERC165 check that validator is actually a valivator + SchemaRecord memory schemaRecord = + SchemaRecord({ validator: validator, registeredAt: _time(), schema: schema }); + + // Computing a unique ID for the schema using its properties + uid = schemaRecord.getUID(); + + if (_schemas[uid].registeredAt != ZERO_TIMESTAMP) revert AlreadyExists(); + + // Storing schema in the _schemas mapping + _schemas[uid] = schemaRecord; + + emit SchemaRegistered(uid, msg.sender); + } +} diff --git a/src/simple/SignedAttestation.sol b/src/simple/SignedAttestation.sol new file mode 100644 index 00000000..9589b99c --- /dev/null +++ b/src/simple/SignedAttestation.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { Attestation } from "./Attestation.sol"; +import { AttestationLib } from "../lib/AttestationLib.sol"; + +contract SignedAttestation is Attestation { + using AttestationLib for AttestationRequestData; + + error InvalidSignature(); + + mapping(address attester => uint256 nonce) public attesterNonce; + + function attest( + SchemaUID schemaUID, + AttestationRequestData calldata request, + address attester, + bytes calldata signature + ) + external + payable + { + // verify signature + uint256 nonce = ++attesterNonce[attester]; + bytes32 digest = request.digest(nonce, schemaUID); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); + if (!valid) revert InvalidSignature(); + + _attest(attester, schemaUID, request); + } + + function attest( + SchemaUID schemaUID, + AttestationRequestData[] calldata requests, + address attester, + bytes[] calldata signature // TODO: should we maybe sign all requests at once? + ) + external + payable + { + // verify all signatures. Iterate nonces and digests + uint256 nonce = attesterNonce[attester]; + uint256 length = requests.length; + if (length != signature.length) revert InvalidSignature(); + for (uint256 i; i < length; i++) { + bytes32 digest = requests[i].digest(nonce + i); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature[i]); + if (!valid) revert InvalidSignature(); + } + // update nonce + attesterNonce[attester] += length; + + _attest(attester, schemaUID, requests); + } +} From c8dec99ba358f88087f4205268f76fab14916f33 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 11:56:13 +0700 Subject: [PATCH 12/84] refactor complete --- docs/Attestation.md | 6 +- docs/Schemas.md | 4 +- ...egistry.s.sol => DeployRegistry.s.sol.bak} | 0 src/Common.sol | 4 + src/DataTypes.sol | 26 +- src/IRegistry.sol | 191 +++++ src/Registry.sol | 71 +- .../{Attestation.sol => Attestation.sol.bak} | 54 +- ...tion.sol => AttestationDelegation.sol.bak} | 34 +- ...Resolve.sol => AttestationResolve.sol.bak} | 0 ...onSimple.sol => AttestationSimple.sol.bak} | 28 +- ...712Verifier.sol => EIP712Verifier.sol.bak} | 20 +- src/base/{Module.sol => Module.sol.bak} | 0 src/base/{Query.sol => Query.sol.bak} | 0 ...ueryAttester.sol => QueryAttester.sol.bak} | 0 src/base/{Schema.sol => Schema.sol.bak} | 0 src/external/IResolver.sol | 49 +- src/external/ISchemaValidator.sol | 9 +- ...{ResolverBase.sol => ResolverBase.sol.bak} | 0 ...orBase.sol => SchemaValidatorBase.sol.bak} | 6 +- ...ebugResolver.sol => DebugResolver.sol.bak} | 0 ...eValidator.sol => SimpleValidator.sol.bak} | 6 +- ...Resolver.sol => TokenizedResolver.sol.bak} | 0 ...alueResolver.sol => ValueResolver.sol.bak} | 0 ...{MockRegistry.sol => MockRegistry.sol.bak} | 0 ... MultiAttesterRegistryIntegration.sol.bak} | 0 ....sol => SimpleRegistryIntegration.sol.bak} | 0 ....sol => SimpleRegistryStorageSlot.sol.bak} | 0 ...{IAttestation.sol => IAttestation.sol.bak} | 0 .../{IERC7484.sol => IERC7484.sol.bak} | 0 .../{IModule.sol => IModule.sol.bak} | 0 src/interface/{IQuery.sol => IQuery.sol.bak} | 0 .../{IRegistry.sol => IRegistry.sol.bak} | 28 +- .../{ISchema.sol => ISchema.sol.bak} | 0 src/lib/AttestationLib.sol | 90 ++- src/lib/Helpers.sol | 30 + src/lib/ModuleTypeLib.sol | 31 + src/lib/StubLib.sol | 48 +- src/simple/Attestation.sol | 162 +--- src/simple/AttestationManager.sol | 191 +++++ src/simple/ModuleManager.sol | 115 +++ src/simple/ResolverManager.sol | 16 +- src/simple/SchemaManager.sol | 15 +- src/simple/SignedAttestation.sol | 79 +- src/simple/TrustManager.sol | 144 ++++ test/Attestation.t.sol | 485 ------------ test/AttestationDelegation.t.sol | 691 ------------------ test/AttestationResolve.t.sol | 404 ---------- test/EIP712Verifier.t.sol | 188 ----- test/Module.t.sol | 110 --- test/Query.t.sol | 172 ----- test/Schema.t.sol | 69 -- test/gas/RegistryGasComparison.t.sol | 129 ---- test/integrations/MockRegistry.t.sol | 39 - test/integrations/SimpleRegIntegration.t.sol | 43 -- test/resolvers/TokenizedResolver.t.sol | 46 -- test/resolvers/ValueResolver.t.sol | 44 -- test/script/DeployRegistry.t.sol | 21 - test/utils/BaseTest.t.sol | 90 --- test/utils/BaseUtils.sol | 309 -------- test/utils/ERC1271Attester.sol | 24 - 61 files changed, 1043 insertions(+), 3278 deletions(-) rename script/{DeployRegistry.s.sol => DeployRegistry.s.sol.bak} (100%) create mode 100644 src/IRegistry.sol rename src/base/{Attestation.sol => Attestation.sol.bak} (90%) rename src/base/{AttestationDelegation.sol => AttestationDelegation.sol.bak} (88%) rename src/base/{AttestationResolve.sol => AttestationResolve.sol.bak} (100%) rename src/base/{AttestationSimple.sol => AttestationSimple.sol.bak} (87%) rename src/base/{EIP712Verifier.sol => EIP712Verifier.sol.bak} (94%) rename src/base/{Module.sol => Module.sol.bak} (100%) rename src/base/{Query.sol => Query.sol.bak} (100%) rename src/base/{QueryAttester.sol => QueryAttester.sol.bak} (100%) rename src/base/{Schema.sol => Schema.sol.bak} (100%) rename src/external/{ResolverBase.sol => ResolverBase.sol.bak} (100%) rename src/external/{SchemaValidatorBase.sol => SchemaValidatorBase.sol.bak} (70%) rename src/external/examples/{DebugResolver.sol => DebugResolver.sol.bak} (100%) rename src/external/examples/{SimpleValidator.sol => SimpleValidator.sol.bak} (73%) rename src/external/examples/{TokenizedResolver.sol => TokenizedResolver.sol.bak} (100%) rename src/external/examples/{ValueResolver.sol => ValueResolver.sol.bak} (100%) rename src/integrations/{MockRegistry.sol => MockRegistry.sol.bak} (100%) rename src/integrations/examples/{MultiAttesterRegistryIntegration.sol => MultiAttesterRegistryIntegration.sol.bak} (100%) rename src/integrations/examples/{SimpleRegistryIntegration.sol => SimpleRegistryIntegration.sol.bak} (100%) rename src/integrations/examples/{SimpleRegistryStorageSlot.sol => SimpleRegistryStorageSlot.sol.bak} (100%) rename src/interface/{IAttestation.sol => IAttestation.sol.bak} (100%) rename src/interface/{IERC7484.sol => IERC7484.sol.bak} (100%) rename src/interface/{IModule.sol => IModule.sol.bak} (100%) rename src/interface/{IQuery.sol => IQuery.sol.bak} (100%) rename src/interface/{IRegistry.sol => IRegistry.sol.bak} (92%) rename src/interface/{ISchema.sol => ISchema.sol.bak} (100%) create mode 100644 src/lib/Helpers.sol create mode 100644 src/lib/ModuleTypeLib.sol create mode 100644 src/simple/AttestationManager.sol create mode 100644 src/simple/ModuleManager.sol create mode 100644 src/simple/TrustManager.sol delete mode 100644 test/Attestation.t.sol delete mode 100644 test/AttestationDelegation.t.sol delete mode 100644 test/AttestationResolve.t.sol delete mode 100644 test/EIP712Verifier.t.sol delete mode 100644 test/Module.t.sol delete mode 100644 test/Query.t.sol delete mode 100644 test/Schema.t.sol delete mode 100644 test/gas/RegistryGasComparison.t.sol delete mode 100644 test/integrations/MockRegistry.t.sol delete mode 100644 test/integrations/SimpleRegIntegration.t.sol delete mode 100644 test/resolvers/TokenizedResolver.t.sol delete mode 100644 test/resolvers/ValueResolver.t.sol delete mode 100644 test/script/DeployRegistry.t.sol delete mode 100644 test/utils/BaseTest.t.sol delete mode 100644 test/utils/BaseUtils.sol delete mode 100644 test/utils/ERC1271Attester.sol diff --git a/docs/Attestation.md b/docs/Attestation.md index 2abb0408..7b586738 100644 --- a/docs/Attestation.md +++ b/docs/Attestation.md @@ -8,11 +8,11 @@ When an Attester creates an Attestation, the Attestation data, structured accord Inputs: -AttestationRequestData +AttestationRequest data is `abi.encode()` according to a defined schema. The data is not stored in the storage of thr Registry, but is rather stored with `SSTORE2` to save gas and a pointer to this data is stored on the Registry. ```solidity -struct AttestationRequestData { +struct AttestationRequest { address moduleAddr; // The moduleAddr of the attestation. uint48 expirationTime; // The time when the attestation expires (Unix timestamp). uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors. @@ -75,7 +75,7 @@ This becomes particularly beneficial when: */ struct SignedAttestationRequest { SchemaUID schemaUID; // The unique identifier of the schema. - AttestationRequestData data; // The arguments of the attestation request. + AttestationRequest data; // The arguments of the attestation request. bytes signature; // The signature data. address attester; // The attesting account. } diff --git a/docs/Schemas.md b/docs/Schemas.md index 39f056e9..f73b7fbd 100644 --- a/docs/Schemas.md +++ b/docs/Schemas.md @@ -24,11 +24,11 @@ The implementation of this Validator is up to the Schema validators discression. ```solidity interface ISchemaValidator is IERC165 { - function validateSchema(AttestationRequestData calldata attestation) + function validateSchema(AttestationRequest calldata attestation) external view returns (bool); - function validateSchema(AttestationRequestData[] calldata attestations) + function validateSchema(AttestationRequest[] calldata attestations) external view returns (bool); diff --git a/script/DeployRegistry.s.sol b/script/DeployRegistry.s.sol.bak similarity index 100% rename from script/DeployRegistry.s.sol rename to script/DeployRegistry.s.sol.bak diff --git a/src/Common.sol b/src/Common.sol index 2ede3e8f..5e09a242 100644 --- a/src/Common.sol +++ b/src/Common.sol @@ -1,8 +1,12 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; +import { ResolverUID, SchemaUID } from "./DataTypes.sol"; + // A representation of an empty/uninitialized UID. bytes32 constant EMPTY_UID = 0; +ResolverUID constant EMPTY_RESOLVER_UID = ResolverUID.wrap(EMPTY_UID); +SchemaUID constant EMPTY_SCHEMA_UID = SchemaUID.wrap(EMPTY_UID); // A zero expiration represents an non-expiring attestation. uint256 constant ZERO_TIMESTAMP = 0; diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 358c5aa0..e4055640 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -14,6 +14,7 @@ struct AttestationRecord { uint48 time; // The time when the attestation was created (Unix timestamp). uint48 expirationTime; // The time when the attestation expires (Unix timestamp). uint48 revocationTime; // The time when the attestation was revoked (Unix timestamp). + PackedModuleTypes moduleTypes; // bit-wise encoded module types. See ModuleTypeLib SchemaUID schemaUID; // The unique identifier of the schema. address moduleAddr; // The implementation address of the module that is being attested. address attester; // The attesting account. @@ -45,12 +46,12 @@ struct ResolverRecord { /** * @dev A struct representing the arguments of the attestation request. */ -struct AttestationRequestData { +struct AttestationRequest { address moduleAddr; // The moduleAddr of the attestation. uint48 expirationTime; // The time when the attestation expires (Unix timestamp). bytes data; // Custom attestation data. + ModuleType[] moduleTypes; // optional: The type(s) of the module. } - /*////////////////////////////////////////////////////////////// Revocation Requests //////////////////////////////////////////////////////////////*/ @@ -58,7 +59,7 @@ struct AttestationRequestData { /** * @dev A struct representing the arguments of the revocation request. */ -struct RevocationRequestData { +struct RevocationRequest { address moduleAddr; // The module address. } @@ -83,8 +84,6 @@ function schemaNotEq(SchemaUID uid1, SchemaUID uid) pure returns (bool) { //--------------------- ResolverUID -----------------------------| type ResolverUID is bytes32; -ResolverUID constant RESOLVER_UID_ZERO = ResolverUID.wrap(bytes32(0)); - using { resolverEq as == } for ResolverUID global; using { resolverNotEq as != } for ResolverUID global; @@ -98,19 +97,6 @@ function resolverNotEq(ResolverUID uid1, ResolverUID uid2) pure returns (bool) { type AttestationDataRef is address; -function readAttestationData(AttestationDataRef dataPointer) view returns (bytes memory data) { - data = SSTORE2.read(AttestationDataRef.unwrap(dataPointer)); -} +type PackedModuleTypes is uint32; -function writeAttestationData( - bytes memory attestationData, - bytes32 salt -) - returns (AttestationDataRef dataPointer) -{ - /** - * @dev We are using CREATE2 to deterministically generate the address of the attestation data. - * Checking if an attestation pointer already exists, would cost more GAS in the average case. - */ - dataPointer = AttestationDataRef.wrap(SSTORE2.writeDeterministic(attestationData, salt)); -} +type ModuleType is uint32; diff --git a/src/IRegistry.sol b/src/IRegistry.sol new file mode 100644 index 00000000..2a6d7f41 --- /dev/null +++ b/src/IRegistry.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { + SchemaUID, + ResolverUID, + AttestationRequest, + AttestationRecord, + AttestationDataRef, + RevocationRequest, + ModuleType +} from "./DataTypes.sol"; + +import { ISchemaValidator } from "./external/ISchemaValidator.sol"; +import { IResolver } from "./external/IResolver.sol"; + +interface IRegistry { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Common Errors */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Query Registry */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + function setAttester(uint8 threshold, address[] calldata attesters) external; + + function check(address module) external view; + + function checkForAccount(address smartAccount, address module) external view; + + function check(address module, ModuleType moduleType) external view; + + function checkForAccount( + address smartAccount, + address module, + ModuleType moduleType + ) + external + view; + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Attestations */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; + + function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; + + function attest( + SchemaUID schemaUID, + address attester, + AttestationRequest calldata request, + bytes calldata signature + ) + external; + + function attest( + SchemaUID schemaUID, + address attester, + AttestationRequest[] calldata requests, + bytes calldata signature + ) + external; + + function readAttestations( + address module, + address[] calldata attesters + ) + external + view + returns (AttestationRecord[] memory attestations); + + function readAttestation( + address module, + address attester + ) + external + view + returns (AttestationRecord memory attestation); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Revocations */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + function revoke(RevocationRequest calldata request) external; + + function revoke(RevocationRequest[] calldata requests) external; + + function revoke( + address attester, + RevocationRequest calldata request, + bytes calldata signature + ) + external; + + function revoke( + address attester, + RevocationRequest[] calldata requests, + bytes calldata signature + ) + external; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Module Registration */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + function deploy( + bytes32 salt, + ResolverUID resolverUID, + bytes calldata code, + bytes calldata deployParams, + bytes calldata metadata + ) + external + payable + returns (address moduleAddr); + + function register( + ResolverUID resolverUID, + address moduleAddress, + bytes calldata metadata + ) + external; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Manage Schemas */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + function registerSchema( + string calldata schema, + ISchemaValidator validator // OPTIONAL + ) + external + returns (SchemaUID uid); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Manage Resolvers */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + function registerResolver(IResolver _resolver) external returns (ResolverUID uid); + + function setResolver(ResolverUID uid, IResolver resolver) external; + + // Event triggered when a module is deployed. + event ModuleRegistration( + address indexed implementation, address indexed sender, bytes32 resolver + ); + event ModuleDeployed(address indexed implementation, bytes32 indexed salt, bytes32 resolver); + event ModuleDeployedExternalFactory( + address indexed implementation, address indexed factory, bytes32 resolver + ); + + error AlreadyRegistered(address module); + error InvalidDeployment(); + // EVENTS + error AlreadyExists(); + error InvalidSignature(); + error InvalidResolver(); + error InvalidModuleType(); + /** + * @dev Emitted when a new schema has been registered + * + * @param uid The schema UID. + * @param registerer The address of the account used to register the schema. + */ + + event SchemaRegistered(SchemaUID indexed uid, address registerer); + + event SchemaResolverRegistered(ResolverUID indexed uid, address registerer); + + /** + * @dev Emitted when a new schema resolver + * + * @param uid The schema UID. + * @param resolver The address of the resolver. + */ + event NewSchemaResolver(ResolverUID indexed uid, address resolver); + + error DifferentResolvers(); + error AlreadyRevoked(); + error AccessDenied(); + error NotFound(); + error InvalidAttestation(); + error InvalidExpirationTime(); + + event Revoked(address moduleAddr, address revoker, SchemaUID schema); + event Attested( + address moduleAddr, address attester, SchemaUID schemaUID, AttestationDataRef sstore2Pointer + ); + + error RevokedAttestation(address attester); + error AttestationNotFound(); + error NoAttestersFound(); +} diff --git a/src/Registry.sol b/src/Registry.sol index 6772e3f5..ba1850dc 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -1,73 +1,12 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { Schema } from "./base/Schema.sol"; -import { AttestationDelegation } from "./base/AttestationDelegation.sol"; -import { Attestation, AttestationResolve } from "./base/Attestation.sol"; -import { Module } from "./base/Module.sol"; -import { Query } from "./base/QueryAttester.sol"; -import { - AttestationRecord, - SchemaUID, - SchemaRecord, - ResolverRecord, - ResolverUID, - ModuleRecord -} from "./DataTypes.sol"; - +import { ModuleManager } from "./simple/ModuleManager.sol"; +import { ResolverManager } from "./simple/ResolverManager.sol"; +import { SchemaManager } from "./simple/SchemaManager.sol"; +import { SignedAttestation } from "./simple/SignedAttestation.sol"; /** * @author zeroknots */ -contract Registry is Schema, Query, AttestationDelegation, Module { - constructor() { } - - /*////////////////////////////////////////////////////////////// - Helper Functions - //////////////////////////////////////////////////////////////*/ - - function getSchema(SchemaUID uid) public view override(Schema) returns (SchemaRecord memory) { - return super.getSchema(uid); - } - - function _getSchema(SchemaUID uid) - internal - view - override(AttestationResolve, Schema) - returns (SchemaRecord storage) - { - return super._getSchema({ schemaUID: uid }); - } - - function _getAttestation( - address module, - address attester - ) - internal - view - virtual - override(Attestation, Query) - returns (AttestationRecord storage) - { - return super._getAttestation(module, attester); - } - - function getResolver(ResolverUID uid) - public - view - virtual - override(AttestationResolve, Module, Schema) - returns (ResolverRecord memory) - { - return super.getResolver(uid); - } - function _getModule(address moduleAddress) - internal - view - virtual - override(AttestationResolve, Module) - returns (ModuleRecord storage) - { - return super._getModule(moduleAddress); - } -} +contract Registry is SignedAttestation { } diff --git a/src/base/Attestation.sol b/src/base/Attestation.sol.bak similarity index 90% rename from src/base/Attestation.sol rename to src/base/Attestation.sol.bak index dd952026..fb0b99b9 100644 --- a/src/base/Attestation.sol +++ b/src/base/Attestation.sol.bak @@ -26,8 +26,8 @@ import { import { AttestationDataRef, AttestationRecord, - AttestationRequestData, - RevocationRequestData, + AttestationRequest, + RevocationRequest, writeAttestationData } from "../DataTypes.sol"; import { AttestationResolve } from "./AttestationResolve.sol"; @@ -54,7 +54,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua */ // @audit is ReentrancyGuard necessary? function attest(AttestationRequest calldata request) external payable nonReentrant { - AttestationRequestData calldata requestData = request.data; + AttestationRequest calldata requestData = request.data; ModuleRecord storage moduleRecord = _getModule({ moduleAddress: request.data.moduleAddr }); @@ -64,7 +64,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // write attestations to registry storge (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ schemaUID: request.schemaUID, - attestationRequestData: requestData, + AttestationRequest: requestData, attester: msg.sender }); @@ -108,7 +108,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua uint256 usedValue = _multiAttest({ schemaUID: multiRequest.schemaUID, resolverUID: moduleRecord.resolverUID, - attestationRequestDatas: multiRequest.data, + AttestationRequests: multiRequest.data, attester: msg.sender, availableValue: availableValue, isLastAttestation: last @@ -180,7 +180,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua availableValue -= _multiRevoke({ schemaUID: multiRequest.schemaUID, resolverUID: moduleRecord.resolverUID, - revocationRequestDatas: multiRequest.data, + RevocationRequests: multiRequest.data, revoker: msg.sender, availableValue: availableValue, isLastRevocation: isLastRevocation @@ -193,7 +193,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua * * @param schemaUID The unique identifier of the schema to attest to. * @param resolverUID The unique identifier of the resolver. - * @param attestationRequestDatas The attestation data. + * @param AttestationRequests The attestation data. * @param attester The attester's address. * @param availableValue Amount of ETH available for the operation. * @param isLastAttestation Indicates if this is the last batch. @@ -203,7 +203,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua function _multiAttest( SchemaUID schemaUID, ResolverUID resolverUID, - AttestationRequestData[] calldata attestationRequestDatas, + AttestationRequest[] calldata AttestationRequests, address attester, uint256 availableValue, bool isLastAttestation @@ -211,10 +211,10 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua internal returns (uint256 usedValue) { - _requireSchemaCheck(schemaUID, attestationRequestDatas); + _requireSchemaCheck(schemaUID, AttestationRequests); // caching length - uint256 length = attestationRequestDatas.length; + uint256 length = AttestationRequests.length; // caching current time as it will be used in the for loop // for loop will run and save the return values in these two arrays @@ -227,7 +227,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua for (uint256 i; i < length; ++i) { (attestationRecords[i], values[i]) = _writeAttestation({ schemaUID: schemaUID, - attestationRequestData: attestationRequestDatas[i], + AttestationRequest: AttestationRequests[i], attester: attester }); } @@ -245,7 +245,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua function _requireSchemaCheck( SchemaUID schemaUID, - AttestationRequestData calldata requestData + AttestationRequest calldata requestData ) internal view @@ -264,7 +264,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua function _requireSchemaCheck( SchemaUID schemaUID, - AttestationRequestData[] calldata requestDatas + AttestationRequest[] calldata requestDatas ) internal view @@ -283,11 +283,11 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua /** * Writes an attestation record to storage and emits an event. * - * @dev the bytes metadata provided in the AttestationRequestData + * @dev the bytes metadata provided in the AttestationRequest * is writted to the EVM with SSTORE2 to allow for large attestations without spending a lot of gas * * @param schemaUID The unique identifier of the schema being attested to. - * @param attestationRequestData The data for the attestation request. + * @param AttestationRequest The data for the attestation request. * @param attester The address of the entity making the attestation. * * @return attestationRecord The written attestation record. @@ -295,7 +295,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua */ function _writeAttestation( SchemaUID schemaUID, - AttestationRequestData calldata attestationRequestData, + AttestationRequest calldata AttestationRequest, address attester ) internal @@ -304,13 +304,13 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua uint48 timeNow = _time(); // Ensure that either no expiration time was set or that it was set in the future. if ( - attestationRequestData.expirationTime != ZERO_TIMESTAMP - && attestationRequestData.expirationTime <= timeNow + AttestationRequest.expirationTime != ZERO_TIMESTAMP + && AttestationRequest.expirationTime <= timeNow ) { revert InvalidExpirationTime(); } // caching module address. - address module = attestationRequestData.moduleAddr; + address module = AttestationRequest.moduleAddr; ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); // Ensure that attestation is for module that was registered. @@ -321,7 +321,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua // get salt used for SSTORE2 to avoid collisions during CREATE2 bytes32 attestationSalt = AttestationLib.attestationSalt(attester, module); AttestationDataRef sstore2Pointer = writeAttestationData({ - attestationData: attestationRequestData.data, + attestationData: AttestationRequest.data, salt: attestationSalt }); @@ -331,12 +331,12 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua moduleAddr: module, attester: attester, time: timeNow, - expirationTime: attestationRequestData.expirationTime, + expirationTime: AttestationRequest.expirationTime, revocationTime: uint48(ZERO_TIMESTAMP), dataPointer: sstore2Pointer }); - value = attestationRequestData.value; + value = AttestationRequest.value; // SSTORE attestation on registry storage _moduleToAttesterToAttestations[module][attester] = attestationRecord; @@ -344,7 +344,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua } function _revoke( - RevocationRequestData memory request, + RevocationRequest memory request, address revoker ) internal @@ -381,7 +381,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua * @dev Revokes an existing attestation to a specific schema. * * @param schemaUID The unique identifier of the schema that was used to attest. - * @param revocationRequestDatas The arguments of the revocation requests. + * @param RevocationRequests The arguments of the revocation requests. * @param revoker The revoking account. * @param availableValue The total available ETH amount that can be sent to the resolver. * @param isLastRevocation Whether this is the last attestations/revocations set. @@ -391,7 +391,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua function _multiRevoke( SchemaUID schemaUID, ResolverUID resolverUID, - RevocationRequestData[] memory revocationRequestDatas, + RevocationRequest[] memory RevocationRequests, address revoker, uint256 availableValue, bool isLastRevocation @@ -404,12 +404,12 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); // caching length - uint256 length = revocationRequestDatas.length; + uint256 length = RevocationRequests.length; AttestationRecord[] memory attestationRecords = new AttestationRecord[](length); uint256[] memory values = new uint256[](length); for (uint256 i; i < length; ++i) { - RevocationRequestData memory revocationRequests = revocationRequestDatas[i]; + RevocationRequest memory revocationRequests = RevocationRequests[i]; attestationRecords[i] = _revoke({ request: revocationRequests, revoker: revoker }); values[i] = revocationRequests.value; diff --git a/src/base/AttestationDelegation.sol b/src/base/AttestationDelegation.sol.bak similarity index 88% rename from src/base/AttestationDelegation.sol rename to src/base/AttestationDelegation.sol.bak index a944df07..98e4a6a1 100644 --- a/src/base/AttestationDelegation.sol +++ b/src/base/AttestationDelegation.sol.bak @@ -8,11 +8,11 @@ import { MultiSignedAttestationRequest, SignedRevocationRequest, MultiSignedRevocationRequest, - AttestationRequestData, + AttestationRequest, ModuleRecord, ResolverUID, AttestationRecord, - RevocationRequestData + RevocationRequest } from "../DataTypes.sol"; import { InvalidLength } from "../Common.sol"; @@ -35,22 +35,22 @@ abstract contract AttestationDelegation is IAttestation, Attestation { payable nonReentrant { - // Get attestationRequestData calldata pointer - AttestationRequestData calldata attestationRequestData = signedRequest.data; + // Get AttestationRequest calldata pointer + AttestationRequest calldata AttestationRequest = signedRequest.data; // check signature. this will revert if signedRequest.attester != signer _requireValidAttestSignatureCalldata(signedRequest); // check if schema exists and is valid. This will revert if validtor returns false - _requireSchemaCheck(signedRequest.schemaUID, attestationRequestData); + _requireSchemaCheck(signedRequest.schemaUID, AttestationRequest); // @audit could this be address(0), what happens if there is no module Record ModuleRecord storage moduleRecord = - _getModule({ moduleAddress: attestationRequestData.moduleAddr }); + _getModule({ moduleAddress: AttestationRequest.moduleAddr }); ResolverUID resolverUID = moduleRecord.resolverUID; // store attestation record (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ schemaUID: signedRequest.schemaUID, - attestationRequestData: attestationRequestData, + AttestationRequest: AttestationRequest, attester: signedRequest.attester }); @@ -106,8 +106,8 @@ abstract contract AttestationDelegation is IAttestation, Attestation { } MultiSignedAttestationRequest calldata multiSignedRequest = multiSignedRequests[i]; - AttestationRequestData[] calldata attestationRequestDatas = multiSignedRequest.data; - uint256 dataLength = attestationRequestDatas.length; + AttestationRequest[] calldata AttestationRequests = multiSignedRequest.data; + uint256 dataLength = AttestationRequests.length; // Ensure that no inputs are missing. if (dataLength != multiSignedRequest.signatures.length) { @@ -119,7 +119,7 @@ abstract contract AttestationDelegation is IAttestation, Attestation { _requireValidAttestSignature( SignedAttestationRequest({ schemaUID: multiSignedRequest.schemaUID, - data: attestationRequestDatas[j], + data: AttestationRequests[j], signature: multiSignedRequest.signatures[j], attester: multiSignedRequest.attester }) @@ -130,7 +130,7 @@ abstract contract AttestationDelegation is IAttestation, Attestation { uint256 usedValue = _multiAttest({ schemaUID: multiSignedRequest.schemaUID, resolverUID: moduleRecord.resolverUID, - attestationRequestDatas: attestationRequestDatas, + AttestationRequests: AttestationRequests, attester: multiSignedRequest.attester, availableValue: availableValue, isLastAttestation: last @@ -151,7 +151,7 @@ abstract contract AttestationDelegation is IAttestation, Attestation { function revoke(SignedRevocationRequest calldata request) external payable nonReentrant { _verifyRevoke(request); - RevocationRequestData[] memory data = new RevocationRequestData[](1); + RevocationRequest[] memory data = new RevocationRequest[](1); data[0] = request.data; ModuleRecord memory moduleRecord = _getModule({ moduleAddress: request.data.moduleAddr }); @@ -159,7 +159,7 @@ abstract contract AttestationDelegation is IAttestation, Attestation { _multiRevoke({ schemaUID: request.schemaUID, resolverUID: moduleRecord.resolverUID, - revocationRequestDatas: data, + RevocationRequests: data, revoker: request.revoker, availableValue: msg.value, isLastRevocation: true @@ -195,8 +195,8 @@ abstract contract AttestationDelegation is IAttestation, Attestation { } MultiSignedRevocationRequest memory multiSignedRequest = multiSignedRequests[i]; - RevocationRequestData[] memory revocationRequestDatas = multiSignedRequest.data; - uint256 dataLength = revocationRequestDatas.length; + RevocationRequest[] memory RevocationRequests = multiSignedRequest.data; + uint256 dataLength = RevocationRequests.length; // Ensure that no inputs are missing. if (dataLength == 0 || dataLength != multiSignedRequest.signatures.length) { @@ -208,7 +208,7 @@ abstract contract AttestationDelegation is IAttestation, Attestation { _verifyRevoke( SignedRevocationRequest({ schemaUID: multiSignedRequest.schemaUID, - data: revocationRequestDatas[j], + data: RevocationRequests[j], signature: multiSignedRequest.signatures[j], revoker: multiSignedRequest.revoker }) @@ -219,7 +219,7 @@ abstract contract AttestationDelegation is IAttestation, Attestation { availableValue -= _multiRevoke({ schemaUID: multiSignedRequest.schemaUID, resolverUID: moduleRecord.resolverUID, - revocationRequestDatas: revocationRequestDatas, + RevocationRequests: RevocationRequests, revoker: multiSignedRequest.revoker, availableValue: availableValue, isLastRevocation: last diff --git a/src/base/AttestationResolve.sol b/src/base/AttestationResolve.sol.bak similarity index 100% rename from src/base/AttestationResolve.sol rename to src/base/AttestationResolve.sol.bak diff --git a/src/base/AttestationSimple.sol b/src/base/AttestationSimple.sol.bak similarity index 87% rename from src/base/AttestationSimple.sol rename to src/base/AttestationSimple.sol.bak index da5ebf15..670951fb 100644 --- a/src/base/AttestationSimple.sol +++ b/src/base/AttestationSimple.sol.bak @@ -26,8 +26,8 @@ import { import { AttestationDataRef, AttestationRecord, - AttestationRequestData, - RevocationRequestData, + AttestationRequest, + RevocationRequest, writeAttestationData } from "../DataTypes.sol"; import { AttestationResolve } from "./AttestationResolve.sol"; @@ -39,25 +39,25 @@ contract Attestation { mapping(address module => mapping(address attester => AttestationRecord attestation)) internal _moduleToAttesterToAttestations; - function attest(SchemaUID schemaUID, AttestationRequestData calldata request) external { + function attest(SchemaUID schemaUID, AttestationRequest calldata request) external { _attestAndCheckExternal(msg.sender, schemaUID, request); } - function attest(SchemaUID schemaUID, AttestationRequestData[] calldata requests) external { + function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external { _attestAndCheckExternal(msg.sender, schemaUID, requests); } function _attestAndCheckExternal( address attester, SchemaUID schemaUID, - AttestationRequestData calldata request + AttestationRequest calldata request ) internal { AttestationRecord memory record = _storeAttestation({ schemaUID: schemaUID, attester: attester, - attestationRequestData: request + AttestationRequest: request }); // check if schema exists and is valid. This will revert if validtor returns false @@ -70,7 +70,7 @@ contract Attestation { function _attestAndCheckExternal( address attester, SchemaUID schemaUID, - AttestationRequestData[] calldata requests + AttestationRequest[] calldata requests ) internal { @@ -81,7 +81,7 @@ contract Attestation { attestationRecord[i] = _storeAttestation({ schemaUID: schemaUID, attester: attester, - attestationRequestData: requests[i] + AttestationRequest: requests[i] }); } @@ -98,7 +98,7 @@ contract Attestation { function _storeAttestation( SchemaUID schemaUID, address attester, - AttestationRequestData calldata attestationRequestData + AttestationRequest calldata AttestationRequest ) internal returns (AttestationRecord memory record) @@ -107,13 +107,13 @@ contract Attestation { uint48 timeNow = _time(); // Ensure that either no expiration time was set or that it was set in the future. if ( - attestationRequestData.expirationTime != ZERO_TIMESTAMP - && attestationRequestData.expirationTime <= timeNow + AttestationRequest.expirationTime != ZERO_TIMESTAMP + && AttestationRequest.expirationTime <= timeNow ) { revert InvalidExpirationTime(); } // caching module address. - address module = attestationRequestData.moduleAddr; + address module = AttestationRequest.moduleAddr; ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); // Ensure that attestation is for module that was registered. @@ -124,7 +124,7 @@ contract Attestation { // get salt used for SSTORE2 to avoid collisions during CREATE2 bytes32 attestationSalt = AttestationLib.attestationSalt(attester, module); AttestationDataRef sstore2Pointer = writeAttestationData({ - attestationData: attestationRequestData.data, + attestationData: AttestationRequest.data, salt: attestationSalt }); @@ -134,7 +134,7 @@ contract Attestation { moduleAddr: module, attester: attester, time: timeNow, - expirationTime: attestationRequestData.expirationTime, + expirationTime: AttestationRequest.expirationTime, revocationTime: uint48(ZERO_TIMESTAMP), dataPointer: sstore2Pointer }); diff --git a/src/base/EIP712Verifier.sol b/src/base/EIP712Verifier.sol.bak similarity index 94% rename from src/base/EIP712Verifier.sol rename to src/base/EIP712Verifier.sol.bak index bfcede3b..e485fc72 100644 --- a/src/base/EIP712Verifier.sol +++ b/src/base/EIP712Verifier.sol.bak @@ -6,10 +6,10 @@ import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; import { InvalidSignature } from "../Common.sol"; import { - AttestationRequestData, + AttestationRequest, SchemaUID, SignedAttestationRequest, - RevocationRequestData, + RevocationRequest, SignedRevocationRequest } from "../DataTypes.sol"; @@ -21,11 +21,11 @@ import { abstract contract EIP712Verifier is EIP712 { // The hash of the data type used to relay calls to the attest function. It's the value of bytes32 private constant ATTEST_TYPEHASH = - keccak256("AttestationRequestData(address,uint48,uint256,bytes)"); + keccak256("AttestationRequest(address,uint48,uint256,bytes)"); // The hash of the data type used to relay calls to the revoke function. It's the value of bytes32 private constant REVOKE_TYPEHASH = - keccak256("RevocationRequestData(address,address,uint256)"); + keccak256("RevocationRequest(address,address,uint256)"); // Replay protection nonces. mapping(address account => uint256 nonce) private _nonces; @@ -82,7 +82,7 @@ abstract contract EIP712Verifier is EIP712 { * @return digest The attestation digest. */ function getAttestationDigest( - AttestationRequestData memory attData, + AttestationRequest memory attData, SchemaUID schemaUID, uint256 nonce ) @@ -103,7 +103,7 @@ abstract contract EIP712Verifier is EIP712 { * @return digest The attestation digest. */ function getAttestationDigest( - AttestationRequestData memory attData, + AttestationRequest memory attData, SchemaUID schemaUID, address attester ) @@ -125,7 +125,7 @@ abstract contract EIP712Verifier is EIP712 { * @return digest The attestation digest. */ function _attestationDigest( - AttestationRequestData memory data, + AttestationRequest memory data, SchemaUID schemaUID, uint256 nonce ) @@ -199,7 +199,7 @@ abstract contract EIP712Verifier is EIP712 { * @return digest The revocation digest. */ function getRevocationDigest( - RevocationRequestData memory revData, + RevocationRequest memory revData, SchemaUID schemaUID, address revoker ) @@ -220,7 +220,7 @@ abstract contract EIP712Verifier is EIP712 { * @return digest The revocation digest. */ function getRevocationDigest( - RevocationRequestData memory revData, + RevocationRequest memory revData, SchemaUID schemaUID, uint256 nonce ) @@ -262,7 +262,7 @@ abstract contract EIP712Verifier is EIP712 { * @param request The arguments of the signed revocation request. */ function _verifyRevoke(SignedRevocationRequest memory request) internal { - RevocationRequestData memory data = request.data; + RevocationRequest memory data = request.data; uint256 nonce = _newNonce(request.revoker); bytes32 digest = _revocationDigest(request.schemaUID, data.moduleAddr, data.attester, nonce); diff --git a/src/base/Module.sol b/src/base/Module.sol.bak similarity index 100% rename from src/base/Module.sol rename to src/base/Module.sol.bak diff --git a/src/base/Query.sol b/src/base/Query.sol.bak similarity index 100% rename from src/base/Query.sol rename to src/base/Query.sol.bak diff --git a/src/base/QueryAttester.sol b/src/base/QueryAttester.sol.bak similarity index 100% rename from src/base/QueryAttester.sol rename to src/base/QueryAttester.sol.bak diff --git a/src/base/Schema.sol b/src/base/Schema.sol.bak similarity index 100% rename from src/base/Schema.sol rename to src/base/Schema.sol.bak diff --git a/src/external/IResolver.sol b/src/external/IResolver.sol index 8d394134..4e951528 100644 --- a/src/external/IResolver.sol +++ b/src/external/IResolver.sol @@ -23,29 +23,11 @@ interface IResolver is IERC165 { * * @return Whether the attestation is valid. */ - function attest(AttestationRecord calldata attestation) external payable returns (bool); - - /** - * @dev Processes a Module Registration - * - * @param module Module registration artefact - * - * @return Whether the registration is valid - */ - function moduleRegistration(ModuleRecord calldata module) external payable returns (bool); - - /** - * @dev Processes multiple attestations and verifies whether they are valid. - * - * @param attestations The new attestations. - * @param values Explicit ETH amounts which were sent with each attestation. - * - * @return Whether all the attestations are valid. - */ - function multiAttest( - AttestationRecord[] calldata attestations, - uint256[] calldata values - ) + function resolveAttestation(AttestationRecord calldata attestation) + external + payable + returns (bool); + function resolveAttestation(AttestationRecord[] calldata attestation) external payable returns (bool); @@ -57,20 +39,23 @@ interface IResolver is IERC165 { * * @return Whether the attestation can be revoked. */ - function revoke(AttestationRecord calldata attestation) external payable returns (bool); + function resolveRevocation(AttestationRecord calldata attestation) + external + payable + returns (bool); + function resolveRevocation(AttestationRecord[] calldata attestation) + external + payable + returns (bool); /** - * @dev Processes revocation of multiple attestation and verifies they can be revoked. + * @dev Processes a Module Registration * - * @param attestations The existing attestations to be revoked. - * @param values Explicit ETH amounts which were sent with each revocation. + * @param module Module registration artefact * - * @return Whether the attestations can be revoked. + * @return Whether the registration is valid */ - function multiRevoke( - AttestationRecord[] calldata attestations, - uint256[] calldata values - ) + function resolveModuleRegistration(ModuleRecord calldata module) external payable returns (bool); diff --git a/src/external/ISchemaValidator.sol b/src/external/ISchemaValidator.sol index f0419540..d417035f 100644 --- a/src/external/ISchemaValidator.sol +++ b/src/external/ISchemaValidator.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { AttestationRequestData } from "../DataTypes.sol"; +import { AttestationRecord } from "../DataTypes.sol"; import { IERC165 } from "forge-std/interfaces/IERC165.sol"; /** @@ -11,15 +11,12 @@ interface ISchemaValidator is IERC165 { /** * @notice Validates an attestation request. */ - function validateSchema(AttestationRequestData calldata attestation) - external - view - returns (bool); + function validateSchema(AttestationRecord calldata attestation) external view returns (bool); /** * @notice Validates an array of attestation requests. */ - function validateSchema(AttestationRequestData[] calldata attestations) + function validateSchema(AttestationRecord[] calldata attestations) external view returns (bool); diff --git a/src/external/ResolverBase.sol b/src/external/ResolverBase.sol.bak similarity index 100% rename from src/external/ResolverBase.sol rename to src/external/ResolverBase.sol.bak diff --git a/src/external/SchemaValidatorBase.sol b/src/external/SchemaValidatorBase.sol.bak similarity index 70% rename from src/external/SchemaValidatorBase.sol rename to src/external/SchemaValidatorBase.sol.bak index c35857de..c494224a 100644 --- a/src/external/SchemaValidatorBase.sol +++ b/src/external/SchemaValidatorBase.sol.bak @@ -1,14 +1,14 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ISchemaValidator, AttestationRequestData } from "./ISchemaValidator.sol"; +import { ISchemaValidator, AttestationRequest } from "./ISchemaValidator.sol"; /** * @title SchemaValidatorBase * @notice Base contract for schema validators */ contract SchemaValidatorBase is ISchemaValidator { - function validateSchema(AttestationRequestData calldata attestation) + function validateSchema(AttestationRequest calldata attestation) external view virtual @@ -16,7 +16,7 @@ contract SchemaValidatorBase is ISchemaValidator { returns (bool) { } - function validateSchema(AttestationRequestData[] calldata attestations) + function validateSchema(AttestationRequest[] calldata attestations) external view virtual diff --git a/src/external/examples/DebugResolver.sol b/src/external/examples/DebugResolver.sol.bak similarity index 100% rename from src/external/examples/DebugResolver.sol rename to src/external/examples/DebugResolver.sol.bak diff --git a/src/external/examples/SimpleValidator.sol b/src/external/examples/SimpleValidator.sol.bak similarity index 73% rename from src/external/examples/SimpleValidator.sol rename to src/external/examples/SimpleValidator.sol.bak index 6278f45a..e662e628 100644 --- a/src/external/examples/SimpleValidator.sol +++ b/src/external/examples/SimpleValidator.sol.bak @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import { ISchemaValidator } from "../ISchemaValidator.sol"; -import { AttestationRequestData } from "../../DataTypes.sol"; +import { AttestationRequest } from "../../DataTypes.sol"; /** * @title SimpleValidator @@ -10,7 +10,7 @@ import { AttestationRequestData } from "../../DataTypes.sol"; * @notice A simple validator that always returns true. */ contract SimpleValidator is ISchemaValidator { - function validateSchema(AttestationRequestData calldata attestation) + function validateSchema(AttestationRequest calldata attestation) external pure override @@ -19,7 +19,7 @@ contract SimpleValidator is ISchemaValidator { return true; } - function validateSchema(AttestationRequestData[] calldata attestation) + function validateSchema(AttestationRequest[] calldata attestation) external pure override diff --git a/src/external/examples/TokenizedResolver.sol b/src/external/examples/TokenizedResolver.sol.bak similarity index 100% rename from src/external/examples/TokenizedResolver.sol rename to src/external/examples/TokenizedResolver.sol.bak diff --git a/src/external/examples/ValueResolver.sol b/src/external/examples/ValueResolver.sol.bak similarity index 100% rename from src/external/examples/ValueResolver.sol rename to src/external/examples/ValueResolver.sol.bak diff --git a/src/integrations/MockRegistry.sol b/src/integrations/MockRegistry.sol.bak similarity index 100% rename from src/integrations/MockRegistry.sol rename to src/integrations/MockRegistry.sol.bak diff --git a/src/integrations/examples/MultiAttesterRegistryIntegration.sol b/src/integrations/examples/MultiAttesterRegistryIntegration.sol.bak similarity index 100% rename from src/integrations/examples/MultiAttesterRegistryIntegration.sol rename to src/integrations/examples/MultiAttesterRegistryIntegration.sol.bak diff --git a/src/integrations/examples/SimpleRegistryIntegration.sol b/src/integrations/examples/SimpleRegistryIntegration.sol.bak similarity index 100% rename from src/integrations/examples/SimpleRegistryIntegration.sol rename to src/integrations/examples/SimpleRegistryIntegration.sol.bak diff --git a/src/integrations/examples/SimpleRegistryStorageSlot.sol b/src/integrations/examples/SimpleRegistryStorageSlot.sol.bak similarity index 100% rename from src/integrations/examples/SimpleRegistryStorageSlot.sol rename to src/integrations/examples/SimpleRegistryStorageSlot.sol.bak diff --git a/src/interface/IAttestation.sol b/src/interface/IAttestation.sol.bak similarity index 100% rename from src/interface/IAttestation.sol rename to src/interface/IAttestation.sol.bak diff --git a/src/interface/IERC7484.sol b/src/interface/IERC7484.sol.bak similarity index 100% rename from src/interface/IERC7484.sol rename to src/interface/IERC7484.sol.bak diff --git a/src/interface/IModule.sol b/src/interface/IModule.sol.bak similarity index 100% rename from src/interface/IModule.sol rename to src/interface/IModule.sol.bak diff --git a/src/interface/IQuery.sol b/src/interface/IQuery.sol.bak similarity index 100% rename from src/interface/IQuery.sol rename to src/interface/IQuery.sol.bak diff --git a/src/interface/IRegistry.sol b/src/interface/IRegistry.sol.bak similarity index 92% rename from src/interface/IRegistry.sol rename to src/interface/IRegistry.sol.bak index 23687250..6aa82502 100644 --- a/src/interface/IRegistry.sol +++ b/src/interface/IRegistry.sol.bak @@ -33,10 +33,10 @@ interface IRegistry { struct AttestationRequest { bytes32 schemaUID; - AttestationRequestData data; + AttestationRequest data; } - struct AttestationRequestData { + struct AttestationRequest { address moduleAddr; uint48 expirationTime; uint256 value; @@ -45,14 +45,14 @@ interface IRegistry { struct SignedAttestationRequest { bytes32 schemaUID; - AttestationRequestData data; + AttestationRequest data; bytes signature; address attester; } struct SignedRevocationRequest { bytes32 schemaUID; - RevocationRequestData data; + RevocationRequest data; bytes signature; address revoker; } @@ -66,26 +66,26 @@ interface IRegistry { struct MultiAttestationRequest { bytes32 schemaUID; - AttestationRequestData[] data; + AttestationRequest[] data; } struct MultiSignedAttestationRequest { bytes32 schemaUID; - AttestationRequestData[] data; + AttestationRequest[] data; bytes[] signatures; address attester; } struct MultiSignedRevocationRequest { bytes32 schemaUID; - RevocationRequestData[] data; + RevocationRequest[] data; bytes[] signatures; address revoker; } struct MultiRevocationRequest { bytes32 schemaUID; - RevocationRequestData[] data; + RevocationRequest[] data; } struct ResolverRecord { @@ -95,10 +95,10 @@ interface IRegistry { struct RevocationRequest { bytes32 schemaUID; - RevocationRequestData data; + RevocationRequest data; } - struct RevocationRequestData { + struct RevocationRequest { address moduleAddr; address attester; uint256 value; @@ -176,7 +176,7 @@ interface IRegistry { returns (AttestationRecord[] memory attestations); function getAttestTypeHash() external pure returns (bytes32); function getAttestationDigest( - AttestationRequestData memory attData, + AttestationRequest memory attData, bytes32 schemaUID, uint256 nonce ) @@ -184,7 +184,7 @@ interface IRegistry { view returns (bytes32 digest); function getAttestationDigest( - AttestationRequestData memory attData, + AttestationRequest memory attData, bytes32 schemaUID, address attester ) @@ -197,7 +197,7 @@ interface IRegistry { function getNonce(address account) external view returns (uint256); function getResolver(bytes32 uid) external view returns (ResolverRecord memory); function getRevocationDigest( - RevocationRequestData memory revData, + RevocationRequest memory revData, bytes32 schemaUID, address revoker ) @@ -205,7 +205,7 @@ interface IRegistry { view returns (bytes32 digest); function getRevocationDigest( - RevocationRequestData memory revData, + RevocationRequest memory revData, bytes32 schemaUID, uint256 nonce ) diff --git a/src/interface/ISchema.sol b/src/interface/ISchema.sol.bak similarity index 100% rename from src/interface/ISchema.sol rename to src/interface/ISchema.sol.bak diff --git a/src/lib/AttestationLib.sol b/src/lib/AttestationLib.sol index 310fedb5..dfc7140e 100644 --- a/src/lib/AttestationLib.sol +++ b/src/lib/AttestationLib.sol @@ -1,35 +1,79 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { EIP712 } from "solady/utils/EIP712.sol"; -import { AttestationRequestData } from "../DataTypes.sol"; +import { AttestationRequest, RevocationRequest, AttestationDataRef } from "../DataTypes.sol"; +import { SSTORE2 } from "solady/utils/SSTORE2.sol"; -// The hash of the data type used to relay calls to the attest function. It's the value of -bytes32 constant ATTEST_TYPEHASH = keccak256("AttestationRequestData(address,uint48,uint256,bytes)"); +library AttestationLib { + // The hash of the data type used to relay calls to the attest function. It's the value of + bytes32 constant ATTEST_TYPEHASH = keccak256("AttestationRequest(address,uint48,uint256,bytes)"); -// The hash of the data type used to relay calls to the revoke function. It's the value of -bytes32 constant REVOKE_TYPEHASH = keccak256("RevocationRequestData(address,address,uint256)"); + // The hash of the data type used to relay calls to the revoke function. It's the value of + bytes32 constant REVOKE_TYPEHASH = keccak256("RevocationRequest(address,address,uint256)"); -library AttestationLib { - function digest( - AttestationRequestData calldata data, + function sload2(AttestationDataRef dataPointer) internal view returns (bytes memory data) { + data = SSTORE2.read(AttestationDataRef.unwrap(dataPointer)); + } + + function sstore2( + AttestationRequest calldata request, + bytes32 salt + ) + internal + returns (AttestationDataRef dataPointer) + { + /** + * @dev We are using CREATE2 to deterministically generate the address of the attestation data. + * Checking if an attestation pointer already exists, would cost more GAS in the average case. + */ + dataPointer = AttestationDataRef.wrap(SSTORE2.writeDeterministic(request.data, salt)); + } + + function sstore2Salt(address attester, address module) internal view returns (bytes32 salt) { + salt = keccak256(abi.encodePacked(attester, module, block.timestamp, block.chainid)); + } + + function hash( + AttestationRequest calldata data, + uint256 nonce + ) + internal + pure + returns (bytes32 _hash) + { + _hash = keccak256(abi.encode(ATTEST_TYPEHASH, keccak256(abi.encode(data)), nonce)); + } + + function hash( + AttestationRequest[] calldata data, + uint256 nonce + ) + internal + pure + returns (bytes32 _hash) + { + _hash = keccak256(abi.encode(ATTEST_TYPEHASH, keccak256(abi.encode(data)), nonce)); + } + + function hash( + RevocationRequest calldata data, + uint256 nonce + ) + internal + pure + returns (bytes32 _hash) + { + _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); + } + + function hash( + RevocationRequest[] calldata data, uint256 nonce ) internal - returns (bytes32 digest) + pure + returns (bytes32 _hash) { - digest = _hashTypedData( - keccak256( - abi.encodePacked( - ATTEST_TYPEHASH, - block.chainid, - data.requester, - data.timestamp, - data.expiration, - data.schema, - nonce - ) - ) - ); + _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); } } diff --git a/src/lib/Helpers.sol b/src/lib/Helpers.sol new file mode 100644 index 00000000..f5fca240 --- /dev/null +++ b/src/lib/Helpers.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { ResolverRecord, SchemaRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; + +library UIDLib { + /** + * @dev Calculates a UID for a given schema. + * + * @param schemaRecord The input schema. + * + * @return schema UID. + */ + function getUID(SchemaRecord memory schemaRecord) internal pure returns (SchemaUID) { + return SchemaUID.wrap( + keccak256(abi.encodePacked(schemaRecord.schema, address(schemaRecord.validator))) + ); + } + + /** + * @dev Calculates a UID for a given resolver. + * + * @param resolver The input schema. + * + * @return ResolverUID. + */ + function getUID(ResolverRecord memory resolver) internal pure returns (ResolverUID) { + return ResolverUID.wrap(keccak256(abi.encodePacked(resolver.resolver))); + } +} diff --git a/src/lib/ModuleTypeLib.sol b/src/lib/ModuleTypeLib.sol new file mode 100644 index 00000000..8b57aa30 --- /dev/null +++ b/src/lib/ModuleTypeLib.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import { PackedModuleTypes, ModuleType } from "../DataTypes.sol"; + +library ModuleTypeLib { + function isType(PackedModuleTypes self, ModuleType moduleType) internal pure returns (bool) { + return (PackedModuleTypes.unwrap(self) & 2 ** ModuleType.unwrap(moduleType)) != 0; + } + + function pack(ModuleType[] memory moduleTypes) internal pure returns (PackedModuleTypes) { + uint32 result; + uint256 length = moduleTypes.length; + for (uint256 i; i < length; i++) { + result = result + uint32(2 ** ModuleType.unwrap(moduleTypes[i])); + } + return PackedModuleTypes.wrap(result); + } + + function packCalldata(ModuleType[] calldata moduleTypes) + internal + pure + returns (PackedModuleTypes) + { + uint32 result; + for (uint256 i; i < moduleTypes.length; i++) { + result = result + uint32(2 ** ModuleType.unwrap(moduleTypes[i])); + } + return PackedModuleTypes.wrap(result); + } +} diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index 88785497..00779a97 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -1,9 +1,16 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { AttestationRecord, ResolverRecord } from "../DataTypes.sol"; +import { AttestationRecord, ResolverRecord, SchemaRecord, ModuleRecord } from "../DataTypes.sol"; +import { ISchemaValidator } from "../external/ISchemaValidator.sol"; +import { IResolver } from "../external/IResolver.sol"; +import { ZERO_ADDRESS, ZERO_TIMESTAMP } from "../Common.sol"; +// TODO: fix errors library StubLib { + error InvalidDeployment(); + error InvalidSchema(); + function requireExternalSchemaValidation( AttestationRecord memory attestationRecord, SchemaRecord storage schema @@ -16,13 +23,23 @@ library StubLib { // validate Schema ISchemaValidator validator = schema.validator; // if validator is set, call the validator - if (address(validator) != ZERO_ADDRESS && validator.validateSchema(requestData) == false) { + if ( + address(validator) != ZERO_ADDRESS + && validator.validateSchema(attestationRecord) == false + ) { // revert if ISchemaValidator returns false - revert InvalidAttestation(); + revert(); + // if (!success) { // If call reverts + // // If there is return data, the call reverted without a reason or a custom error. + // if (result.length == 0) revert(); + // assembly { + // // We use Yul's revert() to bubble up errors from the target contract. + // revert(add(32, result), mload(result)) + // } } } - function _requireSchemaCheck( + function requireExternalSchemaValidation( AttestationRecord[] memory attestationRecords, SchemaRecord storage schema ) @@ -38,7 +55,7 @@ library StubLib { address(validator) != ZERO_ADDRESS && validator.validateSchema(attestationRecords) == false ) { - revert InvalidAttestation(); + revert(); } } @@ -52,11 +69,11 @@ library StubLib { if (address(resolverContract) != ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecord) == false) { - revert InvalidAttestation(); + revert(); } } - function requireExternalResolver( + function requireExternalResolverCheck( AttestationRecord[] memory attestationRecords, ResolverRecord storage resolver ) @@ -67,7 +84,22 @@ library StubLib { if (address(resolverContract) != ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecords) == false) { - revert InvalidAttestation(); + revert(); + } + } + + function requireExternalResolverCheck( + ModuleRecord memory moduleRecord, + ResolverRecord storage resolver + ) + internal + { + IResolver resolverContract = resolver.resolver; + + if (address(resolverContract) != ZERO_ADDRESS) return; + + if (resolverContract.resolveModuleRegistration(moduleRecord) == false) { + revert(); } } } diff --git a/src/simple/Attestation.sol b/src/simple/Attestation.sol index 3aa3b29b..b918aff5 100644 --- a/src/simple/Attestation.sol +++ b/src/simple/Attestation.sol @@ -1,159 +1,55 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { AttestationRecord } from "../DataTypes.sol"; +import { + AttestationRecord, AttestationRequest, RevocationRequest, SchemaUID +} from "../DataTypes.sol"; import { ResolverManager } from "./ResolverManager.sol"; import { SchemaManager } from "./SchemaManager.sol"; +import { AttestationManager } from "./AttestationManager.sol"; +import { IRegistry } from "../IRegistry.sol"; import { StubLib } from "../lib/StubLib.sol"; -contract Attestation is ResolverManager, SchemaManager { - using StubLib for AttestationRecord; - using StubLib for AttestationRecord[]; - - error DifferentResolvers(); - - mapping(address module => mapping(address attester => AttestationRecord attestation)) internal - _moduleToAttesterToAttestations; - - function attest(SchemaUID schemaUID, AttestationRequestData calldata request) external { +abstract contract Attestation is IRegistry, AttestationManager { + function attest(SchemaUID schemaUID, AttestationRequest calldata request) external { _attest(msg.sender, schemaUID, request); } - function attest(SchemaUID schemaUID, AttestationRequestData[] calldata requests) external { + function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external { _attest(msg.sender, schemaUID, requests); } - function revoke(address module) external { } - - function _revoke(address attester, address module) internal { - _storeRevocation(attester, module); + function revoke(RevocationRequest calldata request) external { + _revoke(msg.sender, request); } - function _attest( - address attester, - SchemaUID schemaUID, - AttestationRequestData calldata request - ) - internal - { - (AttestationRecord memory record, ResolverUID resolverUID) = _storeAttestation({ - schemaUID: schemaUID, - attester: attester, - attestationRequestData: request - }); - - record.requireExternalSchemaValidation({ schema: schema[schemaUID] }); - record.requireExternalResolverCheck({ resolver: resolver[resolverUID] }); + function revoke(RevocationRequest[] calldata requests) external { + _revoke(msg.sender, requests); } - function _attest( - address attester, - SchemaUID schemaUID, - AttestationRequestData[] calldata requests + function readAttestation( + address module, + address attester ) - internal + external + view + returns (AttestationRecord memory attestation) { - uint256 length = requests.length; - AttetationRecord[] memory records = new AttestationRecord[](length); - // loop will check that the batched attestation is made ONLY for the same resolver - // @dev if you want to use different resolvers, make different attestation calls - ResolverUID resolverUID; - for (uint256 i; i < length; i++) { - ResolverUID resolverUID_cache; - (records[i], resolverUID_cache) = _storeAttestation({ - schemaUID: schemaUID, - attester: attester, - attestationRequestData: requests[i] - }); - // cache the first resolverUID and compare it to the rest - if (i == 0) resolverUID = resolverUID_cache; - else if (resolverUID_cache != resolverUID) revert DiffernetResolvers(); - } - - records.requireExternalSchemaValidation({ schema: schema[schemaUID] }); - records.requireExternalResolverCheck({ resolver: resolver[resolverUID] }); + attestation = _getAttestation(module, attester); } - function _storeAttestation( - SchemaUID schemaUID, - address attester, - AttestationRequestData calldata attestationRequestData + function readAttestations( + address module, + address[] calldata attesters ) - internal - returns (AttestationRecord memory record, ResolverUID resolverUID) + external + view + returns (AttestationRecord[] memory attestations) { - AttestationRecord storage record = _moduleToAttesterToAttestations[module][attester]; - uint48 timeNow = _time(); - // Ensure that either no expiration time was set or that it was set in the future. - if ( - attestationRequestData.expirationTime != ZERO_TIMESTAMP - && attestationRequestData.expirationTime <= timeNow - ) { - revert InvalidExpirationTime(); - } - // caching module address. - address module = attestationRequestData.moduleAddr; - ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); - - // Ensure that attestation is for module that was registered. - if (moduleRecord.implementation == ZERO_ADDRESS) { - revert InvalidAttestation(); - } - resolverUID = moduleRecord.resolverUID; - - // get salt used for SSTORE2 to avoid collisions during CREATE2 - bytes32 attestationSalt = AttestationLib.attestationSalt(attester, module); - AttestationDataRef sstore2Pointer = writeAttestationData({ - attestationData: attestationRequestData.data, - salt: attestationSalt - }); - - // SSTORE attestation on registry storage - record = AttestationRecord({ - schemaUID: schemaUID, - moduleAddr: module, - attester: attester, - time: timeNow, - expirationTime: attestationRequestData.expirationTime, - revocationTime: uint48(ZERO_TIMESTAMP), - dataPointer: sstore2Pointer - }); - - emit Attested(module, attester, schemaUID, sstore2Pointer); - } - - function _storeRevocation( - address attester, - address moduleAddr - ) - internal - returns (AttestationRecord memory attestationRecord, ResolverUID resolverUID) - { - AttestationRecord storage attestation = - _moduleToAttesterToAttestations[moduleAddr][attester]; - - // Ensure that we aren't attempting to revoke a non-existing attestation. - if (AttestationDataRef.unwrap(attestation.dataPointer) == ZERO_ADDRESS) { - revert NotFound(); - } - - // Allow only original attesters to revoke their attestations. - if (attestation.attester != revoker) { - revert AccessDenied(); - } - - // Ensure that we aren't trying to revoke the same attestation twice. - if (attestation.revocationTime != ZERO_TIMESTAMP) { - revert AlreadyRevoked(); + uint256 length = attesters.length; + attestations = new AttestationRecord[](length); + for (uint256 i; i < length; i++) { + attestations[i] = _getAttestation(module, attesters[i]); } - - // set revocation time to NOW - attestation.revocationTime = _time(); - emit Revoked({ - moduleAddr: attestation.moduleAddr, - revoker: revoker, - schema: attestation.schemaUID - }); - resolverUID = _getModule({ moduleAddress: moduleAddr }).resolverUID; } } diff --git a/src/simple/AttestationManager.sol b/src/simple/AttestationManager.sol new file mode 100644 index 00000000..348f5ee0 --- /dev/null +++ b/src/simple/AttestationManager.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { + AttestationRequest, + AttestationRecord, + AttestationDataRef, + RevocationRequest, + ModuleRecord, + SchemaUID, + ResolverUID, + ModuleType, + PackedModuleTypes +} from "../DataTypes.sol"; +import { ResolverManager } from "./ResolverManager.sol"; +import { SchemaManager } from "./SchemaManager.sol"; +import { ModuleManager } from "./ModuleManager.sol"; +import { TrustManager } from "./TrustManager.sol"; +import { StubLib } from "../lib/StubLib.sol"; +import { AttestationLib } from "../lib/AttestationLib.sol"; +import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; + +import { EMPTY_RESOLVER_UID, ZERO_ADDRESS, ZERO_TIMESTAMP, _time } from "../Common.sol"; +import { IRegistry } from "../IRegistry.sol"; + +abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, SchemaManager { + using StubLib for *; + using AttestationLib for *; // TODO: specify what + using ModuleTypeLib for ModuleType[]; + + mapping(address module => mapping(address attester => AttestationRecord attestation)) internal + _moduleToAttesterToAttestations; + + function _revoke(address attester, RevocationRequest calldata request) internal { + (AttestationRecord memory record, ResolverUID resolverUID) = + _storeRevocation(attester, request); + record.requireExternalResolverCheck({ resolver: resolvers[resolverUID] }); + } + + function _revoke(address attester, RevocationRequest[] calldata requests) internal { + uint256 length = requests.length; + AttestationRecord[] memory records = new AttestationRecord[](length); + ResolverUID resolverUID; + for (uint256 i; i < length; i++) { + ResolverUID resolverUID_cache; + (records[i], resolverUID_cache) = _storeRevocation(attester, requests[i]); + if (i == 0) resolverUID = resolverUID_cache; + else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); + } + + // No schema validation required during revocation. the attestation data was already checked against + + // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? + records.requireExternalResolverCheck({ resolver: resolvers[resolverUID] }); + } + + function _attest( + address attester, + SchemaUID schemaUID, + AttestationRequest calldata request + ) + internal + { + (AttestationRecord memory record, ResolverUID resolverUID) = + _storeAttestation({ schemaUID: schemaUID, attester: attester, request: request }); + + record.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); + record.requireExternalResolverCheck({ resolver: resolvers[resolverUID] }); + } + + function _attest( + address attester, + SchemaUID schemaUID, + AttestationRequest[] calldata requests + ) + internal + { + uint256 length = requests.length; + AttestationRecord[] memory records = new AttestationRecord[](length); + // loop will check that the batched attestation is made ONLY for the same resolver + // @dev if you want to use different resolvers, make different attestation calls + ResolverUID resolverUID; + for (uint256 i; i < length; i++) { + ResolverUID resolverUID_cache; + (records[i], resolverUID_cache) = _storeAttestation({ + schemaUID: schemaUID, + attester: attester, + request: requests[i] + }); + // cache the first resolverUID and compare it to the rest + if (i == 0) resolverUID = resolverUID_cache; + else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); + } + + records.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); + records.requireExternalResolverCheck({ resolver: resolvers[resolverUID] }); + } + + function _storeAttestation( + SchemaUID schemaUID, + address attester, + AttestationRequest calldata request + ) + internal + returns (AttestationRecord memory record, ResolverUID resolverUID) + { + uint48 timeNow = _time(); + // Ensure that either no expiration time was set or that it was set in the future. + if (request.expirationTime != ZERO_TIMESTAMP && request.expirationTime <= timeNow) { + revert InvalidExpirationTime(); + } + // caching module address. + address module = request.moduleAddr; + ModuleRecord storage moduleRecord = _modules[request.moduleAddr]; + // Ensure that attestation is for module that was registered. + if (moduleRecord.resolverUID != EMPTY_RESOLVER_UID) { + revert InvalidAttestation(); + } + resolverUID = moduleRecord.resolverUID; + + // get salt used for SSTORE2 to avoid collisions during CREATE2 + bytes32 attestationSalt = attester.sstore2Salt(module); + AttestationDataRef sstore2Pointer = request.sstore2(attestationSalt); + + record = AttestationRecord({ + time: timeNow, + expirationTime: request.expirationTime, + revocationTime: uint48(ZERO_TIMESTAMP), + moduleTypes: request.moduleTypes.pack(), + schemaUID: schemaUID, + moduleAddr: module, + attester: attester, + dataPointer: sstore2Pointer + }); + // SSTORE attestation to registry storage + _moduleToAttesterToAttestations[request.moduleAddr][attester] = record; + + emit Attested(module, attester, schemaUID, sstore2Pointer); + } + + function _storeRevocation( + address revoker, + RevocationRequest calldata request + ) + internal + returns (AttestationRecord memory attestation, ResolverUID resolverUID) + { + AttestationRecord storage attestationStorage = + _moduleToAttesterToAttestations[request.moduleAddr][revoker]; + // SSLOAD entire record. This will later be passed to the resolver + attestation = attestationStorage; + + // Ensure that we aren't attempting to revoke a non-existing attestation. + if (AttestationDataRef.unwrap(attestation.dataPointer) == ZERO_ADDRESS) { + revert NotFound(); + } + + // Allow only original attesters to revoke their attestations. + if (attestation.attester != revoker) { + revert AccessDenied(); + } + + // Ensure that we aren't trying to revoke the same attestation twice. + if (attestation.revocationTime != ZERO_TIMESTAMP) { + revert AlreadyRevoked(); + } + + // set revocation time to NOW + emit Revoked({ + moduleAddr: attestation.moduleAddr, + revoker: revoker, + schema: attestation.schemaUID + }); + // resolverUID = + + resolverUID = _modules[attestation.moduleAddr].resolverUID; + attestationStorage.revocationTime = _time(); + } + + function _getAttestation( + address module, + address attester + ) + internal + view + override + returns (AttestationRecord storage) + { + return _moduleToAttesterToAttestations[module][attester]; + } +} diff --git a/src/simple/ModuleManager.sol b/src/simple/ModuleManager.sol new file mode 100644 index 00000000..cb9b1d20 --- /dev/null +++ b/src/simple/ModuleManager.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { CREATE3 } from "solady/utils/CREATE3.sol"; + +import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; +import { StubLib } from "../lib/StubLib.sol"; +import { IResolver } from "../external/IResolver.sol"; + +import { InvalidResolver, _isContract, EMPTY_RESOLVER_UID, ZERO_ADDRESS } from "../Common.sol"; +import { ResolverRecord, ModuleRecord, ResolverUID } from "../DataTypes.sol"; +import { ResolverManager } from "./ResolverManager.sol"; +import { IRegistry } from "../IRegistry.sol"; + +/** + * @title Module + * + * @dev The Module contract serves as a component in a larger system for handling smart contracts or "modules" + * within a blockchain ecosystem. This contract inherits from the IModule interface + * + * @dev The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract + * that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, + * sender address, deploy parameters hash, and additional metadata are stored in + * a struct and mapped to the module's address in + * the `_modules` mapping for easy access and management. + * + * @dev In conclusion, the Module is a central part of a system to manage, + * deploy, and interact with a set of smart contracts + * in a structured and controlled manner. + * + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + */ +abstract contract ModuleManager is IRegistry, ResolverManager { + using ModuleDeploymentLib for bytes; + using ModuleDeploymentLib for address; + using StubLib for *; + + mapping(address moduleAddress => ModuleRecord moduleRecord) internal _modules; + + function deploy( + bytes32 salt, + ResolverUID resolverUID, + bytes calldata code, + bytes calldata deployParams, + bytes calldata metadata + ) + external + payable + returns (address moduleAddr) + { + ResolverRecord storage resolver = resolvers[resolverUID]; + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); + + // address predictedModuleAddress = code.calculateAddress(deployParams, salt); + + (moduleAddr,,) = code.deploy(deployParams, salt, msg.value); + // _storeModuleRecord() will check if module is already registered, + // which should prevent reentry to any deploy function + ModuleRecord memory record = _storeModuleRecord({ + moduleAddress: moduleAddr, // TODO: is this reentrancy? + sender: msg.sender, + resolverUID: resolverUID, + metadata: metadata + }); + record.requireExternalResolverCheck(resolver); + } + + function register( + ResolverUID resolverUID, + address moduleAddress, + bytes calldata metadata + ) + external + { + ResolverRecord storage resolver = resolvers[resolverUID]; + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); + + ModuleRecord memory record = _storeModuleRecord({ + moduleAddress: moduleAddress, + sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function + resolverUID: resolverUID, + metadata: metadata + }); + record.requireExternalResolverCheck(resolver); + } + + function _storeModuleRecord( + address moduleAddress, + address sender, + ResolverUID resolverUID, + bytes calldata metadata + ) + internal + returns (ModuleRecord memory moduleRegistration) + { + // ensure that non-zero resolverUID was provided + if (resolverUID == EMPTY_RESOLVER_UID) revert InvalidDeployment(); + // ensure moduleAddress is not already registered + if (_modules[moduleAddress].resolverUID != EMPTY_RESOLVER_UID) { + revert AlreadyRegistered(moduleAddress); + } + // revert if moduleAddress is NOT a contract + if (!_isContract(moduleAddress)) revert InvalidDeployment(); + + // Store module metadata in _modules mapping + moduleRegistration = + ModuleRecord({ resolverUID: resolverUID, sender: sender, metadata: metadata }); + + // Store module record in _modules mapping + _modules[moduleAddress] = moduleRegistration; + + // Emit ModuleRegistration event + emit ModuleRegistration(moduleAddress, sender, ResolverUID.unwrap(resolverUID)); + } +} diff --git a/src/simple/ResolverManager.sol b/src/simple/ResolverManager.sol index 394e9034..21134ef2 100644 --- a/src/simple/ResolverManager.sol +++ b/src/simple/ResolverManager.sol @@ -1,9 +1,15 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; +import { ResolverRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; +import { ZERO_ADDRESS, AccessDenied } from "../Common.sol"; +import { IResolver } from "../external/IResolver.sol"; +import { UIDLib } from "../lib/Helpers.sol"; +import { IRegistry } from "../IRegistry.sol"; + +abstract contract ResolverManager is IRegistry { + using UIDLib for ResolverRecord; -abstract contract ResolverManager { mapping(ResolverUID uid => ResolverRecord resolver) public resolvers; /** @@ -18,9 +24,6 @@ abstract contract ResolverManager { _; } - /** - * @inheritdoc IExternalResolver - */ function registerResolver(IResolver _resolver) external returns (ResolverUID uid) { if (address(_resolver) == ZERO_ADDRESS) revert InvalidResolver(); @@ -42,9 +45,6 @@ abstract contract ResolverManager { emit SchemaResolverRegistered(uid, msg.sender); } - /** - * @inheritdoc IExternalResolver - */ function setResolver(ResolverUID uid, IResolver resolver) external onlyResolverOwner(uid) { ResolverRecord storage referrer = resolvers[uid]; referrer.resolver = resolver; diff --git a/src/simple/SchemaManager.sol b/src/simple/SchemaManager.sol index e923911a..36e38efb 100644 --- a/src/simple/SchemaManager.sol +++ b/src/simple/SchemaManager.sol @@ -1,10 +1,17 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; +import { SchemaRecord, ResolverRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; +import { ISchemaValidator } from "../external/ISchemaValidator.sol"; +import { UIDLib } from "../lib/Helpers.sol"; -abstract contract SchemaManager { +import { ZERO_TIMESTAMP, _time } from "../Common.sol"; +import { IRegistry } from "../IRegistry.sol"; + +abstract contract SchemaManager is IRegistry { + using UIDLib for SchemaRecord; // The global mapping between schema records and their IDs. + mapping(SchemaUID uid => SchemaRecord schemaRecord) internal schemas; function registerSchema( @@ -21,10 +28,10 @@ abstract contract SchemaManager { // Computing a unique ID for the schema using its properties uid = schemaRecord.getUID(); - if (_schemas[uid].registeredAt != ZERO_TIMESTAMP) revert AlreadyExists(); + if (schemas[uid].registeredAt != ZERO_TIMESTAMP) revert AlreadyExists(); // Storing schema in the _schemas mapping - _schemas[uid] = schemaRecord; + schemas[uid] = schemaRecord; emit SchemaRegistered(uid, msg.sender); } diff --git a/src/simple/SignedAttestation.sol b/src/simple/SignedAttestation.sol index 9589b99c..26022127 100644 --- a/src/simple/SignedAttestation.sol +++ b/src/simple/SignedAttestation.sol @@ -2,27 +2,31 @@ pragma solidity ^0.8.19; import { Attestation } from "./Attestation.sol"; +import { AttestationRequest, RevocationRequest, SchemaUID } from "../DataTypes.sol"; import { AttestationLib } from "../lib/AttestationLib.sol"; +import { EIP712 } from "solady/utils/EIP712.sol"; +import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; +import { IRegistry } from "../IRegistry.sol"; -contract SignedAttestation is Attestation { - using AttestationLib for AttestationRequestData; - - error InvalidSignature(); +contract SignedAttestation is IRegistry, Attestation, EIP712 { + using AttestationLib for AttestationRequest; + using AttestationLib for AttestationRequest[]; + using AttestationLib for RevocationRequest; + using AttestationLib for RevocationRequest[]; mapping(address attester => uint256 nonce) public attesterNonce; function attest( SchemaUID schemaUID, - AttestationRequestData calldata request, address attester, + AttestationRequest calldata request, bytes calldata signature ) external - payable { // verify signature uint256 nonce = ++attesterNonce[attester]; - bytes32 digest = request.digest(nonce, schemaUID); + bytes32 digest = _hashTypedData(request.hash(nonce)); bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); if (!valid) revert InvalidSignature(); @@ -31,25 +35,58 @@ contract SignedAttestation is Attestation { function attest( SchemaUID schemaUID, - AttestationRequestData[] calldata requests, address attester, - bytes[] calldata signature // TODO: should we maybe sign all requests at once? + AttestationRequest[] calldata requests, + bytes calldata signature ) external - payable { - // verify all signatures. Iterate nonces and digests - uint256 nonce = attesterNonce[attester]; - uint256 length = requests.length; - if (length != signature.length) revert InvalidSignature(); - for (uint256 i; i < length; i++) { - bytes32 digest = requests[i].digest(nonce + i); - bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature[i]); - if (!valid) revert InvalidSignature(); - } - // update nonce - attesterNonce[attester] += length; + uint256 nonce = ++attesterNonce[attester]; + bytes32 digest = _hashTypedData(requests.hash(nonce)); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); + if (!valid) revert InvalidSignature(); _attest(attester, schemaUID, requests); } + + function revoke( + address attester, + RevocationRequest calldata request, + bytes calldata signature + ) + external + { + uint256 nonce = ++attesterNonce[attester]; + bytes32 digest = _hashTypedData(request.hash(nonce)); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); + if (!valid) revert InvalidSignature(); + + _revoke(attester, request); + } + + function revoke( + address attester, + RevocationRequest[] calldata requests, + bytes calldata signature + ) + external + { + uint256 nonce = ++attesterNonce[attester]; + bytes32 digest = _hashTypedData(requests.hash(nonce)); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); + if (!valid) revert InvalidSignature(); + + _revoke(attester, requests); + } + + function _domainNameAndVersion() + internal + view + virtual + override + returns (string memory name, string memory version) + { + name = "RhinestoneRegistry"; + version = "v1.0"; + } } diff --git a/src/simple/TrustManager.sol b/src/simple/TrustManager.sol new file mode 100644 index 00000000..f2970329 --- /dev/null +++ b/src/simple/TrustManager.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { AttestationRecord, PackedModuleTypes, ModuleType } from "../DataTypes.sol"; +import { ZERO_TIMESTAMP } from "../Common.sol"; +import { IRegistry } from "../IRegistry.sol"; +import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; + +/** + * @title TrustManager + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + * Implements EIP-7484 to query attestations stored in the registry. + * @dev This contract is abstract and provides utility functions to query attestations. + */ +abstract contract TrustManager is IRegistry { + using ModuleTypeLib for PackedModuleTypes; + + event NewAttesters(); + // packed struct to allow for efficient storage. + // if only one attester is trusted, it only requires 1 SLOAD + + struct TrustedAttesters { + uint8 attesterCount; + uint8 threshold; + address attester; + mapping(address attester => address linkedAttester) linkedAttesters; + } + + mapping(address account => TrustedAttesters attesters) internal _accountToAttester; + + function setAttester(uint8 threshold, address[] calldata attesters) external { + uint256 attestersLength = attesters.length; + if (attestersLength == 0) revert(); + // sort attesters + + TrustedAttesters storage _att = _accountToAttester[msg.sender]; + // threshold cannot be greater than the number of attesters + if (threshold > attestersLength) { + threshold = uint8(attestersLength); + } + // + _att.attesterCount = uint8(attestersLength); + _att.threshold = threshold; + _att.attester = attesters[0]; + + attestersLength--; + for (uint256 i; i < attestersLength; i++) { + _att.linkedAttesters[attesters[i]] = attesters[i + 1]; + } + } + + function check(address module) external view { } + + function checkForAccount(address smartAccount, address module) external view { } + + function check(address module, ModuleType moduleType) external view { + _check(msg.sender, module, moduleType); + } + + function checkForAccount( + address smartAccount, + address module, + ModuleType moduleType + ) + external + view + { + _check(smartAccount, module, moduleType); + } + + function _check(address smartAccount, address module, ModuleType moduleType) internal view { + TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; + // SLOAD from one slot + uint256 attesterCount = trustedAttesters.attesterCount; + uint256 threshold = trustedAttesters.threshold; + address attester = trustedAttesters.attester; + + // smart account has no trusted attesters set + if (attester == address(0) && threshold != 0) { + revert NoAttestersFound(); + } + // smart account only has ONE trusted attester + // use this condition to save gas + else if (threshold == 1) { + AttestationRecord storage record = + _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(moduleType, record); + } + // smart account has more than one trusted attester + else { + // loop though list and check if the attestation is valid + AttestationRecord storage record = + _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(moduleType, record); + threshold--; + for (uint256 i = 1; i < attesterCount; i++) { + // get next attester from linked List + attester = trustedAttesters.linkedAttesters[attester]; + record = _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(moduleType, record); + // if threshold reached, exit loop + if (threshold == 0) return; + } + } + } + + function _requireValidAttestation( + ModuleType expectedType, + AttestationRecord storage record + ) + internal + view + { + // cache values + uint256 attestedAt = record.time; + uint256 expirationTime = record.expirationTime; + uint256 revocationTime = record.revocationTime; + PackedModuleTypes packedModuleType = record.moduleTypes; + + if (attestedAt == ZERO_TIMESTAMP) { + revert AttestationNotFound(); + } + + if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { + revert AttestationNotFound(); + } + + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(record.attester); + } + if (!packedModuleType.isType(expectedType)) { + revert InvalidModuleType(); + } + } + + function _getAttestation( + address module, + address attester + ) + internal + view + virtual + returns (AttestationRecord storage attestation); +} diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol deleted file mode 100644 index 12bde519..00000000 --- a/test/Attestation.t.sol +++ /dev/null @@ -1,485 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { Test } from "forge-std/Test.sol"; -import { IAttestation, InvalidSchema, NotFound, AccessDenied } from "../src/base/Attestation.sol"; - -import { ERC1271Attester, EXPECTED_SIGNATURE } from "./utils/ERC1271Attester.sol"; - -import { - BaseTest, - RegistryTestLib, - RegistryInstance, - console2, - AttestationRequestData, - MockModuleWithArgs, - SchemaUID, - ISchemaValidator -} from "./utils/BaseTest.t.sol"; - -import { - AttestationRecord, - MultiSignedAttestationRequest, - MultiAttestationRequest, - MultiRevocationRequest, - RevocationRequestData, - RevocationRequest -} from "../src/DataTypes.sol"; - -struct SampleAttestation { - address[] dependencies; - string comment; - string url; - bytes32 hash; - uint256 severity; -} - -/// @title AttestationTest -/// @author zeroknots -contract AttestationTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - function setUp() public virtual override { - super.setUp(); - } - - function testAttest() public { - instance.mockAttestation(defaultSchema1, defaultModule1); - } - - function testAttest__RevertWhen__InvalidExpirationTime() public { - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(1), - data: abi.encode(true), - value: 0 - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidExpirationTime.selector)); - instance.newAttestation(defaultSchema1, attData); - } - - function testAttest__RevertWhen__ZeroImplementation() public { - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: address(0x69), - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.newAttestation(defaultSchema1, attData); - } - - function testAttest__RevertWhen__InvalidSchema() public { - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.newAttestation(SchemaUID.wrap(0), attData); - } - - function testAttest__RevertWhen__ValidatorSaysInvalidAttestation() public { - SchemaUID schemaId = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.newAttestation(schemaId, attData); - } - - function testAttest__With__LargeAttestation() public { - SampleAttestation memory sample = SampleAttestation({ - dependencies: new address[](20), - comment: "This is a test!!", - url: "https://www.rhinestone.wtf", - hash: bytes32(0), - severity: 0 - }); - bytes memory data = abi.encode(sample); - - console2.log(data.length); - - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: data, - value: 0 - }); - - instance.newAttestation(defaultSchema1, attData); - } - - function testMultiAttest() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiAttestationRequest[] memory reqs = new MultiAttestationRequest[](1); - MultiAttestationRequest memory req1 = - MultiAttestationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__InvalidSchema() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiAttestationRequest[] memory reqs = new MultiAttestationRequest[](1); - MultiAttestationRequest memory req1 = - MultiAttestationRequest({ schemaUID: SchemaUID.wrap(0), data: attArray }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() public { - SchemaUID schemaId = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiAttestationRequest[] memory reqs = new MultiAttestationRequest[](1); - MultiAttestationRequest memory req1 = - MultiAttestationRequest({ schemaUID: schemaId, data: attArray }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__InvalidExpirationTime() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(1), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiAttestationRequest[] memory reqs = new MultiAttestationRequest[](1); - MultiAttestationRequest memory req1 = - MultiAttestationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidExpirationTime.selector)); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__ZeroImplementation() public { - SchemaUID schemaId = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: address(0x69), - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiAttestationRequest[] memory reqs = new MultiAttestationRequest[](1); - MultiAttestationRequest memory req1 = - MultiAttestationRequest({ schemaUID: schemaId, data: attArray }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.registry.multiAttest(reqs); - } - - function testRevoke() public { - address attester = address(this); - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.revokeAttestation(defaultModule1, defaultSchema1, attester); - - AttestationRecord memory attestation = - instance.registry.findAttestation(defaultModule1, attester); - assertTrue(attestation.revocationTime != 0); - } - - function testRevoke__RevertWhen__AttestationNotFound() public { - address attester = address(this); - vm.expectRevert(abi.encodeWithSelector(NotFound.selector)); - instance.revokeAttestation(defaultModule1, defaultSchema1, attester); - } - - function testRevoke__RevertWhen__InvalidSchema() public { - address attester = address(this); - instance.mockAttestation(defaultSchema1, defaultModule1); - - vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.revokeAttestation(defaultModule1, SchemaUID.wrap(0), attester); - } - - function testRevoke__RevertWhen__NotOriginalAttester() public { - address attester = address(this); - address notAttester = makeAddr("notAttester"); - - instance.mockAttestation(defaultSchema1, defaultModule1); - - RevocationRequestData memory revoke = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequest memory req = - RevocationRequest({ schemaUID: defaultSchema1, data: revoke }); - - vm.startPrank(notAttester); - vm.expectRevert(abi.encodeWithSelector(AccessDenied.selector)); - instance.registry.revoke(req); - vm.stopPrank(); - } - - function testRevoke__RevertWhen__AlreadyRevoked() public { - address attester = address(this); - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.revokeAttestation(defaultModule1, defaultSchema1, attester); - - AttestationRecord memory attestation = - instance.registry.findAttestation(defaultModule1, attester); - assertTrue(attestation.revocationTime != 0); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.AlreadyRevoked.selector)); - instance.revokeAttestation(defaultModule1, defaultSchema1, attester); - } - - function testMultiRevoke() public { - address attester = address(this); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.mockAttestation(defaultSchema1, anotherModule); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - instance.registry.multiRevoke(reqs); - - AttestationRecord memory attestation = - instance.registry.findAttestation(defaultModule1, attester); - assertTrue(attestation.revocationTime != 0); - - AttestationRecord memory attestation2 = - instance.registry.findAttestation(anotherModule, attester); - assertTrue(attestation2.revocationTime != 0); - } - - function testMultiRevoke__RevertWhen__InvalidSchema() public { - address attester = address(this); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.mockAttestation(defaultSchema1, anotherModule); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: SchemaUID.wrap(0), data: attArray }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.registry.multiRevoke(reqs); - } - - function testMultiRevoke__RevertWhen__AttestationNotFound() public { - address attester = address(this); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(NotFound.selector)); - instance.registry.multiRevoke(reqs); - } - - function testMultiRevoke__RevertWhen__NotOriginalAttester() public { - address attester = address(this); - address notAttester = makeAddr("notAttester"); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.mockAttestation(defaultSchema1, anotherModule); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - vm.startPrank(notAttester); - vm.expectRevert(abi.encodeWithSelector(AccessDenied.selector)); - instance.registry.multiRevoke(reqs); - vm.stopPrank(); - } - - function testMultiRevoke__RevertWhen__AlreadyRevoked() public { - address attester = address(this); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.mockAttestation(defaultSchema1, anotherModule); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - instance.registry.multiRevoke(reqs); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.AlreadyRevoked.selector)); - instance.registry.multiRevoke(reqs); - } -} diff --git a/test/AttestationDelegation.t.sol b/test/AttestationDelegation.t.sol deleted file mode 100644 index 32e46782..00000000 --- a/test/AttestationDelegation.t.sol +++ /dev/null @@ -1,691 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { Test } from "forge-std/Test.sol"; -import { - Attestation, - IAttestation, - InvalidSchema, - NotFound, - AccessDenied -} from "../src/base/Attestation.sol"; -import { InvalidSignature, InvalidLength } from "../src/Common.sol"; -import { ERC1271Attester, EXPECTED_SIGNATURE } from "./utils/ERC1271Attester.sol"; - -import { - BaseTest, - RegistryTestLib, - RegistryInstance, - console2, - AttestationRequestData, - SignedAttestationRequest, - MockModuleWithArgs, - ResolverUID, - SchemaUID, - ISchemaValidator -} from "./utils/BaseTest.t.sol"; - -import { - AttestationRecord, - MultiAttestationRequest, - MultiSignedAttestationRequest, - MultiRevocationRequest, - RevocationRequestData, - SignedRevocationRequest, - MultiSignedRevocationRequest -} from "../src/DataTypes.sol"; - -struct SampleAttestation { - address[] dependencies; - string comment; - string url; - bytes32 hash; - uint256 severity; -} - -/// @title AttestationDelegationTest -/// @author kopy-kat -contract AttestationDelegationTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - function setUp() public virtual override { - super.setUp(); - } - - function testAttest() public { - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - } - - function testAttest__RevertWhen__InvalidExpirationTime() public { - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(1), - data: abi.encode(true), - value: 0 - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidExpirationTime.selector)); - instance.newSignedAttestation(defaultSchema1, auth1k, attData); - } - - function testAttest__RevertWhen__ZeroImplementation() public { - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: address(0x69), - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.newSignedAttestation(defaultSchema1, auth1k, attData); - } - - function testAttest__RevertWhen__InvalidSchema() public { - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.newSignedAttestation(SchemaUID.wrap(0), auth1k, attData); - } - - function testAttest__RevertWhen__ValidatorSaysInvalidAttestation() public { - SchemaUID schemaUID = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.newSignedAttestation(schemaUID, auth1k, attData); - } - - function testAttest__RevertWhen_InvalidSignature() public { - SchemaUID schemaUID = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - bytes memory signature = ""; - SignedAttestationRequest memory req = SignedAttestationRequest({ - schemaUID: schemaUID, - data: attData, - signature: signature, - attester: vm.addr(auth1k) - }); - vm.expectRevert(abi.encodeWithSelector(InvalidSignature.selector)); - instance.registry.attest(req); - } - - function testAttest__With__LargeAttestation() public { - SampleAttestation memory sample = SampleAttestation({ - dependencies: new address[](20), - comment: "This is a test!!", - url: "https://www.rhinestone.wtf", - hash: bytes32(0), - severity: 0 - }); - bytes memory data = abi.encode(sample); - - console2.log(data.length); - - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: data, - value: 0 - }); - - instance.newSignedAttestation(defaultSchema1, auth1k, attData); - } - - function testMultiAttest() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - bytes[] memory sigs = instance.signAttestation(defaultSchema1, auth1k, attArray); - - MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); - MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ - schemaUID: defaultSchema1, - data: attArray, - signatures: sigs, - attester: vm.addr(auth1k) - }); - reqs[0] = req1; - - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__InvalidSchema() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - bytes[] memory sigs = instance.signAttestation(SchemaUID.wrap(0), auth1k, attArray); - - MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); - MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ - schemaUID: SchemaUID.wrap(0), - data: attArray, - signatures: sigs, - attester: vm.addr(auth1k) - }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() public { - SchemaUID schemaUID = instance.registerSchema("", ISchemaValidator(falseSchemaValidator)); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - bytes[] memory sigs = instance.signAttestation(schemaUID, auth1k, attArray); - - MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); - MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ - schemaUID: schemaUID, - data: attArray, - signatures: sigs, - attester: vm.addr(auth1k) - }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__InvalidExpirationTime() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(1), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - bytes[] memory sigs = instance.signAttestation(defaultSchema1, auth1k, attArray); - - MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); - MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ - schemaUID: defaultSchema1, - data: attArray, - signatures: sigs, - attester: vm.addr(auth1k) - }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidExpirationTime.selector)); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__InvalidLength__DataLength() public { - AttestationRequestData[] memory attArray = new AttestationRequestData[](0); - - bytes[] memory sigs = new bytes[](0); - - MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); - MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ - schemaUID: defaultSchema1, - data: attArray, - signatures: sigs, - attester: vm.addr(auth1k) - }); - reqs[0] = req1; - - vm.expectRevert(); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__InvalidLength__SignatureLength() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(1), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - bytes[] memory sigs = new bytes[](0); - - MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); - MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ - schemaUID: defaultSchema1, - data: attArray, - signatures: sigs, - attester: vm.addr(auth1k) - }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(InvalidLength.selector)); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__InvalidSignature() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(1), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - bytes[] memory sigs = new bytes[](2); - sigs[0] = ""; - sigs[1] = ""; - - MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); - MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ - schemaUID: defaultSchema1, - data: attArray, - signatures: sigs, - attester: vm.addr(auth1k) - }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(InvalidSignature.selector)); - instance.registry.multiAttest(reqs); - } - - function testMultiAttest__RevertWhen__ZeroImplementation() public { - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - AttestationRequestData memory attData1 = AttestationRequestData({ - moduleAddr: address(0x69), - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData memory attData2 = AttestationRequestData({ - moduleAddr: anotherModule, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - AttestationRequestData[] memory attArray = new AttestationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - bytes[] memory sigs = instance.signAttestation(defaultSchema1, auth1k, attArray); - - MultiSignedAttestationRequest[] memory reqs = new MultiSignedAttestationRequest[](1); - MultiSignedAttestationRequest memory req1 = MultiSignedAttestationRequest({ - schemaUID: defaultSchema1, - data: attArray, - signatures: sigs, - attester: vm.addr(auth1k) - }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - instance.registry.multiAttest(reqs); - } - - function testRevoke() public { - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - instance.signedRevokeAttestation(defaultModule1, defaultSchema1, auth1k); - - AttestationRecord memory attestation = - instance.registry.findAttestation(defaultModule1, vm.addr(auth1k)); - assertTrue(attestation.revocationTime != 0); - } - - function testRevoke__RevertWhen__InvalidSignature() public { - address attester = vm.addr(auth1k); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - SignedRevocationRequest memory req = SignedRevocationRequest({ - schemaUID: defaultSchema1, - data: attData1, - revoker: attester, - signature: "" - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidSignature.selector)); - instance.registry.revoke(req); - } - - function testRevoke__RevertWhen__AttestationNotFound() public { - address attester = vm.addr(auth1k); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - bytes memory sig = instance.signRevocation(defaultSchema1, auth1k, attData1); - - SignedRevocationRequest memory req = SignedRevocationRequest({ - schemaUID: defaultSchema1, - data: attData1, - revoker: attester, - signature: sig - }); - - vm.expectRevert(abi.encodeWithSelector(NotFound.selector)); - instance.registry.revoke(req); - } - - function testRevoke__RevertWhen__InvalidSchema() public { - address attester = vm.addr(auth1k); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - bytes memory sig = instance.signRevocation(SchemaUID.wrap(0), auth1k, attData1); - - SignedRevocationRequest memory req = SignedRevocationRequest({ - schemaUID: SchemaUID.wrap(0), - data: attData1, - revoker: attester, - signature: sig - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.registry.revoke(req); - } - - function testRevoke__RevertWhen__AlreadyRevoked() public { - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - address attester = vm.addr(auth1k); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - bytes memory sig = instance.signRevocation(defaultSchema1, auth1k, attData1); - - SignedRevocationRequest memory req = SignedRevocationRequest({ - schemaUID: defaultSchema1, - data: attData1, - revoker: attester, - signature: sig - }); - instance.registry.revoke(req); - - bytes memory sig2 = instance.signRevocation(defaultSchema1, auth1k, attData1); - - SignedRevocationRequest memory req2 = SignedRevocationRequest({ - schemaUID: defaultSchema1, - data: attData1, - revoker: attester, - signature: sig2 - }); - vm.expectRevert(abi.encodeWithSelector(IAttestation.AlreadyRevoked.selector)); - instance.registry.revoke(req2); - } - - function testMultiRevoke() public { - address attester = vm.addr(auth1k); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - instance.mockSignedAttestation(defaultSchema1, anotherModule, auth1k); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - bytes[] memory sigs = instance.signRevocation(defaultSchema1, auth1k, attArray); - - // RevocationRequestData[] memory attArray = new RevocationRequestData[]( - // 1 - // ); - // attArray[0] = attData1; - - // bytes[] memory sigs = instance.signRevocation(defaultSchema1, auth1k, attArray); - - MultiSignedRevocationRequest[] memory reqs = new MultiSignedRevocationRequest[](1); - MultiSignedRevocationRequest memory req1 = MultiSignedRevocationRequest({ - schemaUID: defaultSchema1, - data: attArray, - revoker: attester, - signatures: sigs - }); - reqs[0] = req1; - - instance.registry.multiRevoke(reqs); - - AttestationRecord memory attestation = - instance.registry.findAttestation(defaultModule1, attester); - assertTrue(attestation.revocationTime != 0); - - AttestationRecord memory attestation2 = - instance.registry.findAttestation(anotherModule, attester); - assertTrue(attestation2.revocationTime != 0); - } - - function testMultiRevoke__RevertWhen__InvalidSchema() public { - address attester = address(this); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.mockAttestation(defaultSchema1, anotherModule); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: SchemaUID.wrap(0), data: attArray }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(InvalidSchema.selector)); - instance.registry.multiRevoke(reqs); - } - - function testMultiRevoke__RevertWhen__AttestationNotFound() public { - address attester = address(this); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - vm.expectRevert(abi.encodeWithSelector(NotFound.selector)); - instance.registry.multiRevoke(reqs); - } - - function testMultiRevoke__RevertWhen__NotOriginalAttester() public { - address attester = address(this); - address notAttester = makeAddr("notAttester"); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.mockAttestation(defaultSchema1, anotherModule); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - vm.startPrank(notAttester); - vm.expectRevert(abi.encodeWithSelector(AccessDenied.selector)); - instance.registry.multiRevoke(reqs); - vm.stopPrank(); - } - - function testMultiRevoke__RevertWhen__AlreadyRevoked() public { - address attester = address(this); - address anotherModule = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1_234_819_239_123) - ); - - instance.mockAttestation(defaultSchema1, defaultModule1); - instance.mockAttestation(defaultSchema1, anotherModule); - - RevocationRequestData memory attData1 = - RevocationRequestData({ moduleAddr: defaultModule1, attester: attester, value: 0 }); - - RevocationRequestData memory attData2 = - RevocationRequestData({ moduleAddr: anotherModule, attester: attester, value: 0 }); - - RevocationRequestData[] memory attArray = new RevocationRequestData[](2); - attArray[0] = attData1; - attArray[1] = attData2; - - MultiRevocationRequest[] memory reqs = new MultiRevocationRequest[](1); - MultiRevocationRequest memory req1 = - MultiRevocationRequest({ schemaUID: defaultSchema1, data: attArray }); - reqs[0] = req1; - - instance.registry.multiRevoke(reqs); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.AlreadyRevoked.selector)); - instance.registry.multiRevoke(reqs); - } -} diff --git a/test/AttestationResolve.t.sol b/test/AttestationResolve.t.sol deleted file mode 100644 index 7c41fe33..00000000 --- a/test/AttestationResolve.t.sol +++ /dev/null @@ -1,404 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { BaseTest } from "./utils/BaseTest.t.sol"; -import { AttestationResolve, ResolverUID, SchemaUID } from "../src/base/AttestationResolve.sol"; -import { - AttestationRequest, - MultiAttestationRequest, - SignedAttestationRequest, - MultiSignedAttestationRequest, - RevocationRequest, - SignedRevocationRequest, - MultiSignedRevocationRequest, - MultiRevocationRequest, - IAttestation -} from "../src/interface/IAttestation.sol"; -import { ISchemaValidator } from "../src/interface/ISchema.sol"; -import { IResolver } from "../src/external/IResolver.sol"; -import { ResolverBase } from "../src/external/ResolverBase.sol"; -import { - AttestationRecord, - AttestationDataRef, - MultiAttestationRequest, - MultiSignedAttestationRequest, - MultiRevocationRequest, - SignedRevocationRequest, - MultiSignedRevocationRequest, - SchemaRecord, - ResolverRecord, - ModuleRecord -} from "../src/DataTypes.sol"; - -contract AttestationResolveInstance is AttestationResolve { - function resolveAttestation( - ResolverUID resolverUID, - AttestationRecord memory attestationRecord, - uint256 value, - bool isRevocation, - uint256 availableValue, - bool isLastAttestation - ) - public - returns (uint256) - { - return _requireExternalResolveAttestation( - resolverUID, attestationRecord, value, isRevocation, availableValue, isLastAttestation - ); - } - - function resolveAttestations( - ResolverUID resolverUID, - AttestationRecord[] memory attestationRecords, - uint256[] memory values, - bool isRevocation, - uint256 availableValue, - bool isLast - ) - public - returns (uint256) - { - return _requireExternalResolveAttestations( - resolverUID, attestationRecords, values, isRevocation, availableValue, isLast - ); - } - - // Required by AttestationResolve - mapping(SchemaUID => SchemaRecord) schemaRecords; - mapping(ResolverUID => ResolverRecord) resolverRecords; - mapping(address => ModuleRecord) moduleRecords; - - function _getSchema(SchemaUID schemaUID) - internal - view - override - returns (SchemaRecord storage) - { - return schemaRecords[schemaUID]; - } - - function addResolver(ResolverUID resolverUID, ResolverRecord memory resolverRecord) public { - resolverRecords[resolverUID] = resolverRecord; - } - - function getResolver(ResolverUID resolverUID) - public - view - override - returns (ResolverRecord memory) - { - return resolverRecords[resolverUID]; - } - - function _getModule(address moduleAddress) - internal - view - override - returns (ModuleRecord storage) - { - return moduleRecords[moduleAddress]; - } - - // Required by IAttestation - function attest(AttestationRequest calldata request) external payable { } - function multiAttest(MultiAttestationRequest[] calldata multiRequests) external payable { } - function attest(SignedAttestationRequest calldata signedRequest) external payable { } - function multiAttest(MultiSignedAttestationRequest[] calldata multiSignedRequests) - external - payable - { } - function revoke(RevocationRequest calldata request) external payable { } - function revoke(SignedRevocationRequest calldata request) external payable { } - function multiRevoke(MultiSignedRevocationRequest[] calldata multiSignedRequests) - external - payable - { } - function multiRevoke(MultiRevocationRequest[] calldata multiRequests) external payable { } -} - -contract FalseResolver is ResolverBase { - constructor(address rs) ResolverBase(rs) { } - - function onAttest( - AttestationRecord calldata attestation, - uint256 /*value*/ - ) - internal - view - override - returns (bool) - { - return false; - } - - function onRevoke( - AttestationRecord calldata attestation, - uint256 value - ) - internal - pure - override - returns (bool) - { - return false; - } - - function onModuleRegistration( - ModuleRecord calldata module, - uint256 value - ) - internal - override - returns (bool) - { - return false; - } -} - -contract PayableResolver is ResolverBase { - constructor(address rs) ResolverBase(rs) { } - - function onAttest( - AttestationRecord calldata attestation, - uint256 /*value*/ - ) - internal - view - override - returns (bool) - { - return true; - } - - function onRevoke( - AttestationRecord calldata attestation, - uint256 value - ) - internal - pure - override - returns (bool) - { - return true; - } - - function onModuleRegistration( - ModuleRecord calldata module, - uint256 value - ) - internal - override - returns (bool) - { - return true; - } - - function isPayable() public pure override returns (bool) { - return true; - } -} - -/// @title AttestationResolveTest -/// @author kopy-kat -contract AttestationResolveTest is BaseTest { - AttestationResolveInstance resolverInstance; - FalseResolver falseResolver; - PayableResolver payableResolver; - - function setUp() public override { - super.setUp(); - resolverInstance = new AttestationResolveInstance(); - falseResolver = new FalseResolver(address(resolverInstance)); - payableResolver = new PayableResolver(address(resolverInstance)); - - resolverInstance.addResolver( - ResolverUID.wrap(0), - ResolverRecord({ - resolver: IResolver(address(debugResolver)), - resolverOwner: address(this) - }) - ); - - resolverInstance.addResolver( - ResolverUID.wrap(bytes32(uint256(1))), - ResolverRecord({ - resolver: IResolver(address(falseResolver)), - resolverOwner: address(this) - }) - ); - - resolverInstance.addResolver( - ResolverUID.wrap(bytes32(uint256(2))), - ResolverRecord({ - resolver: IResolver(address(payableResolver)), - resolverOwner: address(this) - }) - ); - - resolverInstance.addResolver( - ResolverUID.wrap(bytes32(uint256(3))), - ResolverRecord({ resolver: IResolver(address(0x6969)), resolverOwner: address(this) }) - ); - } - - function testResolveAttestation() public { - AttestationRecord memory attestationRecord = AttestationRecord({ - schemaUID: defaultSchema1, - moduleAddr: address(this), - attester: address(this), - time: uint48(0), - expirationTime: uint48(0), - revocationTime: uint48(0), - dataPointer: AttestationDataRef.wrap(address(0)) - }); - - resolverInstance.resolveAttestation({ - resolverUID: ResolverUID.wrap(bytes32(uint256(2))), - attestationRecord: attestationRecord, - value: 0, - isRevocation: false, - availableValue: 0, - isLastAttestation: true - }); - } - - function testResolveAttestation__WithValue() public { - AttestationRecord memory attestationRecord = AttestationRecord({ - schemaUID: defaultSchema1, - moduleAddr: address(this), - attester: address(this), - time: uint48(0), - expirationTime: uint48(0), - revocationTime: uint48(0), - dataPointer: AttestationDataRef.wrap(address(0)) - }); - - vm.deal(address(resolverInstance), 2 ether); - - address sender = makeAddr("sender"); - vm.prank(sender); - resolverInstance.resolveAttestation({ - resolverUID: ResolverUID.wrap(bytes32(uint256(2))), - attestationRecord: attestationRecord, - value: 1 ether, - isRevocation: false, - availableValue: 2 ether, - isLastAttestation: true - }); - - assertEq(sender.balance, 1 ether); - } - - function testResolveAttestation__RevertWhen__ZeroResolverAndValue() public { - AttestationRecord memory attestationRecord = AttestationRecord({ - schemaUID: defaultSchema1, - moduleAddr: address(this), - attester: address(this), - time: uint48(0), - expirationTime: uint48(0), - revocationTime: uint48(0), - dataPointer: AttestationDataRef.wrap(address(0)) - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.NotPayable.selector)); - - resolverInstance.resolveAttestation({ - resolverUID: ResolverUID.wrap(bytes32(uint256(200_000))), - attestationRecord: attestationRecord, - value: 1 wei, - isRevocation: false, - availableValue: 0, - isLastAttestation: true - }); - } - - function testResolveAttestation__RevertWhen__ResolverNotPayableAndValue() public { - AttestationRecord memory attestationRecord = AttestationRecord({ - schemaUID: defaultSchema1, - moduleAddr: address(this), - attester: address(this), - time: uint48(0), - expirationTime: uint48(0), - revocationTime: uint48(0), - dataPointer: AttestationDataRef.wrap(address(0)) - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.NotPayable.selector)); - - resolverInstance.resolveAttestation({ - resolverUID: ResolverUID.wrap(bytes32(uint256(0))), - attestationRecord: attestationRecord, - value: 1 wei, - isRevocation: false, - availableValue: 0, - isLastAttestation: true - }); - } - - function testResolveAttestation__RevertWhen__InsufficientValue() public { - AttestationRecord memory attestationRecord = AttestationRecord({ - schemaUID: defaultSchema1, - moduleAddr: address(this), - attester: address(this), - time: uint48(0), - expirationTime: uint48(0), - revocationTime: uint48(0), - dataPointer: AttestationDataRef.wrap(address(0)) - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InsufficientValue.selector)); - - resolverInstance.resolveAttestation({ - resolverUID: ResolverUID.wrap(bytes32(uint256(2))), - attestationRecord: attestationRecord, - value: 2 wei, - isRevocation: false, - availableValue: 1 wei, - isLastAttestation: true - }); - } - - function testResolveAttestation__RevertWhen__InvalidRevocation() public { - AttestationRecord memory attestationRecord = AttestationRecord({ - schemaUID: defaultSchema1, - moduleAddr: address(this), - attester: address(this), - time: uint48(0), - expirationTime: uint48(0), - revocationTime: uint48(0), - dataPointer: AttestationDataRef.wrap(address(0)) - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidRevocation.selector)); - resolverInstance.resolveAttestation({ - resolverUID: ResolverUID.wrap(bytes32(uint256(1))), - attestationRecord: attestationRecord, - value: 0, - isRevocation: true, - availableValue: 0, - isLastAttestation: true - }); - } - - function testResolveAttestation__RevertWhen__InvalidAttestation() public { - AttestationRecord memory attestationRecord = AttestationRecord({ - schemaUID: defaultSchema1, - moduleAddr: address(this), - attester: address(this), - time: uint48(0), - expirationTime: uint48(0), - revocationTime: uint48(0), - dataPointer: AttestationDataRef.wrap(address(0)) - }); - - vm.expectRevert(abi.encodeWithSelector(IAttestation.InvalidAttestation.selector)); - resolverInstance.resolveAttestation({ - resolverUID: ResolverUID.wrap(bytes32(uint256(1))), - attestationRecord: attestationRecord, - value: 0, - isRevocation: false, - availableValue: 0, - isLastAttestation: true - }); - } -} diff --git a/test/EIP712Verifier.t.sol b/test/EIP712Verifier.t.sol deleted file mode 100644 index ffbdca31..00000000 --- a/test/EIP712Verifier.t.sol +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { BaseTest, RegistryTestLib, RegistryInstance } from "./utils/BaseTest.t.sol"; -import { - EIP712Verifier, - SignedAttestationRequest, - SignedRevocationRequest, - SchemaUID, - AttestationRequestData, - RevocationRequestData, - InvalidSignature -} from "../src/base/EIP712Verifier.sol"; - -import { console2 } from "forge-std/console2.sol"; - -struct SampleAttestation { - address[] dependencies; - string comment; - string url; - bytes32 hash; - uint256 severity; -} - -contract EIP712VerifierInstance is EIP712Verifier { - function verifyAttest(SignedAttestationRequest calldata request) public { - _requireValidAttestSignature(request); - } - - function verifyRevoke(SignedRevocationRequest calldata request) public { - _verifyRevoke(request); - } - - function hashTypedData(bytes32 structHash) public view virtual returns (bytes32 digest) { - digest = super._hashTypedData(structHash); - } -} - -/// @title EIP712VerifierTest -/// @author kopy-kat -contract EIP712VerifierTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - EIP712VerifierInstance verifier; - - function setUp() public virtual override { - super.setUp(); - - verifier = new EIP712VerifierInstance(); - } - - function testGetNonce() public { - address account = vm.addr(auth1k); - uint256 nonce = verifier.getNonce(account); - assertEq(nonce, 0); - - // Since _newNonce is private, we verify an Attestation which increments the nonce - testVerifyAttest(); - nonce = verifier.getNonce(account); - assertEq(nonce, 1); - } - - function testGetAttestationDigest() public { - address account = makeAddr("account"); - SchemaUID schemaUID = SchemaUID.wrap(0); - uint256 nonce = verifier.getNonce(account) + 1; - - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: address(0), - expirationTime: uint48(0), - value: 0, - data: "" - }); - - bytes32 digest1 = verifier.getAttestationDigest(attData, schemaUID, account); - bytes32 digest2 = verifier.getAttestationDigest(attData, schemaUID, nonce); - assertEq(digest1, digest2); - - bytes32 digest3 = verifier.hashTypedData( - keccak256( - abi.encode( - verifier.getAttestTypeHash(), - block.chainid, - schemaUID, - attData.moduleAddr, - attData.expirationTime, - keccak256(attData.data), - nonce - ) - ) - ); - - assertEq(digest1, digest3); - } - - function testVerifyAttest() public { - SchemaUID schemaUID = SchemaUID.wrap(0); - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: address(0x69), - expirationTime: uint48(0), - value: 0, - data: abi.encode(true) - }); - uint256 nonce = verifier.getNonce(vm.addr(auth1k)) + 1; - bytes32 digest = - verifier.getAttestationDigest({ attData: attData, schemaUID: schemaUID, nonce: nonce }); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(auth1k, digest); - bytes memory signature = abi.encodePacked(r, s, v); - - SignedAttestationRequest memory request = SignedAttestationRequest({ - schemaUID: schemaUID, - data: attData, - attester: vm.addr(auth1k), - signature: signature - }); - - verifier.verifyAttest(request); - } - - function testVerifyAttest__RevertWhen__InvalidSignature() public { - address attester = makeAddr("attester"); - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: address(0), - expirationTime: uint48(0), - value: 0, - data: "" - }); - SignedAttestationRequest memory request = SignedAttestationRequest({ - schemaUID: SchemaUID.wrap(0), - data: attData, - attester: attester, - signature: "" - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidSignature.selector)); - verifier.verifyAttest(request); - } - - function testGetRevocationDigest() public { - address account = makeAddr("account"); - SchemaUID schemaUID = SchemaUID.wrap(0); - uint256 nonce = verifier.getNonce(account) + 1; - - RevocationRequestData memory revData = - RevocationRequestData({ moduleAddr: address(0), attester: account, value: 0 }); - - bytes32 digest1 = verifier.getRevocationDigest(revData, schemaUID, account); - bytes32 digest2 = verifier.getRevocationDigest(revData, schemaUID, nonce); - assertEq(digest1, digest2); - - bytes32 digest3 = verifier.hashTypedData( - keccak256( - abi.encode( - verifier.getRevokeTypeHash(), - block.chainid, - schemaUID, - revData.moduleAddr, - account, - nonce - ) - ) - ); - - assertEq(digest1, digest3); - } - - function testVerifyRevoke() public { - address revoker = vm.addr(auth1k); - SchemaUID schemaUID = SchemaUID.wrap(0); - RevocationRequestData memory revData = - RevocationRequestData({ moduleAddr: address(0), attester: revoker, value: 0 }); - - uint256 nonce = verifier.getNonce(vm.addr(auth1k)) + 1; - bytes32 digest = - verifier.getRevocationDigest({ revData: revData, schemaUID: schemaUID, nonce: nonce }); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(auth1k, digest); - bytes memory signature = abi.encodePacked(r, s, v); - - SignedRevocationRequest memory request = SignedRevocationRequest({ - schemaUID: schemaUID, - data: revData, - revoker: revoker, - signature: signature - }); - - verifier.verifyRevoke(request); - } -} diff --git a/test/Module.t.sol b/test/Module.t.sol deleted file mode 100644 index 6b35c8e6..00000000 --- a/test/Module.t.sol +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { ISchema, SchemaRecord } from "../src/interface/ISchema.sol"; -import "../src/lib/ModuleDeploymentLib.sol"; -import { IResolver } from "../src/external/IResolver.sol"; -import { InvalidSchema } from "../src/Common.sol"; -import "./utils/BaseTest.t.sol"; -import { IModule } from "../src/interface/IModule.sol"; - -/// @title ModuleTest -/// @author zeroknots -contract ModuleTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - function setUp() public virtual override { - super.setUp(); - } - - function testDeployWithArgs() public returns (SchemaUID schemaUID, address moduleAddr) { - schemaUID = instance.registerSchema("Test ABI 123", ISchemaValidator(address(0))); - - bytes memory bytecode = type(MockModuleWithArgs).creationCode; - moduleAddr = instance.deployAndRegister({ - resolverUID: defaultResolver, - bytecode: bytecode, - constructorArgs: abi.encode(313_131) - }); - - MockModuleWithArgs module = MockModuleWithArgs(moduleAddr); - - assertEq(module.readValue(), 313_131, "value should be set"); - } - - function testDeployNoArgs() public returns (SchemaUID schemaUID, address moduleAddr) { - schemaUID = instance.registerSchema("Test ABI 123", ISchemaValidator(address(0))); - - bytes memory bytecode = type(MockModule).creationCode; - moduleAddr = instance.deployAndRegister({ - resolverUID: defaultResolver, - bytecode: bytecode, - constructorArgs: bytes("") - }); - } - - function testNonexistingModule__ShouldRevert() public { - // TODO - SchemaUID schemaUID = instance.registerSchema("Test ABI 123", ISchemaValidator(address(0))); - - address module = makeAddr("doesntExist"); - vm.expectRevert(); - instance.registry.register(defaultResolver, module, ""); - } - - function testReRegisterModule__ShouldRevert() public { - SchemaUID schemaUID = instance.registerSchema("Test ABI 123", ISchemaValidator(address(0))); - - bytes memory bytecode = type(MockModule).creationCode; - address moduleAddr = instance.deployAndRegister({ - resolverUID: defaultResolver, - bytecode: bytecode, - constructorArgs: abi.encode(313_132) - }); - vm.expectRevert(abi.encodeWithSelector(IModule.AlreadyRegistered.selector, moduleAddr)); - instance.registry.register(defaultResolver, moduleAddr, ""); - } - - function testExternalFactory() public { - ExternalFactory factory = new ExternalFactory(); - - bytes memory bytecode = type(MockModule).creationCode; - - bytes memory ExternalFactoryCallData = - abi.encodeWithSelector(ExternalFactory.deploy.selector, bytecode, "", 123); - - address moduleAddr = instance.registry.deployViaFactory( - address(factory), ExternalFactoryCallData, "foobar", defaultResolver - ); - - ModuleRecord memory record = instance.registry.getModule(moduleAddr); - assertEq(record.implementation, moduleAddr); - assertEq(record.sender, address(this)); - } - - function testCreate3() public { - bytes memory bytecode = type(MockModule).creationCode; - - address moduleAddr = instance.registry.deployC3(bytecode, "", "1", "", defaultResolver); - ModuleRecord memory record = instance.registry.getModule(moduleAddr); - assertEq(record.implementation, moduleAddr); - assertEq(record.sender, address(this)); - } -} - -contract ExternalFactory { - event ExternalFactoryDeploy(address moduleAddr); - - function deploy( - bytes calldata code, - bytes calldata deployParams, - bytes32 salt - ) - external - payable - returns (address moduleAddr) - { - (moduleAddr,,) = ModuleDeploymentLib.deploy(code, deployParams, salt, 0); - emit ExternalFactoryDeploy(moduleAddr); - } -} diff --git a/test/Query.t.sol b/test/Query.t.sol deleted file mode 100644 index a6ce0641..00000000 --- a/test/Query.t.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; - -import "./Attestation.t.sol"; -import "../src/interface/IQuery.sol"; - -/// @title RSRegistryTest -/// @author zeroknots, kopy-kat -contract QueryTest is AttestationTest { - using RegistryTestLib for RegistryInstance; - - address immutable attester = address(this); - - function setUp() public virtual override { - super.setUp(); - } - - function testCheckAttestation() public { - testAttest(); - instance.registry.check(defaultModule1, attester); - } - - function testCheckAttestationInternal() public { - testAttest(); - - address[] memory attesters = new address[](1); - attesters[0] = attester; - instance.registry.setAttester(1, attesters); - uint256 gass = gasleft(); - instance.registry.check(defaultModule1); - gass = gass - gasleft(); - - console2.log("gas used", gass); - } - - function testCheckAttestation__RevertWhen__AttestationNotExistent() public { - vm.expectRevert(IQuery.AttestationNotFound.selector); - instance.registry.check(defaultModule1, attester); - } - - function testCheckAttestation__RevertWhen__Expired() public { - vm.warp(100); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - vm.warp(200); - vm.expectRevert(IQuery.AttestationNotFound.selector); - instance.registry.check(defaultModule1, attester); - } - - function testCheckAttestation__RevertWhen__Revoked() public { - testAttest(); - instance.revokeAttestation(defaultModule1, defaultSchema1, address(this)); - vm.expectRevert(abi.encodeWithSelector(IQuery.RevokedAttestation.selector, attester)); - instance.registry.check(defaultModule1, attester); - } - - function testCheckNAttestation() public { - testAttest(); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); - address[] memory attesters = new address[](2); - attesters[0] = attester; - attesters[1] = vm.addr(auth2k); - instance.registry.checkN(defaultModule1, attesters, 1); - } - - function testCheckNAttestation__RevertWhen__ThresholdNotMet() public { - testAttest(); - address[] memory attesters = new address[](2); - attesters[0] = attester; - attesters[1] = address(0x69); - - vm.expectRevert(IQuery.InsufficientAttestations.selector); - instance.registry.checkN(defaultModule1, attesters, 2); - } - - function testCheckNAttestation__RevertWhen__Expired() public { - vm.warp(100); - testAttest(); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: defaultModule1, - expirationTime: uint48(101), - data: abi.encode(false), - value: 0 - }); - instance.newSignedAttestation(defaultSchema1, auth2k, attData); - vm.warp(200); - address[] memory attesters = new address[](2); - attesters[0] = attester; - attesters[1] = vm.addr(auth2k); - - vm.expectRevert(IQuery.AttestationNotFound.selector); - instance.registry.checkN(defaultModule1, attesters, 1); - } - - function testCheckNAttestation__RevertWhen__Revoked() public { - testAttest(); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); - instance.signedRevokeAttestation(defaultModule1, defaultSchema1, auth2k); - address[] memory attesters = new address[](2); - attesters[0] = vm.addr(auth1k); - attesters[1] = vm.addr(auth2k); - - vm.expectRevert(abi.encodeWithSelector(IQuery.RevokedAttestation.selector, vm.addr(auth2k))); - instance.registry.checkN(defaultModule1, attesters, 1); - } - - function testCheckNAttestationUnsafe() public { - testAttest(); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); - address[] memory attesters = new address[](2); - attesters[0] = vm.addr(auth1k); - attesters[1] = vm.addr(auth2k); - instance.registry.checkNUnsafe(defaultModule1, attesters, 1); - } - - function testCheckNAttestationUnsafe__RevertWhen__ThresholdNotMet() public { - testAttest(); - address[] memory attesters = new address[](2); - attesters[0] = vm.addr(auth1k); - attesters[1] = vm.addr(auth2k); - - vm.expectRevert(IQuery.InsufficientAttestations.selector); - instance.registry.checkNUnsafe(defaultModule1, attesters, 2); - } - - function testCheckNAttestationUnsafe__Expired() public { - vm.warp(100); - testAttest(); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - vm.warp(200); - address[] memory attesters = new address[](2); - attesters[0] = vm.addr(auth1k); - attesters[1] = vm.addr(auth2k); - - instance.registry.checkNUnsafe(defaultModule1, attesters, 1); - } - - function testCheckNAttestationUnsafe__Revoked() public { - testAttest(); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth1k); - instance.revokeAttestation(defaultModule1, defaultSchema1, address(this)); - address[] memory attesters = new address[](2); - attesters[0] = vm.addr(auth1k); - attesters[1] = vm.addr(auth2k); - - instance.registry.checkNUnsafe(defaultModule1, attesters, 1); - } - - function testFindAttestation() public { - testAttest(); - AttestationRecord memory attestation = - instance.registry.findAttestation(defaultModule1, attester); - assertEq(attestation.attester, attester); - } - - function testFindAttestations() public { - testAttest(); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, auth2k); - - address[] memory attesters = new address[](2); - attesters[0] = attester; - attesters[1] = vm.addr(auth2k); - - AttestationRecord[] memory attestations = - instance.registry.findAttestations(defaultModule1, attesters); - - assertEq(attestations[0].attester, attesters[0]); - assertEq(attestations[1].attester, attesters[1]); - } -} diff --git a/test/Schema.t.sol b/test/Schema.t.sol deleted file mode 100644 index 3ce418ba..00000000 --- a/test/Schema.t.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; -import "../src/external/IResolver.sol"; -import "../src/interface/ISchema.sol"; -import { DebugResolver } from "../src/external/examples/DebugResolver.sol"; -import { InvalidResolver } from "../src/Common.sol"; - -import "./utils/BaseTest.t.sol"; - -/// @title SchemaTest -/// @author zeroknots, kopy-kat -contract SchemaTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - DebugResolver simpleResolver; - - function setUp() public virtual override { - super.setUp(); - simpleResolver = new DebugResolver(address(instance.registry)); - } - - function testRegisterSchema() public { - SchemaUID schemaUID = instance.registerSchema("Test ABI 2", ISchemaValidator(address(0))); - assertTrue(SchemaUID.unwrap(schemaUID) != bytes32(0)); - - SchemaRecord memory schema = instance.registry.getSchema(schemaUID); - assertEq(schema.schema, "Test ABI 2"); - } - - function testRegisterSchema__RevertWhen__AlreadyExists() public { - SchemaUID schemaUID = instance.registerSchema("Test ABI 2", ISchemaValidator(address(0))); - assertTrue(SchemaUID.unwrap(schemaUID) != bytes32(0)); - - vm.expectRevert(abi.encodeWithSelector(ISchema.AlreadyExists.selector)); - schemaUID = instance.registerSchema("Test ABI 2", ISchemaValidator(address(0))); - } - - function testRegisterResolver() public { - ResolverUID resolverUID = instance.registerResolver(simpleResolver); - assertTrue(ResolverUID.unwrap(resolverUID) != bytes32(0)); - } - - function testRegisterResolver__RevertWhen__InvalidResolver() public { - vm.expectRevert(abi.encodeWithSelector(InvalidResolver.selector)); - instance.registerResolver(IResolver(address(0))); - } - - function testRegisterResolver__RevertWhen__AlreadyExists() public { - ResolverUID resolverUID = instance.registerResolver(simpleResolver); - assertTrue(ResolverUID.unwrap(resolverUID) != bytes32(0)); - - vm.expectRevert(abi.encodeWithSelector(ISchema.AlreadyExists.selector)); - resolverUID = instance.registerResolver(simpleResolver); - } - - function testSetResolver() public { - address resolverOwner = address(this); - ResolverUID resolverUID = instance.registerResolver(simpleResolver); - ResolverRecord memory resolver = instance.registry.getResolver(resolverUID); - assertEq(resolver.resolverOwner, resolverOwner); - assertEq(address(resolver.resolver), address(simpleResolver)); - - instance.registry.setResolver(resolverUID, IResolver(address(0x69))); - resolver = instance.registry.getResolver(resolverUID); - assertEq(address(resolver.resolver), address(0x69)); - } -} diff --git a/test/gas/RegistryGasComparison.t.sol b/test/gas/RegistryGasComparison.t.sol deleted file mode 100644 index de208522..00000000 --- a/test/gas/RegistryGasComparison.t.sol +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { console2 } from "forge-std/console2.sol"; -import { BaseTest, RegistryTestLib, RegistryInstance } from "../utils/BaseTest.t.sol"; -import { IERC7484 } from "../../src/interface/IERC7484.sol"; - -contract Minimal7484Registry is IERC7484 { - mapping(address module => uint256) public modules; - - function check(address module, address) public view returns (uint256 attestedAt) { - attestedAt = modules[module]; - require(attestedAt > 0, "Module not registered"); - } - - function checkN( - address module, - address[] memory attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray) - { - // unused - } - - function attest(address module) public { - modules[module] = 123_456; - } -} - -contract RegistryGasComparisonTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - address minimal7484Registry1 = address(new Minimal7484Registry()); - address minimal7484Registry2 = address(new Minimal7484Registry()); - address minimal7484Registry3 = address(new Minimal7484Registry()); - - address firstAttester; - address secondAttester; - address thirdAttester; - - function setUp() public override { - vm.warp(0x41414141); - super.setUp(); - - Minimal7484Registry(minimal7484Registry1).attest(defaultModule1); - Minimal7484Registry(minimal7484Registry2).attest(defaultModule1); - Minimal7484Registry(minimal7484Registry3).attest(defaultModule1); - - (address _firstAttester, uint256 firstKey) = makeAddrAndKey("firstAttester"); - firstAttester = _firstAttester; - - (address _secondAttester, uint256 secondKey) = makeAddrAndKey("secondAttester"); - secondAttester = _secondAttester; - - (address _thirdAttester, uint256 thirdKey) = makeAddrAndKey("thirdAttester"); - thirdAttester = _thirdAttester; - - instance.mockSignedAttestation(defaultSchema1, defaultModule1, firstKey); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, secondKey); - instance.mockSignedAttestation(defaultSchema1, defaultModule1, thirdKey); - } - - function testGasCheck() public { - address module = defaultModule1; - - // treat as immutable - address rhinestoneRegistry = address(instance.registry); - - uint256 gasCheck = gasleft(); - uint256 attestedAt = IERC7484(rhinestoneRegistry).check(module, firstAttester); - gasCheck = gasCheck - gasleft() - 10; - - uint256 gasCheckMinimal7484 = gasleft(); - uint256 attestedAtMinimal = IERC7484(minimal7484Registry1).check(module, address(0)); - gasCheckMinimal7484 = gasCheckMinimal7484 - gasleft() - 10; - - console2.log("Rhinestone Registry check: %s", gasCheck); - console2.log("Minimal 7484 Registry check: %s", gasCheckMinimal7484); - } - - function testGasCheckN__Given__TwoAttesters() public { - address module = defaultModule1; - - // treat as immutable - address rhinestoneRegistry = address(instance.registry); - - uint256 gasCheck = gasleft(); - address[] memory attesters = new address[](2); - attesters[0] = firstAttester; - attesters[1] = secondAttester; - uint256[] memory attestedAtArray = IERC7484(rhinestoneRegistry).checkN(module, attesters, 2); - gasCheck = gasCheck - gasleft() - 10; - - uint256 gasCheckMinimal7484 = gasleft(); - uint256 attestedAtMinimal = IERC7484(minimal7484Registry1).check(module, address(0)); - uint256 attestedAtMinimal2 = IERC7484(minimal7484Registry2).check(module, address(0)); - gasCheckMinimal7484 = gasCheckMinimal7484 - gasleft() - 10; - - console2.log("Rhinestone Registry checkN (2): %s", gasCheck); - console2.log("Minimal 7484 Registry check (2): %s", gasCheckMinimal7484); - } - - function testGasCheckN__Given__ThreeAttesters() public { - address module = defaultModule1; - - // treat as immutable - address rhinestoneRegistry = address(instance.registry); - - uint256 gasCheck = gasleft(); - address[] memory attesters = new address[](3); - attesters[0] = firstAttester; - attesters[1] = secondAttester; - attesters[2] = thirdAttester; - uint256[] memory attestedAtArray = IERC7484(rhinestoneRegistry).checkN(module, attesters, 3); - gasCheck = gasCheck - gasleft() - 10; - - uint256 gasCheckMinimal7484 = gasleft(); - uint256 attestedAtMinimal = IERC7484(minimal7484Registry1).check(module, address(0)); - uint256 attestedAtMinimal2 = IERC7484(minimal7484Registry2).check(module, address(0)); - uint256 attestedAtMinimal3 = IERC7484(minimal7484Registry3).check(module, address(0)); - gasCheckMinimal7484 = gasCheckMinimal7484 - gasleft() - 10; - - console2.log("Rhinestone Registry checkN (3): %s", gasCheck); - console2.log("Minimal 7484 Registry check (3): %s", gasCheckMinimal7484); - } -} diff --git a/test/integrations/MockRegistry.t.sol b/test/integrations/MockRegistry.t.sol deleted file mode 100644 index e2fd640e..00000000 --- a/test/integrations/MockRegistry.t.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; -import { BaseTest, RegistryTestLib, RegistryInstance } from "../utils/BaseTest.t.sol"; -import { MockRegistry } from "../../src/integrations/MockRegistry.sol"; - -/// @title MockRegistryTest -/// @author kopy-kat -contract MockRegistryTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - MockRegistry mockRegistry; - - function setUp() public virtual override { - super.setUp(); - mockRegistry = new MockRegistry(); - } - - function testCheck() public { - uint256 attestedAt = mockRegistry.check(address(this), address(this)); - assertGt(attestedAt, uint256(0)); - } - - function testCheckN() public { - uint256[] memory attestedAtArray = mockRegistry.checkN(address(this), new address[](1), 1); - for (uint256 i; i < attestedAtArray.length; ++i) { - assertGt(attestedAtArray[i], uint256(0)); - } - } - - function testCheckNUnsafe() public { - uint256[] memory attestedAtArray = - mockRegistry.checkNUnsafe(address(this), new address[](1), 1); - for (uint256 i; i < attestedAtArray.length; ++i) { - assertGt(attestedAtArray[i], uint256(0)); - } - } -} diff --git a/test/integrations/SimpleRegIntegration.t.sol b/test/integrations/SimpleRegIntegration.t.sol deleted file mode 100644 index 2139d314..00000000 --- a/test/integrations/SimpleRegIntegration.t.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; - -import "../utils/BaseTest.t.sol"; - -import { - RegistryIntegration, - IQuery -} from "../../src/integrations/examples/SimpleRegistryIntegration.sol"; - -contract TestHarness is RegistryIntegration { - constructor(address _registry, address _authority) RegistryIntegration(_registry, _authority) { } - - function query(address _contract) - public - view - onlyWithRegistryCheck(_contract) - returns (uint256) - { - return 1; - } -} - -/// @title SimpleRegistryIntegrationTest -/// @author zeroknots -contract SimpleRegistryIntegrationTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - TestHarness harness; - - function setUp() public override { - super.setUp(); - - harness = new TestHarness(address(instance.registry), address(this)); - } - - function testGasRegistryCheck() public { - instance.mockAttestation(defaultSchema1, defaultModule1); - harness.query(defaultModule1); - } -} diff --git a/test/resolvers/TokenizedResolver.t.sol b/test/resolvers/TokenizedResolver.t.sol deleted file mode 100644 index 97b2ebb2..00000000 --- a/test/resolvers/TokenizedResolver.t.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "solmate/test/utils/mocks/MockERC20.sol"; -import "../utils/BaseTest.t.sol"; -import "../../src/external/examples/TokenizedResolver.sol"; -import "../../src/external/examples/SimpleValidator.sol"; - -contract TokenizedResolverTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - MockERC20 token; - TokenizedResolver resolver; - SimpleValidator validator; - - function setUp() public override { - super.setUp(); - token = new MockERC20("test", "test", 8); - resolver = new TokenizedResolver(address(instance.registry), address(token)); - validator = new SimpleValidator(); - - token.mint(address(this), 10_000); - } - - function testTokenizedResolver() public { - SchemaUID schema = - instance.registerSchema("TokenizedResolver", ISchemaValidator(address(validator))); - ResolverUID resolverUID = instance.registerResolver(IResolver(address(resolver))); - - address module = instance.deployAndRegister( - resolverUID, type(MockModuleWithArgs).creationCode, abi.encode("asdfasdf") - ); - - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: module, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - - vm.prank(address(this)); - token.approve(address(resolver), 1000); - instance.newAttestation(schema, attData); - assertEq(token.balanceOf(address(resolver)), 10); - } -} diff --git a/test/resolvers/ValueResolver.t.sol b/test/resolvers/ValueResolver.t.sol deleted file mode 100644 index ddb81818..00000000 --- a/test/resolvers/ValueResolver.t.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "../utils/BaseTest.t.sol"; -import "../../src/external/examples/ValueResolver.sol"; - -contract ValueResolverTest is BaseTest { - using RegistryTestLib for RegistryInstance; - - ValueResolver resolver; - - function setUp() public override { - super.setUp(); - resolver = new ValueResolver(address(instance.registry)); - } - - function testValueResolver() public { - SchemaUID schema = - instance.registerSchema("TokenizedResolver", ISchemaValidator(address(0))); - ResolverUID resolverUID = instance.registerResolver(IResolver(address(resolver))); - - address module = instance.deployAndRegister( - resolverUID, type(MockModuleWithArgs).creationCode, abi.encode("asdfasdf") - ); - - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: module, - expirationTime: uint48(0), - data: abi.encode(true), - value: 1 ether - }); - - bytes memory signature = RegistryTestLib.signAttestation(instance, schema, auth1k, attData); - SignedAttestationRequest memory req = SignedAttestationRequest({ - schemaUID: schema, - data: attData, - signature: signature, - attester: vm.addr(auth1k) - }); - - instance.registry.attest{ value: 1 ether }(req); - assertTrue(address(resolver).balance > 0); - } -} diff --git a/test/script/DeployRegistry.t.sol b/test/script/DeployRegistry.t.sol deleted file mode 100644 index 120f6ed2..00000000 --- a/test/script/DeployRegistry.t.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { Test } from "forge-std/Test.sol"; -import { DeployRegistryScript } from "../../script/DeployRegistry.s.sol"; -import { ISchema } from "../../src/interface/ISchema.sol"; -import { ResolverRecord, ResolverUID } from "../../src/DataTypes.sol"; - -/// @title DeployRegistryTest -/// @author kopy-kat -contract DeployRegistryTest is Test { - DeployRegistryScript script; - - function setUp() public { - script = new DeployRegistryScript(); - } - - function testRun() public { - script.run(); - } -} diff --git a/test/utils/BaseTest.t.sol b/test/utils/BaseTest.t.sol deleted file mode 100644 index b6fef707..00000000 --- a/test/utils/BaseTest.t.sol +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; -import "./BaseUtils.sol"; -import "src/DataTypes.sol"; -import "../../src/external/IResolver.sol"; -import "../../src/external/examples/DebugResolver.sol"; - -contract MockModuleWithArgs { - uint256 value; - - constructor(uint256 _value) { - value = _value; - } - - function readValue() public view returns (uint256) { - return value; - } -} - -contract MockModule { - uint256 value; - - function readValue() public view returns (uint256) { - return value; - } -} - -contract FalseSchemaValidator is ISchemaValidator { - function validateSchema(AttestationRequestData calldata attestation) - external - view - returns (bool) - { - return false; - } - - /** - * @notice Validates an array of attestation requests. - */ - function validateSchema(AttestationRequestData[] calldata attestations) - external - view - returns (bool) - { - return false; - } - - function supportsInterface(bytes4 interfaceID) external view returns (bool) { } -} - -contract BaseTest is Test, RegistryTestTools { - using RegistryTestLib for RegistryInstance; - - RegistryInstance instance; - - uint256 auth1k; - uint256 auth2k; - - SchemaUID defaultSchema1; - SchemaUID defaultSchema2; - ResolverUID defaultResolver; - address defaultModule1; - address defaultModule2; - - DebugResolver debugResolver; - - address falseSchemaValidator; - - function setUp() public virtual { - instance = _setupInstance({ name: "RegistryL1", salt: 0 }); - (, auth1k) = makeAddrAndKey("auth1"); - (, auth2k) = makeAddrAndKey("auth2"); - - falseSchemaValidator = address(new FalseSchemaValidator()); - - defaultSchema1 = instance.registerSchema("Test ABI", ISchemaValidator(address(0))); - defaultSchema2 = instance.registerSchema("Test ABI2", ISchemaValidator(address(0))); - debugResolver = new DebugResolver(address(instance.registry)); - defaultResolver = instance.registerResolver(IResolver(address(debugResolver))); - - defaultModule1 = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(1234) - ); - defaultModule2 = instance.deployAndRegister( - defaultResolver, type(MockModuleWithArgs).creationCode, abi.encode(5678) - ); - } -} diff --git a/test/utils/BaseUtils.sol b/test/utils/BaseUtils.sol deleted file mode 100644 index bc496770..00000000 --- a/test/utils/BaseUtils.sol +++ /dev/null @@ -1,309 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "forge-std/Test.sol"; - -import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; - -import "../../src/Registry.sol"; -import { - AttestationRequestData, - RevocationRequestData, - SignedAttestationRequest, - SignedRevocationRequest -} from "../../src/base/AttestationDelegation.sol"; -import { ISchemaValidator, IResolver } from "../../src/interface/ISchema.sol"; -import { AttestationRequest, RevocationRequest } from "../../src/DataTypes.sol"; - -address constant VM_ADDR = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; -bytes12 constant ADDR_MASK = 0xffffffffffffffffffffffff; - -function getAddr(uint256 pk) pure returns (address) { - return Vm(VM_ADDR).addr(pk); -} - -struct RegistryInstance { - Registry registry; - string name; -} - -library RegistryTestLib { - function mockAttestation( - RegistryInstance memory instance, - SchemaUID schemaUID, - address moduleAddr - ) - public - { - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: moduleAddr, - expirationTime: uint48(0), - data: abi.encode(true), - value: 0 - }); - newAttestation(instance, schemaUID, attData); - } - - function mockSignedAttestation( - RegistryInstance memory instance, - SchemaUID schemaUID, - address moduleAddr, - uint256 authKey - ) - public - { - AttestationRequestData memory attData = AttestationRequestData({ - moduleAddr: moduleAddr, - expirationTime: uint48(0x42424242), - data: abi.encode(true), - value: 0 - }); - newSignedAttestation(instance, schemaUID, authKey, attData); - } - - // function mockAttestation( - // RegistryInstance memory instance, - // SchemaUID schemaUID, - // AttestationRequestData memory attData - // ) - // public - // { - // newAttestation(instance, schemaUID, attData); - // } - - function newAttestation( - RegistryInstance memory instance, - SchemaUID schemaUID, - AttestationRequestData memory attData - ) - public - { - AttestationRequest memory req = AttestationRequest({ schemaUID: schemaUID, data: attData }); - instance.registry.attest(req); - } - - function newSignedAttestation( - RegistryInstance memory instance, - SchemaUID schemaUID, - uint256 attesterKey, - AttestationRequestData memory attData - ) - public - { - bytes memory signature = signAttestation(instance, schemaUID, attesterKey, attData); - SignedAttestationRequest memory req = SignedAttestationRequest({ - schemaUID: schemaUID, - data: attData, - signature: signature, - attester: getAddr(attesterKey) - }); - instance.registry.attest(req); - } - - function signAttestation( - RegistryInstance memory instance, - SchemaUID schemaUID, - uint256 attesterPk, - AttestationRequestData memory attData - ) - internal - view - returns (bytes memory sig) - { - uint256 nonce = instance.registry.getNonce(getAddr(attesterPk)) + 1; - bytes32 digest = instance.registry.getAttestationDigest({ - attData: attData, - schemaUID: schemaUID, - nonce: nonce - }); - - (uint8 v, bytes32 r, bytes32 s) = Vm(VM_ADDR).sign(attesterPk, digest); - sig = abi.encodePacked(r, s, v); - require( - SignatureCheckerLib.isValidSignatureNow(getAddr(attesterPk), digest, sig) == true, - "Internal Error" - ); - } - - function signAttestation( - RegistryInstance memory instance, - SchemaUID schemaUID, - uint256 attesterPk, - AttestationRequestData[] memory attData - ) - internal - view - returns (bytes[] memory sig) - { - sig = new bytes[](attData.length); - - uint256 nonce = instance.registry.getNonce(getAddr(attesterPk)) + 1; - - for (uint256 i = 0; i < attData.length; i++) { - bytes32 digest = instance.registry.getAttestationDigest({ - attData: attData[i], - schemaUID: schemaUID, - nonce: nonce + i - }); - - (uint8 v, bytes32 r, bytes32 s) = Vm(VM_ADDR).sign(attesterPk, digest); - sig[i] = abi.encodePacked(r, s, v); - - require( - SignatureCheckerLib.isValidSignatureNow(getAddr(attesterPk), digest, sig[i]) == true, - "Internal Error" - ); - } - } - - function revokeAttestation( - RegistryInstance memory instance, - address module, - SchemaUID schemaUID, - address attester - ) - public - { - RevocationRequestData memory revoke = - RevocationRequestData({ moduleAddr: module, attester: attester, value: 0 }); - - RevocationRequest memory req = RevocationRequest({ schemaUID: schemaUID, data: revoke }); - instance.registry.revoke(req); - } - - function signedRevokeAttestation( - RegistryInstance memory instance, - address module, - SchemaUID schemaUID, - uint256 attesterPk - ) - public - { - RevocationRequestData memory revoke = - RevocationRequestData({ moduleAddr: module, attester: getAddr(attesterPk), value: 0 }); - - bytes memory signature = signRevocation(instance, schemaUID, attesterPk, revoke); - - SignedRevocationRequest memory req = SignedRevocationRequest({ - schemaUID: schemaUID, - data: revoke, - signature: signature, - revoker: getAddr(attesterPk) - }); - instance.registry.revoke(req); - } - - function signRevocation( - RegistryInstance memory instance, - SchemaUID schemaUID, - uint256 revokerPk, - RevocationRequestData memory attData - ) - internal - view - returns (bytes memory sig) - { - bytes32 digest = - instance.registry.getRevocationDigest(attData, schemaUID, getAddr(revokerPk)); - - (uint8 v, bytes32 r, bytes32 s) = Vm(VM_ADDR).sign(revokerPk, digest); - sig = abi.encodePacked(r, s, v); - require( - SignatureCheckerLib.isValidSignatureNow(getAddr(revokerPk), digest, sig) == true, - "Internal Error" - ); - } - - function signRevocation( - RegistryInstance memory instance, - SchemaUID schemaUID, - uint256 revokerPk, - RevocationRequestData[] memory attData - ) - internal - view - returns (bytes[] memory sig) - { - sig = new bytes[](attData.length); - uint256 nonce = instance.registry.getNonce(getAddr(revokerPk)) + 1; - for (uint256 i = 0; i < attData.length; ++i) { - bytes32 digest = instance.registry.getRevocationDigest(attData[i], schemaUID, nonce + i); - (uint8 v, bytes32 r, bytes32 s) = Vm(VM_ADDR).sign(revokerPk, digest); - sig[i] = abi.encodePacked(r, s, v); - } - } - - function registerSchemaAndResolver( - RegistryInstance memory instance, - string memory abiString, - ISchemaValidator validator, - IResolver resolver - ) - internal - returns (SchemaUID schemaUID, ResolverUID resolverId) - { - schemaUID = registerSchema(instance, abiString, validator); - resolverId = registerResolver(instance, resolver); - } - - function registerSchema( - RegistryInstance memory instance, - string memory abiString, - ISchemaValidator validator - ) - internal - returns (SchemaUID schemaUID) - { - return instance.registry.registerSchema(abiString, validator); - } - - function registerResolver( - RegistryInstance memory instance, - IResolver resolver - ) - internal - returns (ResolverUID resolverUID) - { - resolverUID = instance.registry.registerResolver(resolver); - } - - function deployAndRegister( - RegistryInstance memory instance, - ResolverUID resolverUID, - bytes memory bytecode, - bytes memory constructorArgs - ) - internal - returns (address moduleAddr) - { - moduleAddr = instance.registry.deploy({ - code: bytecode, - deployParams: constructorArgs, - salt: 0, - metadata: "", - resolverUID: resolverUID - }); - - ModuleRecord memory moduleRecord = instance.registry.getModule(moduleAddr); - require(moduleRecord.resolverUID == resolverUID, "resolverUID mismatch"); - } -} - -contract RegistryTestTools { - using RegistryTestLib for RegistryInstance; - - function _setupInstance( - string memory name, - bytes32 salt - ) - internal - returns (RegistryInstance memory) - { - RegistryInstance memory instance; - - Registry registry = new Registry{ salt: salt }(); - - instance = RegistryInstance(registry, name); - return instance; - } -} diff --git a/test/utils/ERC1271Attester.sol b/test/utils/ERC1271Attester.sol deleted file mode 100644 index b11d2ab9..00000000 --- a/test/utils/ERC1271Attester.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; - -// solhint-disable-next-line max-line-length -bytes constant EXPECTED_SIGNATURE = - hex"ddeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefeadbeef"; - -contract ERC1271Attester is IERC1271 { - bytes4 internal constant MAGICVALUE = 0x1626ba7e; - - function isValidSignature( - bytes32 hash, - bytes memory signature - ) - external - view - returns (bytes4 magicValue) - { - if (signature.length == EXPECTED_SIGNATURE.length) return MAGICVALUE; - } -} From f75db7c0833c79abd199081b6157747861c1b1eb Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 11:58:00 +0700 Subject: [PATCH 13/84] renaming stubs --- docs/Attestation.md | 4 ++-- docs/Resolvers.md | 12 ++++++------ docs/Schemas.md | 8 ++++---- public/docs/all.svg | 2 +- public/docs/attestationOnly.svg | 2 +- public/docs/module-registration.svg | 2 +- public/docs/schema.svg | 2 +- public/docs/sequence.svg | 2 +- script/DeployRegistry.s.sol.bak | 4 ++-- src/DataTypes.sol | 8 ++++---- src/IRegistry.sol | 10 +++++----- src/base/Attestation.sol.bak | 8 ++++---- src/base/AttestationResolve.sol.bak | 14 +++++++------- src/base/AttestationSimple.sol.bak | 10 +++++----- src/base/Module.sol.bak | 4 ++-- src/base/Schema.sol.bak | 10 +++++----- .../{IResolver.sol => IExternalResolver.sol} | 2 +- ...alidator.sol => IExternalSchemaValidator.sol} | 2 +- src/external/ResolverBase.sol.bak | 16 ++++++++-------- src/external/SchemaValidatorBase.sol.bak | 4 ++-- src/external/examples/SimpleValidator.sol.bak | 4 ++-- src/interface/ISchema.sol.bak | 12 ++++++------ src/lib/StubLib.sol | 16 ++++++++-------- src/simple/ModuleManager.sol | 2 +- src/simple/ResolverManager.sol | 6 +++--- src/simple/SchemaManager.sol | 4 ++-- 26 files changed, 85 insertions(+), 85 deletions(-) rename src/external/{IResolver.sol => IExternalResolver.sol} (97%) rename src/external/{ISchemaValidator.sol => IExternalSchemaValidator.sol} (92%) diff --git a/docs/Attestation.md b/docs/Attestation.md index 7b586738..4be579ec 100644 --- a/docs/Attestation.md +++ b/docs/Attestation.md @@ -42,9 +42,9 @@ struct AttestationRecord { Attestation data can be validated with an external contract than may to `abi.decode()` and validate all or specific fields. -### Interaction with the IResolver +### Interaction with the IExternalResolver -Upon an Attestation's revocation, the Registry calls hooks on the associated IResolver, allowing the IResolver to update its internal state or +Upon an Attestation's revocation, the Registry calls hooks on the associated IExternalResolver, allowing the IExternalResolver to update its internal state or perform other necessary actions. This allows for extended business logic integrations. ### The Revocation Process diff --git a/docs/Resolvers.md b/docs/Resolvers.md index a5e654cf..1c243b0f 100644 --- a/docs/Resolvers.md +++ b/docs/Resolvers.md @@ -34,10 +34,10 @@ make this possible, allowing for: Entities can focus their audit efforts on their custom logic without the need to re-audit the underlying core systems. -## The IResolver interface: Standardizing Resolvers +## The IExternalResolver interface: Standardizing Resolvers For any entity looking to employ a resolver, adherence to a standardized -interface is essential. The [IResolver interface](../src/external/IResolver.sol) delineates the essential +interface is essential. The [IExternalResolver interface](../src/external/IExternalResolver.sol) delineates the essential functions a resolver must implement to ensure seamless integration and operation with the Registry. ## Example Resolvers and Custom Development @@ -62,12 +62,12 @@ The Registry exposes the following functions to register and manage Resolvers: * @dev This function allows the registration of a resolver by computing a unique ID and associating it with the schema owner. * Emits a SchemaResolverRegistered event upon successful registration. * - * @param _resolver Address of the IResolver to be registered. + * @param _resolver Address of the IExternalResolver to be registered. * * @return uid The unique ID (ResolverUID) associated with the registered resolver. */ -function registerResolver(IResolver _resolver) external returns (ResolverUID); +function registerResolver(IExternalResolver _resolver) external returns (ResolverUID); /** * @notice Updates the resolver for a given UID. @@ -77,14 +77,14 @@ function registerResolver(IResolver _resolver) external returns (ResolverUID); * @param uid The UID of the schema to update. * @param resolver The new resolver interface. */ -function setResolver(ResolverUID uid, IResolver resolver) external; +function setResolver(ResolverUID uid, IExternalResolver resolver) external; ``` After storing the resolver on the registy, a ResolverUID is emited, that Module Developers can utilize to register Modules. ```solidity struct ResolverRecord { - IResolver resolver; // Optional schema resolver. + IExternalResolver resolver; // Optional schema resolver. address schemaOwner; // The address of the account used to register the schema. } ``` diff --git a/docs/Schemas.md b/docs/Schemas.md index f73b7fbd..2acf89d2 100644 --- a/docs/Schemas.md +++ b/docs/Schemas.md @@ -10,20 +10,20 @@ A Schema can hold a string encoded ABI describtion that defines the data fields ```solidity struct SchemaRecord { uint48 registeredAt; // The time when the schema was registered (Unix timestamp). - ISchemaValidator validator; // Optional external schema validator. + IExternalSchemaValidator validator; // Optional external schema validator. string schema; // Custom specification of the schema (e.g., an ABI). } ``` -### ISchemaValidator +### IExternalSchemaValidator -As an optional feasture to the registry, an `ISchemaValidator` +As an optional feasture to the registry, an `IExternalSchemaValidator` can be provided to `abi.decode` all or parts of attestations made against the schema. The implementation of this Validator is up to the Schema validators discression. ```solidity -interface ISchemaValidator is IERC165 { +interface IExternalSchemaValidator is IERC165 { function validateSchema(AttestationRequest calldata attestation) external view diff --git a/public/docs/all.svg b/public/docs/all.svg index e48a4d46..eb6630ac 100644 --- a/public/docs/all.svg +++ b/public/docs/all.svg @@ -1 +1 @@ -%0Aparticipantgroup%20%23pink%20**Registry%5CnAdapters**%0Aactor%20Smart%20Account%0Aend%0A%0Aparticipantgroup%20%20**Attesters**%0Aparticipantgroup%20%23lightgray%20%0Aentity%20Attester%20Y%0Aend%0Aparticipantgroup%20%23gray%20%0A%0A%0Aend%0Aactor%20Module%20Developer%0A%0Aactor%20Schema%20Creator%202%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0A%0Aparticipant%20SSTORE2%0Adatabase%20IResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0A%0Adatabase%20ISchemaValidator%20%23lightgray%0Adatabase%20Module%201%20%23lightgray%0Apar%20Register%20Schema%20and%20Resolver%0AResolver%20Operator%20--%3E%3E%20*IResolver%3A%20deploy%20resolver%20contract%0A%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0A%0Aactivate%20Registry%0ARegistry%20--%3E%3EResolver%20Operator%3AAdded%20Resolver%0Adeactivate%20Registry%0Adestroy%20Resolver%20Operator%0A%0ASchema%20Creator%202%20--%3E%20*ISchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator%202-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3E%3ESchema%20Creator%202%3AAdded%20Schema%202%0Adestroy%20Schema%20Creator%202%0Adeactivate%20Registry%0A%0Aend%0A%3D%3DModule%20Registration%20%3D%3D%0Apar%20%23white%20Module%20Registrations%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Bytecode%20against%20Resolver%201%0Aactivate%20Registry%0ARegistry%20--%3E%20*Module%201%3A%20Deploy%20Bytecode%0ARegistry%20--%3EIResolver%3A%20validate%20module%5Cn%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Aend%0A%0Adestroy%20Module%20Developer%0A%0Aspace%20%0Aspace%0Aspace%0A%3D%3DAttestations%3D%3D%0A%0AAttester%20Y--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20Y%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23white%20Commit%20onchain%20Attestation%0AAttester%20Y%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%202%0Aactivate%20Attester%20Y%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0ARegistry%20--%3EISchemaValidator%3A%20validate%20attestation%20encoding%0Aactivate%20ISchemaValidator%0AISchemaValidator%20--%3E%3EISchemaValidator%3A%20abi.decode%5Cnvalidate%0AISchemaValidator%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20ISchemaValidator%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IResolver%0A%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20Y%3A%20ok%0Adeactivate%20Attester%20Y%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0A%0A%0A%0A%0A%3D%3D%20ERC-7484%3D%3D%0A%0A%0A%0A%0A%0Apar%20%23pink%20ERC-7484%0Aactivate%20Smart%20Account%0Anote%20left%20of%20Smart%20Account%23red%3A%20Smart%20Account%20Execution%0ASmart%20Account%20--%3ESmart%20Account%3A%20get%20trusted%20attester(s)%0ASmart%20Account%20-%3ERegistry%3A%20ERC-7484%0Aactivate%20Registry%0A%0ARegistry%20--%3E%3ERegistry%3A%20look%20up%20attestation%5Cnwith%20ERC-7484%20constraints%0ARegistry%20--%3ESmart%20Account%3A%20ok%0Adeactivate%20Registry%0ASmart%20Account%20-%3EModule%201%3A%20exec%0Adeactivate%20Smart%20Account%0AendRegistryAdaptersSmart AccountAttester YModule DeveloperSchema Creator 2Resolver OperatorRegistryIResolverdeploy resolver contractRegister external resolver contractAdded ResolverISchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorAdded Schema 2Module Registration Register Module Bytecode against Resolver 1Module 1Deploy Bytecodevalidate moduleBusiness LogiccallokokModule Registered on address xAttestationsReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 2find Validator for SchemaUIDvalidate attestation encodingabi.decodevalidateokfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefok ERC-7484Smart Account Executionget trusted attester(s)ERC-7484look up attestationwith ERC-7484 constraintsokexecpar[Register Schema and Resolver]par[Module Registrations]par[Commit onchain Attestation]par[ERC-7484] \ No newline at end of file +%0Aparticipantgroup%20%23pink%20**Registry%5CnAdapters**%0Aactor%20Smart%20Account%0Aend%0A%0Aparticipantgroup%20%20**Attesters**%0Aparticipantgroup%20%23lightgray%20%0Aentity%20Attester%20Y%0Aend%0Aparticipantgroup%20%23gray%20%0A%0A%0Aend%0Aactor%20Module%20Developer%0A%0Aactor%20Schema%20Creator%202%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0A%0Aparticipant%20SSTORE2%0Adatabase%20IExternalResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0A%0Adatabase%20IExternalSchemaValidator%20%23lightgray%0Adatabase%20Module%201%20%23lightgray%0Apar%20Register%20Schema%20and%20Resolver%0AResolver%20Operator%20--%3E%3E%20*IExternalResolver%3A%20deploy%20resolver%20contract%0A%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0A%0Aactivate%20Registry%0ARegistry%20--%3E%3EResolver%20Operator%3AAdded%20Resolver%0Adeactivate%20Registry%0Adestroy%20Resolver%20Operator%0A%0ASchema%20Creator%202%20--%3E%20*IExternalSchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator%202-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3E%3ESchema%20Creator%202%3AAdded%20Schema%202%0Adestroy%20Schema%20Creator%202%0Adeactivate%20Registry%0A%0Aend%0A%3D%3DModule%20Registration%20%3D%3D%0Apar%20%23white%20Module%20Registrations%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Bytecode%20against%20Resolver%201%0Aactivate%20Registry%0ARegistry%20--%3E%20*Module%201%3A%20Deploy%20Bytecode%0ARegistry%20--%3EIExternalResolver%3A%20validate%20module%5Cn%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Aend%0A%0Adestroy%20Module%20Developer%0A%0Aspace%20%0Aspace%0Aspace%0A%3D%3DAttestations%3D%3D%0A%0AAttester%20Y--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20Y%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23white%20Commit%20onchain%20Attestation%0AAttester%20Y%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%202%0Aactivate%20Attester%20Y%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0ARegistry%20--%3EIExternalSchemaValidator%3A%20validate%20attestation%20encoding%0Aactivate%20IExternalSchemaValidator%0AIExternalSchemaValidator%20--%3E%3EIExternalSchemaValidator%3A%20abi.decode%5Cnvalidate%0AIExternalSchemaValidator%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalSchemaValidator%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIExternalResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IExternalResolver%0A%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20Y%3A%20ok%0Adeactivate%20Attester%20Y%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0A%0A%0A%0A%0A%3D%3D%20ERC-7484%3D%3D%0A%0A%0A%0A%0A%0Apar%20%23pink%20ERC-7484%0Aactivate%20Smart%20Account%0Anote%20left%20of%20Smart%20Account%23red%3A%20Smart%20Account%20Execution%0ASmart%20Account%20--%3ESmart%20Account%3A%20get%20trusted%20attester(s)%0ASmart%20Account%20-%3ERegistry%3A%20ERC-7484%0Aactivate%20Registry%0A%0ARegistry%20--%3E%3ERegistry%3A%20look%20up%20attestation%5Cnwith%20ERC-7484%20constraints%0ARegistry%20--%3ESmart%20Account%3A%20ok%0Adeactivate%20Registry%0ASmart%20Account%20-%3EModule%201%3A%20exec%0Adeactivate%20Smart%20Account%0AendRegistryAdaptersSmart AccountAttester YModule DeveloperSchema Creator 2Resolver OperatorRegistryIExternalResolverdeploy resolver contractRegister external resolver contractAdded ResolverIExternalSchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorAdded Schema 2Module Registration Register Module Bytecode against Resolver 1Module 1Deploy Bytecodevalidate moduleBusiness LogiccallokokModule Registered on address xAttestationsReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 2find Validator for SchemaUIDvalidate attestation encodingabi.decodevalidateokfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefok ERC-7484Smart Account Executionget trusted attester(s)ERC-7484look up attestationwith ERC-7484 constraintsokexecpar[Register Schema and Resolver]par[Module Registrations]par[Commit onchain Attestation]par[ERC-7484] \ No newline at end of file diff --git a/public/docs/attestationOnly.svg b/public/docs/attestationOnly.svg index 7a616133..50f1a29d 100644 --- a/public/docs/attestationOnly.svg +++ b/public/docs/attestationOnly.svg @@ -1 +1 @@ -%0A%0A%0Aparticipantgroup%20%20**Attesters**%0Aparticipantgroup%20%23lightgray%20%0Aentity%20Attester%20X%0Aend%0Aparticipantgroup%20%23gray%20%0A%0Aentity%20Attester%20Y%0Aend%0Aend%0Aactor%20Module%20Developer%0A%0Aactor%20Schema%20Creator%202%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0A%0Aparticipant%20SSTORE2%0Adatabase%20IResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0A%0Adatabase%20ISchemaValidator%20%23lightgray%0Adatabase%20Module%201%20%23lightgray%0Apar%20%23white%20Register%20Schema%20%26%20Resolver%0AResolver%20Operator%20--%3E%3E%20*IResolver%3A%20deploy%20resolver%20contract%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0Adestroy%20Resolver%20Operator%0A%0ASchema%20Creator%202%20--%3E%20*ISchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator%202-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0Adestroy%20Schema%20Creator%202%0Adeactivate%20Registry%0Aend%0A%0Apar%20%23white%20Module%20Deployment%0AModule%20Developer-%3ERegistry%3A%20Depoy%20Process%0Aend%0A%3D%3DAttestations%3D%3D%0Apar%20%23yellow%20Attestations%0AAttester%20Y--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20Y%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23FFC300%20Commit%20onchain%20Attestation%0AAttester%20Y%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%202%0Aactivate%20Attester%20Y%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0ARegistry%20--%3EISchemaValidator%3A%20validate%20attestation%20encoding%0Aactivate%20ISchemaValidator%0AISchemaValidator%20--%3E%3EISchemaValidator%3A%20abi.decode%5Cnvalidate%0AISchemaValidator%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20ISchemaValidator%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IResolver%0A%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20Y%3A%20ok%0Adeactivate%20Attester%20Y%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0A%0A%0AAttester%20X--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20X%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23FFC300%20Create%20onchain%20Attestation%0A%0AAttester%20X%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%201%0Aactivate%20Attester%20X%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0Anote%20right%20of%20Registry%3A%20No%20Validator%20for%20this%20SchemaUID%0A%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IResolver%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20X%3A%20ok%0Adeactivate%20Attester%20X%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0Aend%0A%0A%0A AttestersAttester XAttester YModule DeveloperSchema Creator 2Resolver OperatorRegistryModule 1IResolverdeploy resolver contractRegister external resolver contractISchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorDepoy ProcessAttestationsReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 2find Validator for SchemaUIDvalidate attestation encodingabi.decodevalidateokfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefokReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 1find Validator for SchemaUIDNo Validator for this SchemaUIDfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefokpar[Register Schema & Resolver]par[Module Deployment]par[Attestations]par[Commit onchain Attestation]par[Create onchain Attestation] \ No newline at end of file +%0A%0A%0Aparticipantgroup%20%20**Attesters**%0Aparticipantgroup%20%23lightgray%20%0Aentity%20Attester%20X%0Aend%0Aparticipantgroup%20%23gray%20%0A%0Aentity%20Attester%20Y%0Aend%0Aend%0Aactor%20Module%20Developer%0A%0Aactor%20Schema%20Creator%202%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0A%0Aparticipant%20SSTORE2%0Adatabase%20IExternalResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0A%0Adatabase%20IExternalSchemaValidator%20%23lightgray%0Adatabase%20Module%201%20%23lightgray%0Apar%20%23white%20Register%20Schema%20%26%20Resolver%0AResolver%20Operator%20--%3E%3E%20*IExternalResolver%3A%20deploy%20resolver%20contract%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0Adestroy%20Resolver%20Operator%0A%0ASchema%20Creator%202%20--%3E%20*IExternalSchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator%202-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0Adestroy%20Schema%20Creator%202%0Adeactivate%20Registry%0Aend%0A%0Apar%20%23white%20Module%20Deployment%0AModule%20Developer-%3ERegistry%3A%20Depoy%20Process%0Aend%0A%3D%3DAttestations%3D%3D%0Apar%20%23yellow%20Attestations%0AAttester%20Y--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20Y%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23FFC300%20Commit%20onchain%20Attestation%0AAttester%20Y%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%202%0Aactivate%20Attester%20Y%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0ARegistry%20--%3EIExternalSchemaValidator%3A%20validate%20attestation%20encoding%0Aactivate%20IExternalSchemaValidator%0AIExternalSchemaValidator%20--%3E%3EIExternalSchemaValidator%3A%20abi.decode%5Cnvalidate%0AIExternalSchemaValidator%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalSchemaValidator%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIExternalResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IExternalResolver%0A%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20Y%3A%20ok%0Adeactivate%20Attester%20Y%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0A%0A%0AAttester%20X--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20X%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23FFC300%20Create%20onchain%20Attestation%0A%0AAttester%20X%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%201%0Aactivate%20Attester%20X%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0Anote%20right%20of%20Registry%3A%20No%20Validator%20for%20this%20SchemaUID%0A%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIExternalResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IExternalResolver%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20X%3A%20ok%0Adeactivate%20Attester%20X%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0Aend%0A%0A%0A AttestersAttester XAttester YModule DeveloperSchema Creator 2Resolver OperatorRegistryModule 1IExternalResolverdeploy resolver contractRegister external resolver contractIExternalSchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorDepoy ProcessAttestationsReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 2find Validator for SchemaUIDvalidate attestation encodingabi.decodevalidateokfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefokReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 1find Validator for SchemaUIDNo Validator for this SchemaUIDfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefokpar[Register Schema & Resolver]par[Module Deployment]par[Attestations]par[Commit onchain Attestation]par[Create onchain Attestation] \ No newline at end of file diff --git a/public/docs/module-registration.svg b/public/docs/module-registration.svg index edf5e646..9045ad51 100644 --- a/public/docs/module-registration.svg +++ b/public/docs/module-registration.svg @@ -1 +1 @@ -%0A%0Aactor%20Module%20Developer%0A%0Aactor%20Schema%20Creator%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0Adatabase%20IResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0Adatabase%20ISchemaValidator%20%23lightgray%0Adatabase%20Module%201%20%23lightgray%0A%0A%0A%0Apar%20%23white%20Register%20Resolver%20%26%20Schema%0A%0A%0A%0A%0AResolver%20Operator%20--%3E%3E%20*IResolver%3A%20deploy%20resolver%20contract%0A%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0A%0Aactivate%20Registry%0ARegistry%20--%3E%3EResolver%20Operator%3AAdded%20Resolver%0Adeactivate%20Registry%0Adestroy%20Resolver%20Operator%0A%0ASchema%20Creator%20--%3E%20*ISchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3E%3ESchema%20Creator%3AAdded%20Schema%202%0Adestroy%20Schema%20Creator%0Adeactivate%20Registry%0Aend%0A%0A%3D%3DModule%20Registration%20%3D%3D%0Apar%20%23yellow%20Module%20Registrations%0Athread%20Option1%3A%20Deploy%20with%20Registry%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Bytecode%20against%20Resolver%201%0Aactivate%20Registry%0ARegistry%20--%3E%20*Module%201%3A%20Deploy%20Bytecode%0ARegistry%20--%3EIResolver%3A%20validate%20module%5Cn%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Athread%20Option2%3A%20Register%20existing%20Module%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Address%20against%20Resolver%201%0Aactivate%20Registry%0A%0ARegistry%20--%3EIResolver%3A%20validate%20module%5Cn%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0A%0A%0ARegistry--%3EModule%20Developer%3A%20ok%0Adeactivate%20Registry%0A%0A%0A%0Athread%20Option3%3A%20Deploy%20with%20external%20Factory%0AModule%20Developer%20-%3ERegistry%3A%20Deploy%20bytecode%20with%20external%20Factory%0Aactivate%20Registry%0Anote%20right%20of%20Registry%3ACreate%20Salt%0ARegistry-%3E*External%20Factory%3A%20Deploy%20Bytecode%0AExternal%20Factory--%3ERegistry%3A%20Module%20Address%0Adestroy%20External%20Factory%0ARegistry%20--%3EIResolver%3A%20validate%20module%5Cn%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Aend%0A%0A%0Adestroy%20Module%20Developer%0AModule DeveloperSchema CreatorResolver OperatorRegistryIResolverdeploy resolver contractRegister external resolver contractAdded ResolverISchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorAdded Schema 2Module Registration Register Module Bytecode against Resolver 1Module 1Deploy Bytecodevalidate moduleBusiness LogiccallokokModule Registered on address xRegister Module Address against Resolver 1validate moduleBusiness LogiccallokokModule Registered on address xokDeploy bytecode with external FactoryCreate SaltExternal FactoryDeploy BytecodeModule Addressvalidate moduleBusiness LogiccallokokModule Registered on address xpar[Register Resolver & Schema]par[Module Registrations][Option1: Deploy with Registry][Option2: Register existing Module][Option3: Deploy with external Factory] \ No newline at end of file +%0A%0Aactor%20Module%20Developer%0A%0Aactor%20Schema%20Creator%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0Adatabase%20IExternalResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0Adatabase%20IExternalSchemaValidator%20%23lightgray%0Adatabase%20Module%201%20%23lightgray%0A%0A%0A%0Apar%20%23white%20Register%20Resolver%20%26%20Schema%0A%0A%0A%0A%0AResolver%20Operator%20--%3E%3E%20*IExternalResolver%3A%20deploy%20resolver%20contract%0A%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0A%0Aactivate%20Registry%0ARegistry%20--%3E%3EResolver%20Operator%3AAdded%20Resolver%0Adeactivate%20Registry%0Adestroy%20Resolver%20Operator%0A%0ASchema%20Creator%20--%3E%20*IExternalSchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3E%3ESchema%20Creator%3AAdded%20Schema%202%0Adestroy%20Schema%20Creator%0Adeactivate%20Registry%0Aend%0A%0A%3D%3DModule%20Registration%20%3D%3D%0Apar%20%23yellow%20Module%20Registrations%0Athread%20Option1%3A%20Deploy%20with%20Registry%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Bytecode%20against%20Resolver%201%0Aactivate%20Registry%0ARegistry%20--%3E%20*Module%201%3A%20Deploy%20Bytecode%0ARegistry%20--%3EIExternalResolver%3A%20validate%20module%5Cn%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Athread%20Option2%3A%20Register%20existing%20Module%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Address%20against%20Resolver%201%0Aactivate%20Registry%0A%0ARegistry%20--%3EIExternalResolver%3A%20validate%20module%5Cn%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0A%0A%0ARegistry--%3EModule%20Developer%3A%20ok%0Adeactivate%20Registry%0A%0A%0A%0Athread%20Option3%3A%20Deploy%20with%20external%20Factory%0AModule%20Developer%20-%3ERegistry%3A%20Deploy%20bytecode%20with%20external%20Factory%0Aactivate%20Registry%0Anote%20right%20of%20Registry%3ACreate%20Salt%0ARegistry-%3E*External%20Factory%3A%20Deploy%20Bytecode%0AExternal%20Factory--%3ERegistry%3A%20Module%20Address%0Adestroy%20External%20Factory%0ARegistry%20--%3EIExternalResolver%3A%20validate%20module%5Cn%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Aend%0A%0A%0Adestroy%20Module%20Developer%0AModule DeveloperSchema CreatorResolver OperatorRegistryIExternalResolverdeploy resolver contractRegister external resolver contractAdded ResolverIExternalSchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorAdded Schema 2Module Registration Register Module Bytecode against Resolver 1Module 1Deploy Bytecodevalidate moduleBusiness LogiccallokokModule Registered on address xRegister Module Address against Resolver 1validate moduleBusiness LogiccallokokModule Registered on address xokDeploy bytecode with external FactoryCreate SaltExternal FactoryDeploy BytecodeModule Addressvalidate moduleBusiness LogiccallokokModule Registered on address xpar[Register Resolver & Schema]par[Module Registrations][Option1: Deploy with Registry][Option2: Register existing Module][Option3: Deploy with external Factory] \ No newline at end of file diff --git a/public/docs/schema.svg b/public/docs/schema.svg index f977401f..95650852 100644 --- a/public/docs/schema.svg +++ b/public/docs/schema.svg @@ -1 +1 @@ -%0A%0Aactor%20Schema%20Creator%201%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0A%0Aparticipant%20SSTORE2%0Adatabase%20IResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0A%0Adatabase%20ISchemaValidator%20%23lightgray%0Apar%20Register%20Schema%20and%20Resolver%0AResolver%20Operator%20--%3E%3E%20*IResolver%3A%20deploy%20resolver%20contract%0A%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0A%0Aactivate%20Registry%0ARegistry%20--%3E%3EResolver%20Operator%3AAdded%20Resolver%0Adeactivate%20Registry%0Adestroy%20Resolver%20Operator%0Aspace%0Aspace%20%0Aspace%0A%0ASchema%20Creator%201%20--%3E%20*ISchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator%201-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3E%3ESchema%20Creator%201%3AAdded%20Schema%20%0Adestroy%20Schema%20Creator%201%0Adeactivate%20Registry%0A%0AendSchema Creator 1Resolver OperatorRegistrySSTORE2Business LogicIResolverdeploy resolver contractRegister external resolver contractAdded ResolverISchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorAdded Schema par[Register Schema and Resolver] \ No newline at end of file +%0A%0Aactor%20Schema%20Creator%201%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0A%0Aparticipant%20SSTORE2%0Adatabase%20IExternalResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0A%0Adatabase%20IExternalSchemaValidator%20%23lightgray%0Apar%20Register%20Schema%20and%20Resolver%0AResolver%20Operator%20--%3E%3E%20*IExternalResolver%3A%20deploy%20resolver%20contract%0A%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0A%0Aactivate%20Registry%0ARegistry%20--%3E%3EResolver%20Operator%3AAdded%20Resolver%0Adeactivate%20Registry%0Adestroy%20Resolver%20Operator%0Aspace%0Aspace%20%0Aspace%0A%0ASchema%20Creator%201%20--%3E%20*IExternalSchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator%201-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3E%3ESchema%20Creator%201%3AAdded%20Schema%20%0Adestroy%20Schema%20Creator%201%0Adeactivate%20Registry%0A%0AendSchema Creator 1Resolver OperatorRegistrySSTORE2Business LogicIExternalResolverdeploy resolver contractRegister external resolver contractAdded ResolverIExternalSchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorAdded Schema par[Register Schema and Resolver] \ No newline at end of file diff --git a/public/docs/sequence.svg b/public/docs/sequence.svg index c5c3b6d5..2f05216c 100644 --- a/public/docs/sequence.svg +++ b/public/docs/sequence.svg @@ -1 +1 @@ -%0Aparticipantgroup%20%23pink%20**Registry%5CnAdapters**%0Aactor%20Smart%20Account%0Aend%0A%0Aparticipantgroup%20%20**Attesters**%0Aparticipantgroup%20%23lightgray%20%0Aentity%20Attester%20X%0Aend%0Aparticipantgroup%20%23gray%20%0A%0Aentity%20Attester%20Y%0Aend%0Aend%0Aactor%20Module%20Developer%0A%0Aactor%20Schema%20Creator%201%0Aactor%20Schema%20Creator%202%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0A%0Aparticipant%20SSTORE2%0Adatabase%20IResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0A%0Adatabase%20ISchemaValidator%20%23lightgray%0Adatabase%20Module%201%20%23lightgray%0AResolver%20Operator%20--%3E%3E%20*IResolver%3A%20deploy%20resolver%20contract%0A%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0A%0Aactivate%20Registry%0ARegistry%20--%3E%3EResolver%20Operator%3AAdded%20Resolver%0Adeactivate%20Registry%0Adestroy%20Resolver%20Operator%0Aspace%20%0ASchema%20Creator%201%20-%3ERegistry%3A%20Create%20Schema%20without%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3ESchema%20Creator%201%3AAdded%20Schema%201%0Adestroy%20Schema%20Creator%201%0Adeactivate%20Registry%0Aspace%0A%0ASchema%20Creator%202%20--%3E%20*ISchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator%202-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3E%3ESchema%20Creator%202%3AAdded%20Schema%202%0Adestroy%20Schema%20Creator%202%0Adeactivate%20Registry%0Aspace%20%0Aspace%20%0A%3D%3DModule%20Registration%20%3D%3D%0Apar%20%23white%20Module%20Registrations%0Athread%20Option1%3A%20Deploy%20with%20Registry%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Bytecode%20against%20Resolver%201%0Aactivate%20Registry%0ARegistry%20--%3E%20*Module%201%3A%20Deploy%20Bytecode%0ARegistry%20--%3EIResolver%3A%20validate%20module%5Cn%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Athread%20Option2%3A%20Register%20existing%20Module%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Address%20against%20Resolver%201%0Aactivate%20Registry%0A%0ARegistry%20--%3EIResolver%3A%20validate%20module%5Cn%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0A%0A%0ARegistry--%3EModule%20Developer%3A%20ok%0Adeactivate%20Registry%0A%0A%0A%0Athread%20Option3%3A%20Deploy%20with%20external%20Factory%0AModule%20Developer%20-%3ERegistry%3A%20Deploy%20bytecode%20with%20external%20Factory%0Aactivate%20Registry%0Anote%20right%20of%20Registry%3ACreate%20Salt%0ARegistry-%3E*External%20Factory%3A%20Deploy%20Bytecode%0AExternal%20Factory--%3ERegistry%3A%20Module%20Address%0Adestroy%20External%20Factory%0ARegistry%20--%3EIResolver%3A%20validate%20module%5Cn%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Aend%0A%0A%0Adestroy%20Module%20Developer%0A%0Aspace%20%0Aspace%0Aspace%0A%3D%3DAttestations%3D%3D%0A%0AAttester%20Y--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20Y%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23white%20Commit%20onchain%20Attestation%0AAttester%20Y%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%202%0Aactivate%20Attester%20Y%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0ARegistry%20--%3EISchemaValidator%3A%20validate%20attestation%20encoding%0Aactivate%20ISchemaValidator%0AISchemaValidator%20--%3E%3EISchemaValidator%3A%20abi.decode%5Cnvalidate%0AISchemaValidator%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20ISchemaValidator%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IResolver%0A%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20Y%3A%20ok%0Adeactivate%20Attester%20Y%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0A%0A%0AAttester%20X--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20X%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23white%20Create%20onchain%20Attestation%0A%0AAttester%20X%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%201%0Aactivate%20Attester%20X%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0Anote%20right%20of%20Registry%3A%20No%20Validator%20for%20this%20SchemaUID%0A%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IResolver%0AIResolver--%3E*Business%20Logic%3Acall%0AIResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IResolver%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20X%3A%20ok%0Adeactivate%20Attester%20X%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0A%0A%0A%0A%0A%3D%3D%20ERC-7484%3D%3D%0A%0A%0A%0A%0A%0Apar%20%23pink%20ERC-7484%0Aactivate%20Smart%20Account%0Anote%20left%20of%20Smart%20Account%23red%3A%20Smart%20Account%20Execution%0ASmart%20Account%20--%3ESmart%20Account%3A%20get%20trusted%20attester(s)%0ASmart%20Account%20-%3ERegistry%3A%20ERC-7484%0Aactivate%20Registry%0A%0ARegistry%20--%3E%3ERegistry%3A%20look%20up%20attestation%5Cnwith%20ERC-7484%20constraints%0ARegistry%20--%3ESmart%20Account%3A%20ok%0Adeactivate%20Registry%0ASmart%20Account%20-%3EModule%201%3A%20exec%0Adeactivate%20Smart%20Account%0AendRegistryAdapters AttestersSmart AccountAttester XAttester YModule DeveloperSchema Creator 1Schema Creator 2Resolver OperatorRegistryIResolverdeploy resolver contractRegister external resolver contractAdded ResolverCreate Schema without SchemaValidatorAdded Schema 1ISchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorAdded Schema 2Module Registration Register Module Bytecode against Resolver 1Module 1Deploy Bytecodevalidate moduleBusiness LogiccallokokModule Registered on address xRegister Module Address against Resolver 1validate moduleBusiness LogiccallokokModule Registered on address xokDeploy bytecode with external FactoryCreate SaltExternal FactoryDeploy BytecodeModule Addressvalidate moduleBusiness LogiccallokokModule Registered on address xAttestationsReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 2find Validator for SchemaUIDvalidate attestation encodingabi.decodevalidateokfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefokReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 1find Validator for SchemaUIDNo Validator for this SchemaUIDfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefok ERC-7484Smart Account Executionget trusted attester(s)ERC-7484look up attestationwith ERC-7484 constraintsokexecpar[Module Registrations][Option1: Deploy with Registry][Option2: Register existing Module][Option3: Deploy with external Factory]par[Commit onchain Attestation]par[Create onchain Attestation]par[ERC-7484] \ No newline at end of file +%0Aparticipantgroup%20%23pink%20**Registry%5CnAdapters**%0Aactor%20Smart%20Account%0Aend%0A%0Aparticipantgroup%20%20**Attesters**%0Aparticipantgroup%20%23lightgray%20%0Aentity%20Attester%20X%0Aend%0Aparticipantgroup%20%23gray%20%0A%0Aentity%20Attester%20Y%0Aend%0Aend%0Aactor%20Module%20Developer%0A%0Aactor%20Schema%20Creator%201%0Aactor%20Schema%20Creator%202%0A%0A%0Aactor%20Resolver%20Operator%0A%0A%0A%0A%0Aparticipantgroup%20%23lightblue%0Adatabase%20Registry%0Aend%0A%0Aparticipant%20SSTORE2%0Adatabase%20IExternalResolver%20%23lightgray%0Aparticipant%20Business%20Logic%0A%0Adatabase%20IExternalSchemaValidator%20%23lightgray%0Adatabase%20Module%201%20%23lightgray%0AResolver%20Operator%20--%3E%3E%20*IExternalResolver%3A%20deploy%20resolver%20contract%0A%0AResolver%20Operator%20-%3E%20Registry%3A%20Register%20external%20resolver%20contract%0A%0Aactivate%20Registry%0ARegistry%20--%3E%3EResolver%20Operator%3AAdded%20Resolver%0Adeactivate%20Registry%0Adestroy%20Resolver%20Operator%0Aspace%20%0ASchema%20Creator%201%20-%3ERegistry%3A%20Create%20Schema%20without%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3ESchema%20Creator%201%3AAdded%20Schema%201%0Adestroy%20Schema%20Creator%201%0Adeactivate%20Registry%0Aspace%0A%0ASchema%20Creator%202%20--%3E%20*IExternalSchemaValidator%3A%20deploy%20external%20SchemaValidator%20contract%0ASchema%20Creator%202-%3ERegistry%3ACreate%20Schema%20and%20register%20external%20SchemaValidator%0Aactivate%20Registry%0ARegistry%20--%3E%3ESchema%20Creator%202%3AAdded%20Schema%202%0Adestroy%20Schema%20Creator%202%0Adeactivate%20Registry%0Aspace%20%0Aspace%20%0A%3D%3DModule%20Registration%20%3D%3D%0Apar%20%23white%20Module%20Registrations%0Athread%20Option1%3A%20Deploy%20with%20Registry%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Bytecode%20against%20Resolver%201%0Aactivate%20Registry%0ARegistry%20--%3E%20*Module%201%3A%20Deploy%20Bytecode%0ARegistry%20--%3EIExternalResolver%3A%20validate%20module%5Cn%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Athread%20Option2%3A%20Register%20existing%20Module%0AModule%20Developer%20-%3ERegistry%3A%20Register%20Module%20Address%20against%20Resolver%201%0Aactivate%20Registry%0A%0ARegistry%20--%3EIExternalResolver%3A%20validate%20module%5Cn%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0A%0A%0ARegistry--%3EModule%20Developer%3A%20ok%0Adeactivate%20Registry%0A%0A%0A%0Athread%20Option3%3A%20Deploy%20with%20external%20Factory%0AModule%20Developer%20-%3ERegistry%3A%20Deploy%20bytecode%20with%20external%20Factory%0Aactivate%20Registry%0Anote%20right%20of%20Registry%3ACreate%20Salt%0ARegistry-%3E*External%20Factory%3A%20Deploy%20Bytecode%0AExternal%20Factory--%3ERegistry%3A%20Module%20Address%0Adestroy%20External%20Factory%0ARegistry%20--%3EIExternalResolver%3A%20validate%20module%5Cn%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalResolver%0ARegistry%20--%3EModule%20Developer%3AModule%20Registered%20on%20address%20x%0Adeactivate%20Registry%0Aend%0A%0A%0Adestroy%20Module%20Developer%0A%0Aspace%20%0Aspace%0Aspace%0A%3D%3DAttestations%3D%3D%0A%0AAttester%20Y--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20Y%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23white%20Commit%20onchain%20Attestation%0AAttester%20Y%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%202%0Aactivate%20Attester%20Y%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0ARegistry%20--%3EIExternalSchemaValidator%3A%20validate%20attestation%20encoding%0Aactivate%20IExternalSchemaValidator%0AIExternalSchemaValidator%20--%3E%3EIExternalSchemaValidator%3A%20abi.decode%5Cnvalidate%0AIExternalSchemaValidator%20--%3E%3ERegistry%3A%20ok%0Adeactivate%20IExternalSchemaValidator%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIExternalResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IExternalResolver%0A%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20Y%3A%20ok%0Adeactivate%20Attester%20Y%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0A%0A%0AAttester%20X--%3E%3EModule%201%3A%20Review%20Module%20Code%0Anote%20over%20Attester%20X%20%23orange%3A%20Assessment%5Cnof%20Module%201%20%0Apar%20%23white%20Create%20onchain%20Attestation%0A%0AAttester%20X%20-%3ERegistry%3AAttest%20to%20Module%20%201%5CnUsing%20Schema%20ID%201%0Aactivate%20Attester%20X%0Aactivate%20Registry%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Validator%20for%20SchemaUID%0Anote%20right%20of%20Registry%3A%20No%20Validator%20for%20this%20SchemaUID%0A%0Aspace%20%0ARegistry%20--%3E%3ERegistry%3A%20find%20Resolver%20for%20Module%0ARegistry%20--%3EIExternalResolver%3A%20validate%20attestation%20with%20%5Cn%20external%20business%20logic%0Aactivate%20IExternalResolver%0AIExternalResolver--%3E*Business%20Logic%3Acall%0AIExternalResolver%3C-Business%20Logic%3Aok%0Adestroy%20Business%20Logic%0AIExternalResolver%20--%3E%3ERegistry%3Aok%0Adeactivate%20IExternalResolver%0Aspace%0A%0ARegistry--%3E*SSTORE2%3A%3C%3Cbytes%20data%3E%3E%0ARegistry%3C-SSTORE2%3A%20AttestationDataRef%0Adestroy%20SSTORE2%0ARegistry%20--%3E%3EAttester%20X%3A%20ok%0Adeactivate%20Attester%20X%0Adeactivate%20Registry%0Aspace%20%0Aend%0A%0A%0A%0A%0A%0A%0A%3D%3D%20ERC-7484%3D%3D%0A%0A%0A%0A%0A%0Apar%20%23pink%20ERC-7484%0Aactivate%20Smart%20Account%0Anote%20left%20of%20Smart%20Account%23red%3A%20Smart%20Account%20Execution%0ASmart%20Account%20--%3ESmart%20Account%3A%20get%20trusted%20attester(s)%0ASmart%20Account%20-%3ERegistry%3A%20ERC-7484%0Aactivate%20Registry%0A%0ARegistry%20--%3E%3ERegistry%3A%20look%20up%20attestation%5Cnwith%20ERC-7484%20constraints%0ARegistry%20--%3ESmart%20Account%3A%20ok%0Adeactivate%20Registry%0ASmart%20Account%20-%3EModule%201%3A%20exec%0Adeactivate%20Smart%20Account%0AendRegistryAdapters AttestersSmart AccountAttester XAttester YModule DeveloperSchema Creator 1Schema Creator 2Resolver OperatorRegistryIExternalResolverdeploy resolver contractRegister external resolver contractAdded ResolverCreate Schema without SchemaValidatorAdded Schema 1IExternalSchemaValidatordeploy external SchemaValidator contractCreate Schema and register external SchemaValidatorAdded Schema 2Module Registration Register Module Bytecode against Resolver 1Module 1Deploy Bytecodevalidate moduleBusiness LogiccallokokModule Registered on address xRegister Module Address against Resolver 1validate moduleBusiness LogiccallokokModule Registered on address xokDeploy bytecode with external FactoryCreate SaltExternal FactoryDeploy BytecodeModule Addressvalidate moduleBusiness LogiccallokokModule Registered on address xAttestationsReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 2find Validator for SchemaUIDvalidate attestation encodingabi.decodevalidateokfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefokReview Module CodeAssessmentof Module 1 Attest to Module  1Using Schema ID 1find Validator for SchemaUIDNo Validator for this SchemaUIDfind Resolver for Modulevalidate attestation with  external business logicBusiness LogiccallokokSSTORE2<<bytes data>>AttestationDataRefok ERC-7484Smart Account Executionget trusted attester(s)ERC-7484look up attestationwith ERC-7484 constraintsokexecpar[Module Registrations][Option1: Deploy with Registry][Option2: Register existing Module][Option3: Deploy with external Factory]par[Commit onchain Attestation]par[Create onchain Attestation]par[ERC-7484] \ No newline at end of file diff --git a/script/DeployRegistry.s.sol.bak b/script/DeployRegistry.s.sol.bak index 097f6ac1..39228d8d 100644 --- a/script/DeployRegistry.s.sol.bak +++ b/script/DeployRegistry.s.sol.bak @@ -4,7 +4,7 @@ pragma solidity ^0.8.19; import { Script } from "forge-std/Script.sol"; import { RegistryTestTools, RegistryInstance } from "../test/utils/BaseUtils.sol"; import { DebugResolver } from "../src/external/examples/DebugResolver.sol"; -import { IResolver } from "../src/external/IResolver.sol"; +import { IExternalResolver } from "../src/external/IExternalResolver.sol"; import { ResolverUID } from "../src/DataTypes.sol"; import { console2 } from "forge-std/console2.sol"; @@ -23,7 +23,7 @@ contract DeployRegistryScript is Script, RegistryTestTools { // Set up default resolver DebugResolver debugResolver = new DebugResolver{ salt: salt }(address(instance.registry)); - instance.registry.registerResolver(IResolver(address(debugResolver))); + instance.registry.registerResolver(IExternalResolver(address(debugResolver))); vm.stopBroadcast(); } diff --git a/src/DataTypes.sol b/src/DataTypes.sol index e4055640..a2036790 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ISchemaValidator } from "./external/ISchemaValidator.sol"; -import { IResolver } from "./external/IResolver.sol"; +import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.sol"; +import { IExternalResolver } from "./external/IExternalResolver.sol"; import { SSTORE2 } from "solady/utils/SSTORE2.sol"; /*////////////////////////////////////////////////////////////// @@ -30,12 +30,12 @@ struct ModuleRecord { struct SchemaRecord { uint48 registeredAt; // The time when the schema was registered (Unix timestamp). - ISchemaValidator validator; // Optional external schema validator. + IExternalSchemaValidator validator; // Optional external schema validator. string schema; // Custom specification of the schema (e.g., an ABI). } struct ResolverRecord { - IResolver resolver; // Optional resolver. + IExternalResolver resolver; // Optional resolver. address resolverOwner; // The address of the account used to register the resolver. } diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 2a6d7f41..633de9d7 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -11,8 +11,8 @@ import { ModuleType } from "./DataTypes.sol"; -import { ISchemaValidator } from "./external/ISchemaValidator.sol"; -import { IResolver } from "./external/IResolver.sol"; +import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.sol"; +import { IExternalResolver } from "./external/IExternalResolver.sol"; interface IRegistry { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -125,7 +125,7 @@ interface IRegistry { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function registerSchema( string calldata schema, - ISchemaValidator validator // OPTIONAL + IExternalSchemaValidator validator // OPTIONAL ) external returns (SchemaUID uid); @@ -134,9 +134,9 @@ interface IRegistry { /* Manage Resolvers */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - function registerResolver(IResolver _resolver) external returns (ResolverUID uid); + function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid); - function setResolver(ResolverUID uid, IResolver resolver) external; + function setResolver(ResolverUID uid, IExternalResolver resolver) external; // Event triggered when a module is deployed. event ModuleRegistration( diff --git a/src/base/Attestation.sol.bak b/src/base/Attestation.sol.bak index fb0b99b9..5378343d 100644 --- a/src/base/Attestation.sol.bak +++ b/src/base/Attestation.sol.bak @@ -11,7 +11,7 @@ import { MultiRevocationRequest, AttestationLib } from "../interface/IAttestation.sol"; -import { SchemaUID, ResolverUID, SchemaRecord, ISchemaValidator } from "./Schema.sol"; +import { SchemaUID, ResolverUID, SchemaRecord, IExternalSchemaValidator } from "./Schema.sol"; import { ModuleRecord } from "./Module.sol"; import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; import { @@ -254,10 +254,10 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); // validate Schema - ISchemaValidator validator = schema.validator; + IExternalSchemaValidator validator = schema.validator; // if validator is set, call the validator if (address(validator) != ZERO_ADDRESS && validator.validateSchema(requestData) == false) { - // revert if ISchemaValidator returns false + // revert if IExternalSchemaValidator returns false revert InvalidAttestation(); } } @@ -273,7 +273,7 @@ abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGua SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); // validate Schema - ISchemaValidator validator = schema.validator; + IExternalSchemaValidator validator = schema.validator; // if validator is set, call the validator if (address(validator) != ZERO_ADDRESS && validator.validateSchema(requestDatas) == false) { revert InvalidAttestation(); diff --git a/src/base/AttestationResolve.sol.bak b/src/base/AttestationResolve.sol.bak index 39e8a983..af2ee6cf 100644 --- a/src/base/AttestationResolve.sol.bak +++ b/src/base/AttestationResolve.sol.bak @@ -9,7 +9,7 @@ import { EIP712Verifier } from "./EIP712Verifier.sol"; import { ZERO_ADDRESS } from "../Common.sol"; import { AttestationRecord, SchemaRecord, ModuleRecord, ResolverRecord } from "../DataTypes.sol"; -import { IResolver } from "../external/IResolver.sol"; +import { IExternalResolver } from "../external/IExternalResolver.sol"; /** * @title AttestationResolve @@ -43,7 +43,7 @@ abstract contract AttestationResolve is IAttestation, EIP712Verifier { returns (uint256) { ResolverRecord memory resolver = getResolver(resolverUID); - IResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = resolver.resolver; if (address(resolverContract) == ZERO_ADDRESS) { // Ensure that we don't accept payments if there is no resolver. @@ -67,12 +67,12 @@ abstract contract AttestationResolve is IAttestation, EIP712Verifier { availableValue -= value; } - // Resolve a revocation with external IResolver + // Resolve a revocation with external IExternalResolver if (isRevocation) { if (!resolverContract.revoke{ value: value }(attestationRecord)) { revert InvalidRevocation(); } - // Resolve an attestation with external IResolver + // Resolve an attestation with external IExternalResolver } else if (!resolverContract.attest{ value: value }(attestationRecord)) { revert InvalidAttestation(); } @@ -120,7 +120,7 @@ abstract contract AttestationResolve is IAttestation, EIP712Verifier { }); } ResolverRecord memory resolver = getResolver({ resolverUID: resolverUID }); - IResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = resolver.resolver; if (address(resolverContract) == ZERO_ADDRESS) { // Ensure that we don't accept payments if there is no resolver. for (uint256 i; i < length; ++i) { @@ -150,13 +150,13 @@ abstract contract AttestationResolve is IAttestation, EIP712Verifier { } } - // Resolve a revocation with external IResolver + // Resolve a revocation with external IExternalResolver if (isRevocation) { if (!resolverContract.multiRevoke{ value: totalUsedValue }(attestationRecords, values)) { revert InvalidRevocations(); } - // Resolve an attestation with external IResolver + // Resolve an attestation with external IExternalResolver } else if ( !resolverContract.multiAttest{ value: totalUsedValue }(attestationRecords, values) ) { diff --git a/src/base/AttestationSimple.sol.bak b/src/base/AttestationSimple.sol.bak index 670951fb..01d467d6 100644 --- a/src/base/AttestationSimple.sol.bak +++ b/src/base/AttestationSimple.sol.bak @@ -11,7 +11,7 @@ import { MultiRevocationRequest, AttestationLib } from "../interface/IAttestation.sol"; -import { SchemaUID, ResolverUID, SchemaRecord, ISchemaValidator } from "./Schema.sol"; +import { SchemaUID, ResolverUID, SchemaRecord, IExternalSchemaValidator } from "./Schema.sol"; import { ModuleRecord } from "./Module.sol"; import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; import { @@ -154,10 +154,10 @@ contract Attestation { SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); // validate Schema - ISchemaValidator validator = schema.validator; + IExternalSchemaValidator validator = schema.validator; // if validator is set, call the validator if (address(validator) != ZERO_ADDRESS && validator.validateSchema(record) == false) { - // revert if ISchemaValidator returns false + // revert if IExternalSchemaValidator returns false revert InvalidAttestation(); } } @@ -173,10 +173,10 @@ contract Attestation { SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); // validate Schema - ISchemaValidator validator = schema.validator; + IExternalSchemaValidator validator = schema.validator; // if validator is set, call the validator if (address(validator) != ZERO_ADDRESS && validator.validateSchema(records) == false) { - // revert if ISchemaValidator returns false + // revert if IExternalSchemaValidator returns false revert InvalidAttestation(); } } diff --git a/src/base/Module.sol.bak b/src/base/Module.sol.bak index 0dc7ea35..5cb8020e 100644 --- a/src/base/Module.sol.bak +++ b/src/base/Module.sol.bak @@ -7,7 +7,7 @@ import { CREATE3 } from "solady/utils/CREATE3.sol"; import { IModule } from "../interface/IModule.sol"; import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; -import { IResolver } from "../external/IResolver.sol"; +import { IExternalResolver } from "../external/IExternalResolver.sol"; import { InvalidResolver, _isContract, ZERO_ADDRESS } from "../Common.sol"; import { ResolverRecord, ModuleRecord, ResolverUID } from "../DataTypes.sol"; @@ -205,7 +205,7 @@ abstract contract Module is IModule, ReentrancyGuard { * @param moduleRegistration Module record to be registered. */ function _resolveRegistration( - IResolver resolverContract, + IExternalResolver resolverContract, ModuleRecord memory moduleRegistration ) private diff --git a/src/base/Schema.sol.bak b/src/base/Schema.sol.bak index 24eb6cc3..6bf13ab3 100644 --- a/src/base/Schema.sol.bak +++ b/src/base/Schema.sol.bak @@ -3,8 +3,8 @@ pragma solidity ^0.8.19; import { AccessDenied, _time, ZERO_TIMESTAMP, ZERO_ADDRESS, InvalidResolver } from "../Common.sol"; import { ISchema, SchemaLib } from "../interface/ISchema.sol"; -import { IResolver } from "../external/IResolver.sol"; -import { ISchemaValidator } from "../external/ISchemaValidator.sol"; +import { IExternalResolver } from "../external/IExternalResolver.sol"; +import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; import { SchemaRecord, ResolverRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; @@ -28,7 +28,7 @@ abstract contract Schema is ISchema { */ function registerSchema( string calldata schema, - ISchemaValidator validator // OPTIONAL + IExternalSchemaValidator validator // OPTIONAL ) external returns (SchemaUID uid) @@ -50,7 +50,7 @@ abstract contract Schema is ISchema { /** * @inheritdoc ISchema */ - function registerResolver(IResolver _resolver) external returns (ResolverUID uid) { + function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid) { if (address(_resolver) == ZERO_ADDRESS) revert InvalidResolver(); // build a ResolverRecord from the input @@ -74,7 +74,7 @@ abstract contract Schema is ISchema { /** * @inheritdoc ISchema */ - function setResolver(ResolverUID uid, IResolver resolver) external onlyResolverOwner(uid) { + function setResolver(ResolverUID uid, IExternalResolver resolver) external onlyResolverOwner(uid) { ResolverRecord storage referrer = _resolvers[uid]; referrer.resolver = resolver; emit NewSchemaResolver(uid, address(resolver)); diff --git a/src/external/IResolver.sol b/src/external/IExternalResolver.sol similarity index 97% rename from src/external/IResolver.sol rename to src/external/IExternalResolver.sol index 4e951528..b52ae529 100644 --- a/src/external/IResolver.sol +++ b/src/external/IExternalResolver.sol @@ -10,7 +10,7 @@ import { IERC165 } from "forge-std/interfaces/IERC165.sol"; * @dev The resolver is also responsible for processing the attestation and revocation requests. * */ -interface IResolver is IERC165 { +interface IExternalResolver is IERC165 { /** * @dev Returns whether the resolver supports ETH transfers. */ diff --git a/src/external/ISchemaValidator.sol b/src/external/IExternalSchemaValidator.sol similarity index 92% rename from src/external/ISchemaValidator.sol rename to src/external/IExternalSchemaValidator.sol index d417035f..68bd61dd 100644 --- a/src/external/ISchemaValidator.sol +++ b/src/external/IExternalSchemaValidator.sol @@ -7,7 +7,7 @@ import { IERC165 } from "forge-std/interfaces/IERC165.sol"; /** * @title The interface of an optional schema resolver. */ -interface ISchemaValidator is IERC165 { +interface IExternalSchemaValidator is IERC165 { /** * @notice Validates an attestation request. */ diff --git a/src/external/ResolverBase.sol.bak b/src/external/ResolverBase.sol.bak index 19a077cc..2c156fd0 100644 --- a/src/external/ResolverBase.sol.bak +++ b/src/external/ResolverBase.sol.bak @@ -3,14 +3,14 @@ pragma solidity ^0.8.19; import { AccessDenied, ZERO_ADDRESS } from "../Common.sol"; import { AttestationRecord, ModuleRecord } from "../DataTypes.sol"; -import { IResolver } from "./IResolver.sol"; +import { IExternalResolver } from "./IExternalResolver.sol"; /** * @title A base resolver contract * * @author zeroknots.eth */ -abstract contract ResolverBase is IResolver { +abstract contract ResolverBase is IExternalResolver { error InsufficientValue(); error NotPayable(); error InvalidRS(); @@ -42,7 +42,7 @@ abstract contract ResolverBase is IResolver { } /** - * @inheritdoc IResolver + * @inheritdoc IExternalResolver */ function isPayable() public pure virtual returns (bool) { return false; @@ -58,7 +58,7 @@ abstract contract ResolverBase is IResolver { } /** - * @inheritdoc IResolver + * @inheritdoc IExternalResolver */ function attest(AttestationRecord calldata attestation) external @@ -70,7 +70,7 @@ abstract contract ResolverBase is IResolver { } /** - * @inheritdoc IResolver + * @inheritdoc IExternalResolver */ function moduleRegistration(ModuleRecord calldata module) external @@ -82,7 +82,7 @@ abstract contract ResolverBase is IResolver { } /** - * @inheritdoc IResolver + * @inheritdoc IExternalResolver */ function multiAttest( AttestationRecord[] calldata attestations, @@ -123,7 +123,7 @@ abstract contract ResolverBase is IResolver { } /** - * @inheritdoc IResolver + * @inheritdoc IExternalResolver */ function revoke(AttestationRecord calldata attestation) external @@ -135,7 +135,7 @@ abstract contract ResolverBase is IResolver { } /** - * @inheritdoc IResolver + * @inheritdoc IExternalResolver */ function multiRevoke( AttestationRecord[] calldata attestations, diff --git a/src/external/SchemaValidatorBase.sol.bak b/src/external/SchemaValidatorBase.sol.bak index c494224a..ea22bdbf 100644 --- a/src/external/SchemaValidatorBase.sol.bak +++ b/src/external/SchemaValidatorBase.sol.bak @@ -1,13 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ISchemaValidator, AttestationRequest } from "./ISchemaValidator.sol"; +import { IExternalSchemaValidator, AttestationRequest } from "./IExternalSchemaValidator.sol"; /** * @title SchemaValidatorBase * @notice Base contract for schema validators */ -contract SchemaValidatorBase is ISchemaValidator { +contract SchemaValidatorBase is IExternalSchemaValidator { function validateSchema(AttestationRequest calldata attestation) external view diff --git a/src/external/examples/SimpleValidator.sol.bak b/src/external/examples/SimpleValidator.sol.bak index e662e628..78caf7df 100644 --- a/src/external/examples/SimpleValidator.sol.bak +++ b/src/external/examples/SimpleValidator.sol.bak @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import { ISchemaValidator } from "../ISchemaValidator.sol"; +import { IExternalSchemaValidator } from "../IExternalSchemaValidator.sol"; import { AttestationRequest } from "../../DataTypes.sol"; /** @@ -9,7 +9,7 @@ import { AttestationRequest } from "../../DataTypes.sol"; * @author zeroknots * @notice A simple validator that always returns true. */ -contract SimpleValidator is ISchemaValidator { +contract SimpleValidator is IExternalSchemaValidator { function validateSchema(AttestationRequest calldata attestation) external pure diff --git a/src/interface/ISchema.sol.bak b/src/interface/ISchema.sol.bak index 91ba4a16..373aa069 100644 --- a/src/interface/ISchema.sol.bak +++ b/src/interface/ISchema.sol.bak @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { IResolver } from "../external/IResolver.sol"; -import { ISchemaValidator } from "../external/ISchemaValidator.sol"; +import { IExternalResolver } from "../external/IExternalResolver.sol"; +import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; import { SchemaUID, SchemaRecord, ResolverUID, ResolverRecord } from "../DataTypes.sol"; /** @@ -43,7 +43,7 @@ interface ISchema { */ function registerSchema( string calldata schema, - ISchemaValidator validator + IExternalSchemaValidator validator ) external returns (SchemaUID); @@ -53,11 +53,11 @@ interface ISchema { * @dev This function allows the registration of a resolver by computing a unique ID and associating it with the owner. * Emits a SchemaResolverRegistered event upon successful registration. * - * @param _resolver Address of the IResolver to be registered. + * @param _resolver Address of the IExternalResolver to be registered. * * @return uid The unique ID (ResolverUID) associated with the registered resolver. */ - function registerResolver(IResolver _resolver) external returns (ResolverUID); + function registerResolver(IExternalResolver _resolver) external returns (ResolverUID); /** * @notice Updates the resolver for a given UID. @@ -67,7 +67,7 @@ interface ISchema { * @param uid The UID of the schema to update. * @param resolver The new resolver interface. */ - function setResolver(ResolverUID uid, IResolver resolver) external; + function setResolver(ResolverUID uid, IExternalResolver resolver) external; /** * @notice Retrieves the schema record for a given UID. diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index 00779a97..f7b01705 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.19; import { AttestationRecord, ResolverRecord, SchemaRecord, ModuleRecord } from "../DataTypes.sol"; -import { ISchemaValidator } from "../external/ISchemaValidator.sol"; -import { IResolver } from "../external/IResolver.sol"; +import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; +import { IExternalResolver } from "../external/IExternalResolver.sol"; import { ZERO_ADDRESS, ZERO_TIMESTAMP } from "../Common.sol"; // TODO: fix errors @@ -21,13 +21,13 @@ library StubLib { // only run this function if the selected schemaUID exists if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); // validate Schema - ISchemaValidator validator = schema.validator; + IExternalSchemaValidator validator = schema.validator; // if validator is set, call the validator if ( address(validator) != ZERO_ADDRESS && validator.validateSchema(attestationRecord) == false ) { - // revert if ISchemaValidator returns false + // revert if IExternalSchemaValidator returns false revert(); // if (!success) { // If call reverts // // If there is return data, the call reverted without a reason or a custom error. @@ -49,7 +49,7 @@ library StubLib { // only run this function if the selected schemaUID exists if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); // validate Schema - ISchemaValidator validator = schema.validator; + IExternalSchemaValidator validator = schema.validator; // if validator is set, call the validator if ( address(validator) != ZERO_ADDRESS @@ -65,7 +65,7 @@ library StubLib { ) internal { - IResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = resolver.resolver; if (address(resolverContract) != ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecord) == false) { @@ -79,7 +79,7 @@ library StubLib { ) internal { - IResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = resolver.resolver; if (address(resolverContract) != ZERO_ADDRESS) return; @@ -94,7 +94,7 @@ library StubLib { ) internal { - IResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = resolver.resolver; if (address(resolverContract) != ZERO_ADDRESS) return; diff --git a/src/simple/ModuleManager.sol b/src/simple/ModuleManager.sol index cb9b1d20..c02f68ff 100644 --- a/src/simple/ModuleManager.sol +++ b/src/simple/ModuleManager.sol @@ -5,7 +5,7 @@ import { CREATE3 } from "solady/utils/CREATE3.sol"; import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; import { StubLib } from "../lib/StubLib.sol"; -import { IResolver } from "../external/IResolver.sol"; +import { IExternalResolver } from "../external/IExternalResolver.sol"; import { InvalidResolver, _isContract, EMPTY_RESOLVER_UID, ZERO_ADDRESS } from "../Common.sol"; import { ResolverRecord, ModuleRecord, ResolverUID } from "../DataTypes.sol"; diff --git a/src/simple/ResolverManager.sol b/src/simple/ResolverManager.sol index 21134ef2..1de3c977 100644 --- a/src/simple/ResolverManager.sol +++ b/src/simple/ResolverManager.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; import { ResolverRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; import { ZERO_ADDRESS, AccessDenied } from "../Common.sol"; -import { IResolver } from "../external/IResolver.sol"; +import { IExternalResolver } from "../external/IExternalResolver.sol"; import { UIDLib } from "../lib/Helpers.sol"; import { IRegistry } from "../IRegistry.sol"; @@ -24,7 +24,7 @@ abstract contract ResolverManager is IRegistry { _; } - function registerResolver(IResolver _resolver) external returns (ResolverUID uid) { + function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid) { if (address(_resolver) == ZERO_ADDRESS) revert InvalidResolver(); // build a ResolverRecord from the input @@ -45,7 +45,7 @@ abstract contract ResolverManager is IRegistry { emit SchemaResolverRegistered(uid, msg.sender); } - function setResolver(ResolverUID uid, IResolver resolver) external onlyResolverOwner(uid) { + function setResolver(ResolverUID uid, IExternalResolver resolver) external onlyResolverOwner(uid) { ResolverRecord storage referrer = resolvers[uid]; referrer.resolver = resolver; emit NewSchemaResolver(uid, address(resolver)); diff --git a/src/simple/SchemaManager.sol b/src/simple/SchemaManager.sol index 36e38efb..f1190105 100644 --- a/src/simple/SchemaManager.sol +++ b/src/simple/SchemaManager.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import { SchemaRecord, ResolverRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; -import { ISchemaValidator } from "../external/ISchemaValidator.sol"; +import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; import { UIDLib } from "../lib/Helpers.sol"; import { ZERO_TIMESTAMP, _time } from "../Common.sol"; @@ -16,7 +16,7 @@ abstract contract SchemaManager is IRegistry { function registerSchema( string calldata schema, - ISchemaValidator validator // OPTIONAL + IExternalSchemaValidator validator // OPTIONAL ) external returns (SchemaUID uid) From 5428d29edc8a80ffa4ea8fbc329166cf0bdcd011 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 12:13:43 +0700 Subject: [PATCH 14/84] chore: cleaned interface --- src/IRegistry.sol | 117 ++++++++++++++++-------------- src/simple/AttestationManager.sol | 2 +- src/simple/ResolverManager.sol | 14 +++- src/simple/SchemaManager.sol | 2 +- src/simple/TrustManager.sol | 7 +- 5 files changed, 78 insertions(+), 64 deletions(-) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 633de9d7..808ae246 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -14,16 +14,7 @@ import { import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.sol"; import { IExternalResolver } from "./external/IExternalResolver.sol"; -interface IRegistry { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Common Errors */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Query Registry */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - function setAttester(uint8 threshold, address[] calldata attesters) external; - +interface IERC7484 { function check(address module) external view; function checkForAccount(address smartAccount, address module) external view; @@ -37,9 +28,41 @@ interface IRegistry { ) external view; +} + +interface IRegistry is IERC7484 { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Smart Account - Trust Management */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + event NewTrustedAttesters(); + + error NoTrustedAttestersFound(); + error RevokedAttestation(address attester); + error InvalidModuleType(); + error AttestationNotFound(); + + function trustAttesters(uint8 threshold, address[] calldata attesters) external; + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Attestations */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + event Revoked(address indexed moduleAddr, address indexed revoker, SchemaUID schema); + event Attested( + address indexed moduleAddr, + address indexed attester, + SchemaUID schemaUID, + AttestationDataRef sstore2Pointer + ); + + error AlreadyRevoked(); + error AccessDenied(); + error InvalidAttestation(); + error InvalidExpirationTime(); + error DifferentResolvers(); + error InvalidSignature(); + function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; @@ -101,6 +124,17 @@ interface IRegistry { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Module Registration */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + // Event triggered when a module is deployed. + event ModuleRegistration( + address indexed implementation, address indexed sender, bytes32 resolver + ); + event ModuleDeployed(address indexed implementation, bytes32 indexed salt, bytes32 resolver); + event ModuleDeployedExternalFactory( + address indexed implementation, address indexed factory, bytes32 resolver + ); + + error AlreadyRegistered(address module); + error InvalidDeployment(); function deploy( bytes32 salt, @@ -123,6 +157,11 @@ interface IRegistry { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Manage Schemas */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + event SchemaRegistered(SchemaUID indexed uid, address registerer); + + error SchemaAlreadyExists(SchemaUID uid); + function registerSchema( string calldata schema, IExternalSchemaValidator validator // OPTIONAL @@ -134,58 +173,28 @@ interface IRegistry { /* Manage Resolvers */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + event NewResolver(ResolverUID indexed uid, address resolver); + + error ResolverAlreadyExists(); + error InvalidResolver(); + function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid); function setResolver(ResolverUID uid, IExternalResolver resolver) external; - // Event triggered when a module is deployed. - event ModuleRegistration( - address indexed implementation, address indexed sender, bytes32 resolver - ); - event ModuleDeployed(address indexed implementation, bytes32 indexed salt, bytes32 resolver); - event ModuleDeployedExternalFactory( - address indexed implementation, address indexed factory, bytes32 resolver - ); - - error AlreadyRegistered(address module); - error InvalidDeployment(); - // EVENTS - error AlreadyExists(); - error InvalidSignature(); - error InvalidResolver(); - error InvalidModuleType(); + // error InvalidDeployment(); + // // EVENTS + // error AlreadyExists(); + // error InvalidSignature(); + // error InvalidResolver(); /** * @dev Emitted when a new schema has been registered * * @param uid The schema UID. * @param registerer The address of the account used to register the schema. */ - - event SchemaRegistered(SchemaUID indexed uid, address registerer); - - event SchemaResolverRegistered(ResolverUID indexed uid, address registerer); - - /** - * @dev Emitted when a new schema resolver - * - * @param uid The schema UID. - * @param resolver The address of the resolver. - */ - event NewSchemaResolver(ResolverUID indexed uid, address resolver); - - error DifferentResolvers(); - error AlreadyRevoked(); - error AccessDenied(); - error NotFound(); - error InvalidAttestation(); - error InvalidExpirationTime(); - - event Revoked(address moduleAddr, address revoker, SchemaUID schema); - event Attested( - address moduleAddr, address attester, SchemaUID schemaUID, AttestationDataRef sstore2Pointer - ); - - error RevokedAttestation(address attester); - error AttestationNotFound(); - error NoAttestersFound(); + // error DifferentResolvers(); + // error NotFound(); + // error InvalidAttestation(); + // error InvalidExpirationTime(); } diff --git a/src/simple/AttestationManager.sol b/src/simple/AttestationManager.sol index 348f5ee0..b4873575 100644 --- a/src/simple/AttestationManager.sol +++ b/src/simple/AttestationManager.sol @@ -152,7 +152,7 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, // Ensure that we aren't attempting to revoke a non-existing attestation. if (AttestationDataRef.unwrap(attestation.dataPointer) == ZERO_ADDRESS) { - revert NotFound(); + revert AttestationNotFound(); } // Allow only original attesters to revoke their attestations. diff --git a/src/simple/ResolverManager.sol b/src/simple/ResolverManager.sol index 1de3c977..fcb2096d 100644 --- a/src/simple/ResolverManager.sol +++ b/src/simple/ResolverManager.sol @@ -36,18 +36,24 @@ abstract contract ResolverManager is IRegistry { // Checking if a schema with this UID already exists -> resolver can never be ZERO_ADDRESS if (address(resolvers[uid].resolver) != ZERO_ADDRESS) { - revert AlreadyExists(); + revert ResolverAlreadyExists(); } // Storing schema in the _schemas mapping resolvers[uid] = resolver; - emit SchemaResolverRegistered(uid, msg.sender); + emit NewResolver(uid, address(_resolver)); } - function setResolver(ResolverUID uid, IExternalResolver resolver) external onlyResolverOwner(uid) { + function setResolver( + ResolverUID uid, + IExternalResolver resolver + ) + external + onlyResolverOwner(uid) + { ResolverRecord storage referrer = resolvers[uid]; referrer.resolver = resolver; - emit NewSchemaResolver(uid, address(resolver)); + emit NewResolver(uid, address(resolver)); } } diff --git a/src/simple/SchemaManager.sol b/src/simple/SchemaManager.sol index f1190105..13deae87 100644 --- a/src/simple/SchemaManager.sol +++ b/src/simple/SchemaManager.sol @@ -28,7 +28,7 @@ abstract contract SchemaManager is IRegistry { // Computing a unique ID for the schema using its properties uid = schemaRecord.getUID(); - if (schemas[uid].registeredAt != ZERO_TIMESTAMP) revert AlreadyExists(); + if (schemas[uid].registeredAt != ZERO_TIMESTAMP) revert SchemaAlreadyExists(uid); // Storing schema in the _schemas mapping schemas[uid] = schemaRecord; diff --git a/src/simple/TrustManager.sol b/src/simple/TrustManager.sol index f2970329..c0d18bcb 100644 --- a/src/simple/TrustManager.sol +++ b/src/simple/TrustManager.sol @@ -15,7 +15,6 @@ import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; abstract contract TrustManager is IRegistry { using ModuleTypeLib for PackedModuleTypes; - event NewAttesters(); // packed struct to allow for efficient storage. // if only one attester is trusted, it only requires 1 SLOAD @@ -28,9 +27,9 @@ abstract contract TrustManager is IRegistry { mapping(address account => TrustedAttesters attesters) internal _accountToAttester; - function setAttester(uint8 threshold, address[] calldata attesters) external { + function trustAttesters(uint8 threshold, address[] calldata attesters) external { uint256 attestersLength = attesters.length; - if (attestersLength == 0) revert(); + if (attestersLength == 0) revert NoTrustedAttestersFound(); // sort attesters TrustedAttesters storage _att = _accountToAttester[msg.sender]; @@ -77,7 +76,7 @@ abstract contract TrustManager is IRegistry { // smart account has no trusted attesters set if (attester == address(0) && threshold != 0) { - revert NoAttestersFound(); + revert NoTrustedAttestersFound(); } // smart account only has ONE trusted attester // use this condition to save gas From 4fb94cd87692cd6670517d2dee39688f83437d57 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 12:23:31 +0700 Subject: [PATCH 15/84] feat: changed resolver hooks to differenciate attestation and revocation --- src/IRegistry.sol | 25 +++++-------- src/lib/StubLib.sol | 61 ++++++++++++++++++++----------- src/simple/AttestationManager.sol | 14 +++---- src/simple/ModuleManager.sol | 4 +- 4 files changed, 58 insertions(+), 46 deletions(-) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 808ae246..fde3855e 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -162,6 +162,8 @@ interface IRegistry is IERC7484 { error SchemaAlreadyExists(SchemaUID uid); + error InvalidSchema(); + function registerSchema( string calldata schema, IExternalSchemaValidator validator // OPTIONAL @@ -182,19 +184,12 @@ interface IRegistry is IERC7484 { function setResolver(ResolverUID uid, IExternalResolver resolver) external; - // error InvalidDeployment(); - // // EVENTS - // error AlreadyExists(); - // error InvalidSignature(); - // error InvalidResolver(); - /** - * @dev Emitted when a new schema has been registered - * - * @param uid The schema UID. - * @param registerer The address of the account used to register the schema. - */ - // error DifferentResolvers(); - // error NotFound(); - // error InvalidAttestation(); - // error InvalidExpirationTime(); + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Stub Errors */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + error ExternalError_SchemaValidation(); + error ExternalError_ResolveAtteststation(); + error ExternalError_ResolveRevocation(); + error ExternalError_ModuleRegistration(); } diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index f7b01705..e0fb00c4 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -5,12 +5,10 @@ import { AttestationRecord, ResolverRecord, SchemaRecord, ModuleRecord } from ". import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; import { IExternalResolver } from "../external/IExternalResolver.sol"; import { ZERO_ADDRESS, ZERO_TIMESTAMP } from "../Common.sol"; +import { IRegistry } from "../IRegistry.sol"; // TODO: fix errors library StubLib { - error InvalidDeployment(); - error InvalidSchema(); - function requireExternalSchemaValidation( AttestationRecord memory attestationRecord, SchemaRecord storage schema @@ -19,7 +17,7 @@ library StubLib { view { // only run this function if the selected schemaUID exists - if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); + if (schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); // validate Schema IExternalSchemaValidator validator = schema.validator; // if validator is set, call the validator @@ -27,15 +25,7 @@ library StubLib { address(validator) != ZERO_ADDRESS && validator.validateSchema(attestationRecord) == false ) { - // revert if IExternalSchemaValidator returns false - revert(); - // if (!success) { // If call reverts - // // If there is return data, the call reverted without a reason or a custom error. - // if (result.length == 0) revert(); - // assembly { - // // We use Yul's revert() to bubble up errors from the target contract. - // revert(add(32, result), mload(result)) - // } + revert IRegistry.ExternalError_SchemaValidation(); } } @@ -47,7 +37,7 @@ library StubLib { view { // only run this function if the selected schemaUID exists - if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); + if (schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); // validate Schema IExternalSchemaValidator validator = schema.validator; // if validator is set, call the validator @@ -55,11 +45,11 @@ library StubLib { address(validator) != ZERO_ADDRESS && validator.validateSchema(attestationRecords) == false ) { - revert(); + revert IRegistry.ExternalError_SchemaValidation(); } } - function requireExternalResolverCheck( + function requireExternalResolverOnAttestation( AttestationRecord memory attestationRecord, ResolverRecord storage resolver ) @@ -69,11 +59,40 @@ library StubLib { if (address(resolverContract) != ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecord) == false) { - revert(); + revert IRegistry.ExternalError_ResolveAtteststation(); + } + } + + function requireExternalResolverOnAttestation( + AttestationRecord[] memory attestationRecords, + ResolverRecord storage resolver + ) + internal + { + IExternalResolver resolverContract = resolver.resolver; + + if (address(resolverContract) != ZERO_ADDRESS) return; + + if (resolverContract.resolveAttestation(attestationRecords) == false) { + revert IRegistry.ExternalError_ResolveAtteststation(); + } + } + + function requireExternalResolverOnRevocation( + AttestationRecord memory attestationRecord, + ResolverRecord storage resolver + ) + internal + { + IExternalResolver resolverContract = resolver.resolver; + + if (address(resolverContract) != ZERO_ADDRESS) return; + if (resolverContract.resolveRevocation(attestationRecord) == false) { + revert IRegistry.ExternalError_ResolveAtteststation(); } } - function requireExternalResolverCheck( + function requireExternalResolverOnRevocation( AttestationRecord[] memory attestationRecords, ResolverRecord storage resolver ) @@ -84,11 +103,11 @@ library StubLib { if (address(resolverContract) != ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecords) == false) { - revert(); + revert IRegistry.ExternalError_ResolveAtteststation(); } } - function requireExternalResolverCheck( + function requireExternalResolverOnModuleRegistration( ModuleRecord memory moduleRecord, ResolverRecord storage resolver ) @@ -99,7 +118,7 @@ library StubLib { if (address(resolverContract) != ZERO_ADDRESS) return; if (resolverContract.resolveModuleRegistration(moduleRecord) == false) { - revert(); + revert IRegistry.ExternalError_ModuleRegistration(); } } } diff --git a/src/simple/AttestationManager.sol b/src/simple/AttestationManager.sol index b4873575..b12c9ecb 100644 --- a/src/simple/AttestationManager.sol +++ b/src/simple/AttestationManager.sol @@ -34,7 +34,7 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, function _revoke(address attester, RevocationRequest calldata request) internal { (AttestationRecord memory record, ResolverUID resolverUID) = _storeRevocation(attester, request); - record.requireExternalResolverCheck({ resolver: resolvers[resolverUID] }); + record.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); } function _revoke(address attester, RevocationRequest[] calldata requests) internal { @@ -51,7 +51,7 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, // No schema validation required during revocation. the attestation data was already checked against // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? - records.requireExternalResolverCheck({ resolver: resolvers[resolverUID] }); + records.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); } function _attest( @@ -65,7 +65,7 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, _storeAttestation({ schemaUID: schemaUID, attester: attester, request: request }); record.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); - record.requireExternalResolverCheck({ resolver: resolvers[resolverUID] }); + record.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); } function _attest( @@ -93,7 +93,7 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, } records.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); - records.requireExternalResolverCheck({ resolver: resolvers[resolverUID] }); + records.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); } function _storeAttestation( @@ -165,16 +165,14 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, revert AlreadyRevoked(); } + resolverUID = _modules[attestation.moduleAddr].resolverUID; + attestationStorage.revocationTime = _time(); // set revocation time to NOW emit Revoked({ moduleAddr: attestation.moduleAddr, revoker: revoker, schema: attestation.schemaUID }); - // resolverUID = - - resolverUID = _modules[attestation.moduleAddr].resolverUID; - attestationStorage.revocationTime = _time(); } function _getAttestation( diff --git a/src/simple/ModuleManager.sol b/src/simple/ModuleManager.sol index c02f68ff..b6afb4b2 100644 --- a/src/simple/ModuleManager.sol +++ b/src/simple/ModuleManager.sol @@ -62,7 +62,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { resolverUID: resolverUID, metadata: metadata }); - record.requireExternalResolverCheck(resolver); + record.requireExternalResolverOnModuleRegistration(resolver); } function register( @@ -81,7 +81,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { resolverUID: resolverUID, metadata: metadata }); - record.requireExternalResolverCheck(resolver); + record.requireExternalResolverOnModuleRegistration(resolver); } function _storeModuleRecord( From 33690a3682f9e7c626847b260764c658fea409c4 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 14:28:45 +0700 Subject: [PATCH 16/84] feat: tests --- src/DataTypes.sol | 1 - src/IRegistry.sol | 8 +- src/Registry.sol | 8 +- src/{simple => core}/Attestation.sol | 3 - src/{simple => core}/AttestationManager.sol | 6 +- src/{simple => core}/ModuleManager.sol | 11 +- src/{simple => core}/ResolverManager.sol | 19 +- src/{simple => core}/SchemaManager.sol | 14 +- src/{simple => core}/SignedAttestation.sol | 11 + src/{simple => core}/TrustManager.sol | 0 src/external/IExternalResolver.sol | 6 +- src/lib/AttestationLib.sol | 5 +- src/lib/ModuleTypeLib.sol | 5 +- src/lib/StubLib.sol | 8 +- test/Attestation.t.sol | 225 ++++++++++++++++++++ test/Base.t.sol | 93 ++++++++ test/ModuleRegistration.t.sol | 42 ++++ test/Resolver.t.sol | 28 +++ test/SchemaValidation.t.sol | 17 ++ test/TrustDelegation.t.sol | 42 ++++ test/branchingTree/Attestation.tree | 29 +++ test/branchingTree/ModuleRegistration.tree | 7 + test/branchingTree/Resolver.tree | 11 + test/branchingTree/SchemaValidation.tree | 7 + test/branchingTree/TrustDelegation.tree | 19 ++ test/mocks/MockERC1271Attester.sol | 12 ++ test/mocks/MockModule.sol | 8 + test/mocks/MockResolver.sol | 76 +++++++ test/mocks/MockSchemaValidator.sol | 34 +++ 29 files changed, 716 insertions(+), 39 deletions(-) rename src/{simple => core}/Attestation.sol (90%) rename src/{simple => core}/AttestationManager.sol (97%) rename src/{simple => core}/ModuleManager.sol (95%) rename src/{simple => core}/ResolverManager.sol (76%) rename src/{simple => core}/SchemaManager.sol (75%) rename src/{simple => core}/SignedAttestation.sol (92%) rename src/{simple => core}/TrustManager.sol (100%) create mode 100644 test/Attestation.t.sol create mode 100644 test/Base.t.sol create mode 100644 test/ModuleRegistration.t.sol create mode 100644 test/Resolver.t.sol create mode 100644 test/SchemaValidation.t.sol create mode 100644 test/TrustDelegation.t.sol create mode 100644 test/branchingTree/Attestation.tree create mode 100644 test/branchingTree/ModuleRegistration.tree create mode 100644 test/branchingTree/Resolver.tree create mode 100644 test/branchingTree/SchemaValidation.tree create mode 100644 test/branchingTree/TrustDelegation.tree create mode 100644 test/mocks/MockERC1271Attester.sol create mode 100644 test/mocks/MockModule.sol create mode 100644 test/mocks/MockResolver.sol create mode 100644 test/mocks/MockSchemaValidator.sol diff --git a/src/DataTypes.sol b/src/DataTypes.sol index a2036790..3534e81e 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.19; import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.sol"; import { IExternalResolver } from "./external/IExternalResolver.sol"; -import { SSTORE2 } from "solady/utils/SSTORE2.sol"; /*////////////////////////////////////////////////////////////// STORAGE diff --git a/src/IRegistry.sol b/src/IRegistry.sol index fde3855e..b02285ab 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -62,6 +62,7 @@ interface IRegistry is IERC7484 { error InvalidExpirationTime(); error DifferentResolvers(); error InvalidSignature(); + error InvalidModuleTypes(); function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; @@ -136,7 +137,7 @@ interface IRegistry is IERC7484 { error AlreadyRegistered(address module); error InvalidDeployment(); - function deploy( + function deployModule( bytes32 salt, ResolverUID resolverUID, bytes calldata code, @@ -147,7 +148,7 @@ interface IRegistry is IERC7484 { payable returns (address moduleAddr); - function register( + function registerModule( ResolverUID resolverUID, address moduleAddress, bytes calldata metadata @@ -163,6 +164,7 @@ interface IRegistry is IERC7484 { error SchemaAlreadyExists(SchemaUID uid); error InvalidSchema(); + error InvalidSchemaValidator(IExternalSchemaValidator validator); function registerSchema( string calldata schema, @@ -178,7 +180,7 @@ interface IRegistry is IERC7484 { event NewResolver(ResolverUID indexed uid, address resolver); error ResolverAlreadyExists(); - error InvalidResolver(); + error InvalidResolver(IExternalResolver resolver); function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid); diff --git a/src/Registry.sol b/src/Registry.sol index ba1850dc..4f47ab4f 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ModuleManager } from "./simple/ModuleManager.sol"; -import { ResolverManager } from "./simple/ResolverManager.sol"; -import { SchemaManager } from "./simple/SchemaManager.sol"; -import { SignedAttestation } from "./simple/SignedAttestation.sol"; +import { SignedAttestation } from "./core/SignedAttestation.sol"; +import { IRegistry } from "./IRegistry.sol"; /** * @author zeroknots */ -contract Registry is SignedAttestation { } +contract Registry is IRegistry, SignedAttestation { } diff --git a/src/simple/Attestation.sol b/src/core/Attestation.sol similarity index 90% rename from src/simple/Attestation.sol rename to src/core/Attestation.sol index b918aff5..779ae3f0 100644 --- a/src/simple/Attestation.sol +++ b/src/core/Attestation.sol @@ -4,11 +4,8 @@ pragma solidity ^0.8.19; import { AttestationRecord, AttestationRequest, RevocationRequest, SchemaUID } from "../DataTypes.sol"; -import { ResolverManager } from "./ResolverManager.sol"; -import { SchemaManager } from "./SchemaManager.sol"; import { AttestationManager } from "./AttestationManager.sol"; import { IRegistry } from "../IRegistry.sol"; -import { StubLib } from "../lib/StubLib.sol"; abstract contract Attestation is IRegistry, AttestationManager { function attest(SchemaUID schemaUID, AttestationRequest calldata request) external { diff --git a/src/simple/AttestationManager.sol b/src/core/AttestationManager.sol similarity index 97% rename from src/simple/AttestationManager.sol rename to src/core/AttestationManager.sol index b12c9ecb..1f5d18d4 100644 --- a/src/simple/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -9,10 +9,8 @@ import { ModuleRecord, SchemaUID, ResolverUID, - ModuleType, - PackedModuleTypes + ModuleType } from "../DataTypes.sol"; -import { ResolverManager } from "./ResolverManager.sol"; import { SchemaManager } from "./SchemaManager.sol"; import { ModuleManager } from "./ModuleManager.sol"; import { TrustManager } from "./TrustManager.sol"; @@ -126,7 +124,7 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, time: timeNow, expirationTime: request.expirationTime, revocationTime: uint48(ZERO_TIMESTAMP), - moduleTypes: request.moduleTypes.pack(), + moduleTypes: request.moduleTypes.packCalldata(), schemaUID: schemaUID, moduleAddr: module, attester: attester, diff --git a/src/simple/ModuleManager.sol b/src/core/ModuleManager.sol similarity index 95% rename from src/simple/ModuleManager.sol rename to src/core/ModuleManager.sol index b6afb4b2..6e2e82aa 100644 --- a/src/simple/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -1,11 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { CREATE3 } from "solady/utils/CREATE3.sol"; - import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; import { StubLib } from "../lib/StubLib.sol"; -import { IExternalResolver } from "../external/IExternalResolver.sol"; import { InvalidResolver, _isContract, EMPTY_RESOLVER_UID, ZERO_ADDRESS } from "../Common.sol"; import { ResolverRecord, ModuleRecord, ResolverUID } from "../DataTypes.sol"; @@ -37,7 +34,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { mapping(address moduleAddress => ModuleRecord moduleRecord) internal _modules; - function deploy( + function deployModule( bytes32 salt, ResolverUID resolverUID, bytes calldata code, @@ -49,7 +46,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { returns (address moduleAddr) { ResolverRecord storage resolver = resolvers[resolverUID]; - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); // address predictedModuleAddress = code.calculateAddress(deployParams, salt); @@ -65,7 +62,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { record.requireExternalResolverOnModuleRegistration(resolver); } - function register( + function registerModule( ResolverUID resolverUID, address moduleAddress, bytes calldata metadata @@ -73,7 +70,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { external { ResolverRecord storage resolver = resolvers[resolverUID]; - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); ModuleRecord memory record = _storeModuleRecord({ moduleAddress: moduleAddress, diff --git a/src/simple/ResolverManager.sol b/src/core/ResolverManager.sol similarity index 76% rename from src/simple/ResolverManager.sol rename to src/core/ResolverManager.sol index fcb2096d..5ae400ad 100644 --- a/src/simple/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ResolverRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; +import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; import { ZERO_ADDRESS, AccessDenied } from "../Common.sol"; import { IExternalResolver } from "../external/IExternalResolver.sol"; import { UIDLib } from "../lib/Helpers.sol"; @@ -24,9 +24,21 @@ abstract contract ResolverManager is IRegistry { _; } - function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid) { - if (address(_resolver) == ZERO_ADDRESS) revert InvalidResolver(); + modifier onlyResolver(IExternalResolver resolver) { + if ( + address(resolver) == address(0) + || !resolver.supportsInterface(type(IExternalResolver).interfaceId) + ) { + revert InvalidResolver(resolver); + } + _; + } + function registerResolver(IExternalResolver _resolver) + external + onlyResolver(_resolver) + returns (ResolverUID uid) + { // build a ResolverRecord from the input ResolverRecord memory resolver = ResolverRecord({ resolver: _resolver, resolverOwner: msg.sender }); @@ -50,6 +62,7 @@ abstract contract ResolverManager is IRegistry { IExternalResolver resolver ) external + onlyResolver(resolver) onlyResolverOwner(uid) { ResolverRecord storage referrer = resolvers[uid]; diff --git a/src/simple/SchemaManager.sol b/src/core/SchemaManager.sol similarity index 75% rename from src/simple/SchemaManager.sol rename to src/core/SchemaManager.sol index 13deae87..1d3e2741 100644 --- a/src/simple/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { SchemaRecord, ResolverRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; +import { SchemaRecord, SchemaUID } from "../DataTypes.sol"; import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; import { UIDLib } from "../lib/Helpers.sol"; @@ -19,9 +19,11 @@ abstract contract SchemaManager is IRegistry { IExternalSchemaValidator validator // OPTIONAL ) external + onlySchemaValidator(validator) returns (SchemaUID uid) { // TODO: ERC165 check that validator is actually a valivator + SchemaRecord memory schemaRecord = SchemaRecord({ validator: validator, registeredAt: _time(), schema: schema }); @@ -35,4 +37,14 @@ abstract contract SchemaManager is IRegistry { emit SchemaRegistered(uid, msg.sender); } + + modifier onlySchemaValidator(IExternalSchemaValidator validator) { + if ( + address(validator) != address(0) + && !validator.supportsInterface(type(IExternalSchemaValidator).interfaceId) + ) { + revert InvalidSchemaValidator(validator); + } + _; + } } diff --git a/src/simple/SignedAttestation.sol b/src/core/SignedAttestation.sol similarity index 92% rename from src/simple/SignedAttestation.sol rename to src/core/SignedAttestation.sol index 26022127..c651d110 100644 --- a/src/simple/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -89,4 +89,15 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { name = "RhinestoneRegistry"; version = "v1.0"; } + + function getDigest( + AttestationRequest calldata request, + address attester + ) + external + view + returns (bytes32) + { + return _hashTypedData(request.hash(attesterNonce[attester] + 1)); + } } diff --git a/src/simple/TrustManager.sol b/src/core/TrustManager.sol similarity index 100% rename from src/simple/TrustManager.sol rename to src/core/TrustManager.sol diff --git a/src/external/IExternalResolver.sol b/src/external/IExternalResolver.sol index b52ae529..1ac39eb1 100644 --- a/src/external/IExternalResolver.sol +++ b/src/external/IExternalResolver.sol @@ -11,11 +11,6 @@ import { IERC165 } from "forge-std/interfaces/IERC165.sol"; * */ interface IExternalResolver is IERC165 { - /** - * @dev Returns whether the resolver supports ETH transfers. - */ - function isPayable() external pure returns (bool); - /** * @dev Processes an attestation and verifies whether it's valid. * @@ -27,6 +22,7 @@ interface IExternalResolver is IERC165 { external payable returns (bool); + function resolveAttestation(AttestationRecord[] calldata attestation) external payable diff --git a/src/lib/AttestationLib.sol b/src/lib/AttestationLib.sol index dfc7140e..1e99e21d 100644 --- a/src/lib/AttestationLib.sol +++ b/src/lib/AttestationLib.sol @@ -6,10 +6,11 @@ import { SSTORE2 } from "solady/utils/SSTORE2.sol"; library AttestationLib { // The hash of the data type used to relay calls to the attest function. It's the value of - bytes32 constant ATTEST_TYPEHASH = keccak256("AttestationRequest(address,uint48,uint256,bytes)"); + bytes32 internal constant ATTEST_TYPEHASH = + keccak256("AttestationRequest(address,uint48,bytes,uint32[])"); // The hash of the data type used to relay calls to the revoke function. It's the value of - bytes32 constant REVOKE_TYPEHASH = keccak256("RevocationRequest(address,address,uint256)"); + bytes32 internal constant REVOKE_TYPEHASH = keccak256("RevocationRequest(address)"); function sload2(AttestationDataRef dataPointer) internal view returns (bytes memory data) { data = SSTORE2.read(AttestationDataRef.unwrap(dataPointer)); diff --git a/src/lib/ModuleTypeLib.sol b/src/lib/ModuleTypeLib.sol index 8b57aa30..43475fe4 100644 --- a/src/lib/ModuleTypeLib.sol +++ b/src/lib/ModuleTypeLib.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.21; import { PackedModuleTypes, ModuleType } from "../DataTypes.sol"; +import { IRegistry } from "../IRegistry.sol"; library ModuleTypeLib { function isType(PackedModuleTypes self, ModuleType moduleType) internal pure returns (bool) { @@ -24,7 +25,9 @@ library ModuleTypeLib { { uint32 result; for (uint256 i; i < moduleTypes.length; i++) { - result = result + uint32(2 ** ModuleType.unwrap(moduleTypes[i])); + uint32 _type = ModuleType.unwrap(moduleTypes[i]); + if (_type > 31) revert IRegistry.InvalidModuleType(); + result = result + uint32(2 ** _type); } return PackedModuleTypes.wrap(result); } diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index e0fb00c4..45809738 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -57,7 +57,7 @@ library StubLib { { IExternalResolver resolverContract = resolver.resolver; - if (address(resolverContract) != ZERO_ADDRESS) return; + if (address(resolverContract) == ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecord) == false) { revert IRegistry.ExternalError_ResolveAtteststation(); } @@ -71,7 +71,7 @@ library StubLib { { IExternalResolver resolverContract = resolver.resolver; - if (address(resolverContract) != ZERO_ADDRESS) return; + if (address(resolverContract) == ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecords) == false) { revert IRegistry.ExternalError_ResolveAtteststation(); @@ -86,7 +86,7 @@ library StubLib { { IExternalResolver resolverContract = resolver.resolver; - if (address(resolverContract) != ZERO_ADDRESS) return; + if (address(resolverContract) == ZERO_ADDRESS) return; if (resolverContract.resolveRevocation(attestationRecord) == false) { revert IRegistry.ExternalError_ResolveAtteststation(); } @@ -100,7 +100,7 @@ library StubLib { { IExternalResolver resolverContract = resolver.resolver; - if (address(resolverContract) != ZERO_ADDRESS) return; + if (address(resolverContract) == ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecords) == false) { revert IRegistry.ExternalError_ResolveAtteststation(); diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol new file mode 100644 index 00000000..02f4c60e --- /dev/null +++ b/test/Attestation.t.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "./Base.t.sol"; +import "src/DataTypes.sol"; + +contract AttestationTest is BaseTest { + function setUp() public override { + super.setUp(); + } + + function mockAttestation( + address module, + uint48 expirationTime, + bytes memory data, + uint32[] memory types + ) + internal + returns (AttestationRequest memory request) + { + ModuleType[] memory typesEnc = new ModuleType[](types.length); + for (uint256 i; i < types.length; i++) { + typesEnc[i] = ModuleType.wrap(types[i]); + } + request = AttestationRequest({ + moduleAddr: module, + expirationTime: expirationTime, + data: data, + moduleTypes: typesEnc + }); + } + + function mockRevocation(address module) internal returns (RevocationRequest memory request) { + request = RevocationRequest({ moduleAddr: module }); + } + + function test_WhenAttestingWithExpirationTimeInThePast( + address module, + bytes memory data, + uint32 moduleType + ) + external + { + vm.assume(moduleType > 31); + uint48 expirationTime = uint48(block.timestamp - 1000); + + uint32[] memory types = new uint32[](1); + types[0] = moduleType; + AttestationRequest memory request = mockAttestation(module, expirationTime, data, types); + + // It should revert. + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidExpirationTime.selector)); + registry.attest(defaultSchemaUID, request); + } + + function test_WhenAttestingWithTooHighModuleType( + address module, + uint48 expirationTime, + bytes memory data, + uint32 moduleType + ) + external + { + vm.assume(moduleType > 31); + // ensure that the expiration time is in the future + // function test_WhenAttestingWithExpirationTimeInThePast covers this + expirationTime = uint48(block.timestamp + expirationTime); + uint32[] memory types = new uint32[](1); + types[0] = moduleType; + AttestationRequest memory request = mockAttestation(module, expirationTime, data, types); + + // It should revert. + vm.expectRevert(); + registry.attest(defaultSchemaUID, request); + } + + function test_WhenAttestingToNon_existingModule( + address module, + uint48 expirationTime, + bytes memory data, + uint32[] memory types + ) + external + prankWithAccount(attester1) + { + for (uint256 i; i < types.length; i++) { + vm.assume(types[i] < 32); + } + + expirationTime = uint48(block.timestamp + expirationTime + 100); + AttestationRequest memory request = mockAttestation(module, expirationTime, data, types); + // It should revert. + vm.expectRevert(); // TODO: should we allow counterfactual? + registry.attest(defaultSchemaUID, request); + } + + function test_WhenRevokingAttestationThatDoesntExist(address module) + external + prankWithAccount(attester1) + { + // It should revert. + vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + registry.revoke(mockRevocation(module)); + } + + function test_WhenAttestingWithNoAttestationData() external prankWithAccount(attester1) { + uint32[] memory types = new uint32[](1); + AttestationRequest memory request = + mockAttestation(makeAddr("module"), uint48(block.timestamp + 1), "", types); + // It should store. + registry.attest(defaultSchemaUID, request); + AttestationRecord memory record = + registry.readAttestation(makeAddr("module"), attester1.addr); + + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, request.expirationTime); + assertEq(record.moduleAddr, request.moduleAddr); + assertEq(record.attester, attester1.addr); + } + + function test_WhenAttesting_ShouldCallResolver() external { + resolverTrue.reset(); + // It should call ExternalResolver. + + uint32[] memory types = new uint32[](1); + AttestationRequest memory request = + mockAttestation(makeAddr("module"), uint48(block.timestamp + 1), "", types); + // It should store. + // TODO: it seems that the resolver is not being called + registry.attest(defaultSchemaUID, request); + + assertTrue(resolverTrue.onAttestCalled()); + } + + modifier whenAttestingWithTokenomicsResolver() { + _; + } + + function test_WhenTokensArePaid() external whenAttestingWithTokenomicsResolver { + // It should work. + } + + function test_WhenTokensAreNotPaid() external whenAttestingWithTokenomicsResolver { + // It should revert. + } + + modifier whenAttestingWithSignature() { + _; + } + + function test_WhenUsingValidECDSA() external whenAttestingWithSignature { + uint256 nonceBefore = registry.attesterNonce(attester1.addr); + // It should recover. + uint32[] memory types = new uint32[](1); + AttestationRequest memory request = + mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + + bytes32 digest = registry.getDigest(request, attester1.addr); + bytes memory sig = ecdsaSign(attester1.key, digest); + registry.attest(defaultSchemaUID, attester1.addr, request, sig); + + AttestationRecord memory record = + registry.readAttestation(makeAddr("module"), attester1.addr); + uint256 nonceAfter = registry.attesterNonce(attester1.addr); + + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, request.expirationTime); + assertEq(record.moduleAddr, request.moduleAddr); + assertEq(record.attester, attester1.addr); + assertEq(nonceAfter, nonceBefore + 1); + } + + function test_WhenUsingInvalidECDSA() external whenAttestingWithSignature { + uint32[] memory types = new uint32[](1); + AttestationRequest memory request = + mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + + bytes32 digest = registry.getDigest(request, attester1.addr); + bytes memory sig = ecdsaSign(attester1.key, digest); + sig = abi.encodePacked(sig, "foo"); + // It should revert. + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidSignature.selector)); + registry.attest(defaultSchemaUID, attester1.addr, request, sig); + } + + function test_WhenUsingValidERC1271() external whenAttestingWithSignature { + uint32[] memory types = new uint32[](1); + AttestationRequest memory request = + mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + + bytes memory sig = "signature"; + registry.attest(defaultSchemaUID, address(erc1271AttesterTrue), request, sig); + + AttestationRecord memory record = + registry.readAttestation(makeAddr("module"), address(erc1271AttesterTrue)); + + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, request.expirationTime); + assertEq(record.moduleAddr, request.moduleAddr); + assertEq(record.attester, address(erc1271AttesterTrue)); + } + + function test_WhenUsingInvalidERC1271() external whenAttestingWithSignature { + // It should revert. + uint32[] memory types = new uint32[](1); + AttestationRequest memory request = + mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + + bytes memory sig = "signature"; + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidSignature.selector)); + registry.attest(defaultSchemaUID, address(erc1271AttesterFalse), request, sig); + } + + function ecdsaSign( + uint256 privKey, + bytes32 digest + ) + internal + pure + returns (bytes memory signature) + { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privKey, digest); + return abi.encodePacked(r, s, v); + } +} diff --git a/test/Base.t.sol b/test/Base.t.sol new file mode 100644 index 00000000..39a81c81 --- /dev/null +++ b/test/Base.t.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import "src/Registry.sol"; +import "src/DataTypes.sol"; +import "./mocks/MockResolver.sol"; +import "./mocks/MockSchemaValidator.sol"; +import "./mocks/MockERC1271Attester.sol"; +import "./mocks/MockModule.sol"; + +contract BaseTest is Test { + Registry registry; + + Account smartAccount1; + Account smartAccount2; + + Account attester1; + Account attester2; + + Account moduleDev1; + Account moduleDev2; + + Account opsEntity1; + Account opsEntity2; + + MockResolver resolverFalse; + MockResolver resolverTrue; + + MockSchemaValidator schemaValidatorFalse; + MockSchemaValidator schemaValidatorTrue; + + MockERC1271Attester erc1271AttesterFalse; + MockERC1271Attester erc1271AttesterTrue; + + MockModule module1; + MockModule module2; + MockModule module3; + + string defaultSchema = "Foobar"; + SchemaUID defaultSchemaUID; + ResolverUID defaultResolverUID; + + function setUp() public virtual { + vm.warp(1_641_070_800); + registry = new Registry(); + + smartAccount1 = makeAccount("smartAccount1"); + smartAccount2 = makeAccount("smartAccount2"); + + attester1 = makeAccount("attester1"); + attester2 = makeAccount("attester2"); + + moduleDev1 = makeAccount("moduleDev1"); + moduleDev2 = makeAccount("moduleDev2"); + + opsEntity1 = makeAccount("opsEntity1"); + opsEntity2 = makeAccount("opsEntity2"); + + resolverFalse = new MockResolver(false); + resolverTrue = new MockResolver(true); + + schemaValidatorFalse = new MockSchemaValidator(false); + schemaValidatorTrue = new MockSchemaValidator(true); + + erc1271AttesterFalse = new MockERC1271Attester(false); + erc1271AttesterTrue = new MockERC1271Attester(true); + + module1 = new MockModule(); + module2 = new MockModule(); + module3 = new MockModule(); + + initDefaultEnv(); + } + + modifier prankWithAccount(Account memory account) { + vm.startPrank(account.addr); + _; + vm.stopPrank(); + } + + function initDefaultEnv() internal { + vm.prank(opsEntity1.addr); + defaultResolverUID = registry.registerResolver(IExternalResolver(address(resolverTrue))); + vm.prank(opsEntity1.addr); + defaultSchemaUID = registry.registerSchema( + defaultSchema, IExternalSchemaValidator(address(schemaValidatorTrue)) + ); + + vm.prank(moduleDev1.addr); + registry.registerModule(defaultResolverUID, address(module1), ""); + } +} diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol new file mode 100644 index 00000000..25958cb6 --- /dev/null +++ b/test/ModuleRegistration.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "./Base.t.sol"; + +contract ModuleRegistrationTest is BaseTest { + function test_WhenRegisteringAModuleOnAnInvalidResolverUID() + external + prankWithAccount(moduleDev1) + { + // It should revert. + ResolverUID invalidUID = ResolverUID.wrap(hex"00"); + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidResolver.selector, address(0))); + registry.registerModule(invalidUID, address(module2), ""); + + invalidUID = ResolverUID.wrap("1"); + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidResolver.selector, address(0))); + registry.registerModule(invalidUID, address(module2), ""); + } + + function test_WhenRegisteringAModuleOnAValidResolverUID() + external + prankWithAccount(moduleDev1) + { + // It should register. + + registry.registerModule(defaultResolverUID, address(module2), ""); + } + + function test_WhenRegisteringTwoModulesWithTheSameBytecode() + external + prankWithAccount(moduleDev1) + { + // It should revert. + registry.registerModule(defaultResolverUID, address(module2), ""); + + vm.expectRevert( + abi.encodeWithSelector(IRegistry.AlreadyRegistered.selector, address(module2)) + ); + registry.registerModule(defaultResolverUID, address(module2), ""); + } +} diff --git a/test/Resolver.t.sol b/test/Resolver.t.sol new file mode 100644 index 00000000..1a54f422 --- /dev/null +++ b/test/Resolver.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +contract ResolverTest { + modifier whenRegisteringResolver() { + _; + } + + function test_WhenNewResolver() external whenRegisteringResolver { + // It should work. + } + + function test_WhenResolverAlreadyRegistered() external whenRegisteringResolver { + // It should revert. + } + + modifier whenUpdatingResolver() { + _; + } + + function test_WhenUsingUnauthorizedAccount() external whenUpdatingResolver { + // It should revert. + } + + function test_WhenUsingAuthorizedAccount() external whenUpdatingResolver { + // It should update. + } +} diff --git a/test/SchemaValidation.t.sol b/test/SchemaValidation.t.sol new file mode 100644 index 00000000..4a935bf0 --- /dev/null +++ b/test/SchemaValidation.t.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +contract SchemaValidationTest { + modifier whenRegisteringNewSchema() { + _; + } + + function test_WhenSchemaAlreadyRegistered() external whenRegisteringNewSchema { + // It should revert. + } + + function test_WhenSchemaNew() external whenRegisteringNewSchema { + // It should register schema. + // It should emit event. + } +} diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol new file mode 100644 index 00000000..e63e6292 --- /dev/null +++ b/test/TrustDelegation.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +contract TrustTest { + modifier whenSettingAttester() { + _; + } + + function test_WhenSupplyingSameAttesterMultipleTimes() external whenSettingAttester { + // It should revert. + } + + function test_WhenSupplyingOneAttester() external whenSettingAttester { + // It should set. + // It should emit event. + } + + function test_WhenSupplyingManyAttesters() external whenSettingAttester { + // It should set. + // It should emit event. + } + + modifier whenQueryingRegisty() { + _; + } + + function test_WhenNoAttestersSet() external whenQueryingRegisty { + // It should revert. + } + + function test_WhenAttesterSetButNoAttestationMade() external whenQueryingRegisty { + // It should revert. + } + + function test_WhenAttestersSetButThresholdTooLow() external whenQueryingRegisty { + // It should revert. + } + + function test_WhenAttestersSetAndAllOk() external whenQueryingRegisty { + // It should not revert. + } +} diff --git a/test/branchingTree/Attestation.tree b/test/branchingTree/Attestation.tree new file mode 100644 index 00000000..10b44cce --- /dev/null +++ b/test/branchingTree/Attestation.tree @@ -0,0 +1,29 @@ +AttestationTest +├── When attesting to non-existing Module +│ └── It should revert. +├── When revoking attestation that doesn't exist +│ └── It should revert. +├── When attesting to module with invalid type +│ └── It should revert. +├── When attesting with expiration time in the past +│ └── It should revert. +├── When Attesting with no attestationData +│ └── It should store. +├── When Attesting +│ ├── It should call SchemaValidator. +│ └── It should call ExternalResolver. +├── When attesting with tokenomics Resolver +│ ├── When tokens are paid +│ │ └── It should work. +│ └── When tokens are not paid +│ └── It should revert. +└── When attesting with signature + ├── When using valid ECDSA + │ └── It should recover. + ├── When using invalid ECDSA + │ └── It should revert. + ├── When using valid ERC1271 + │ └── It should recover. + └── When using invalid ERC1271 + └── It should revert. + diff --git a/test/branchingTree/ModuleRegistration.tree b/test/branchingTree/ModuleRegistration.tree new file mode 100644 index 00000000..21299daf --- /dev/null +++ b/test/branchingTree/ModuleRegistration.tree @@ -0,0 +1,7 @@ +ModuleRegistrationTest +├── When registering a module on an invalid ResolverUID +│ └── It should revert. +├── When registering a module on a valid ResolverUID +│ └── It should register. +└── When registering two modules with the same bytecode + └── It should revert. diff --git a/test/branchingTree/Resolver.tree b/test/branchingTree/Resolver.tree new file mode 100644 index 00000000..b762225f --- /dev/null +++ b/test/branchingTree/Resolver.tree @@ -0,0 +1,11 @@ +ResolverTest +├── When registering resolver +│ ├── When new resolver +│ │ └── It should work. +│ └── When resolver already registered +│ └── It should revert. +└── When updating resolver + ├── When using unauthorized account + │ └── It should revert. + └── When using authorized account + └── It should update. diff --git a/test/branchingTree/SchemaValidation.tree b/test/branchingTree/SchemaValidation.tree new file mode 100644 index 00000000..2b884d91 --- /dev/null +++ b/test/branchingTree/SchemaValidation.tree @@ -0,0 +1,7 @@ +SchemaValidationTest +└── When registering new schema + ├── When schema already registered + │ └── It should revert. + └── When schema new + ├── It should register schema. + └── It should emit event. diff --git a/test/branchingTree/TrustDelegation.tree b/test/branchingTree/TrustDelegation.tree new file mode 100644 index 00000000..64563b3d --- /dev/null +++ b/test/branchingTree/TrustDelegation.tree @@ -0,0 +1,19 @@ +TrustTest +├── When setting attester +│ ├── When supplying same attester multiple times +│ │ └── It should revert. +│ ├── When supplying one attester +│ │ ├── It should set. +│ │ └── It should emit event. +│ └── When supplying many attesters +│ ├── It should set. +│ └── It should emit event. +└── When querying registy + ├── When no attesters set + │ └── It should revert. + ├── When attester set but no attestation made + │ └── It should revert. + ├── When attesters set but threshold too low + │ └── It should revert. + └── When attesters set and all ok + └── It should not revert. diff --git a/test/mocks/MockERC1271Attester.sol b/test/mocks/MockERC1271Attester.sol new file mode 100644 index 00000000..a73e8746 --- /dev/null +++ b/test/mocks/MockERC1271Attester.sol @@ -0,0 +1,12 @@ +contract MockERC1271Attester { + bool immutable returnVal; + + constructor(bool ret) { + returnVal = ret; + } + + function isValidSignature(bytes32 hash, bytes memory signature) public view returns (bytes4) { + if (returnVal) return this.isValidSignature.selector; + else return 0x0000000; + } +} diff --git a/test/mocks/MockModule.sol b/test/mocks/MockModule.sol new file mode 100644 index 00000000..d97be71f --- /dev/null +++ b/test/mocks/MockModule.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +contract MockModule { + function foo() public pure returns (uint256) { + return 1; + } +} diff --git a/test/mocks/MockResolver.sol b/test/mocks/MockResolver.sol new file mode 100644 index 00000000..7a7d2ddc --- /dev/null +++ b/test/mocks/MockResolver.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "src/external/IExternalResolver.sol"; + +contract MockResolver is IExternalResolver { + bool immutable returnVal; + + bool public onAttestCalled; + bool public onRevokeCalled; + bool public onModuleCalled; + + constructor(bool ret) { + returnVal = ret; + } + + function reset() public { + onAttestCalled = false; + onRevokeCalled = false; + onModuleCalled = false; + } + + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + if (interfaceId == type(IExternalResolver).interfaceId) return true; + } + + function resolveAttestation(AttestationRecord calldata attestation) + external + payable + override + returns (bool) + { + onAttestCalled = true; + return returnVal; + } + + function resolveAttestation(AttestationRecord[] calldata attestation) + external + payable + override + returns (bool) + { + onAttestCalled = true; + return returnVal; + } + + function resolveRevocation(AttestationRecord calldata attestation) + external + payable + override + returns (bool) + { + onRevokeCalled = true; + return returnVal; + } + + function resolveRevocation(AttestationRecord[] calldata attestation) + external + payable + override + returns (bool) + { + onRevokeCalled = true; + return returnVal; + } + + function resolveModuleRegistration(ModuleRecord calldata module) + external + payable + override + returns (bool) + { + onModuleCalled = true; + return returnVal; + } +} diff --git a/test/mocks/MockSchemaValidator.sol b/test/mocks/MockSchemaValidator.sol new file mode 100644 index 00000000..2e0c9b7c --- /dev/null +++ b/test/mocks/MockSchemaValidator.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "src/external/IExternalSchemaValidator.sol"; + +contract MockSchemaValidator is IExternalSchemaValidator { + bool immutable returnVal; + + constructor(bool ret) { + returnVal = ret; + } + + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + if (interfaceId == type(IExternalSchemaValidator).interfaceId) return true; + } + + function validateSchema(AttestationRecord calldata attestation) + external + view + override + returns (bool) + { + return returnVal; + } + + function validateSchema(AttestationRecord[] calldata attestations) + external + view + override + returns (bool) + { + return returnVal; + } +} From 80e99e8e8a85e61bc85b3dab69c6248fb487ac47 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 15:09:39 +0700 Subject: [PATCH 17/84] del: removing old files --- src/Common.sol | 7 - src/IRegistry.sol | 3 +- src/base/Attestation.sol.bak | 447 ----------------------- src/base/AttestationDelegation.sol.bak | 229 ------------ src/base/AttestationResolve.sol.bak | 221 ----------- src/base/AttestationSimple.sol.bak | 183 ---------- src/base/EIP712Verifier.sol.bak | 273 -------------- src/base/Module.sol.bak | 254 ------------- src/base/Query.sol.bak | 237 ------------ src/base/QueryAttester.sol.bak | 326 ----------------- src/base/Schema.sol.bak | 128 ------- src/core/ModuleManager.sol | 2 +- src/core/ResolverManager.sol | 2 +- src/core/TrustManager.sol | 97 ++++- src/external/ResolverBase.sol.bak | 240 ------------ src/external/SchemaValidatorBase.sol.bak | 30 -- src/interface/IAttestation.sol.bak | 184 ---------- src/interface/IERC7484.sol.bak | 42 --- src/interface/IModule.sol.bak | 116 ------ src/interface/IQuery.sol.bak | 104 ------ src/interface/IRegistry.sol.bak | 238 ------------ src/interface/ISchema.sol.bak | 115 ------ test/Attestation.t.sol | 41 ++- test/TrustDelegation.t.sol | 60 ++- 24 files changed, 173 insertions(+), 3406 deletions(-) delete mode 100644 src/base/Attestation.sol.bak delete mode 100644 src/base/AttestationDelegation.sol.bak delete mode 100644 src/base/AttestationResolve.sol.bak delete mode 100644 src/base/AttestationSimple.sol.bak delete mode 100644 src/base/EIP712Verifier.sol.bak delete mode 100644 src/base/Module.sol.bak delete mode 100644 src/base/Query.sol.bak delete mode 100644 src/base/QueryAttester.sol.bak delete mode 100644 src/base/Schema.sol.bak delete mode 100644 src/external/ResolverBase.sol.bak delete mode 100644 src/external/SchemaValidatorBase.sol.bak delete mode 100644 src/interface/IAttestation.sol.bak delete mode 100644 src/interface/IERC7484.sol.bak delete mode 100644 src/interface/IModule.sol.bak delete mode 100644 src/interface/IQuery.sol.bak delete mode 100644 src/interface/IRegistry.sol.bak delete mode 100644 src/interface/ISchema.sol.bak diff --git a/src/Common.sol b/src/Common.sol index 5e09a242..468e803a 100644 --- a/src/Common.sol +++ b/src/Common.sol @@ -13,13 +13,6 @@ uint256 constant ZERO_TIMESTAMP = 0; address constant ZERO_ADDRESS = address(0); -error AccessDenied(); -error InvalidSchema(); -error InvalidResolver(); -error InvalidLength(); -error InvalidSignature(); -error NotFound(); - /** * @dev Returns the current's block timestamp. This method is overridden during tests and used to simulate the * current block time. diff --git a/src/IRegistry.sol b/src/IRegistry.sol index b02285ab..ead5ab9d 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -37,6 +37,8 @@ interface IRegistry is IERC7484 { event NewTrustedAttesters(); + error InvalidResolver(IExternalResolver resolver); + error InvalidTrustedAttesterInput(); error NoTrustedAttestersFound(); error RevokedAttestation(address attester); error InvalidModuleType(); @@ -180,7 +182,6 @@ interface IRegistry is IERC7484 { event NewResolver(ResolverUID indexed uid, address resolver); error ResolverAlreadyExists(); - error InvalidResolver(IExternalResolver resolver); function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid); diff --git a/src/base/Attestation.sol.bak b/src/base/Attestation.sol.bak deleted file mode 100644 index 5378343d..00000000 --- a/src/base/Attestation.sol.bak +++ /dev/null @@ -1,447 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { ReentrancyGuard } from "solmate/utils/ReentrancyGuard.sol"; - -import { - IAttestation, - AttestationRequest, - MultiAttestationRequest, - RevocationRequest, - MultiRevocationRequest, - AttestationLib -} from "../interface/IAttestation.sol"; -import { SchemaUID, ResolverUID, SchemaRecord, IExternalSchemaValidator } from "./Schema.sol"; -import { ModuleRecord } from "./Module.sol"; -import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; -import { - ZERO_ADDRESS, - AccessDenied, - NotFound, - ZERO_TIMESTAMP, - InvalidSchema, - _time -} from "../Common.sol"; - -import { - AttestationDataRef, - AttestationRecord, - AttestationRequest, - RevocationRequest, - writeAttestationData -} from "../DataTypes.sol"; -import { AttestationResolve } from "./AttestationResolve.sol"; - -/** - * @title Attestation - * @dev Manages attestations and revocations for modules. - * - * @author rhinestone | zeroknots.eth, Konrad Kopp(@kopy-kat) - */ -abstract contract Attestation is IAttestation, AttestationResolve, ReentrancyGuard { - using ModuleDeploymentLib for address; - - // Mapping of module addresses to attester addresses to their attestation records. - mapping(address module => mapping(address attester => AttestationRecord attestation)) internal - _moduleToAttesterToAttestations; - - /*////////////////////////////////////////////////////////////// - ATTEST - //////////////////////////////////////////////////////////////*/ - - /** - * @inheritdoc IAttestation - */ - // @audit is ReentrancyGuard necessary? - function attest(AttestationRequest calldata request) external payable nonReentrant { - AttestationRequest calldata requestData = request.data; - - ModuleRecord storage moduleRecord = _getModule({ moduleAddress: request.data.moduleAddr }); - - // check if schema exists and is valid. This will revert if validtor returns false - _requireSchemaCheck({ schemaUID: request.schemaUID, requestData: requestData }); - - // write attestations to registry storge - (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ - schemaUID: request.schemaUID, - AttestationRequest: requestData, - attester: msg.sender - }); - - // trigger the resolver procedure - _requireExternalResolveAttestation({ - resolverUID: moduleRecord.resolverUID, - attestationRecord: attestationRecord, - value: value, - isRevocation: false, - availableValue: msg.value, - isLastAttestation: true - }); - } - - /** - * @inheritdoc IAttestation - */ - function multiAttest(MultiAttestationRequest[] calldata multiRequests) - external - payable - nonReentrant - { - uint256 length = multiRequests.length; - uint256 availableValue = msg.value; - - // Batched Revocations can only be done for a single resolver. See IAttestation.sol - ModuleRecord storage moduleRecord = - _getModule({ moduleAddress: multiRequests[0].data[0].moduleAddr }); - - for (uint256 i; i < length; ++i) { - // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there - // is a remainder - it will be refunded back to the attester (something that we can only verify during the - // last and final batch). - bool last; - unchecked { - last = i == length - 1; - } - - // Process the current batch of attestations. - MultiAttestationRequest calldata multiRequest = multiRequests[i]; - uint256 usedValue = _multiAttest({ - schemaUID: multiRequest.schemaUID, - resolverUID: moduleRecord.resolverUID, - AttestationRequests: multiRequest.data, - attester: msg.sender, - availableValue: availableValue, - isLastAttestation: last - }); - - // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. - availableValue -= usedValue; - } - } - - /*////////////////////////////////////////////////////////////// - REVOKE - //////////////////////////////////////////////////////////////*/ - - /** - * @inheritdoc IAttestation - */ - function revoke(RevocationRequest calldata request) external payable nonReentrant { - ModuleRecord memory moduleRecord = _getModule({ moduleAddress: request.data.moduleAddr }); - - SchemaRecord storage schema = _getSchema({ schemaUID: request.schemaUID }); - if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); - - AttestationRecord memory attestationRecord = - _revoke({ request: request.data, revoker: msg.sender }); - - _requireExternalResolveAttestation({ - resolverUID: moduleRecord.resolverUID, - attestationRecord: attestationRecord, - value: 0, - isRevocation: true, - availableValue: msg.value, - isLastAttestation: true - }); - } - - /** - * @inheritdoc IAttestation - */ - function multiRevoke(MultiRevocationRequest[] calldata multiRequests) - external - payable - nonReentrant - { - // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting - // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless - // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be - // possible to send too much ETH anyway. - uint256 availableValue = msg.value; - - // Batched Revocations can only be done for a single resolver. See IAttestation.sol - ModuleRecord memory moduleRecord = - _getModule({ moduleAddress: multiRequests[0].data[0].moduleAddr }); - uint256 requestsLength = multiRequests.length; - - // should cache length - for (uint256 i; i < requestsLength; ++i) { - // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there - // is a remainder - it will be refunded back to the attester (something that we can only verify during the - // last and final batch). - bool isLastRevocation; - unchecked { - isLastRevocation = i == requestsLength - 1; - } - - MultiRevocationRequest calldata multiRequest = multiRequests[i]; - - // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. - availableValue -= _multiRevoke({ - schemaUID: multiRequest.schemaUID, - resolverUID: moduleRecord.resolverUID, - RevocationRequests: multiRequest.data, - revoker: msg.sender, - availableValue: availableValue, - isLastRevocation: isLastRevocation - }); - } - } - - /** - * @dev Attests to a specific schema. - * - * @param schemaUID The unique identifier of the schema to attest to. - * @param resolverUID The unique identifier of the resolver. - * @param AttestationRequests The attestation data. - * @param attester The attester's address. - * @param availableValue Amount of ETH available for the operation. - * @param isLastAttestation Indicates if this is the last batch. - * - * @return usedValue Amount of ETH used. - */ - function _multiAttest( - SchemaUID schemaUID, - ResolverUID resolverUID, - AttestationRequest[] calldata AttestationRequests, - address attester, - uint256 availableValue, - bool isLastAttestation - ) - internal - returns (uint256 usedValue) - { - _requireSchemaCheck(schemaUID, AttestationRequests); - - // caching length - uint256 length = AttestationRequests.length; - // caching current time as it will be used in the for loop - - // for loop will run and save the return values in these two arrays - AttestationRecord[] memory attestationRecords = new AttestationRecord[](length); - - // msg.values used for resolver - uint256[] memory values = new uint256[](length); - - // write every attesatation provided to registry's storage - for (uint256 i; i < length; ++i) { - (attestationRecords[i], values[i]) = _writeAttestation({ - schemaUID: schemaUID, - AttestationRequest: AttestationRequests[i], - attester: attester - }); - } - - // trigger the resolver procedure - usedValue = _requireExternalResolveAttestations({ - resolverUID: resolverUID, - attestationRecords: attestationRecords, - values: values, - isRevocation: false, - availableValue: availableValue, - isLast: isLastAttestation - }); - } - - function _requireSchemaCheck( - SchemaUID schemaUID, - AttestationRequest calldata requestData - ) - internal - view - { - // only run this function if the selected schemaUID exists - SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); - if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); - // validate Schema - IExternalSchemaValidator validator = schema.validator; - // if validator is set, call the validator - if (address(validator) != ZERO_ADDRESS && validator.validateSchema(requestData) == false) { - // revert if IExternalSchemaValidator returns false - revert InvalidAttestation(); - } - } - - function _requireSchemaCheck( - SchemaUID schemaUID, - AttestationRequest[] calldata requestDatas - ) - internal - view - { - // only run this function if the selected schemaUID exists - SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); - if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); - // validate Schema - IExternalSchemaValidator validator = schema.validator; - // if validator is set, call the validator - if (address(validator) != ZERO_ADDRESS && validator.validateSchema(requestDatas) == false) { - revert InvalidAttestation(); - } - } - - /** - * Writes an attestation record to storage and emits an event. - * - * @dev the bytes metadata provided in the AttestationRequest - * is writted to the EVM with SSTORE2 to allow for large attestations without spending a lot of gas - * - * @param schemaUID The unique identifier of the schema being attested to. - * @param AttestationRequest The data for the attestation request. - * @param attester The address of the entity making the attestation. - * - * @return attestationRecord The written attestation record. - * @return value The value associated with the attestation request. - */ - function _writeAttestation( - SchemaUID schemaUID, - AttestationRequest calldata AttestationRequest, - address attester - ) - internal - returns (AttestationRecord memory attestationRecord, uint256 value) - { - uint48 timeNow = _time(); - // Ensure that either no expiration time was set or that it was set in the future. - if ( - AttestationRequest.expirationTime != ZERO_TIMESTAMP - && AttestationRequest.expirationTime <= timeNow - ) { - revert InvalidExpirationTime(); - } - // caching module address. - address module = AttestationRequest.moduleAddr; - ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); - - // Ensure that attestation is for module that was registered. - if (moduleRecord.implementation == ZERO_ADDRESS) { - revert InvalidAttestation(); - } - - // get salt used for SSTORE2 to avoid collisions during CREATE2 - bytes32 attestationSalt = AttestationLib.attestationSalt(attester, module); - AttestationDataRef sstore2Pointer = writeAttestationData({ - attestationData: AttestationRequest.data, - salt: attestationSalt - }); - - // write attestationdata with SSTORE2 to EVM, and prepare return value - attestationRecord = AttestationRecord({ - schemaUID: schemaUID, - moduleAddr: module, - attester: attester, - time: timeNow, - expirationTime: AttestationRequest.expirationTime, - revocationTime: uint48(ZERO_TIMESTAMP), - dataPointer: sstore2Pointer - }); - - value = AttestationRequest.value; - - // SSTORE attestation on registry storage - _moduleToAttesterToAttestations[module][attester] = attestationRecord; - emit Attested(module, attester, schemaUID, sstore2Pointer); - } - - function _revoke( - RevocationRequest memory request, - address revoker - ) - internal - returns (AttestationRecord memory) - { - AttestationRecord storage attestation = - _moduleToAttesterToAttestations[request.moduleAddr][request.attester]; - - // Ensure that we aren't attempting to revoke a non-existing attestation. - if (AttestationDataRef.unwrap(attestation.dataPointer) == ZERO_ADDRESS) { - revert NotFound(); - } - - // Allow only original attesters to revoke their attestations. - if (attestation.attester != revoker) { - revert AccessDenied(); - } - - // Ensure that we aren't trying to revoke the same attestation twice. - if (attestation.revocationTime != ZERO_TIMESTAMP) { - revert AlreadyRevoked(); - } - - attestation.revocationTime = _time(); - emit Revoked({ - moduleAddr: attestation.moduleAddr, - revoker: revoker, - schema: attestation.schemaUID - }); - return attestation; - } - - /** - * @dev Revokes an existing attestation to a specific schema. - * - * @param schemaUID The unique identifier of the schema that was used to attest. - * @param RevocationRequests The arguments of the revocation requests. - * @param revoker The revoking account. - * @param availableValue The total available ETH amount that can be sent to the resolver. - * @param isLastRevocation Whether this is the last attestations/revocations set. - * - * @return Returns the total sent ETH amount. - */ - function _multiRevoke( - SchemaUID schemaUID, - ResolverUID resolverUID, - RevocationRequest[] memory RevocationRequests, - address revoker, - uint256 availableValue, - bool isLastRevocation - ) - internal - returns (uint256) - { - // only run this function if the selected schemaUID exists - SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); - if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); - - // caching length - uint256 length = RevocationRequests.length; - AttestationRecord[] memory attestationRecords = new AttestationRecord[](length); - uint256[] memory values = new uint256[](length); - - for (uint256 i; i < length; ++i) { - RevocationRequest memory revocationRequests = RevocationRequests[i]; - - attestationRecords[i] = _revoke({ request: revocationRequests, revoker: revoker }); - values[i] = revocationRequests.value; - } - - return _requireExternalResolveAttestations({ - resolverUID: resolverUID, - attestationRecords: attestationRecords, - values: values, - isRevocation: true, - availableValue: availableValue, - isLast: isLastRevocation - }); - } - - /** - * @dev Returns the attestation record for a specific module and attester. - * - * @param module The module address. - * @param attester The attester address. - * - * @return attestationRecord The attestation record. - */ - function _getAttestation( - address module, - address attester - ) - internal - view - virtual - returns (AttestationRecord storage) - { - return _moduleToAttesterToAttestations[module][attester]; - } -} diff --git a/src/base/AttestationDelegation.sol.bak b/src/base/AttestationDelegation.sol.bak deleted file mode 100644 index 98e4a6a1..00000000 --- a/src/base/AttestationDelegation.sol.bak +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { IAttestation } from "../interface/IAttestation.sol"; -import { Attestation } from "./Attestation.sol"; -import { - SignedAttestationRequest, - MultiSignedAttestationRequest, - SignedRevocationRequest, - MultiSignedRevocationRequest, - AttestationRequest, - ModuleRecord, - ResolverUID, - AttestationRecord, - RevocationRequest -} from "../DataTypes.sol"; -import { InvalidLength } from "../Common.sol"; - -/** - * @title AttestationDelegation - * @dev This contract provides a signed approach to attesting and revoking attestations. - * The contract extends both IAttestation and Attestation. - * @author rhinestone | zeroknots.eth, Konrad Kopp(@kopy-kat) - */ -abstract contract AttestationDelegation is IAttestation, Attestation { - /*////////////////////////////////////////////////////////////// - ATTEST - //////////////////////////////////////////////////////////////*/ - - /** - * @inheritdoc IAttestation - */ - function attest(SignedAttestationRequest calldata signedRequest) - external - payable - nonReentrant - { - // Get AttestationRequest calldata pointer - AttestationRequest calldata AttestationRequest = signedRequest.data; - // check signature. this will revert if signedRequest.attester != signer - _requireValidAttestSignatureCalldata(signedRequest); - // check if schema exists and is valid. This will revert if validtor returns false - _requireSchemaCheck(signedRequest.schemaUID, AttestationRequest); - - // @audit could this be address(0), what happens if there is no module Record - ModuleRecord storage moduleRecord = - _getModule({ moduleAddress: AttestationRequest.moduleAddr }); - ResolverUID resolverUID = moduleRecord.resolverUID; - - // store attestation record - (AttestationRecord memory attestationRecord, uint256 value) = _writeAttestation({ - schemaUID: signedRequest.schemaUID, - AttestationRequest: AttestationRequest, - attester: signedRequest.attester - }); - - // if a external resolver is configured for the resolver UID, - // this will call the external resolver contract to validate the attestationrequest - // should the external resolver return false, this will revert - _requireExternalResolveAttestation({ - resolverUID: resolverUID, - attestationRecord: attestationRecord, - value: value, - isRevocation: false, - availableValue: msg.value, - isLastAttestation: true - }); - } - - /** - * @inheritdoc IAttestation - */ - function multiAttest(MultiSignedAttestationRequest[] calldata multiSignedRequests) - external - payable - nonReentrant - { - // check if schema exists and is valid. This will revert if validtor returns false - _requireSchemaCheck({ - schemaUID: multiSignedRequests.schemaUID, - requestDatas: multiSignedRequests.data - }); - uint256 length = multiSignedRequests.length; - - // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting - // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless - // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be - // possible to send too much ETH anyway. - uint256 availableValue = msg.value; - - // Batched Revocations can only be done for a single resolver. See IAttestation.sol - ModuleRecord memory moduleRecord = - _getModule({ moduleAddress: multiSignedRequests[0].data[0].moduleAddr }); - // TODO: - // I think it would be much better to move this into the for loop so we can iterate over the requests. - // Its possible that the MultiAttestationRequests is attesting different modules, - // that thus have different resolvers gas bad - - for (uint256 i; i < length; ++i) { - // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there - // is a remainder - it will be refunded back to the attester (something that we can only verify during the - // last and final batch). - bool last; - unchecked { - last = i == length - 1; - } - - MultiSignedAttestationRequest calldata multiSignedRequest = multiSignedRequests[i]; - AttestationRequest[] calldata AttestationRequests = multiSignedRequest.data; - uint256 dataLength = AttestationRequests.length; - - // Ensure that no inputs are missing. - if (dataLength != multiSignedRequest.signatures.length) { - revert InvalidLength(); - } - - // Verify signatures. Note that the signatures are assumed to be signed with increasing nonces. - for (uint256 j; j < dataLength; ++j) { - _requireValidAttestSignature( - SignedAttestationRequest({ - schemaUID: multiSignedRequest.schemaUID, - data: AttestationRequests[j], - signature: multiSignedRequest.signatures[j], - attester: multiSignedRequest.attester - }) - ); - } - - // Process the current batch of attestations. - uint256 usedValue = _multiAttest({ - schemaUID: multiSignedRequest.schemaUID, - resolverUID: moduleRecord.resolverUID, - AttestationRequests: AttestationRequests, - attester: multiSignedRequest.attester, - availableValue: availableValue, - isLastAttestation: last - }); - - // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. - availableValue -= usedValue; - } - } - - /*////////////////////////////////////////////////////////////// - REVOKE - //////////////////////////////////////////////////////////////*/ - - /** - * @inheritdoc IAttestation - */ - function revoke(SignedRevocationRequest calldata request) external payable nonReentrant { - _verifyRevoke(request); - - RevocationRequest[] memory data = new RevocationRequest[](1); - data[0] = request.data; - - ModuleRecord memory moduleRecord = _getModule({ moduleAddress: request.data.moduleAddr }); - - _multiRevoke({ - schemaUID: request.schemaUID, - resolverUID: moduleRecord.resolverUID, - RevocationRequests: data, - revoker: request.revoker, - availableValue: msg.value, - isLastRevocation: true - }); - } - - /** - * @inheritdoc IAttestation - */ - function multiRevoke(MultiSignedRevocationRequest[] calldata multiSignedRequests) - external - payable - nonReentrant - { - // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting - // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless - // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be - // possible to send too much ETH anyway. - uint256 availableValue = msg.value; - uint256 length = multiSignedRequests.length; - - // Batched Revocations can only be done for a single resolver. See IAttestation.sol - ModuleRecord memory moduleRecord = - _getModule({ moduleAddress: multiSignedRequests[0].data[0].moduleAddr }); - - for (uint256 i; i < length; ++i) { - // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there - // is a remainder - it will be refunded back to the attester (something that we can only verify during the - // last and final batch). - bool last; - unchecked { - last = i == length - 1; - } - - MultiSignedRevocationRequest memory multiSignedRequest = multiSignedRequests[i]; - RevocationRequest[] memory RevocationRequests = multiSignedRequest.data; - uint256 dataLength = RevocationRequests.length; - - // Ensure that no inputs are missing. - if (dataLength == 0 || dataLength != multiSignedRequest.signatures.length) { - revert InvalidLength(); - } - - // Verify EIP712 signatures. Please note that the signatures are assumed to be signed with increasing nonces. - for (uint256 j; j < dataLength; ++j) { - _verifyRevoke( - SignedRevocationRequest({ - schemaUID: multiSignedRequest.schemaUID, - data: RevocationRequests[j], - signature: multiSignedRequest.signatures[j], - revoker: multiSignedRequest.revoker - }) - ); - } - - // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. - availableValue -= _multiRevoke({ - schemaUID: multiSignedRequest.schemaUID, - resolverUID: moduleRecord.resolverUID, - RevocationRequests: RevocationRequests, - revoker: multiSignedRequest.revoker, - availableValue: availableValue, - isLastRevocation: last - }); - } - } -} diff --git a/src/base/AttestationResolve.sol.bak b/src/base/AttestationResolve.sol.bak deleted file mode 100644 index af2ee6cf..00000000 --- a/src/base/AttestationResolve.sol.bak +++ /dev/null @@ -1,221 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { Address } from "@openzeppelin/contracts/utils/Address.sol"; - -import { IAttestation, SchemaUID } from "../interface/IAttestation.sol"; -import { ResolverUID } from "./Schema.sol"; -import { EIP712Verifier } from "./EIP712Verifier.sol"; - -import { ZERO_ADDRESS } from "../Common.sol"; -import { AttestationRecord, SchemaRecord, ModuleRecord, ResolverRecord } from "../DataTypes.sol"; -import { IExternalResolver } from "../external/IExternalResolver.sol"; - -/** - * @title AttestationResolve - * @dev This contract provides functions to resolve non-signed attestations and revocations. - * @author rhinestone | zeroknots.eth, Konrad Kopp(@kopy-kat) - */ -abstract contract AttestationResolve is IAttestation, EIP712Verifier { - using Address for address payable; - - /** - * @dev Resolves a new attestation or a revocation of an existing attestation. - * - * @param resolverUID The schema of the attestation. - * @param attestationRecord The data of the attestation to make/revoke. - * @param value An explicit ETH amount to send to the resolver. - * @param isRevocation Whether to resolve an attestation or its revocation. - * @param availableValue The total available ETH amount that can be sent to the resolver. - * @param isLastAttestation Whether this is the last attestations/revocations set. - * - * @return Returns the total sent ETH amount. - */ - function _requireExternalResolveAttestation( - ResolverUID resolverUID, - AttestationRecord memory attestationRecord, - uint256 value, - bool isRevocation, - uint256 availableValue, - bool isLastAttestation - ) - internal - returns (uint256) - { - ResolverRecord memory resolver = getResolver(resolverUID); - IExternalResolver resolverContract = resolver.resolver; - - if (address(resolverContract) == ZERO_ADDRESS) { - // Ensure that we don't accept payments if there is no resolver. - if (value != 0) revert NotPayable(); - - return 0; - } - - // Ensure that we don't accept payments which can't be forwarded to the resolver. - if (value != 0 && !resolverContract.isPayable()) { - revert NotPayable(); - } - - // Ensure that the attester/revoker doesn't try to spend more than available. - if (value > availableValue) { - revert InsufficientValue(); - } - - // Ensure to deduct the sent value explicitly. - unchecked { - availableValue -= value; - } - - // Resolve a revocation with external IExternalResolver - if (isRevocation) { - if (!resolverContract.revoke{ value: value }(attestationRecord)) { - revert InvalidRevocation(); - } - // Resolve an attestation with external IExternalResolver - } else if (!resolverContract.attest{ value: value }(attestationRecord)) { - revert InvalidAttestation(); - } - - if (isLastAttestation) { - _refund(availableValue); - } - - return value; - } - - /** - * @dev Resolves multiple attestations or revocations of existing attestations. - * - * @param resolverUID THe bytes32 uid of the resolver - * @param attestationRecords The data of the attestations to make/revoke. - * @param values Explicit ETH amounts to send to the resolver. - * @param isRevocation Whether to resolve an attestation or its revocation. - * @param availableValue The total available ETH amount that can be sent to the resolver. - * @param isLast Whether this is the last attestations/revocations set. - * - * @return Returns the total sent ETH amount. - */ - // solhint-disable-next-line code-complexity - function _requireExternalResolveAttestations( - ResolverUID resolverUID, - AttestationRecord[] memory attestationRecords, - uint256[] memory values, - bool isRevocation, - uint256 availableValue, - bool isLast - ) - internal - returns (uint256) - { - uint256 length = attestationRecords.length; - if (length == 1) { - return _requireExternalResolveAttestation({ - resolverUID: resolverUID, - attestationRecord: attestationRecords[0], - value: values[0], - isRevocation: isRevocation, - availableValue: availableValue, - isLastAttestation: isLast - }); - } - ResolverRecord memory resolver = getResolver({ resolverUID: resolverUID }); - IExternalResolver resolverContract = resolver.resolver; - if (address(resolverContract) == ZERO_ADDRESS) { - // Ensure that we don't accept payments if there is no resolver. - for (uint256 i; i < length; ++i) { - if (values[i] != 0) revert NotPayable(); - } - - return 0; - } - - uint256 totalUsedValue; - - for (uint256 i; i < length; ++i) { - uint256 value = values[i]; - - // Ensure that we don't accept payments which can't be forwarded to the resolver. - if (value != 0 && !resolverContract.isPayable()) { - revert NotPayable(); - } - - // Ensure that the attester/revoker doesn't try to spend more than available. - if (value > availableValue) revert InsufficientValue(); - - // Ensure to deduct the sent value explicitly and add it to the total used value by the batch. - unchecked { - availableValue -= value; - totalUsedValue += value; - } - } - - // Resolve a revocation with external IExternalResolver - if (isRevocation) { - if (!resolverContract.multiRevoke{ value: totalUsedValue }(attestationRecords, values)) - { - revert InvalidRevocations(); - } - // Resolve an attestation with external IExternalResolver - } else if ( - !resolverContract.multiAttest{ value: totalUsedValue }(attestationRecords, values) - ) { - revert InvalidAttestations(); - } - - if (isLast) { - _refund({ remainingValue: availableValue }); - } - - return totalUsedValue; - } - - /** - * @dev Refunds remaining ETH amount to the attester. - * - * @param remainingValue The remaining ETH amount that was not sent to the resolver. - */ - function _refund(uint256 remainingValue) private { - if (remainingValue > 0) { - // Using a regular transfer here might revert, for some non-EOA attesters, due to exceeding of the 2300 - // gas limit which is why we're using call instead (via sendValue), which the 2300 gas limit does not - // apply for. - payable(msg.sender).sendValue(remainingValue); - } - } - - /** - * @dev Internal function to get a schema record - * - * @param schemaUID The UID of the schema. - * - * @return schemaRecord The schema record. - */ - function _getSchema(SchemaUID schemaUID) internal view virtual returns (SchemaRecord storage); - - /** - * @dev Function to get a resolver record - * - * @param resolverUID The UID of the resolver. - * - * @return resolverRecord The resolver record. - */ - function getResolver(ResolverUID resolverUID) - public - view - virtual - returns (ResolverRecord memory); - - /** - * @dev Internal function to get a module record - * - * @param moduleAddress The address of the module. - * - * @return moduleRecord The module record. - */ - function _getModule(address moduleAddress) - internal - view - virtual - returns (ModuleRecord storage); -} diff --git a/src/base/AttestationSimple.sol.bak b/src/base/AttestationSimple.sol.bak deleted file mode 100644 index 01d467d6..00000000 --- a/src/base/AttestationSimple.sol.bak +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { ReentrancyGuard } from "solmate/utils/ReentrancyGuard.sol"; - -import { - IAttestation, - AttestationRequest, - MultiAttestationRequest, - RevocationRequest, - MultiRevocationRequest, - AttestationLib -} from "../interface/IAttestation.sol"; -import { SchemaUID, ResolverUID, SchemaRecord, IExternalSchemaValidator } from "./Schema.sol"; -import { ModuleRecord } from "./Module.sol"; -import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; -import { - ZERO_ADDRESS, - AccessDenied, - NotFound, - ZERO_TIMESTAMP, - InvalidSchema, - _time -} from "../Common.sol"; - -import { - AttestationDataRef, - AttestationRecord, - AttestationRequest, - RevocationRequest, - writeAttestationData -} from "../DataTypes.sol"; -import { AttestationResolve } from "./AttestationResolve.sol"; - -contract Attestation { - using ModuleDeploymentLib for address; - - // Mapping of module addresses to attester addresses to their attestation records. - mapping(address module => mapping(address attester => AttestationRecord attestation)) internal - _moduleToAttesterToAttestations; - - function attest(SchemaUID schemaUID, AttestationRequest calldata request) external { - _attestAndCheckExternal(msg.sender, schemaUID, request); - } - - function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external { - _attestAndCheckExternal(msg.sender, schemaUID, requests); - } - - function _attestAndCheckExternal( - address attester, - SchemaUID schemaUID, - AttestationRequest calldata request - ) - internal - { - AttestationRecord memory record = _storeAttestation({ - schemaUID: schemaUID, - attester: attester, - AttestationRequest: request - }); - - // check if schema exists and is valid. This will revert if validtor returns false - _requireSchemaCheck({ schemaUID: schemaUID, record: record }); - - // trigger the resolver procedure - _requireExternalResolveAttestation({ resolverUID: moduleRecord.resolverUID, record: record }); - } - - function _attestAndCheckExternal( - address attester, - SchemaUID schemaUID, - AttestationRequest[] calldata requests - ) - internal - { - uint256 length = requests.length; - AttestationRecord[] memory attestationRecords = new AttestationRecord[](length); - - for (uint256 i; i < length; i++) { - attestationRecord[i] = _storeAttestation({ - schemaUID: schemaUID, - attester: attester, - AttestationRequest: requests[i] - }); - } - - // check if schema exists and is valid. This will revert if validtor returns false - _requireSchemaCheck({ schemaUID: schemaUID, records: attestationRecords }); - - // trigger the resolver procedure - _requireExternalResolveAttestation({ - resolverUID: moduleRecord.resolverUID, - records: attestationRecords - }); - } - - function _storeAttestation( - SchemaUID schemaUID, - address attester, - AttestationRequest calldata AttestationRequest - ) - internal - returns (AttestationRecord memory record) - { - AttestationRecord storage recordStorage = _moduleToAttesterToAttestations[module][attester]; - uint48 timeNow = _time(); - // Ensure that either no expiration time was set or that it was set in the future. - if ( - AttestationRequest.expirationTime != ZERO_TIMESTAMP - && AttestationRequest.expirationTime <= timeNow - ) { - revert InvalidExpirationTime(); - } - // caching module address. - address module = AttestationRequest.moduleAddr; - ModuleRecord storage moduleRecord = _getModule({ moduleAddress: module }); - - // Ensure that attestation is for module that was registered. - if (moduleRecord.resolverUID != RESOLVER_UID_ZERO) { - revert ModuleNotRegistered(); - } - - // get salt used for SSTORE2 to avoid collisions during CREATE2 - bytes32 attestationSalt = AttestationLib.attestationSalt(attester, module); - AttestationDataRef sstore2Pointer = writeAttestationData({ - attestationData: AttestationRequest.data, - salt: attestationSalt - }); - - // SSTORE attestation on registry storage - record = AttestationRecord({ - schemaUID: schemaUID, - moduleAddr: module, - attester: attester, - time: timeNow, - expirationTime: AttestationRequest.expirationTime, - revocationTime: uint48(ZERO_TIMESTAMP), - dataPointer: sstore2Pointer - }); - recordStorage = record; - - emit Attested(module, attester, schemaUID, sstore2Pointer); - } - - function _requireSchemaCheck( - SchemaUID schemaUID, - AttestationRecord memory record - ) - internal - view - { - // only run this function if the selected schemaUID exists - SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); - if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); - // validate Schema - IExternalSchemaValidator validator = schema.validator; - // if validator is set, call the validator - if (address(validator) != ZERO_ADDRESS && validator.validateSchema(record) == false) { - // revert if IExternalSchemaValidator returns false - revert InvalidAttestation(); - } - } - - function _requireSchemaCheck( - SchemaUID schemaUID, - AttestationRecord[] memory records - ) - internal - view - { - // only run this function if the selected schemaUID exists - SchemaRecord storage schema = _getSchema({ schemaUID: schemaUID }); - if (schema.registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); - // validate Schema - IExternalSchemaValidator validator = schema.validator; - // if validator is set, call the validator - if (address(validator) != ZERO_ADDRESS && validator.validateSchema(records) == false) { - // revert if IExternalSchemaValidator returns false - revert InvalidAttestation(); - } - } -} diff --git a/src/base/EIP712Verifier.sol.bak b/src/base/EIP712Verifier.sol.bak deleted file mode 100644 index e485fc72..00000000 --- a/src/base/EIP712Verifier.sol.bak +++ /dev/null @@ -1,273 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { EIP712 } from "solady/utils/EIP712.sol"; -import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; - -import { InvalidSignature } from "../Common.sol"; -import { - AttestationRequest, - SchemaUID, - SignedAttestationRequest, - RevocationRequest, - SignedRevocationRequest -} from "../DataTypes.sol"; - -/** - * @title Singature Verifier. If provided signed is a contract, this function will fallback to ERC1271 - * - * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) - */ -abstract contract EIP712Verifier is EIP712 { - // The hash of the data type used to relay calls to the attest function. It's the value of - bytes32 private constant ATTEST_TYPEHASH = - keccak256("AttestationRequest(address,uint48,uint256,bytes)"); - - // The hash of the data type used to relay calls to the revoke function. It's the value of - bytes32 private constant REVOKE_TYPEHASH = - keccak256("RevocationRequest(address,address,uint256)"); - - // Replay protection nonces. - mapping(address account => uint256 nonce) private _nonces; - - function _domainNameAndVersion() - internal - pure - override - returns (string memory name, string memory version) - { - name = "Registry"; - version = "0.2.1"; - } - - /** - * @dev Returns the domain separator used in the encoding of the signatures for attest, and revoke. - */ - function getDomainSeparator() public view returns (bytes32) { - return _domainSeparator(); - } - - /** - * @dev Returns the current nonce per-account. - * - * @param account The requested account. - * - * @return The current nonce. - */ - function getNonce(address account) public view returns (uint256) { - return _nonces[account]; - } - - /** - * Returns the EIP712 type hash for the attest function. - */ - function getAttestTypeHash() public pure returns (bytes32) { - return ATTEST_TYPEHASH; - } - - /** - * Returns the EIP712 type hash for the revoke function. - */ - function getRevokeTypeHash() public pure returns (bytes32) { - return REVOKE_TYPEHASH; - } - - /** - * @dev Gets the attestation digest - * - * @param attData The data in the attestation request. - * @param schemaUID The UID of the schema. - * @param nonce The nonce of the attestation request. - * - * @return digest The attestation digest. - */ - function getAttestationDigest( - AttestationRequest memory attData, - SchemaUID schemaUID, - uint256 nonce - ) - public - view - returns (bytes32 digest) - { - digest = _attestationDigest(attData, schemaUID, nonce); - } - - /** - * @dev Gets the attestation digest - * - * @param attData The data in the attestation request. - * @param schemaUID The UID of the schema. - * @param attester The address of the attester. - * - * @return digest The attestation digest. - */ - function getAttestationDigest( - AttestationRequest memory attData, - SchemaUID schemaUID, - address attester - ) - public - view - returns (bytes32 digest) - { - uint256 nonce = getNonce(attester) + 1; - digest = _attestationDigest(attData, schemaUID, nonce); - } - - /** - * @dev Gets the attestation digest - * - * @param data The data in the attestation request. - * @param schemaUID The UID of the schema. - * @param nonce The nonce of the attestation request. - * - * @return digest The attestation digest. - */ - function _attestationDigest( - AttestationRequest memory data, - SchemaUID schemaUID, - uint256 nonce - ) - private - view - returns (bytes32 digest) - { - digest = _hashTypedData( - keccak256( - abi.encode( - ATTEST_TYPEHASH, - block.chainid, - schemaUID, - data.moduleAddr, - data.expirationTime, - keccak256(data.data), - nonce - ) - ) - ); - } - - /** - * @dev Verifies signed attestation request. - * - * @param request The arguments of the signed attestation request. - */ - function _requireValidAttestSignature(SignedAttestationRequest memory request) internal { - uint256 nonce = _newNonce(request.attester); - bytes32 digest = _attestationDigest(request.data, request.schemaUID, nonce); - bool valid = - SignatureCheckerLib.isValidSignatureNow(request.attester, digest, request.signature); - if (!valid) revert InvalidSignature(); - } - - /** - * @dev Verifies signed attestation request. - * - * @param request The arguments of the signed attestation request. - */ - function _requireValidAttestSignatureCalldata(SignedAttestationRequest calldata request) - internal - { - uint256 nonce = _newNonce(request.attester); - bytes32 digest = _attestationDigest(request.data, request.schemaUID, nonce); - bool valid = - SignatureCheckerLib.isValidSignatureNow(request.attester, digest, request.signature); - if (!valid) revert InvalidSignature(); - } - - /** - * @dev Gets a new sequential nonce - * - * @param account The requested account. - * - * @return nonce The new nonce. - */ - function _newNonce(address account) private returns (uint256 nonce) { - // TODO: gas bad. this will be iterated in a for loop - unchecked { - nonce = ++_nonces[account]; - } - } - - /** - * @dev Gets the revocation digest - * @param revData The data in the revocation request. - * @param schemaUID The UID of the schema. - * @param revoker The address of the revoker. - * - * @return digest The revocation digest. - */ - function getRevocationDigest( - RevocationRequest memory revData, - SchemaUID schemaUID, - address revoker - ) - public - view - returns (bytes32 digest) - { - uint256 nonce = getNonce(revoker) + 1; - digest = _revocationDigest(schemaUID, revData.moduleAddr, revData.attester, nonce); - } - - /** - * @dev Gets the revocation digest - * @param revData The data in the revocation request. - * @param schemaUID The UID of the schema. - * @param nonce The nonce of the attestation request. - * - * @return digest The revocation digest. - */ - function getRevocationDigest( - RevocationRequest memory revData, - SchemaUID schemaUID, - uint256 nonce - ) - public - view - returns (bytes32 digest) - { - digest = _revocationDigest(schemaUID, revData.moduleAddr, revData.attester, nonce); - } - - /** - * @dev Gets the revocation digest - * @param schemaUID The UID of the schema. - * @param moduleAddr The address of the moduleAddr. - * @param nonce The nonce of the attestation request. - * - * @return digest The revocation digest. - */ - function _revocationDigest( - SchemaUID schemaUID, - address moduleAddr, - address attester, - uint256 nonce - ) - private - view - returns (bytes32 digest) - { - digest = _hashTypedData( - keccak256( - abi.encode(REVOKE_TYPEHASH, block.chainid, schemaUID, moduleAddr, attester, nonce) - ) - ); - } - - /** - * @dev Verifies signed revocation request. - * - * @param request The arguments of the signed revocation request. - */ - function _verifyRevoke(SignedRevocationRequest memory request) internal { - RevocationRequest memory data = request.data; - - uint256 nonce = _newNonce(request.revoker); - bytes32 digest = _revocationDigest(request.schemaUID, data.moduleAddr, data.attester, nonce); - bool valid = - SignatureCheckerLib.isValidSignatureNow(request.revoker, digest, request.signature); - if (!valid) revert InvalidSignature(); - } -} diff --git a/src/base/Module.sol.bak b/src/base/Module.sol.bak deleted file mode 100644 index 5cb8020e..00000000 --- a/src/base/Module.sol.bak +++ /dev/null @@ -1,254 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { ReentrancyGuard } from "solmate/utils/ReentrancyGuard.sol"; -import { CREATE3 } from "solady/utils/CREATE3.sol"; - -import { IModule } from "../interface/IModule.sol"; - -import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; -import { IExternalResolver } from "../external/IExternalResolver.sol"; - -import { InvalidResolver, _isContract, ZERO_ADDRESS } from "../Common.sol"; -import { ResolverRecord, ModuleRecord, ResolverUID } from "../DataTypes.sol"; - -/** - * @title Module - * - * @dev The Module contract serves as a component in a larger system for handling smart contracts or "modules" - * within a blockchain ecosystem. This contract inherits from the IModule interface - * - * @dev The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract - * that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, - * sender address, deploy parameters hash, and additional metadata are stored in - * a struct and mapped to the module's address in - * the `_modules` mapping for easy access and management. - * - * @dev In conclusion, the Module is a central part of a system to manage, - * deploy, and interact with a set of smart contracts - * in a structured and controlled manner. - * - * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) - */ -abstract contract Module is IModule, ReentrancyGuard { - using ModuleDeploymentLib for bytes; - using ModuleDeploymentLib for address; - - mapping(address moduleAddress => ModuleRecord modueRecord) private _modules; - - /** - * @inheritdoc IModule - */ - function deploy( - bytes calldata code, - bytes calldata deployParams, - bytes32 salt, - bytes calldata metadata, - ResolverUID resolverUID - ) - external - payable - nonReentrant - returns (address moduleAddr) - { - ResolverRecord memory resolver = getResolver(resolverUID); - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); - - (moduleAddr,,) = code.deploy(deployParams, salt, msg.value); - - _storeModuleRecord({ - moduleAddress: moduleAddr, - sender: msg.sender, - resolver: resolver, - resolverUID: resolverUID, - metadata: metadata - }); - } - - /** - * @inheritdoc IModule - */ - function deployC3( - bytes calldata code, - bytes calldata deployParams, - bytes32 salt, - bytes calldata metadata, - ResolverUID resolverUID - ) - external - payable - nonReentrant - returns (address moduleAddr) - { - ResolverRecord memory resolver = getResolver(resolverUID); - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); - bytes memory creationCode = abi.encodePacked(code, deployParams); - bytes32 senderSalt = keccak256(abi.encodePacked(salt, msg.sender)); - moduleAddr = CREATE3.deploy(senderSalt, creationCode, msg.value); - - _storeModuleRecord({ - moduleAddress: moduleAddr, - sender: msg.sender, - resolver: resolver, - resolverUID: resolverUID, - metadata: metadata - }); - } - - /** - * @inheritdoc IModule - */ - function deployViaFactory( - address factory, - bytes calldata callOnFactory, - bytes calldata metadata, - ResolverUID resolverUID - ) - external - payable - nonReentrant - returns (address moduleAddr) - { - ResolverRecord memory resolver = getResolver(resolverUID); - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); - (bool ok, bytes memory returnData) = factory.call{ value: msg.value }(callOnFactory); - - if (!ok) revert InvalidDeployment(); - moduleAddr = abi.decode(returnData, (address)); - if (moduleAddr == ZERO_ADDRESS) revert InvalidDeployment(); - if (_isContract(moduleAddr) != true) revert InvalidDeployment(); - - _storeModuleRecord({ - moduleAddress: moduleAddr, - sender: msg.sender, - resolver: resolver, - resolverUID: resolverUID, - metadata: metadata - }); - } - - /** - * @inheritdoc IModule - */ - function register( - ResolverUID resolverUID, - address moduleAddress, - bytes calldata metadata - ) - external - nonReentrant - { - ResolverRecord memory resolver = getResolver(resolverUID); - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(); - - _storeModuleRecord({ - moduleAddress: moduleAddress, - sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function - resolver: resolver, - resolverUID: resolverUID, - metadata: metadata - }); - } - - /** - * @dev Registers a module, ensuring it's not already registered. - * This function ensures that the module is a contract. - * Also ensures that moduleAddress is not ZERO_ADDRESS. - * - * @param moduleAddress Address of the module. - * @param sender Address of the sender registering the module. - * @param resolver Resolver record associated with the module. - * @param resolverUID Unique ID of the resolver. - * @param metadata Data associated with the module. - */ - function _storeModuleRecord( - address moduleAddress, - address sender, - ResolverRecord memory resolver, - ResolverUID resolverUID, - bytes calldata metadata - ) - private - { - // ensure moduleAddress is not already registered - if (_modules[moduleAddress].implementation != ZERO_ADDRESS) { - revert AlreadyRegistered(moduleAddress); - } - // revert if moduleAddress is NOT a contract - if (!_isContract(moduleAddress)) revert InvalidDeployment(); - - // Store module metadata in _modules mapping - ModuleRecord memory moduleRegistration = ModuleRecord({ - implementation: moduleAddress, - resolverUID: resolverUID, - sender: sender, - metadata: metadata - }); - - // Resolve module registration using resolver - _resolveRegistration({ - resolverContract: resolver.resolver, - moduleRegistration: moduleRegistration - }); - - // Store module record in _modules mapping - _modules[moduleAddress] = moduleRegistration; - - // Emit ModuleRegistration event - emit ModuleRegistration(moduleAddress, sender, ResolverUID.unwrap(resolverUID)); - } - - /** - * @dev Resolves the module registration using the provided resolver. - * - * @param resolverContract Resolver to validate the module registration. - * @param moduleRegistration Module record to be registered. - */ - function _resolveRegistration( - IExternalResolver resolverContract, - ModuleRecord memory moduleRegistration - ) - private - { - if (address(resolverContract) == ZERO_ADDRESS) return; - if (resolverContract.moduleRegistration(moduleRegistration) == false) { - revert InvalidDeployment(); - } - } - - /** - * @notice Retrieves the resolver record for a given UID. - * - * @param uid The UID of the resolver to retrieve. - * - * @return The resolver record associated with the given UID. - */ - function getResolver(ResolverUID uid) public view virtual returns (ResolverRecord memory); - - /** - * @dev Retrieves the module record for a given address. - * - * @param moduleAddress The address of the module to retrieve. - * - * @return moduleRecord The module record associated with the given address. - */ - function _getModule(address moduleAddress) - internal - view - virtual - returns (ModuleRecord storage) - { - return _modules[moduleAddress]; - } - - /** - * @notice Retrieves the module record for a given address. - * - * @param moduleAddress The address of the module to retrieve. - * - * @return moduleRecord The module record associated with the given address. - */ - function getModule(address moduleAddress) public view returns (ModuleRecord memory) { - return _getModule(moduleAddress); - } -} diff --git a/src/base/Query.sol.bak b/src/base/Query.sol.bak deleted file mode 100644 index 57f526d0..00000000 --- a/src/base/Query.sol.bak +++ /dev/null @@ -1,237 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { IQuery } from "../interface/IQuery.sol"; - -import { ZERO_TIMESTAMP } from "../Common.sol"; -import { AttestationRecord } from "../DataTypes.sol"; - -/** - * @title Query - * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) - * Implements EIP-7484 to query attestations stored in the registry. - * @dev This contract is abstract and provides utility functions to query attestations. - */ -abstract contract Query is IQuery { - /** - * @inheritdoc IQuery - */ - function check( - address module, - address attester - ) - public - view - override(IQuery) - returns (uint256 attestedAt) - { - AttestationRecord storage attestation = _getAttestation(module, attester); - - // attestedAt = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestedAt := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (attestedAt == ZERO_TIMESTAMP) { - revert AttestationNotFound(); - } - - if (expirationTime != ZERO_TIMESTAMP) { - if (block.timestamp > expirationTime) { - revert AttestationNotFound(); - } - } - - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(attestation.attester); - } - } - - /** - * @inheritdoc IQuery - */ - function checkN( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - override(IQuery) - returns (uint256[] memory attestedAtArray) - { - uint256 attestersLength = attesters.length; - if (attestersLength < threshold || threshold == 0) { - threshold = attestersLength; - } - - uint256 timeNow = block.timestamp; - attestedAtArray = new uint256[](attestersLength); - - for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage attestation = - _getAttestation({ moduleAddress: module, attester: attesters[i] }); - - uint256 attestationTime; // = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestationTime := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(attestation.attester); - } - - if (expirationTime != ZERO_TIMESTAMP) { - if (timeNow > expirationTime) { - revert AttestationNotFound(); - } - } - - attestedAtArray[i] = attestationTime; - - if (attestationTime == ZERO_TIMESTAMP) continue; - if (threshold != 0) --threshold; - } - if (threshold == 0) return attestedAtArray; - revert InsufficientAttestations(); - } - - /** - * @inheritdoc IQuery - */ - function checkNUnsafe( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray) - { - uint256 attestersLength = attesters.length; - if (attestersLength < threshold || threshold == 0) { - threshold = attestersLength; - } - - uint256 timeNow = block.timestamp; - attestedAtArray = new uint256[](attestersLength); - - for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage attestation = - _getAttestation({ moduleAddress: module, attester: attesters[i] }); - - uint256 attestationTime; // = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestationTime := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (revocationTime != ZERO_TIMESTAMP) { - attestedAtArray[i] = 0; - continue; - } - - attestedAtArray[i] = attestationTime; - - if (expirationTime != ZERO_TIMESTAMP) { - if (timeNow > expirationTime) { - attestedAtArray[i] = 0; - continue; - } - } - - if (attestationTime == ZERO_TIMESTAMP) continue; - if (threshold != 0) --threshold; - } - if (threshold == 0) return attestedAtArray; - revert InsufficientAttestations(); - } - - /** - * @inheritdoc IQuery - */ - function findAttestation( - address module, - address attesters - ) - public - view - override(IQuery) - returns (AttestationRecord memory attestation) - { - attestation = _getAttestation(module, attesters); - } - - /** - * @inheritdoc IQuery - */ - function findAttestations( - address module, - address[] memory attesters - ) - external - view - override(IQuery) - returns (AttestationRecord[] memory attestations) - { - uint256 attesterssLength = attesters.length; - attestations = new AttestationRecord[](attesterssLength); - for (uint256 i; i < attesterssLength; ++i) { - attestations[i] = findAttestation(module, attesters[i]); - } - } - - /** - * @notice Internal function to retrieve an attestation record. - * - * @dev This is a virtual function and is meant to be overridden in derived contracts. - * - * @param moduleAddress The address of the module for which the attestation is retrieved. - * @param attester The address of the attester whose record is being retrieved. - * - * @return Attestation record associated with the given module and attester. - */ - function _getAttestation( - address moduleAddress, - address attester - ) - internal - view - virtual - returns (AttestationRecord storage); -} diff --git a/src/base/QueryAttester.sol.bak b/src/base/QueryAttester.sol.bak deleted file mode 100644 index fbb4a654..00000000 --- a/src/base/QueryAttester.sol.bak +++ /dev/null @@ -1,326 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { IQuery } from "../interface/IQuery.sol"; - -import { ZERO_TIMESTAMP } from "../Common.sol"; -import { AttestationRecord } from "../DataTypes.sol"; - -/** - * @title Query - * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) - * Implements EIP-7484 to query attestations stored in the registry. - * @dev This contract is abstract and provides utility functions to query attestations. - */ -abstract contract Query is IQuery { - struct Attesters { - uint8 attesterCount; - uint8 threshold; - address attester; - mapping(address attester => address linkedAttester) linkedAttesters; - } - - mapping(address account => Attesters attesters) internal _attesters; - - function setAttester(uint8 threshold, address[] calldata attesters) external payable { - uint256 attestersLength = attesters.length; - - Attesters storage _att = _attesters[msg.sender]; - if (threshold > attestersLength) { - threshold = uint8(attestersLength); - } - _att.attesterCount = uint8(attestersLength); - _att.threshold = threshold; - - _attesters[msg.sender].attester = attesters[0]; - - attestersLength--; - for (uint256 i; i < attestersLength; i++) { - _att.linkedAttesters[attesters[i]] = attesters[i + 1]; - } - } - - function _getAttesters( - Attesters storage attesterStorage, - address linkedAttester, - uint256 length - ) - internal - view - returns (address[] memory attesters) - { - // this function is used by check(), length is always > 0. Trying to be as gas efficient as possible. - attesters = new address[](length); - attesters[0] = linkedAttester; // the first attester - - for (uint256 i = 1; i < length; i++) { - // loop over the linked list, add entries to array, - // use read out attester value as the next value to query the linked list - linkedAttester = attesterStorage.linkedAttesters[linkedAttester]; - attesters[i] = linkedAttester; - } - } - - function check(address module) external view { - Attesters storage _att = _attesters[msg.sender]; - uint256 threshold = _att.threshold; - uint256 attesterCount = _att.attesterCount; - address attester0 = _att.attester; - - // if there is no attester or threshold, the user never configured any attesters. This is a revert. - if (attesterCount == 0 || threshold == 0) { - revert NoAttesterSet(); - } else if (attesterCount == 1) { - check({ module: module, attester: attester0 }); - } else if (attesterCount > 1) { - address[] memory attesters = _getAttesters({ - attesterStorage: _att, - linkedAttester: attester0, - length: attesterCount - }); - checkN({ module: module, attesters: attesters, threshold: threshold }); - } - } - - function checkOnBehalf(address account, address module) external view { - Attesters storage _att = _attesters[account]; - uint256 threshold = _att.threshold; - uint256 attesterCount = _att.attesterCount; - address attester0 = _att.attester; - // if there is no attester or threshold, the user never configured any attesters. This is a revert. - if (attesterCount == 0 || threshold == 0) { - revert NoAttesterSet(); - } else if (_att.attesterCount == 1) { - check({ module: module, attester: attester0 }); - } else if (_att.attesterCount > 1) { - address[] memory attesters = _getAttesters({ - attesterStorage: _att, - linkedAttester: attester0, - length: attesterCount - }); - checkN({ module: module, attesters: attesters, threshold: threshold }); - } - } - - /** - * @inheritdoc IQuery - */ - function check( - address module, - address attester - ) - public - view - override(IQuery) - returns (uint256 attestedAt) - { - AttestationRecord storage attestation = _getAttestation(module, attester); - - // attestedAt = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestedAt := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (attestedAt == ZERO_TIMESTAMP) { - revert AttestationNotFound(); - } - - if (expirationTime != ZERO_TIMESTAMP) { - if (block.timestamp > expirationTime) { - revert AttestationNotFound(); - } - } - - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(attestation.attester); - } - } - - /** - * @inheritdoc IQuery - */ - function checkN( - address module, - address[] memory attesters, - uint256 threshold - ) - public - view - override(IQuery) - returns (uint256[] memory attestedAtArray) - { - uint256 attestersLength = attesters.length; - if (attestersLength < threshold || threshold == 0) { - threshold = attestersLength; - } - - uint256 timeNow = block.timestamp; - attestedAtArray = new uint256[](attestersLength); - - for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage attestation = - _getAttestation({ moduleAddress: module, attester: attesters[i] }); - - uint256 attestationTime; // = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestationTime := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(attestation.attester); - } - - if (expirationTime != ZERO_TIMESTAMP) { - if (timeNow > expirationTime) { - revert AttestationNotFound(); - } - } - - attestedAtArray[i] = attestationTime; - - if (attestationTime == ZERO_TIMESTAMP) continue; - if (threshold != 0) --threshold; - } - if (threshold == 0) return attestedAtArray; - revert InsufficientAttestations(); - } - - /** - * @inheritdoc IQuery - */ - function checkNUnsafe( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray) - { - uint256 attestersLength = attesters.length; - if (attestersLength < threshold || threshold == 0) { - threshold = attestersLength; - } - - uint256 timeNow = block.timestamp; - attestedAtArray = new uint256[](attestersLength); - - for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage attestation = - _getAttestation({ moduleAddress: module, attester: attesters[i] }); - - uint256 attestationTime; // = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestationTime := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (revocationTime != ZERO_TIMESTAMP) { - attestedAtArray[i] = 0; - continue; - } - - attestedAtArray[i] = attestationTime; - - if (expirationTime != ZERO_TIMESTAMP) { - if (timeNow > expirationTime) { - attestedAtArray[i] = 0; - continue; - } - } - - if (attestationTime == ZERO_TIMESTAMP) continue; - if (threshold != 0) --threshold; - } - if (threshold == 0) return attestedAtArray; - revert InsufficientAttestations(); - } - - /** - * @inheritdoc IQuery - */ - function findAttestation( - address module, - address attesters - ) - public - view - override(IQuery) - returns (AttestationRecord memory attestation) - { - attestation = _getAttestation(module, attesters); - } - - /** - * @inheritdoc IQuery - */ - function findAttestations( - address module, - address[] memory attesters - ) - external - view - override(IQuery) - returns (AttestationRecord[] memory attestations) - { - uint256 attesterssLength = attesters.length; - attestations = new AttestationRecord[](attesterssLength); - for (uint256 i; i < attesterssLength; ++i) { - attestations[i] = findAttestation(module, attesters[i]); - } - } - - /** - * @notice Internal function to retrieve an attestation record. - * - * @dev This is a virtual function and is meant to be overridden in derived contracts. - * - * @param moduleAddress The address of the module for which the attestation is retrieved. - * @param attester The address of the attester whose record is being retrieved. - * - * @return Attestation record associated with the given module and attester. - */ - function _getAttestation( - address moduleAddress, - address attester - ) - internal - view - virtual - returns (AttestationRecord storage); -} diff --git a/src/base/Schema.sol.bak b/src/base/Schema.sol.bak deleted file mode 100644 index 6bf13ab3..00000000 --- a/src/base/Schema.sol.bak +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { AccessDenied, _time, ZERO_TIMESTAMP, ZERO_ADDRESS, InvalidResolver } from "../Common.sol"; -import { ISchema, SchemaLib } from "../interface/ISchema.sol"; -import { IExternalResolver } from "../external/IExternalResolver.sol"; -import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; - -import { SchemaRecord, ResolverRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; - -/** - * @title Schema - * - * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) - * - */ -abstract contract Schema is ISchema { - using SchemaLib for SchemaRecord; - using SchemaLib for ResolverRecord; - - // The global mapping between schema records and their IDs. - mapping(SchemaUID uid => SchemaRecord schemaRecord) private _schemas; - - mapping(ResolverUID uid => ResolverRecord resolverRecord) private _resolvers; - - /** - * @inheritdoc ISchema - */ - function registerSchema( - string calldata schema, - IExternalSchemaValidator validator // OPTIONAL - ) - external - returns (SchemaUID uid) - { - SchemaRecord memory schemaRecord = - SchemaRecord({ validator: validator, registeredAt: _time(), schema: schema }); - - // Computing a unique ID for the schema using its properties - uid = schemaRecord.getUID(); - - if (_schemas[uid].registeredAt != ZERO_TIMESTAMP) revert AlreadyExists(); - - // Storing schema in the _schemas mapping - _schemas[uid] = schemaRecord; - - emit SchemaRegistered(uid, msg.sender); - } - - /** - * @inheritdoc ISchema - */ - function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid) { - if (address(_resolver) == ZERO_ADDRESS) revert InvalidResolver(); - - // build a ResolverRecord from the input - ResolverRecord memory resolver = - ResolverRecord({ resolver: _resolver, resolverOwner: msg.sender }); - - // Computing a unique ID for the schema using its properties - uid = resolver.getUID(); - - // Checking if a schema with this UID already exists -> resolver can never be ZERO_ADDRESS - if (address(_resolvers[uid].resolver) != ZERO_ADDRESS) { - revert AlreadyExists(); - } - - // Storing schema in the _schemas mapping - _resolvers[uid] = resolver; - - emit SchemaResolverRegistered(uid, msg.sender); - } - - /** - * @inheritdoc ISchema - */ - function setResolver(ResolverUID uid, IExternalResolver resolver) external onlyResolverOwner(uid) { - ResolverRecord storage referrer = _resolvers[uid]; - referrer.resolver = resolver; - emit NewSchemaResolver(uid, address(resolver)); - } - - /** - * @inheritdoc ISchema - */ - function getSchema(SchemaUID uid) public view virtual returns (SchemaRecord memory) { - return _schemas[uid]; - } - - /** - * @dev Internal function to get a schema record - * - * @param schemaUID The UID of the schema. - * - * @return schemaRecord The schema record. - */ - function _getSchema(SchemaUID schemaUID) internal view virtual returns (SchemaRecord storage) { - return _schemas[schemaUID]; - } - - /** - * @inheritdoc ISchema - */ - function getResolver(ResolverUID uid) public view virtual returns (ResolverRecord memory) { - return _resolvers[uid]; - } - - /** - * @dev Modifier to require that the caller is the owner of a resolver - * - * @param uid The UID of the resolver. - */ - modifier onlyResolverOwner(ResolverUID uid) { - _onlyResolverOwner(uid); - _; - } - - /** - * @dev Verifies that the caller is the owner of a resolver - * - * @param uid The UID of the resolver. - */ - function _onlyResolverOwner(ResolverUID uid) private view { - if (_resolvers[uid].resolverOwner != msg.sender) { - revert AccessDenied(); - } - } -} diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 6e2e82aa..8bd92ce1 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.19; import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; import { StubLib } from "../lib/StubLib.sol"; -import { InvalidResolver, _isContract, EMPTY_RESOLVER_UID, ZERO_ADDRESS } from "../Common.sol"; +import { _isContract, EMPTY_RESOLVER_UID, ZERO_ADDRESS } from "../Common.sol"; import { ResolverRecord, ModuleRecord, ResolverUID } from "../DataTypes.sol"; import { ResolverManager } from "./ResolverManager.sol"; import { IRegistry } from "../IRegistry.sol"; diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index 5ae400ad..42fffce4 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; -import { ZERO_ADDRESS, AccessDenied } from "../Common.sol"; +import { ZERO_ADDRESS } from "../Common.sol"; import { IExternalResolver } from "../external/IExternalResolver.sol"; import { UIDLib } from "../lib/Helpers.sol"; import { IRegistry } from "../IRegistry.sol"; diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index c0d18bcb..4e544b59 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -5,6 +5,7 @@ import { AttestationRecord, PackedModuleTypes, ModuleType } from "../DataTypes.s import { ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; +import { LibSort } from "solady/utils/LibSort.sol"; /** * @title TrustManager @@ -14,6 +15,7 @@ import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; */ abstract contract TrustManager is IRegistry { using ModuleTypeLib for PackedModuleTypes; + using LibSort for address[]; // packed struct to allow for efficient storage. // if only one attester is trusted, it only requires 1 SLOAD @@ -27,9 +29,13 @@ abstract contract TrustManager is IRegistry { mapping(address account => TrustedAttesters attesters) internal _accountToAttester; - function trustAttesters(uint8 threshold, address[] calldata attesters) external { + // Deliberately using memory here, so we can sort the array + function trustAttesters(uint8 threshold, address[] memory attesters) external { uint256 attestersLength = attesters.length; - if (attestersLength == 0) revert NoTrustedAttestersFound(); + attesters.sort(); + attesters.uniquifySorted(); + if (attestersLength == 0) revert InvalidTrustedAttesterInput(); + if (attesters.length != attestersLength) revert InvalidTrustedAttesterInput(); // sort attesters TrustedAttesters storage _att = _accountToAttester[msg.sender]; @@ -44,13 +50,39 @@ abstract contract TrustManager is IRegistry { attestersLength--; for (uint256 i; i < attestersLength; i++) { - _att.linkedAttesters[attesters[i]] = attesters[i + 1]; + address _attester = attesters[i]; + if (_attester == address(0)) revert InvalidTrustedAttesterInput(); + _att.linkedAttesters[_attester] = attesters[i + 1]; } } - function check(address module) external view { } + function getTrustedAttesters() public view returns (address[] memory attesters) { + return getTrustedAttesters(msg.sender); + } + + function getTrustedAttesters(address smartAccount) + public + view + returns (address[] memory attesters) + { + TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; + uint256 count = trustedAttesters.attesterCount; + attesters = new address[](count); + attesters[0] = trustedAttesters.attester; + + for (uint256 i = 1; i < count; i++) { + // get next attester from linked List + attesters[i] = trustedAttesters.linkedAttesters[attesters[i - 1]]; + } + } - function checkForAccount(address smartAccount, address module) external view { } + function check(address module) external view { + _check(msg.sender, module); + } + + function checkForAccount(address smartAccount, address module) external view { + _check(smartAccount, module); + } function check(address module, ModuleType moduleType) external view { _check(msg.sender, module, moduleType); @@ -103,6 +135,42 @@ abstract contract TrustManager is IRegistry { } } + function _check(address smartAccount, address module) internal view { + TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; + // SLOAD from one slot + uint256 attesterCount = trustedAttesters.attesterCount; + uint256 threshold = trustedAttesters.threshold; + address attester = trustedAttesters.attester; + + // smart account has no trusted attesters set + if (attester == address(0) && threshold != 0) { + revert NoTrustedAttestersFound(); + } + // smart account only has ONE trusted attester + // use this condition to save gas + else if (threshold == 1) { + AttestationRecord storage record = + _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(record); + } + // smart account has more than one trusted attester + else { + // loop though list and check if the attestation is valid + AttestationRecord storage record = + _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(record); + threshold--; + for (uint256 i = 1; i < attesterCount; i++) { + // get next attester from linked List + attester = trustedAttesters.linkedAttesters[attester]; + record = _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(record); + // if threshold reached, exit loop + if (threshold == 0) return; + } + } + } + function _requireValidAttestation( ModuleType expectedType, AttestationRecord storage record @@ -132,6 +200,25 @@ abstract contract TrustManager is IRegistry { } } + function _requireValidAttestation(AttestationRecord storage record) internal view { + // cache values + uint256 attestedAt = record.time; + uint256 expirationTime = record.expirationTime; + uint256 revocationTime = record.revocationTime; + + if (attestedAt == ZERO_TIMESTAMP) { + revert AttestationNotFound(); + } + + if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { + revert AttestationNotFound(); + } + + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(record.attester); + } + } + function _getAttestation( address module, address attester diff --git a/src/external/ResolverBase.sol.bak b/src/external/ResolverBase.sol.bak deleted file mode 100644 index 2c156fd0..00000000 --- a/src/external/ResolverBase.sol.bak +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { AccessDenied, ZERO_ADDRESS } from "../Common.sol"; -import { AttestationRecord, ModuleRecord } from "../DataTypes.sol"; -import { IExternalResolver } from "./IExternalResolver.sol"; - -/** - * @title A base resolver contract - * - * @author zeroknots.eth - */ -abstract contract ResolverBase is IExternalResolver { - error InsufficientValue(); - error NotPayable(); - error InvalidRS(); - - // The version of the contract. - string public constant VERSION = "0.1"; - - // The global Rhinestone Registry contract. - address internal immutable REGISTRY; - - /** - * @dev Creates a new resolver. - * - * @param rs The address of the global RS contract. - */ - constructor(address rs) { - if (rs == ZERO_ADDRESS) { - revert InvalidRS(); - } - REGISTRY = rs; - } - - /** - * @dev Ensures that only the RS contract can make this call. - */ - modifier onlyRS() { - _onlyRSRegistry(); - _; - } - - /** - * @inheritdoc IExternalResolver - */ - function isPayable() public pure virtual returns (bool) { - return false; - } - - /** - * @dev ETH callback. - */ - receive() external payable virtual { - if (!isPayable()) { - revert NotPayable(); - } - } - - /** - * @inheritdoc IExternalResolver - */ - function attest(AttestationRecord calldata attestation) - external - payable - onlyRS - returns (bool) - { - return onAttest(attestation, msg.value); - } - - /** - * @inheritdoc IExternalResolver - */ - function moduleRegistration(ModuleRecord calldata module) - external - payable - onlyRS - returns (bool) - { - return onModuleRegistration(module, msg.value); - } - - /** - * @inheritdoc IExternalResolver - */ - function multiAttest( - AttestationRecord[] calldata attestations, - uint256[] calldata values - ) - external - payable - onlyRS - returns (bool) - { - uint256 length = attestations.length; - - // We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting - // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless - // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be - // possible to send too much ETH anyway. - uint256 remainingValue = msg.value; - - for (uint256 i; i < length; ++i) { - // Ensure that the attester/revoker doesn't try to spend more than available. - uint256 value = values[i]; - if (value > remainingValue) { - revert InsufficientValue(); - } - - // Forward the attestation to the underlying resolver and revert in case it isn't approved. - if (!onAttest(attestations[i], value)) { - return false; - } - - unchecked { - // Subtract the ETH amount, that was provided to this attestation, from the global remaining ETH amount. - remainingValue -= value; - } - } - - return true; - } - - /** - * @inheritdoc IExternalResolver - */ - function revoke(AttestationRecord calldata attestation) - external - payable - onlyRS - returns (bool) - { - return onRevoke(attestation, msg.value); - } - - /** - * @inheritdoc IExternalResolver - */ - function multiRevoke( - AttestationRecord[] calldata attestations, - uint256[] calldata values - ) - external - payable - onlyRS - returns (bool) - { - uint256 length = attestations.length; - - // We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting - // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless - // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be - // possible to send too much ETH anyway. - uint256 remainingValue = msg.value; - - for (uint256 i; i < length; ++i) { - // Ensure that the attester/revoker doesn't try to spend more than available. - uint256 value = values[i]; - if (value > remainingValue) { - revert InsufficientValue(); - } - - // Forward the revocation to the underlying resolver and revert in case it isn't approved. - if (!onRevoke(attestations[i], value)) { - return false; - } - - unchecked { - // Subtract the ETH amount, that was provided to this attestation, from the global remaining ETH amount. - remainingValue -= value; - } - } - - return true; - } - - /** - * @dev A resolver callback that should be implemented by child contracts. - * - * @param attestation The new attestation. - * @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in - * both attest() and multiAttest() callbacks RS-only callbacks and that in case of multi attestations, it'll - * usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the attestations - * in the batch. - * - * @return Whether the attestation is valid. - */ - function onAttest( - AttestationRecord calldata attestation, - uint256 value - ) - internal - virtual - returns (bool); - - /** - * @dev Processes an attestation revocation and verifies if it can be revoked. - * - * @param attestation The existing attestation to be revoked. - * @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in - * both revoke() and multiRevoke() callbacks RS-only callbacks and that in case of multi attestations, it'll - * usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the attestations - * in the batch. - * - * @return Whether the attestation can be revoked. - */ - function onRevoke( - AttestationRecord calldata attestation, - uint256 value - ) - internal - virtual - returns (bool); - - function onModuleRegistration( - ModuleRecord calldata module, - uint256 value - ) - internal - virtual - returns (bool); - - /** - * @dev Ensures that only the RS contract can make this call. - */ - function _onlyRSRegistry() private view { - if (msg.sender != REGISTRY) { - revert AccessDenied(); - } - } - - function supportsInterface(bytes4 interfaceID) external pure returns (bool) { - return interfaceID == this.supportsInterface.selector - || interfaceID == this.isPayable.selector || interfaceID == this.attest.selector - || interfaceID == this.moduleRegistration.selector - || interfaceID == this.multiAttest.selector || interfaceID == this.revoke.selector - || interfaceID == this.multiRevoke.selector; - } -} diff --git a/src/external/SchemaValidatorBase.sol.bak b/src/external/SchemaValidatorBase.sol.bak deleted file mode 100644 index ea22bdbf..00000000 --- a/src/external/SchemaValidatorBase.sol.bak +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { IExternalSchemaValidator, AttestationRequest } from "./IExternalSchemaValidator.sol"; - -/** - * @title SchemaValidatorBase - * @notice Base contract for schema validators - */ -contract SchemaValidatorBase is IExternalSchemaValidator { - function validateSchema(AttestationRequest calldata attestation) - external - view - virtual - override - returns (bool) - { } - - function validateSchema(AttestationRequest[] calldata attestations) - external - view - virtual - override - returns (bool) - { } - - function supportsInterface(bytes4 interfaceID) external view virtual returns (bool) { - return interfaceID == this.supportsInterface.selector; - } -} diff --git a/src/interface/IAttestation.sol.bak b/src/interface/IAttestation.sol.bak deleted file mode 100644 index ec9ba9c0..00000000 --- a/src/interface/IAttestation.sol.bak +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { - SchemaUID, - AttestationDataRef, - AttestationRequest, - MultiAttestationRequest, - SignedAttestationRequest, - MultiSignedAttestationRequest, - RevocationRequest, - SignedRevocationRequest, - MultiSignedRevocationRequest, - MultiRevocationRequest -} from "../DataTypes.sol"; - -/** - * @dev The global attestation interface. - */ -interface IAttestation { - error AlreadyRevoked(); - error AlreadyRevokedOffchain(); - error AlreadyTimestamped(); - error InsufficientValue(); - error InvalidAttestation(); - error InvalidAttestationRefUID(bytes32 missingRefUID); - error IncompatibleAttestation(bytes32 sourceCodeHash, bytes32 targetCodeHash); - error InvalidAttestations(); - error InvalidExpirationTime(); - error InvalidOffset(); - error InvalidRegistry(); - error InvalidRevocation(); - error InvalidRevocations(); - error InvalidVerifier(); - error NotPayable(); - error WrongSchema(); - error InvalidSender(address moduleAddr, address sender); - - /** - * @dev Emitted when an attestation has been made. - * - * @param moduleAddr The moduleAddr of the attestation. - * @param attester The attesting account. - * @param schema The UID of the schema. - */ - event Attested( - address indexed moduleAddr, - address indexed attester, - SchemaUID schema, - AttestationDataRef indexed dataPointer - ); - - /** - * @dev Emitted when an attestation has been revoked. - * - * @param moduleAddr The moduleAddr of the attestation. - * @param revoker The attesting account. - * @param schema The UID of the schema. - */ - event Revoked(address indexed moduleAddr, address indexed revoker, SchemaUID indexed schema); - - /** - * @dev Emitted when a data has been timestamped. - * - * @param data The data. - * @param timestamp The timestamp. - */ - event Timestamped(bytes32 indexed data, uint64 indexed timestamp); - - /** - * @dev Emitted when a data has been revoked. - * - * @param revoker The address of the revoker. - * @param data The data. - * @param timestamp The timestamp. - */ - event RevokedOffchain(address indexed revoker, bytes32 indexed data, uint64 indexed timestamp); - /** - * @notice Creates an attestation for a specified schema. - * - * @param request The attestation request. - */ - - function attest(AttestationRequest calldata request) external payable; - - /** - * @notice Creates multiple attestations for multiple schemas. - * @dev Although the registry supports batched attestations, the function only allows - * batched Attestations for a single resolver. - * If you want to attest to multiple resolvers, you need to call the function multiple times. - * - * @param multiRequests An array of multi attestation requests. - */ - function multiAttest(MultiAttestationRequest[] calldata multiRequests) external payable; - - /** - * @notice Handles a single signed attestation request - * - * @dev The function verifies the attestation, - * wraps the data in an array and forwards it to the _multiAttest() function - * - * @param signedRequest A signed attestation request - */ - function attest(SignedAttestationRequest calldata signedRequest) external payable; - - /** - * @notice Function to handle multiple signed attestation requests - * - * @dev It iterates over the attestation requests and processes them. It collects the returned UIDs into a list. - * @dev Although the registry supports batched attestations, the function only allows - * batched Attestations for a single resolver. - * If you want to attest to multiple resolvers, you need to call the function multiple times. - * - * @param multiSignedRequests An array of multiple signed attestation requests - */ - function multiAttest(MultiSignedAttestationRequest[] calldata multiSignedRequests) - external - payable; - - /** - * @notice Revokes an existing attestation for a specified schema. - * - * @param request The revocation request. - */ - function revoke(RevocationRequest calldata request) external payable; - /** - * @notice Handles a single signed revocation request - * - * @dev The function verifies the revocation, prepares data for the _multiRevoke() function and revokes the requestZ - * - * @param request A signed revocation request - */ - function revoke(SignedRevocationRequest calldata request) external payable; - - /** - * @notice Handles multiple signed revocation requests - * - * @dev The function iterates over the multiSignedRequests array, verifies each revocation and revokes the request - * @dev Although the registry supports batched revocations, the function only allows - * batched Attestations for a single resolver. - * If you want to attest to multiple resolvers, you need to call the function multiple times. - * - * @param multiSignedRequests An array of multiple signed revocation requests - */ - function multiRevoke(MultiSignedRevocationRequest[] calldata multiSignedRequests) - external - payable; - - /** - * @notice Revokes multiple existing attestations for multiple schemas. - * @dev Although the registry supports batched revocations, the function only allows - * batched Attestations for a single resolver. - * If you want to attest to multiple resolvers, you need to call the function multiple times. - * @param multiRequests An array of multi revocation requests. - */ - function multiRevoke(MultiRevocationRequest[] calldata multiRequests) external payable; -} - -/** - * @dev Library for attestation related functions. - */ -library AttestationLib { - /** - * @dev Generates a unique salt for an attestation using the provided attester and module addresses. - * The salt is generated using a keccak256 hash of the module address, - * attester address, current timestamp, and chain ID. - * This salt will be used for SSTORE2 - * - * @param attester Address of the entity making the attestation. - * @param module Address of the module being attested to. - * - * @return dataPointerSalt A unique salt for the attestation data storage. - */ - function attestationSalt( - address attester, - address module - ) - internal - returns (bytes32 dataPointerSalt) - { - dataPointerSalt = - keccak256(abi.encodePacked(module, attester, block.timestamp, block.chainid)); - } -} diff --git a/src/interface/IERC7484.sol.bak b/src/interface/IERC7484.sol.bak deleted file mode 100644 index 1ca6ccbf..00000000 --- a/src/interface/IERC7484.sol.bak +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; - -/** - * ERC-7484 compliant interface for the registry. - * - * @author zeroknots - */ -interface IERC7484 { - /** - * @notice Queries the attestation of a specific attester for a given module. - * - * @dev If an attestation is not found, expired or is revoked, the function will revert. - * - * @param module The address of the module being queried. - * @param attester The address of the attester attestation is being queried. - * - * @return attestedAt The time the attestation was listed. Returns 0 if not listed or expired. - */ - function check(address module, address attester) external view returns (uint256 attestedAt); - - /** - * @notice Verifies the validity of attestations for a given module against a threshold. - * - * @dev This function will revert if the threshold is not met. - * @dev Will also revert if any of the attestations have been revoked (even if threshold is met). - * - * @param module The address of the module being verified. - * @param attesters The list of attesters whose attestations are being verified. - * @param threshold The minimum number of valid attestations required. - * - * @return attestedAtArray The list of attestation times associated with the given module and attesters. - */ - function checkN( - address module, - address[] memory attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray); -} diff --git a/src/interface/IModule.sol.bak b/src/interface/IModule.sol.bak deleted file mode 100644 index bcc933d9..00000000 --- a/src/interface/IModule.sol.bak +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { ResolverUID } from "../DataTypes.sol"; - -/** - * Module interface allows for the deployment and registering of modules. - * - * @author zeroknots - */ -interface IModule { - // Event triggered when a module is deployed. - event ModuleRegistration( - address indexed implementation, address indexed sender, bytes32 resolver - ); - event ModuleDeployed(address indexed implementation, bytes32 indexed salt, bytes32 resolver); - event ModuleDeployedExternalFactory( - address indexed implementation, address indexed factory, bytes32 resolver - ); - - error AlreadyRegistered(address module); - error InvalidDeployment(); - - /** - * @notice Deploys a new module. - * - * @dev Ensures the resolver is valid and then deploys the module. - * - * @param code The bytecode for the module. - * @param deployParams Parameters required for deployment. - * @param salt Salt for creating the address. - * @param metadata Data associated with the module. - * Entities can use this to store additional information about the module. - * This metadata will be forwarded to the resolver. - * @param resolverUID Unique ID of the resolver. - * - * @return moduleAddr The address of the deployed module. - */ - function deploy( - bytes calldata code, - bytes calldata deployParams, - bytes32 salt, - bytes calldata metadata, - ResolverUID resolverUID - ) - external - payable - returns (address moduleAddr); - - /** - * @notice Deploys a new module using the CREATE3 method. - * - * @dev Similar to the deploy function but uses CREATE3 for deployment. - * @dev the salt supplied here will be hashed again with msg.sender - * - * @param code The bytecode for the module. - * @param deployParams Parameters required for deployment. - * @param salt Initial salt for creating the final salt. - * @param metadata Data associated with the module. - * Entities can use this to store additional information about the module. - * This metadata will be forwarded to the resolver. - * @param resolverUID Unique ID of the resolver. - * - * @return moduleAddr The address of the deployed module. - */ - function deployC3( - bytes calldata code, - bytes calldata deployParams, - bytes32 salt, - bytes calldata metadata, - ResolverUID resolverUID - ) - external - payable - returns (address moduleAddr); - - /** - * @notice Deploys a new module via an external factory contract. - * - * @param factory Address of the factory contract. - * @param callOnFactory Encoded call to be made on the factory contract. - * @param metadata Data associated with the module. - * Entities can use this to store additional information about the module. - * This metadata will be forwarded to the resolver. - * @param resolverUID Unique ID of the resolver. - * - * @return moduleAddr The address of the deployed module. - */ - function deployViaFactory( - address factory, - bytes calldata callOnFactory, - bytes calldata metadata, - ResolverUID resolverUID - ) - external - payable - returns (address moduleAddr); - - /** - * @notice Registers an existing module with the contract. - * @dev since anyone can register an existing module, - * the 'sender' attribute in ModuleRecord will be address(0) - * - * @param resolverUID Unique ID of the resolver. - * @param moduleAddress Address of the module. - * @param metadata Data associated with the module. - * Entities can use this to store additional information about the module. - * This metadata will be forwarded to the resolver. - */ - function register( - ResolverUID resolverUID, - address moduleAddress, - bytes calldata metadata - ) - external; -} diff --git a/src/interface/IQuery.sol.bak b/src/interface/IQuery.sol.bak deleted file mode 100644 index 39aa9591..00000000 --- a/src/interface/IQuery.sol.bak +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { AttestationRecord } from "../DataTypes.sol"; -import { IERC7484 } from "./IERC7484.sol"; - -/** - * Query interface allows for the verification of attestations - * with potential for reversion in case of invalid attestation. - * - * @author zeroknots - */ -interface IQuery is IERC7484 { - error NoAttesterSet(); - error RevokedAttestation(address attester); - error AttestationNotFound(); - error InsufficientAttestations(); - - /** - * @notice Queries the attestation status of a specific attester for a given module. - * - * @dev If an attestation is not found or is revoked, the function will revert. - * - * @param module The address of the module being queried. - * @param attester The address of the attester whose status is being queried. - * - * @return attestedAt The time the attestation was listed. Returns 0 if not listed or expired. - */ - function check(address module, address attester) external view returns (uint256 attestedAt); - - /** - * @notice Verifies the validity of attestations for a given module against a threshold. - * - * @dev This function will revert if the threshold is not met. - * @dev Will also revert if any of the attestations have been revoked (even if threshold is met). - * - * @param module The address of the module being verified. - * @param attesters The list of attesters whose attestations are being verified. - * @param threshold The minimum number of valid attestations required. - * - * @return attestedAtArray The list of attestation times associated with the given module and attesters. - */ - function checkN( - address module, - address[] memory attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray); - - /** - * @notice Verifies attestations for a given module against a threshold, but does not check revocation. - * - * @dev This function will revert if the threshold is not met. - * @dev Does not revert on revoked attestations but treats them the same as non-existent attestations. - * - * @param module The address of the module being verified. - * @param attesters The list of attesters whose attestations are being verified. - * @param threshold The minimum number of valid attestations required. - */ - function checkNUnsafe( - address module, - address[] memory attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray); - - /** - * @notice Retrieves the attestation record for a given module and attester. - * - * @param module The address of the module being queried. - * @param attester The address of the attester whose record is being retrieved. - * - * @return attestation The attestation record associated with the given module and attester. - */ - function findAttestation( - address module, - address attester - ) - external - view - returns (AttestationRecord memory attestation); - - /** - * Find an attestations associated with a given module and attester. - * - * @notice Retrieves attestation records for a given module and a list of attesters. - * - * @param module The address of the module being queried. - * @param attesters The list of attesters whose records are being retrieved. - * - * @return attestations The list of attestation records associated with the given module and attesters. - */ - function findAttestations( - address module, - address[] memory attesters - ) - external - view - returns (AttestationRecord[] memory attestations); -} diff --git a/src/interface/IRegistry.sol.bak b/src/interface/IRegistry.sol.bak deleted file mode 100644 index 6aa82502..00000000 --- a/src/interface/IRegistry.sol.bak +++ /dev/null @@ -1,238 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -interface IRegistry { - event Attested( - address indexed moduleAddr, - address indexed attester, - bytes32 schema, - address indexed dataPointer - ); - event EIP712DomainChanged(); - event ModuleDeployed(address indexed implementation, bytes32 indexed salt, bytes32 resolver); - event ModuleDeployedExternalFactory( - address indexed implementation, address indexed factory, bytes32 resolver - ); - event ModuleRegistration(address indexed implementation, bytes32 resolver); - event NewSchemaResolver(bytes32 indexed uid, address resolver); - event Revoked(address indexed moduleAddr, address indexed attester, bytes32 indexed schema); - event RevokedOffchain(address indexed revoker, bytes32 indexed data, uint64 indexed timestamp); - event SchemaRegistered(bytes32 indexed uid, address registerer); - event SchemaResolverRegistered(bytes32 indexed uid, address registerer); - event Timestamped(bytes32 indexed data, uint64 indexed timestamp); - - struct AttestationRecord { - bytes32 schemaUID; - address moduleAddr; - address attester; - uint48 time; - uint48 expirationTime; - uint48 revocationTime; - address dataPointer; - } - - struct AttestationRequest { - bytes32 schemaUID; - AttestationRequest data; - } - - struct AttestationRequest { - address moduleAddr; - uint48 expirationTime; - uint256 value; - bytes data; - } - - struct SignedAttestationRequest { - bytes32 schemaUID; - AttestationRequest data; - bytes signature; - address attester; - } - - struct SignedRevocationRequest { - bytes32 schemaUID; - RevocationRequest data; - bytes signature; - address revoker; - } - - struct ModuleRecord { - bytes32 resolverUID; - address implementation; - address sender; - bytes data; - } - - struct MultiAttestationRequest { - bytes32 schemaUID; - AttestationRequest[] data; - } - - struct MultiSignedAttestationRequest { - bytes32 schemaUID; - AttestationRequest[] data; - bytes[] signatures; - address attester; - } - - struct MultiSignedRevocationRequest { - bytes32 schemaUID; - RevocationRequest[] data; - bytes[] signatures; - address revoker; - } - - struct MultiRevocationRequest { - bytes32 schemaUID; - RevocationRequest[] data; - } - - struct ResolverRecord { - address resolver; - address schemaOwner; - } - - struct RevocationRequest { - bytes32 schemaUID; - RevocationRequest data; - } - - struct RevocationRequest { - address moduleAddr; - address attester; - uint256 value; - } - - struct SchemaRecord { - uint48 registeredAt; - address validator; - string schema; - } - - function attest(SignedAttestationRequest memory signedRequest) external payable; - function attest(AttestationRequest memory request) external payable; - function check( - address module, - address attester - ) - external - view - returns (uint48 listedAt, uint48 revokedAt); - function deploy( - bytes memory code, - bytes memory deployParams, - bytes32 salt, - bytes memory data, - bytes32 resolverUID - ) - external - payable - returns (address moduleAddr); - function deployC3( - bytes memory code, - bytes memory deployParams, - bytes32 salt, - bytes memory data, - bytes32 resolverUID - ) - external - payable - returns (address moduleAddr); - function deployViaFactory( - address factory, - bytes memory callOnFactory, - bytes memory data, - bytes32 resolverUID - ) - external - payable - returns (address moduleAddr); - function eip712Domain() - external - view - returns ( - bytes1 fields, - string memory name, - string memory version, - uint256 chainId, - address verifyingContract, - bytes32 salt, - uint256[] memory extensions - ); - function findAttestation( - address module, - address attesters - ) - external - view - returns (AttestationRecord memory attestation); - function findAttestations( - address module, - address[] memory attesters - ) - external - view - returns (AttestationRecord[] memory attestations); - function getAttestTypeHash() external pure returns (bytes32); - function getAttestationDigest( - AttestationRequest memory attData, - bytes32 schemaUID, - uint256 nonce - ) - external - view - returns (bytes32 digest); - function getAttestationDigest( - AttestationRequest memory attData, - bytes32 schemaUID, - address attester - ) - external - view - returns (bytes32 digest); - function getDomainSeparator() external view returns (bytes32); - function getModule(address moduleAddress) external view returns (ModuleRecord memory); - function getName() external view returns (string memory); - function getNonce(address account) external view returns (uint256); - function getResolver(bytes32 uid) external view returns (ResolverRecord memory); - function getRevocationDigest( - RevocationRequest memory revData, - bytes32 schemaUID, - address revoker - ) - external - view - returns (bytes32 digest); - function getRevocationDigest( - RevocationRequest memory revData, - bytes32 schemaUID, - uint256 nonce - ) - external - view - returns (bytes32 digest); - function getRevokeTypeHash() external pure returns (bytes32); - function getSchema(bytes32 uid) external view returns (SchemaRecord memory); - function multiAttest(MultiSignedAttestationRequest[] memory multiSignedRequests) - external - payable; - function multiAttest(MultiAttestationRequest[] memory multiRequests) external payable; - function multiRevoke(MultiRevocationRequest[] memory multiRequests) external payable; - function multiRevoke(MultiSignedRevocationRequest[] memory multiSignedRequests) - external - payable; - function register(bytes32 resolverUID, address moduleAddress, bytes memory data) external; - function registerResolver(address _resolver) external returns (bytes32); - function registerSchema(string memory schema, address validator) external returns (bytes32); - function revoke(RevocationRequest memory request) external payable; - function setResolver(bytes32 uid, address resolver) external; - function verify(address module, address[] memory attesters, uint256 threshold) external view; - function verifyUnsafe( - address module, - address[] memory attesters, - uint256 threshold - ) - external - view; -} diff --git a/src/interface/ISchema.sol.bak b/src/interface/ISchema.sol.bak deleted file mode 100644 index 373aa069..00000000 --- a/src/interface/ISchema.sol.bak +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -import { IExternalResolver } from "../external/IExternalResolver.sol"; -import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; -import { SchemaUID, SchemaRecord, ResolverUID, ResolverRecord } from "../DataTypes.sol"; - -/** - * @title The global schema interface. - */ -interface ISchema { - // Error to throw if the SchemaID already exists - error AlreadyExists(); - - /** - * @dev Emitted when a new schema has been registered - * - * @param uid The schema UID. - * @param registerer The address of the account used to register the schema. - */ - event SchemaRegistered(SchemaUID indexed uid, address registerer); - - event SchemaResolverRegistered(ResolverUID indexed uid, address registerer); - - /** - * @dev Emitted when a new schema resolver - * - * @param uid The schema UID. - * @param resolver The address of the resolver. - */ - event NewSchemaResolver(ResolverUID indexed uid, address resolver); - - /** - * @notice Registers a new schema. - * - * @dev Ensures that the schema does not already exist and calculates a unique ID for it. - * - * @param schema The schema as a string representation. - * @param validator OPTIONAL Contract address that validates this schema. - * If not provided, all attestations made against this schema is assumed to be valid. - * - * @return uid The unique ID of the registered schema. - */ - function registerSchema( - string calldata schema, - IExternalSchemaValidator validator - ) - external - returns (SchemaUID); - - /** - * @notice Registers a resolver and associates it with the caller. - * @dev This function allows the registration of a resolver by computing a unique ID and associating it with the owner. - * Emits a SchemaResolverRegistered event upon successful registration. - * - * @param _resolver Address of the IExternalResolver to be registered. - * - * @return uid The unique ID (ResolverUID) associated with the registered resolver. - */ - function registerResolver(IExternalResolver _resolver) external returns (ResolverUID); - - /** - * @notice Updates the resolver for a given UID. - * - * @dev Can only be called by the owner of the schema. - * - * @param uid The UID of the schema to update. - * @param resolver The new resolver interface. - */ - function setResolver(ResolverUID uid, IExternalResolver resolver) external; - - /** - * @notice Retrieves the schema record for a given UID. - * - * @param uid The UID of the schema to retrieve. - * - * @return The schema record associated with the given UID. - */ - function getSchema(SchemaUID uid) external view returns (SchemaRecord memory); - - /** - * @notice Retrieves the resolver record for a given UID. - * - * @param uid The UID of the resolver to retrieve. - * - * @return The resolver record associated with the given UID. - */ - function getResolver(ResolverUID uid) external view returns (ResolverRecord memory); -} - -library SchemaLib { - /** - * @dev Calculates a UID for a given schema. - * - * @param schemaRecord The input schema. - * - * @return schema UID. - */ - function getUID(SchemaRecord memory schemaRecord) internal pure returns (SchemaUID) { - return SchemaUID.wrap( - keccak256(abi.encodePacked(schemaRecord.schema, address(schemaRecord.validator))) - ); - } - - /** - * @dev Calculates a UID for a given resolver. - * - * @param resolver The input schema. - * - * @return ResolverUID. - */ - function getUID(ResolverRecord memory resolver) internal pure returns (ResolverUID) { - return ResolverUID.wrap(keccak256(abi.encodePacked(resolver.resolver))); - } -} diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 02f4c60e..f013afd2 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -5,7 +5,7 @@ import "./Base.t.sol"; import "src/DataTypes.sol"; contract AttestationTest is BaseTest { - function setUp() public override { + function setUp() public virtual override { super.setUp(); } @@ -16,6 +16,7 @@ contract AttestationTest is BaseTest { uint32[] memory types ) internal + pure returns (AttestationRequest memory request) { ModuleType[] memory typesEnc = new ModuleType[](types.length); @@ -30,10 +31,31 @@ contract AttestationTest is BaseTest { }); } - function mockRevocation(address module) internal returns (RevocationRequest memory request) { + function mockRevocation(address module) + internal + pure + returns (RevocationRequest memory request) + { request = RevocationRequest({ moduleAddr: module }); } + function test_WhenAttestingWithNoAttestationData(address module) + public + prankWithAccount(attester1) + { + uint32[] memory types = new uint32[](1); + AttestationRequest memory request = + mockAttestation(module, uint48(block.timestamp + 1), "", types); + // It should store. + registry.attest(defaultSchemaUID, request); + AttestationRecord memory record = registry.readAttestation(module, attester1.addr); + + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, request.expirationTime); + assertEq(record.moduleAddr, request.moduleAddr); + assertEq(record.attester, attester1.addr); + } + function test_WhenAttestingWithExpirationTimeInThePast( address module, bytes memory data, @@ -103,21 +125,6 @@ contract AttestationTest is BaseTest { registry.revoke(mockRevocation(module)); } - function test_WhenAttestingWithNoAttestationData() external prankWithAccount(attester1) { - uint32[] memory types = new uint32[](1); - AttestationRequest memory request = - mockAttestation(makeAddr("module"), uint48(block.timestamp + 1), "", types); - // It should store. - registry.attest(defaultSchemaUID, request); - AttestationRecord memory record = - registry.readAttestation(makeAddr("module"), attester1.addr); - - assertEq(record.time, block.timestamp); - assertEq(record.expirationTime, request.expirationTime); - assertEq(record.moduleAddr, request.moduleAddr); - assertEq(record.attester, attester1.addr); - } - function test_WhenAttesting_ShouldCallResolver() external { resolverTrue.reset(); // It should call ExternalResolver. diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol index e63e6292..889822a6 100644 --- a/test/TrustDelegation.t.sol +++ b/test/TrustDelegation.t.sol @@ -1,23 +1,61 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -contract TrustTest { - modifier whenSettingAttester() { - _; +import "./Attestation.t.sol"; +import "src/DataTypes.sol"; +import { LibSort } from "solady/utils/LibSort.sol"; + +contract TrustTest is AttestationTest { + using LibSort for address[]; + + function setUp() public override { + super.setUp(); + // test_WhenAttestingWithNoAttestationData(address(module1)); } - function test_WhenSupplyingSameAttesterMultipleTimes() external whenSettingAttester { - // It should revert. + modifier whenSettingAttester() { + _; } function test_WhenSupplyingOneAttester() external whenSettingAttester { // It should set. - // It should emit event. + address[] memory trustedAttesters = new address[](1); + trustedAttesters[0] = address(attester1.addr); + registry.trustAttesters(1, trustedAttesters); + address[] memory result = registry.getTrustedAttesters(); + assertEq(result.length, 1); + assertEq(result[0], address(attester1.addr)); } - function test_WhenSupplyingManyAttesters() external whenSettingAttester { + function test_WhenSupplyingManyAttesters(address[] memory attesters) + external + whenSettingAttester + { + vm.assume(attesters.length < 100); + vm.assume(attesters.length > 0); + for (uint256 i; i < attesters.length; i++) { + vm.assume(attesters[i] != address(0)); + } + attesters.sort(); + attesters.uniquifySorted(); + registry.trustAttesters(uint8(attesters.length), attesters); // It should set. // It should emit event. + address[] memory result = registry.getTrustedAttesters(); + + assertEq(result.length, attesters.length); + for (uint256 i; i < attesters.length; i++) { + assertEq(result[i], attesters[i]); + } + } + + function test_WhenSupplyingSameAttesterMultipleTimes() external whenSettingAttester { + address[] memory attesters = new address[](2); + attesters[0] = address(attester1.addr); + attesters[1] = address(attester1.addr); + // It should revert. + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidTrustedAttesterInput.selector)); + registry.trustAttesters(uint8(attesters.length), attesters); } modifier whenQueryingRegisty() { @@ -26,6 +64,14 @@ contract TrustTest { function test_WhenNoAttestersSet() external whenQueryingRegisty { // It should revert. + vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + registry.check(address(module1), ModuleType.wrap(1)); + vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + registry.checkForAccount(makeAddr("foo"), address(module1), ModuleType.wrap(1)); + vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + registry.check(address(module1)); + vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + registry.checkForAccount(makeAddr("foo"), address(module1)); } function test_WhenAttesterSetButNoAttestationMade() external whenQueryingRegisty { From e427e7f82bec0887c649c0e401494c07da577fd6 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 15:12:23 +0700 Subject: [PATCH 18/84] del: removing old files --- src/integrations/MockRegistry.sol.bak | 80 ------------------- .../MultiAttesterRegistryIntegration.sol.bak | 61 -------------- .../SimpleRegistryIntegration.sol.bak | 58 -------------- .../SimpleRegistryStorageSlot.sol.bak | 73 ----------------- 4 files changed, 272 deletions(-) delete mode 100644 src/integrations/MockRegistry.sol.bak delete mode 100644 src/integrations/examples/MultiAttesterRegistryIntegration.sol.bak delete mode 100644 src/integrations/examples/SimpleRegistryIntegration.sol.bak delete mode 100644 src/integrations/examples/SimpleRegistryStorageSlot.sol.bak diff --git a/src/integrations/MockRegistry.sol.bak b/src/integrations/MockRegistry.sol.bak deleted file mode 100644 index 8fd4c865..00000000 --- a/src/integrations/MockRegistry.sol.bak +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { IQuery } from "../interface/IQuery.sol"; -import { AttestationRecord } from "../DataTypes.sol"; - -/** - * @title MockRegistry - * @author zeroknots - * @notice Mock registry for testing purposes. - */ -contract MockRegistry is IQuery { - function check( - address plugin, - address trustedEntity - ) - external - view - override - returns (uint256 listedAt) - { - return uint256(1234); - } - - function checkN( - address module, - address[] memory attesters, - uint256 threshold - ) - external - view - override - returns (uint256[] memory) - { - uint256 attestersLength = attesters.length; - uint256[] memory attestedAtArray = new uint256[](attestersLength); - for (uint256 i; i < attestersLength; ++i) { - attestedAtArray[i] = uint256(1234); - } - return attestedAtArray; - } - - function checkNUnsafe( - address module, - address[] memory attesters, - uint256 threshold - ) - external - view - override - returns (uint256[] memory) - { - uint256 attestersLength = attesters.length; - uint256[] memory attestedAtArray = new uint256[](attestersLength); - for (uint256 i; i < attestersLength; ++i) { - attestedAtArray[i] = uint256(1234); - } - return attestedAtArray; - } - - function findAttestation( - address module, - address attester - ) - external - view - override - returns (AttestationRecord memory attestation) - { } - - function findAttestations( - address module, - address[] memory attersters - ) - external - view - override - returns (AttestationRecord[] memory attestations) - { } -} diff --git a/src/integrations/examples/MultiAttesterRegistryIntegration.sol.bak b/src/integrations/examples/MultiAttesterRegistryIntegration.sol.bak deleted file mode 100644 index 73d5fb67..00000000 --- a/src/integrations/examples/MultiAttesterRegistryIntegration.sol.bak +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { IQuery } from "../../interface/IQuery.sol"; - -/** - * @title SimpleRegistryIntegration - * @author zeroknots - * - * @dev This contract allows only trusted contracts (attested by a specific attester) - * to interact with it by leveraging the IQuery registry - */ -abstract contract RegistryIntegration { - IQuery public immutable registry; // Instance of the registry - address[] public trustedAttesters; // Address of the trusted attesters - uint256 public immutable threshold; // Number of attestations required for verification - - error TargetContractNotPermitted(address target); - - /** - * @dev Constructs the contract and initializes the registry and the trusted attester - * - * @param _registry The address of the IQuery registry - * @param _trustedAttester The address of the trusted attester - */ - constructor(address _registry, address[] memory _trustedAttester, uint256 _threshold) { - registry = IQuery(_registry); - trustedAttesters = _trustedAttester; - threshold = _threshold; - } - - /** - * @notice Internal function that checks the registry for a contract's status - * - * @dev Queries the registry with the provided contract address and the trusted attester - * - * @param _contract The address of the contract to be checked in the registry - * @return validCheck the registry returned a boolean if the attestations with selected threshold was valid - */ - function _checkRegistry(address _contract) internal view returns (bool validCheck) { - registry.checkN(_contract, trustedAttesters, threshold); - return true; - } - - /** - * @dev Modifier that allows only allowed contracts to interact - * - * @notice If the contract has ever been flagged or was never attested to, the interaction will be reverted - * - * @param _contract The address of the contract to be checked - */ - modifier onlyWithRegistryCheck(address _contract) { - bool valid = _checkRegistry(_contract); - - // revert if contract was ever flagged or was never attested to - if (!valid) { - revert TargetContractNotPermitted(_contract); - } - _; - } -} diff --git a/src/integrations/examples/SimpleRegistryIntegration.sol.bak b/src/integrations/examples/SimpleRegistryIntegration.sol.bak deleted file mode 100644 index 48a3e29d..00000000 --- a/src/integrations/examples/SimpleRegistryIntegration.sol.bak +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { IQuery } from "../../interface/IQuery.sol"; - -/** - * @title SimpleRegistryIntegration - * @author zeroknots - * - * @dev This contract allows only trusted contracts (attested by a specific attester) - * to interact with it by leveraging the IQuery registry - */ -abstract contract RegistryIntegration { - IQuery public immutable registry; // Instance of the registry - address public immutable trustedAttester; // Address of the trusted authority for attesting - - error TargetContractNotPermitted(address target, uint256 listedAt, uint256 flaggedAt); - - /** - * @dev Constructs the contract and initializes the registry and the trusted attester - * - * @param _registry The address of the IQuery registry - * @param _trustedAttester The address of the trusted attester - */ - constructor(address _registry, address _trustedAttester) { - registry = IQuery(_registry); - trustedAttester = _trustedAttester; - } - - /** - * @notice Internal function that checks the registry for a contract's status - * - * @dev Queries the registry with the provided contract address and the trusted attester - * - * @param _contract The address of the contract to be checked in the registry - * @return listedAt The timestamp at which the contract was listed (0 if never listed) - */ - function _checkRegistry(address _contract) internal view returns (uint256 listedAt) { - return registry.check(_contract, trustedAttester); - } - - /** - * @dev Modifier that allows only allowed contracts to interact - * - * @notice If the contract has ever been flagged or was never attested to, the interaction will be reverted - * - * @param _contract The address of the contract to be checked - */ - modifier onlyWithRegistryCheck(address _contract) { - uint256 listedAt = _checkRegistry(_contract); - - // revert if contract was ever flagged or was never attested to - if (listedAt == 0) { - revert TargetContractNotPermitted(_contract, listedAt, 0); - } - _; - } -} diff --git a/src/integrations/examples/SimpleRegistryStorageSlot.sol.bak b/src/integrations/examples/SimpleRegistryStorageSlot.sol.bak deleted file mode 100644 index 52cd2ce9..00000000 --- a/src/integrations/examples/SimpleRegistryStorageSlot.sol.bak +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { IQuery } from "../../interface/IQuery.sol"; - -bytes32 constant REGISTRY_INTEGRATION_SLOT = keccak256("RegistryIntegration.storage.location"); - -library RegistryIntegrationStorage { - struct Storage { - IQuery registry; - address trustedAttester; - } - - function store() internal pure returns (Storage storage s) { - bytes32 slot = REGISTRY_INTEGRATION_SLOT; - assembly { - s.slot := slot - } - } -} - -/** - * @title RegistryIntegrationStorageSlot - * @author zeroknots - * - * @dev This contract allows only trusted contracts (attested by a specific attester) - * to interact with it by leveraging the IQuery registry - */ -abstract contract RegistryIntegrationStorageSlot { - error TargetContractNotPermitted(address target, uint256 listedAt, uint256 flaggedAt); - - /** - * @dev Constructs the contract and initializes the registry and the trusted attester - * - * @param _registry The address of the IQuery registry - * @param _trustedAttester The address of the trusted attester - */ - function _set(address _registry, address _trustedAttester) internal { - RegistryIntegrationStorage.Storage storage s = RegistryIntegrationStorage.store(); - s.registry = IQuery(_registry); - s.trustedAttester = _trustedAttester; - } - - /** - * @notice Internal function that checks the registry for a contract's status - * - * @dev Queries the registry with the provided contract address and the trusted attester - * - * @param _contract The address of the contract to be checked in the registry - * @return listedAt The timestamp at which the contract was listed (0 if never listed) - */ - function _checkRegistry(address _contract) internal view returns (uint256 listedAt) { - RegistryIntegrationStorage.Storage storage s = RegistryIntegrationStorage.store(); - return s.registry.check(_contract, s.trustedAttester); - } - - /** - * @dev Modifier that allows only allowed contracts to interact - * - * @notice If the contract has ever been flagged or was never attested to, the interaction will be reverted - * - * @param _contract The address of the contract to be checked - */ - modifier onlyWithRegistryCheck(address _contract) { - uint256 listedAt = _checkRegistry(_contract); - - // revert if contract was ever flagged or was never attested to - if (listedAt == 0) { - revert TargetContractNotPermitted(_contract, listedAt, 0); - } - _; - } -} From d560e89de2e7327557801a392e4c11d75a514c8d Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 15:36:20 +0700 Subject: [PATCH 19/84] chore: adding tests for attestation arrays --- src/core/SignedAttestation.sol | 11 ++++++ test/Attestation.t.sol | 69 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/core/SignedAttestation.sol b/src/core/SignedAttestation.sol index c651d110..e061a061 100644 --- a/src/core/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -100,4 +100,15 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { { return _hashTypedData(request.hash(attesterNonce[attester] + 1)); } + + function getDigest( + AttestationRequest[] calldata requests, + address attester + ) + external + view + returns (bytes32) + { + return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); + } } diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index f013afd2..7dc8060e 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -177,6 +177,30 @@ contract AttestationTest is BaseTest { assertEq(nonceAfter, nonceBefore + 1); } + function test_WhenUsingValidECDSAMulti() external whenAttestingWithSignature { + uint256 nonceBefore = registry.attesterNonce(attester1.addr); + // It should recover. + uint32[] memory types = new uint32[](1); + + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(makeAddr("module1"), uint48(block.timestamp + 100), "", types); + + bytes32 digest = registry.getDigest(requests, attester1.addr); + bytes memory sig = ecdsaSign(attester1.key, digest); + registry.attest(defaultSchemaUID, attester1.addr, requests, sig); + + AttestationRecord memory record = + registry.readAttestation(makeAddr("module"), attester1.addr); + uint256 nonceAfter = registry.attesterNonce(attester1.addr); + + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, requests[0].expirationTime); + assertEq(record.moduleAddr, requests[0].moduleAddr); + assertEq(record.attester, attester1.addr); + assertEq(nonceAfter, nonceBefore + 1); + } + function test_WhenUsingInvalidECDSA() external whenAttestingWithSignature { uint32[] memory types = new uint32[](1); AttestationRequest memory request = @@ -190,6 +214,20 @@ contract AttestationTest is BaseTest { registry.attest(defaultSchemaUID, attester1.addr, request, sig); } + function test_WhenUsingInvalidECDSAMulti() external whenAttestingWithSignature { + uint32[] memory types = new uint32[](1); + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(makeAddr("module1"), uint48(block.timestamp + 100), "", types); + + bytes32 digest = registry.getDigest(requests, attester1.addr); + bytes memory sig = ecdsaSign(attester1.key, digest); + sig = abi.encodePacked(sig, "foo"); + // It should revert. + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidSignature.selector)); + registry.attest(defaultSchemaUID, attester1.addr, requests, sig); + } + function test_WhenUsingValidERC1271() external whenAttestingWithSignature { uint32[] memory types = new uint32[](1); AttestationRequest memory request = @@ -207,6 +245,37 @@ contract AttestationTest is BaseTest { assertEq(record.attester, address(erc1271AttesterTrue)); } + function test_WhenUsingValidERC1271Multi() external whenAttestingWithSignature { + uint32[] memory types = new uint32[](1); + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(makeAddr("module1"), uint48(block.timestamp + 100), "", types); + + bytes memory sig = "signature"; + registry.attest(defaultSchemaUID, address(erc1271AttesterTrue), requests, sig); + + AttestationRecord memory record = + registry.readAttestation(makeAddr("module"), address(erc1271AttesterTrue)); + + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, requests[0].expirationTime); + assertEq(record.moduleAddr, requests[0].moduleAddr); + assertEq(record.attester, address(erc1271AttesterTrue)); + } + + function test_WhenUsingInvalidERC1271Multi() external whenAttestingWithSignature { + // It should revert. + uint32[] memory types = new uint32[](1); + + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(makeAddr("module1"), uint48(block.timestamp + 100), "", types); + + bytes memory sig = "signature"; + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidSignature.selector)); + registry.attest(defaultSchemaUID, address(erc1271AttesterFalse), requests, sig); + } + function test_WhenUsingInvalidERC1271() external whenAttestingWithSignature { // It should revert. uint32[] memory types = new uint32[](1); From 4d96a077f7ea9118001fcde82607b2f659f9e66e Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 15:49:32 +0700 Subject: [PATCH 20/84] chore: tests --- lcov.info | 11919 +++++++++++++++++++++++++++++-- src/core/SignedAttestation.sol | 22 + test/Attestation.t.sol | 28 +- test/ModuleRegistration.t.sol | 6 + 4 files changed, 11251 insertions(+), 724 deletions(-) diff --git a/lcov.info b/lcov.info index ee45d336..bed8558f 100644 --- a/lcov.info +++ b/lcov.info @@ -1,825 +1,11300 @@ TN: -SF:src/RhinestoneRegistry.sol -FN:24,RhinestoneRegistry.getBridges -FNDA:0,RhinestoneRegistry.getBridges -DA:30,0 -FN:33,RhinestoneRegistry.getSchema -FNDA:0,RhinestoneRegistry.getSchema -DA:39,123 -FN:42,RhinestoneRegistry._getAttestation -FNDA:9,RhinestoneRegistry._getAttestation -DA:52,9 -FN:55,RhinestoneRegistry._getAttestation -FNDA:9,RhinestoneRegistry._getAttestation -DA:62,9 -FN:65,RhinestoneRegistry._getModule -FNDA:116,RhinestoneRegistry._getModule -DA:72,116 -FNF:5 -FNH:3 -LF:5 -LH:4 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/base/RSAttestation.sol -FN:86,RSAttestation.attest -FNDA:52,RSAttestation.attest -DA:91,52 -DA:93,52 -DA:94,52 -DA:96,52 -FN:103,RSAttestation.multiAttest -FNDA:3,RSAttestation.multiAttest -DA:108,3 -DA:112,3 -DA:113,3 -DA:119,3 -DA:121,3 -DA:125,3 -DA:127,3 -DA:130,3 -DA:132,3 -DA:133,3 -DA:136,3 -BRDA:136,0,0,- -BRDA:136,0,1,3 +SF:node_modules/ds-test/src/test.sol +FN:47,DSTest.failed +FNDA:0,DSTest.failed +DA:48,0 +BRDA:48,0,0,- +BRDA:48,0,1,- +DA:49,0 +DA:51,0 +DA:52,0 +BRDA:52,1,0,- +BRDA:52,1,1,- +DA:53,0 +DA:59,0 +DA:61,0 +FN:65,DSTest.fail +FNDA:0,DSTest.fail +DA:66,0 +BRDA:66,2,0,- +BRDA:66,2,1,- +DA:67,0 +DA:75,0 +FN:78,DSTest.hasHEVMContext +FNDA:0,DSTest.hasHEVMContext +DA:79,0 +DA:81,0 +DA:83,0 +FN:93,DSTest.assertTrue +FNDA:0,DSTest.assertTrue +DA:94,0 +BRDA:94,3,0,- +BRDA:94,3,1,- +DA:95,0 +DA:96,0 +FN:100,DSTest.assertTrue +FNDA:0,DSTest.assertTrue +DA:101,0 +BRDA:101,4,0,- +BRDA:101,4,1,- +DA:102,0 +DA:103,0 +FN:107,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:108,0 +BRDA:108,5,0,- +BRDA:108,5,1,- +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +FN:115,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:116,0 +BRDA:116,6,0,- +BRDA:116,6,1,- +DA:117,0 +DA:118,0 +FN:122,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:123,0 +BRDA:123,7,0,- +BRDA:123,7,1,- +DA:124,0 +DA:125,0 +DA:126,0 +DA:127,0 +FN:130,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:131,0 +BRDA:131,8,0,- +BRDA:131,8,1,- +DA:132,0 +DA:133,0 +FN:136,DSTest.assertEq32 +FNDA:0,DSTest.assertEq32 DA:137,0 -DA:141,3 -DA:142,6 -DA:153,3 -DA:162,3 -DA:165,3 -DA:167,3 -DA:172,3 -FN:178,RSAttestation.propagateAttest -FNDA:10,RSAttestation.propagateAttest -DA:187,10 -DA:188,10 -DA:189,10 -DA:191,10 -DA:192,22 -DA:193,22 -DA:194,21 -BRDA:194,1,0,- -BRDA:194,1,1,21 +FN:139,DSTest.assertEq32 +FNDA:0,DSTest.assertEq32 +DA:140,0 +FN:143,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:144,0 +BRDA:144,9,0,- +BRDA:144,9,1,- +DA:145,0 +DA:146,0 +DA:147,0 +DA:148,0 +FN:151,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:152,0 +BRDA:152,10,0,- +BRDA:152,10,1,- +DA:153,0 +DA:154,0 +FN:157,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:158,0 +BRDA:158,11,0,- +BRDA:158,11,1,- +DA:159,0 +DA:160,0 +DA:161,0 +DA:162,0 +FN:165,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:166,0 +BRDA:166,12,0,- +BRDA:166,12,1,- +DA:167,0 +DA:168,0 +FN:171,DSTest.assertEqDecimal +FNDA:0,DSTest.assertEqDecimal +DA:172,0 +BRDA:172,13,0,- +BRDA:172,13,1,- +DA:173,0 +DA:174,0 +DA:175,0 +DA:176,0 +FN:179,DSTest.assertEqDecimal +FNDA:0,DSTest.assertEqDecimal +DA:180,0 +BRDA:180,14,0,- +BRDA:180,14,1,- +DA:181,0 +DA:182,0 +FN:185,DSTest.assertEqDecimal +FNDA:0,DSTest.assertEqDecimal +DA:186,0 +BRDA:186,15,0,- +BRDA:186,15,1,- +DA:187,0 +DA:188,0 +DA:189,0 +DA:190,0 +FN:193,DSTest.assertEqDecimal +FNDA:0,DSTest.assertEqDecimal +DA:194,0 +BRDA:194,16,0,- +BRDA:194,16,1,- DA:195,0 -DA:198,21 -DA:205,21 -DA:207,9 -FN:213,RSAttestation.propagateAttest -FNDA:4,RSAttestation.propagateAttest -DA:223,4 -DA:224,4 -DA:227,4 -DA:229,4 -BRDA:229,2,0,- -BRDA:229,2,1,4 +DA:196,0 +FN:200,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:201,0 +BRDA:201,17,0,- +BRDA:201,17,1,- +DA:202,0 +DA:203,0 +DA:204,0 +DA:205,0 +FN:208,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:209,0 +BRDA:209,18,0,- +BRDA:209,18,1,- +DA:210,0 +DA:211,0 +FN:215,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:216,0 +BRDA:216,19,0,- +BRDA:216,19,1,- +DA:217,0 +DA:218,0 +DA:219,0 +DA:220,0 +FN:223,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:224,0 +BRDA:224,20,0,- +BRDA:224,20,1,- +DA:225,0 +DA:226,0 +FN:229,DSTest.assertNotEq32 +FNDA:0,DSTest.assertNotEq32 DA:230,0 -DA:234,4 -DA:239,4 -DA:240,4 -DA:242,4 -DA:244,4 -FN:250,RSAttestation.attestByPropagation -FNDA:23,RSAttestation.attestByPropagation -DA:260,23 -BRDA:260,3,0,1 -BRDA:260,3,1,22 -DA:261,1 -DA:265,22 -BRDA:265,4,0,2 -BRDA:265,4,1,20 -DA:268,20 -BRDA:268,5,0,1 -BRDA:268,5,1,7 -DA:270,8 -BRDA:270,6,0,1 -BRDA:270,6,1,7 -DA:271,1 -DA:276,19 -DA:277,19 -BRDA:277,7,0,- -BRDA:277,7,1,19 -DA:279,19 -BRDA:279,8,0,- -BRDA:279,8,1,19 -DA:282,19 -DA:283,19 -DA:286,19 -FN:294,RSAttestation.revoke -FNDA:8,RSAttestation.revoke -DA:295,8 -DA:297,8 -DA:298,8 -DA:300,8 -FN:306,RSAttestation.multiRevoke -FNDA:0,RSAttestation.multiRevoke -DA:314,0 -DA:315,0 +FN:232,DSTest.assertNotEq32 +FNDA:0,DSTest.assertNotEq32 +DA:233,0 +FN:236,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:237,0 +BRDA:237,21,0,- +BRDA:237,21,1,- +DA:238,0 +DA:239,0 +DA:240,0 +DA:241,0 +FN:244,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:245,0 +BRDA:245,22,0,- +BRDA:245,22,1,- +DA:246,0 +DA:247,0 +FN:250,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:251,0 +BRDA:251,23,0,- +BRDA:251,23,1,- +DA:252,0 +DA:253,0 +DA:254,0 +DA:255,0 +FN:258,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:259,0 +BRDA:259,24,0,- +BRDA:259,24,1,- +DA:260,0 +DA:261,0 +FN:264,DSTest.assertNotEqDecimal +FNDA:0,DSTest.assertNotEqDecimal +DA:265,0 +BRDA:265,25,0,- +BRDA:265,25,1,- +DA:266,0 +DA:267,0 +DA:268,0 +DA:269,0 +FN:272,DSTest.assertNotEqDecimal +FNDA:0,DSTest.assertNotEqDecimal +DA:273,0 +BRDA:273,26,0,- +BRDA:273,26,1,- +DA:274,0 +DA:275,0 +FN:278,DSTest.assertNotEqDecimal +FNDA:0,DSTest.assertNotEqDecimal +DA:279,0 +BRDA:279,27,0,- +BRDA:279,27,1,- +DA:280,0 +DA:281,0 +DA:282,0 +DA:283,0 +FN:286,DSTest.assertNotEqDecimal +FNDA:0,DSTest.assertNotEqDecimal +DA:287,0 +BRDA:287,28,0,- +BRDA:287,28,1,- +DA:288,0 +DA:289,0 +FN:293,DSTest.assertGt +FNDA:0,DSTest.assertGt +DA:294,0 +BRDA:294,29,0,- +BRDA:294,29,1,- +DA:295,0 +DA:296,0 +DA:297,0 +DA:298,0 +FN:301,DSTest.assertGt +FNDA:0,DSTest.assertGt +DA:302,0 +BRDA:302,30,0,- +BRDA:302,30,1,- +DA:303,0 +DA:304,0 +FN:307,DSTest.assertGt +FNDA:0,DSTest.assertGt +DA:308,0 +BRDA:308,31,0,- +BRDA:308,31,1,- +DA:309,0 +DA:310,0 +DA:311,0 +DA:312,0 +FN:315,DSTest.assertGt +FNDA:0,DSTest.assertGt +DA:316,0 +BRDA:316,32,0,- +BRDA:316,32,1,- DA:317,0 -DA:321,0 +DA:318,0 +FN:321,DSTest.assertGtDecimal +FNDA:0,DSTest.assertGtDecimal +DA:322,0 +BRDA:322,33,0,- +BRDA:322,33,1,- DA:323,0 +DA:324,0 +DA:325,0 DA:326,0 -DA:327,0 -DA:328,0 +FN:329,DSTest.assertGtDecimal +FNDA:0,DSTest.assertGtDecimal +DA:330,0 +BRDA:330,34,0,- +BRDA:330,34,1,- DA:331,0 -BRDA:331,9,0,- -BRDA:331,9,1,- DA:332,0 +FN:335,DSTest.assertGtDecimal +FNDA:0,DSTest.assertGtDecimal DA:336,0 +BRDA:336,35,0,- +BRDA:336,35,1,- DA:337,0 -DA:348,0 -FN:369,RSAttestation._attest -FNDA:55,RSAttestation._attest -DA:379,55 -DA:381,55 -DA:382,55 -DA:385,55 -DA:386,55 -BRDA:386,10,0,- -BRDA:386,10,1,55 +DA:338,0 +DA:339,0 +DA:340,0 +FN:343,DSTest.assertGtDecimal +FNDA:0,DSTest.assertGtDecimal +DA:344,0 +BRDA:344,36,0,- +BRDA:344,36,1,- +DA:345,0 +DA:346,0 +FN:350,DSTest.assertGe +FNDA:0,DSTest.assertGe +DA:351,0 +BRDA:351,37,0,- +BRDA:351,37,1,- +DA:352,0 +DA:353,0 +DA:354,0 +DA:355,0 +FN:358,DSTest.assertGe +FNDA:0,DSTest.assertGe +DA:359,0 +BRDA:359,38,0,- +BRDA:359,38,1,- +DA:360,0 +DA:361,0 +FN:364,DSTest.assertGe +FNDA:0,DSTest.assertGe +DA:365,0 +BRDA:365,39,0,- +BRDA:365,39,1,- +DA:366,0 +DA:367,0 +DA:368,0 +DA:369,0 +FN:372,DSTest.assertGe +FNDA:0,DSTest.assertGe +DA:373,0 +BRDA:373,40,0,- +BRDA:373,40,1,- +DA:374,0 +DA:375,0 +FN:378,DSTest.assertGeDecimal +FNDA:0,DSTest.assertGeDecimal +DA:379,0 +BRDA:379,41,0,- +BRDA:379,41,1,- +DA:380,0 +DA:381,0 +DA:382,0 +DA:383,0 +FN:386,DSTest.assertGeDecimal +FNDA:0,DSTest.assertGeDecimal DA:387,0 -DA:390,55 -DA:391,55 -DA:393,55 -DA:394,58 -DA:397,58 -BRDA:397,11,0,- -BRDA:397,11,1,58 -DA:398,0 -DA:402,58 -BRDA:402,12,0,- -BRDA:402,12,1,58 +BRDA:387,42,0,- +BRDA:387,42,1,- +DA:388,0 +DA:389,0 +FN:392,DSTest.assertGeDecimal +FNDA:0,DSTest.assertGeDecimal +DA:393,0 +BRDA:393,43,0,- +BRDA:393,43,1,- +DA:394,0 +DA:395,0 +DA:396,0 +DA:397,0 +FN:400,DSTest.assertGeDecimal +FNDA:0,DSTest.assertGeDecimal +DA:401,0 +BRDA:401,44,0,- +BRDA:401,44,1,- +DA:402,0 DA:403,0 -DA:407,58 -BRDA:407,13,0,- -BRDA:407,13,1,58 +FN:407,DSTest.assertLt +FNDA:0,DSTest.assertLt DA:408,0 -DA:412,58 -BRDA:412,14,0,12 -BRDA:412,14,1,46 -DA:413,12 -DA:416,46 -DA:431,46 -DA:432,46 -DA:435,46 -DA:436,46 -DA:438,46 -BRDA:438,15,0,2 -BRDA:438,15,1,14 -DA:440,16 -BRDA:440,16,0,2 -BRDA:440,16,1,14 -DA:441,2 -DA:445,44 -DA:446,44 -DA:448,44 -DA:450,44 -DA:453,41 -DA:456,41 -FN:470,RSAttestation._revoke -FNDA:8,RSAttestation._revoke -DA:481,8 -DA:482,8 -BRDA:482,17,0,- -BRDA:482,17,1,8 +BRDA:408,45,0,- +BRDA:408,45,1,- +DA:409,0 +DA:410,0 +DA:411,0 +DA:412,0 +FN:415,DSTest.assertLt +FNDA:0,DSTest.assertLt +DA:416,0 +BRDA:416,46,0,- +BRDA:416,46,1,- +DA:417,0 +DA:418,0 +FN:421,DSTest.assertLt +FNDA:0,DSTest.assertLt +DA:422,0 +BRDA:422,47,0,- +BRDA:422,47,1,- +DA:423,0 +DA:424,0 +DA:425,0 +DA:426,0 +FN:429,DSTest.assertLt +FNDA:0,DSTest.assertLt +DA:430,0 +BRDA:430,48,0,- +BRDA:430,48,1,- +DA:431,0 +DA:432,0 +FN:435,DSTest.assertLtDecimal +FNDA:0,DSTest.assertLtDecimal +DA:436,0 +BRDA:436,49,0,- +BRDA:436,49,1,- +DA:437,0 +DA:438,0 +DA:439,0 +DA:440,0 +FN:443,DSTest.assertLtDecimal +FNDA:0,DSTest.assertLtDecimal +DA:444,0 +BRDA:444,50,0,- +BRDA:444,50,1,- +DA:445,0 +DA:446,0 +FN:449,DSTest.assertLtDecimal +FNDA:0,DSTest.assertLtDecimal +DA:450,0 +BRDA:450,51,0,- +BRDA:450,51,1,- +DA:451,0 +DA:452,0 +DA:453,0 +DA:454,0 +FN:457,DSTest.assertLtDecimal +FNDA:0,DSTest.assertLtDecimal +DA:458,0 +BRDA:458,52,0,- +BRDA:458,52,1,- +DA:459,0 +DA:460,0 +FN:464,DSTest.assertLe +FNDA:0,DSTest.assertLe +DA:465,0 +BRDA:465,53,0,- +BRDA:465,53,1,- +DA:466,0 +DA:467,0 +DA:468,0 +DA:469,0 +FN:472,DSTest.assertLe +FNDA:0,DSTest.assertLe +DA:473,0 +BRDA:473,54,0,- +BRDA:473,54,1,- +DA:474,0 +DA:475,0 +FN:478,DSTest.assertLe +FNDA:0,DSTest.assertLe +DA:479,0 +BRDA:479,55,0,- +BRDA:479,55,1,- +DA:480,0 +DA:481,0 +DA:482,0 DA:483,0 -DA:486,8 -DA:487,8 -DA:488,8 -DA:490,8 -DA:491,8 -DA:493,8 -DA:496,8 -BRDA:496,18,0,- -BRDA:496,18,1,8 +FN:486,DSTest.assertLe +FNDA:0,DSTest.assertLe +DA:487,0 +BRDA:487,56,0,- +BRDA:487,56,1,- +DA:488,0 +DA:489,0 +FN:492,DSTest.assertLeDecimal +FNDA:0,DSTest.assertLeDecimal +DA:493,0 +BRDA:493,57,0,- +BRDA:493,57,1,- +DA:494,0 +DA:495,0 +DA:496,0 DA:497,0 -DA:501,8 -BRDA:501,19,0,- -BRDA:501,19,1,8 +FN:500,DSTest.assertLeDecimal +FNDA:0,DSTest.assertLeDecimal +DA:501,0 +BRDA:501,58,0,- +BRDA:501,58,1,- DA:502,0 -DA:506,8 -BRDA:506,20,0,- -BRDA:506,20,1,8 +DA:503,0 +FN:506,DSTest.assertLeDecimal +FNDA:0,DSTest.assertLeDecimal DA:507,0 -DA:512,8 -BRDA:512,21,0,- -BRDA:512,21,1,8 -DA:513,0 -DA:517,8 -BRDA:517,22,0,- -BRDA:517,22,1,8 -DA:518,0 -DA:521,8 -DA:523,8 -DA:524,8 -DA:526,8 -DA:529,8 -FN:544,RSAttestation._requireExternalResolveAttestation -FNDA:46,RSAttestation._requireExternalResolveAttestation -DA:555,46 -DA:556,46 -BRDA:556,23,0,- -BRDA:556,23,1,46 -DA:558,46 -BRDA:558,24,0,- -BRDA:558,24,1,46 -DA:559,0 -DA:562,46 +BRDA:507,59,0,- +BRDA:507,59,1,- +DA:508,0 +DA:509,0 +DA:510,0 +DA:511,0 +FN:514,DSTest.assertLeDecimal +FNDA:0,DSTest.assertLeDecimal +DA:515,0 +BRDA:515,60,0,- +BRDA:515,60,1,- +DA:516,0 +DA:517,0 +FN:521,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:522,0 +BRDA:522,61,0,- +BRDA:522,61,1,- +DA:523,0 +DA:524,0 +DA:525,0 +DA:526,0 +FN:529,DSTest.assertEq +FNDA:0,DSTest.assertEq +DA:530,0 +BRDA:530,62,0,- +BRDA:530,62,1,- +DA:531,0 +DA:532,0 +FN:536,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:537,0 +BRDA:537,63,0,- +BRDA:537,63,1,- +DA:538,0 +DA:539,0 +DA:540,0 +DA:541,0 +FN:544,DSTest.assertNotEq +FNDA:0,DSTest.assertNotEq +DA:545,0 +BRDA:545,64,0,- +BRDA:545,64,1,- +DA:546,0 +DA:547,0 +FN:551,DSTest.checkEq0 +FNDA:0,DSTest.checkEq0 +DA:552,0 +DA:553,0 +BRDA:553,65,0,- +BRDA:553,65,1,- +DA:554,0 +DA:555,0 +BRDA:555,66,0,- +BRDA:555,66,1,- +DA:556,0 +DA:560,0 +FN:563,DSTest.assertEq0 +FNDA:0,DSTest.assertEq0 +DA:564,0 +BRDA:564,67,0,- +BRDA:564,67,1,- +DA:565,0 DA:566,0 -BRDA:566,25,0,- -BRDA:566,25,1,- DA:567,0 -DA:571,0 -BRDA:571,26,0,- -BRDA:571,26,1,- +DA:568,0 +FN:571,DSTest.assertEq0 +FNDA:0,DSTest.assertEq0 DA:572,0 -DA:577,0 +BRDA:572,68,0,- +BRDA:572,68,1,- +DA:573,0 +DA:574,0 +FN:578,DSTest.assertNotEq0 +FNDA:0,DSTest.assertNotEq0 +DA:579,0 +BRDA:579,69,0,- +BRDA:579,69,1,- DA:580,0 -BRDA:580,27,0,- -BRDA:580,27,1,- DA:581,0 -BRDA:581,28,0,- -BRDA:581,28,1,- DA:582,0 -DA:584,0 -BRDA:584,29,0,- -BRDA:584,29,1,- -DA:585,0 +DA:583,0 +FN:586,DSTest.assertNotEq0 +FNDA:0,DSTest.assertNotEq0 +DA:587,0 +BRDA:587,70,0,- +BRDA:587,70,1,- DA:588,0 -BRDA:588,30,0,- -BRDA:588,30,1,- DA:589,0 -DA:592,0 -FN:607,RSAttestation._requireExternalResolveAttestations -FNDA:49,RSAttestation._requireExternalResolveAttestations -DA:618,49 -DA:619,49 -BRDA:619,31,0,- -BRDA:619,31,1,46 -DA:620,46 -DA:625,3 -DA:626,3 -BRDA:626,32,0,- -BRDA:626,32,1,6 -DA:628,3 -DA:629,6 -BRDA:629,33,0,- -BRDA:629,33,1,6 +FNF:74 +FNH:0 +LF:285 +LH:0 +BRF:142 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdAssertions.sol +FN:15,StdAssertions.fail +FNDA:0,StdAssertions.fail +DA:16,0 +DA:17,0 +FN:20,StdAssertions.assertFalse +FNDA:0,StdAssertions.assertFalse +DA:21,0 +FN:24,StdAssertions.assertFalse +FNDA:0,StdAssertions.assertFalse +DA:25,0 +FN:28,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:29,0 +BRDA:29,0,0,- +BRDA:29,0,1,- +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +FN:37,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:38,0 +BRDA:38,1,0,- +BRDA:38,1,1,- +DA:39,0 +DA:40,0 +FN:44,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:45,0 +FN:48,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:49,0 +FN:52,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:53,0 +BRDA:53,2,0,- +BRDA:53,2,1,- +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +FN:61,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:62,0 +BRDA:62,3,0,- +BRDA:62,3,1,- +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +FN:70,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:71,0 +BRDA:71,4,0,- +BRDA:71,4,1,- +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +FN:79,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:80,0 +BRDA:80,5,0,- +BRDA:80,5,1,- +DA:81,0 +DA:82,0 +FN:86,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:87,0 +BRDA:87,6,0,- +BRDA:87,6,1,- +DA:88,0 +DA:89,0 +FN:93,StdAssertions.assertEq +FNDA:0,StdAssertions.assertEq +DA:94,0 +BRDA:94,7,0,- +BRDA:94,7,1,- +DA:95,0 +DA:96,0 +FN:101,StdAssertions.assertEqUint +FNDA:0,StdAssertions.assertEqUint +DA:102,0 +FN:105,StdAssertions.assertApproxEqAbs +FNDA:0,StdAssertions.assertApproxEqAbs +DA:106,0 +DA:108,0 +BRDA:108,8,0,- +BRDA:108,8,1,- +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +FN:118,StdAssertions.assertApproxEqAbs +FNDA:0,StdAssertions.assertApproxEqAbs +DA:119,0 +DA:121,0 +BRDA:121,9,0,- +BRDA:121,9,1,- +DA:122,0 +DA:123,0 +FN:127,StdAssertions.assertApproxEqAbsDecimal +FNDA:0,StdAssertions.assertApproxEqAbsDecimal +DA:128,0 +DA:130,0 +BRDA:130,10,0,- +BRDA:130,10,1,- +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +FN:140,StdAssertions.assertApproxEqAbsDecimal +FNDA:0,StdAssertions.assertApproxEqAbsDecimal +DA:144,0 +DA:146,0 +BRDA:146,11,0,- +BRDA:146,11,1,- +DA:147,0 +DA:148,0 +FN:152,StdAssertions.assertApproxEqAbs +FNDA:0,StdAssertions.assertApproxEqAbs +DA:153,0 +DA:155,0 +BRDA:155,12,0,- +BRDA:155,12,1,- +DA:156,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:161,0 +FN:165,StdAssertions.assertApproxEqAbs +FNDA:0,StdAssertions.assertApproxEqAbs +DA:166,0 +DA:168,0 +BRDA:168,13,0,- +BRDA:168,13,1,- +DA:169,0 +DA:170,0 +FN:174,StdAssertions.assertApproxEqAbsDecimal +FNDA:0,StdAssertions.assertApproxEqAbsDecimal +DA:175,0 +DA:177,0 +BRDA:177,14,0,- +BRDA:177,14,1,- +DA:178,0 +DA:179,0 +DA:180,0 +DA:181,0 +DA:182,0 +DA:183,0 +FN:187,StdAssertions.assertApproxEqAbsDecimal +FNDA:0,StdAssertions.assertApproxEqAbsDecimal +DA:191,0 +DA:193,0 +BRDA:193,15,0,- +BRDA:193,15,1,- +DA:194,0 +DA:195,0 +FN:199,StdAssertions.assertApproxEqRel +FNDA:0,StdAssertions.assertApproxEqRel +DA:204,0 +BRDA:204,16,0,- +BRDA:204,16,1,- +DA:206,0 +DA:208,0 +BRDA:208,17,0,- +BRDA:208,17,1,- +DA:209,0 +DA:210,0 +DA:211,0 +DA:212,0 +DA:213,0 +DA:214,0 +FN:218,StdAssertions.assertApproxEqRel +FNDA:0,StdAssertions.assertApproxEqRel +DA:224,0 +BRDA:224,18,0,- +BRDA:224,18,1,- +DA:226,0 +DA:228,0 +BRDA:228,19,0,- +BRDA:228,19,1,- +DA:229,0 +DA:230,0 +FN:234,StdAssertions.assertApproxEqRelDecimal +FNDA:0,StdAssertions.assertApproxEqRelDecimal +DA:240,0 +BRDA:240,20,0,- +BRDA:240,20,1,- +DA:242,0 +DA:244,0 +BRDA:244,21,0,- +BRDA:244,21,1,- +DA:245,0 +DA:246,0 +DA:247,0 +DA:248,0 +DA:249,0 +DA:250,0 +FN:254,StdAssertions.assertApproxEqRelDecimal +FNDA:0,StdAssertions.assertApproxEqRelDecimal +DA:261,0 +BRDA:261,22,0,- +BRDA:261,22,1,- +DA:263,0 +DA:265,0 +BRDA:265,23,0,- +BRDA:265,23,1,- +DA:266,0 +DA:267,0 +FN:271,StdAssertions.assertApproxEqRel +FNDA:0,StdAssertions.assertApproxEqRel +DA:272,0 +BRDA:272,24,0,- +BRDA:272,24,1,- +DA:274,0 +DA:276,0 +BRDA:276,25,0,- +BRDA:276,25,1,- +DA:277,0 +DA:278,0 +DA:279,0 +DA:280,0 +DA:281,0 +DA:282,0 +FN:286,StdAssertions.assertApproxEqRel +FNDA:0,StdAssertions.assertApproxEqRel +DA:287,0 +BRDA:287,26,0,- +BRDA:287,26,1,- +DA:289,0 +DA:291,0 +BRDA:291,27,0,- +BRDA:291,27,1,- +DA:292,0 +DA:293,0 +FN:297,StdAssertions.assertApproxEqRelDecimal +FNDA:0,StdAssertions.assertApproxEqRelDecimal +DA:298,0 +BRDA:298,28,0,- +BRDA:298,28,1,- +DA:300,0 +DA:302,0 +BRDA:302,29,0,- +BRDA:302,29,1,- +DA:303,0 +DA:304,0 +DA:305,0 +DA:306,0 +DA:307,0 +DA:308,0 +FN:312,StdAssertions.assertApproxEqRelDecimal +FNDA:0,StdAssertions.assertApproxEqRelDecimal +DA:316,0 +BRDA:316,30,0,- +BRDA:316,30,1,- +DA:318,0 +DA:320,0 +BRDA:320,31,0,- +BRDA:320,31,1,- +DA:321,0 +DA:322,0 +FN:326,StdAssertions.assertEqCall +FNDA:0,StdAssertions.assertEqCall +DA:327,0 +FN:330,StdAssertions.assertEqCall +FNDA:0,StdAssertions.assertEqCall +DA:334,0 +FN:337,StdAssertions.assertEqCall +FNDA:0,StdAssertions.assertEqCall +DA:341,0 +FN:344,StdAssertions.assertEqCall +FNDA:0,StdAssertions.assertEqCall +DA:351,0 +DA:352,0 +DA:354,0 +BRDA:354,32,0,- +BRDA:354,32,1,- +DA:355,0 +DA:358,0 +BRDA:358,33,0,- +BRDA:358,33,1,- +DA:359,0 +DA:362,0 +BRDA:362,34,0,- +BRDA:362,34,1,- +DA:363,0 +DA:364,0 +DA:365,0 +DA:366,0 +DA:369,0 +BRDA:369,35,0,- +BRDA:369,35,1,- +DA:370,0 +DA:371,0 +DA:372,0 +DA:373,0 +FNF:34 +FNH:0 +LF:162 +LH:0 +BRF:72 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdChains.sol +FN:69,StdChains.getChain +FNDA:0,StdChains.getChain +DA:70,0 +BRDA:70,0,0,- +BRDA:70,0,1,- +DA:72,0 +DA:73,0 +DA:74,0 +BRDA:74,1,0,- +BRDA:74,1,1,- +DA:79,0 +FN:82,StdChains.getChain +FNDA:0,StdChains.getChain +DA:83,0 +BRDA:83,2,0,- +BRDA:83,2,1,- +DA:84,0 +DA:85,0 +DA:87,0 +DA:89,0 +BRDA:89,3,0,- +BRDA:89,3,1,- +DA:94,0 +FN:98,StdChains.setChain +FNDA:0,StdChains.setChain +DA:99,0 +BRDA:99,4,0,- +BRDA:99,4,1,- +DA:104,0 +BRDA:104,5,0,- +BRDA:104,5,1,- +DA:106,0 +DA:107,0 +DA:109,0 +BRDA:109,6,0,- +BRDA:109,6,1,- +DA:122,0 +DA:123,0 +DA:125,0 +DA:127,0 +FN:131,StdChains.setChain +FNDA:0,StdChains.setChain +DA:132,0 +FN:135,StdChains._toUpper +FNDA:0,StdChains._toUpper +DA:136,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:140,0 +BRDA:140,7,0,- +BRDA:140,7,1,- +DA:141,0 +DA:143,0 +DA:146,0 +FN:151,StdChains.getChainWithUpdatedRpcUrl +FNDA:0,StdChains.getChainWithUpdatedRpcUrl +DA:156,0 +BRDA:156,8,0,- +BRDA:156,8,1,- +DA:157,0 +DA:185,0 +FN:188,StdChains.setFallbackToDefaultRpcUrls +FNDA:0,StdChains.setFallbackToDefaultRpcUrls +DA:189,0 +FN:192,StdChains.initializeStdChains +FNDA:0,StdChains.initializeStdChains +DA:193,0 +BRDA:193,9,0,- +BRDA:193,9,1,- +DA:195,0 +DA:198,0 +DA:199,0 +DA:202,0 +DA:205,0 +DA:208,0 +DA:209,0 +DA:210,0 +DA:211,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:219,0 +DA:220,0 +DA:223,0 +DA:226,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:235,0 +DA:236,0 +DA:237,0 +FN:241,StdChains.setChainWithDefaultRpcUrl +FNDA:0,StdChains.setChainWithDefaultRpcUrl +DA:242,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:246,0 +FNF:9 +FNH:0 +LF:61 +LH:0 +BRF:20 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdCheats.sol +FN:209,StdCheatsSafe.assumeNotBlacklisted +FNDA:0,StdCheatsSafe.assumeNotBlacklisted +DA:211,0 +DA:213,0 +DA:215,0 +BRDA:215,0,0,- +BRDA:215,0,1,- +DA:217,0 +DA:218,0 +DA:221,0 +DA:222,0 +DA:225,0 +DA:226,0 +FN:233,StdCheatsSafe.assumeNoBlacklisted +FNDA:0,StdCheatsSafe.assumeNoBlacklisted +DA:234,0 +FN:237,StdCheatsSafe.assumeAddressIsNot +FNDA:0,StdCheatsSafe.assumeAddressIsNot +DA:238,0 +BRDA:238,1,0,- +BRDA:238,1,1,- +DA:239,0 +DA:240,0 +BRDA:240,2,0,- +BRDA:240,2,1,- +DA:241,0 +DA:242,0 +BRDA:242,3,0,- +BRDA:242,3,1,- +DA:243,0 +DA:244,0 +BRDA:244,4,0,- +BRDA:244,4,1,- +DA:245,0 +DA:246,0 +BRDA:246,5,0,- +BRDA:246,5,1,- +DA:247,0 +FN:251,StdCheatsSafe.assumeAddressIsNot +FNDA:0,StdCheatsSafe.assumeAddressIsNot +DA:252,0 +DA:253,0 +FN:256,StdCheatsSafe.assumeAddressIsNot +FNDA:0,StdCheatsSafe.assumeAddressIsNot +DA:262,0 +DA:263,0 +DA:264,0 +FN:267,StdCheatsSafe.assumeAddressIsNot +FNDA:0,StdCheatsSafe.assumeAddressIsNot +DA:274,0 +DA:275,0 +DA:276,0 +DA:277,0 +FN:284,StdCheatsSafe._isPayable +FNDA:0,StdCheatsSafe._isPayable +DA:285,0 +BRDA:285,6,0,- +BRDA:285,6,1,- +DA:289,0 +DA:290,0 +DA:292,0 +DA:293,0 +DA:296,0 +DA:297,0 +DA:299,0 +FN:305,StdCheatsSafe.assumePayable +FNDA:0,StdCheatsSafe.assumePayable +DA:306,0 +FN:309,StdCheatsSafe.assumeNotPayable +FNDA:0,StdCheatsSafe.assumeNotPayable +DA:310,0 +FN:313,StdCheatsSafe.assumeNotZeroAddress +FNDA:0,StdCheatsSafe.assumeNotZeroAddress +DA:314,0 +FN:317,StdCheatsSafe.assumeNotPrecompile +FNDA:0,StdCheatsSafe.assumeNotPrecompile +DA:318,0 +FN:321,StdCheatsSafe.assumeNotPrecompile +FNDA:0,StdCheatsSafe.assumeNotPrecompile +DA:326,0 +DA:329,0 +BRDA:329,7,0,- +BRDA:329,7,1,- +DA:331,0 +DA:332,0 +BRDA:332,8,0,- +BRDA:332,8,1,- +DA:334,0 +DA:335,0 +BRDA:335,9,0,- +BRDA:335,9,1,- +DA:337,0 +DA:338,0 +DA:339,0 +FN:344,StdCheatsSafe.assumeNotForgeAddress +FNDA:0,StdCheatsSafe.assumeNotForgeAddress +DA:346,0 +FN:352,StdCheatsSafe.readEIP1559ScriptArtifact +FNDA:0,StdCheatsSafe.readEIP1559ScriptArtifact +DA:358,0 +DA:359,0 +DA:360,0 +DA:361,0 +DA:362,0 +DA:363,0 +DA:364,0 +DA:365,0 +DA:366,0 +DA:367,0 +DA:368,0 +DA:369,0 +FN:372,StdCheatsSafe.rawToConvertedEIPTx1559s +FNDA:0,StdCheatsSafe.rawToConvertedEIPTx1559s +DA:373,0 +DA:374,0 +DA:375,0 +DA:377,0 +FN:380,StdCheatsSafe.rawToConvertedEIPTx1559 +FNDA:0,StdCheatsSafe.rawToConvertedEIPTx1559 +DA:381,0 +DA:382,0 +DA:383,0 +DA:384,0 +DA:385,0 +DA:386,0 +DA:387,0 +DA:388,0 +FN:391,StdCheatsSafe.rawToConvertedEIP1559Detail +FNDA:0,StdCheatsSafe.rawToConvertedEIP1559Detail +DA:397,0 +DA:398,0 +DA:399,0 +DA:400,0 +DA:401,0 +DA:402,0 +DA:403,0 +DA:404,0 +DA:405,0 +DA:406,0 +FN:409,StdCheatsSafe.readTx1559s +FNDA:0,StdCheatsSafe.readTx1559s +DA:410,0 +DA:411,0 +DA:412,0 +DA:413,0 +FN:416,StdCheatsSafe.readTx1559 +FNDA:0,StdCheatsSafe.readTx1559 +DA:417,0 +DA:418,0 +DA:419,0 +DA:420,0 +DA:421,0 +FN:425,StdCheatsSafe.readReceipts +FNDA:0,StdCheatsSafe.readReceipts +DA:426,0 +DA:427,0 +DA:428,0 +DA:429,0 +FN:432,StdCheatsSafe.readReceipt +FNDA:0,StdCheatsSafe.readReceipt +DA:433,0 +DA:434,0 +DA:435,0 +DA:436,0 +DA:437,0 +FN:440,StdCheatsSafe.rawToConvertedReceipts +FNDA:0,StdCheatsSafe.rawToConvertedReceipts +DA:441,0 +DA:442,0 +DA:443,0 +DA:445,0 +FN:448,StdCheatsSafe.rawToConvertedReceipt +FNDA:0,StdCheatsSafe.rawToConvertedReceipt +DA:449,0 +DA:450,0 +DA:451,0 +DA:452,0 +DA:453,0 +DA:454,0 +DA:455,0 +DA:456,0 +DA:457,0 +DA:458,0 +DA:459,0 +DA:460,0 +DA:461,0 +DA:462,0 +DA:463,0 +FN:466,StdCheatsSafe.rawToConvertedReceiptLogs +FNDA:0,StdCheatsSafe.rawToConvertedReceiptLogs +DA:472,0 +DA:473,0 +DA:474,0 +DA:475,0 +DA:476,0 +DA:477,0 +DA:478,0 +DA:479,0 +DA:480,0 +DA:481,0 +DA:482,0 +DA:484,0 +FN:490,StdCheatsSafe.deployCode +FNDA:0,StdCheatsSafe.deployCode +DA:491,0 +DA:494,0 +DA:497,0 +BRDA:497,10,0,- +BRDA:497,10,1,- +FN:500,StdCheatsSafe.deployCode +FNDA:0,StdCheatsSafe.deployCode +DA:501,0 +DA:504,0 +DA:507,0 +BRDA:507,11,0,- +BRDA:507,11,1,- +FN:511,StdCheatsSafe.deployCode +FNDA:0,StdCheatsSafe.deployCode +DA:512,0 +DA:515,0 +DA:518,0 +BRDA:518,12,0,- +BRDA:518,12,1,- +FN:521,StdCheatsSafe.deployCode +FNDA:0,StdCheatsSafe.deployCode +DA:522,0 +DA:525,0 +DA:528,0 +BRDA:528,13,0,- +BRDA:528,13,1,- +FN:532,StdCheatsSafe.makeAddrAndKey +FNDA:0,StdCheatsSafe.makeAddrAndKey +DA:533,0 +DA:534,0 +DA:535,0 +FN:539,StdCheatsSafe.makeAddr +FNDA:0,StdCheatsSafe.makeAddr +DA:540,0 +FN:547,StdCheatsSafe.destroyAccount +FNDA:0,StdCheatsSafe.destroyAccount +DA:548,0 +DA:549,0 +DA:550,0 +DA:551,0 +DA:553,0 +DA:554,0 +FN:558,StdCheatsSafe.makeAccount +FNDA:0,StdCheatsSafe.makeAccount +DA:559,0 +FN:562,StdCheatsSafe.deriveRememberKey +FNDA:0,StdCheatsSafe.deriveRememberKey +DA:567,0 +DA:568,0 +FN:571,StdCheatsSafe._bytesToUint +FNDA:0,StdCheatsSafe._bytesToUint +DA:572,0 +BRDA:572,14,0,- +BRDA:572,14,1,- +DA:573,0 +FN:576,StdCheatsSafe.isFork +FNDA:0,StdCheatsSafe.isFork +DA:577,0 +FN:619,StdCheatsSafe._viewChainId +FNDA:0,StdCheatsSafe._viewChainId +DA:622,0 +DA:625,0 +FN:628,StdCheatsSafe._pureChainId +FNDA:0,StdCheatsSafe._pureChainId +DA:629,0 DA:630,0 -DA:634,3 -DA:637,0 -DA:639,0 -DA:640,0 -DA:643,0 -BRDA:643,34,0,- -BRDA:643,34,1,- -DA:644,0 +DA:632,0 +DA:634,0 +FN:647,StdCheats.skip +FNDA:0,StdCheats.skip DA:648,0 -BRDA:648,35,0,- -BRDA:648,35,1,- -DA:649,0 -DA:654,0 -DA:655,0 -DA:659,0 -BRDA:659,36,0,- -BRDA:659,36,1,- -DA:660,0 -BRDA:660,37,0,- -BRDA:660,37,1,- -DA:661,0 +FN:651,StdCheats.rewind +FNDA:0,StdCheats.rewind +DA:652,0 +FN:656,StdCheats.hoax +FNDA:0,StdCheats.hoax +DA:657,0 +DA:658,0 +FN:661,StdCheats.hoax +FNDA:0,StdCheats.hoax +DA:662,0 DA:663,0 -BRDA:663,38,0,- -BRDA:663,38,1,- -DA:664,0 +FN:666,StdCheats.hoax +FNDA:0,StdCheats.hoax DA:667,0 -BRDA:667,39,0,- -BRDA:667,39,1,- DA:668,0 -DA:671,0 -FN:674,RSAttestation._newUID -FNDA:46,RSAttestation._newUID -DA:675,46 -DA:677,51 -DA:678,51 -BRDA:678,40,0,46 -BRDA:678,40,1,5 -DA:679,46 -DA:683,5 -FN:696,RSAttestation._getUID -FNDA:51,RSAttestation._getUID -DA:697,51 -FN:721,RSAttestation._toUint256Array -FNDA:0,RSAttestation._toUint256Array -DA:722,0 -DA:723,0 -DA:724,0 +FN:671,StdCheats.hoax +FNDA:0,StdCheats.hoax +DA:672,0 +DA:673,0 +FN:677,StdCheats.startHoax +FNDA:0,StdCheats.startHoax +DA:678,0 +DA:679,0 +FN:682,StdCheats.startHoax +FNDA:0,StdCheats.startHoax +DA:683,0 +DA:684,0 +FN:689,StdCheats.startHoax +FNDA:0,StdCheats.startHoax +DA:690,0 +DA:691,0 +FN:694,StdCheats.startHoax +FNDA:0,StdCheats.startHoax +DA:695,0 +DA:696,0 +FN:699,StdCheats.changePrank +FNDA:0,StdCheats.changePrank +DA:700,0 +DA:701,0 +DA:702,0 +FN:705,StdCheats.changePrank +FNDA:0,StdCheats.changePrank +DA:706,0 +DA:707,0 +FN:712,StdCheats.deal +FNDA:0,StdCheats.deal +DA:713,0 +FN:718,StdCheats.deal +FNDA:0,StdCheats.deal +DA:719,0 +FN:724,StdCheats.dealERC1155 +FNDA:0,StdCheats.dealERC1155 DA:725,0 -DA:727,0 -FN:735,RSAttestation._refund -FNDA:0,RSAttestation._refund -DA:736,0 -BRDA:736,41,0,- -BRDA:736,41,1,- +FN:728,StdCheats.deal +FNDA:0,StdCheats.deal +DA:730,0 +DA:731,0 +DA:734,0 +DA:737,0 +BRDA:737,0,0,- +BRDA:737,0,1,- +DA:738,0 +DA:739,0 DA:740,0 -FN:748,RSAttestation._time -FNDA:54,RSAttestation._time -DA:749,54 -FN:760,RSAttestation._mergeUIDs -FNDA:3,RSAttestation._mergeUIDs -DA:768,3 -DA:770,3 -DA:771,3 -DA:772,3 -DA:773,3 -DA:774,3 -DA:775,3 -DA:776,6 -DA:779,6 -DA:784,3 -FN:787,RSAttestation.isAttestationValid -FNDA:0,RSAttestation.isAttestationValid -DA:788,16 -FN:798,RSAttestation._enforceOnlySchemaOwner -FNDA:26,RSAttestation._enforceOnlySchemaOwner -DA:799,26 -DA:800,26 -BRDA:800,42,0,1 -BRDA:800,42,1,25 -DA:801,1 -FN:811,RSAttestation._getAttestation -FNDA:9,RSAttestation._getAttestation -DA:820,9 -FNF:20 -FNH:16 -LF:202 -LH:137 -BRF:86 -BRH:38 -end_of_record -TN: -SF:src/base/RSModule.sol -FN:46,RSModule.deploy -FNDA:10,RSModule.deploy -DA:57,10 -BRDA:57,0,0,- -BRDA:57,0,1,10 -DA:59,10 -DA:60,10 -DA:61,10 -DA:63,10 -DA:65,10 -FN:68,RSModule.register -FNDA:2,RSModule.register -DA:70,2 -BRDA:70,1,0,- -BRDA:70,1,1,2 -DA:73,2 -DA:74,2 -DA:76,2 -FN:79,RSModule._storeModuleRecord -FNDA:12,RSModule._storeModuleRecord -DA:90,12 -BRDA:90,2,0,- -BRDA:90,2,1,12 -DA:91,0 -DA:94,12 -FN:106,RSModule._getModule -FNDA:116,RSModule._getModule -DA:107,116 -FNF:4 -FNH:4 -LF:14 -LH:13 -BRF:6 -BRH:3 +BRDA:740,1,0,- +BRDA:740,1,1,- +DA:741,0 +DA:743,0 +DA:745,0 +FN:749,StdCheats.dealERC1155 +FNDA:0,StdCheats.dealERC1155 +DA:751,0 +DA:752,0 +DA:755,0 +DA:758,0 +BRDA:758,2,0,- +BRDA:758,2,1,- +DA:759,0 +DA:760,0 +BRDA:760,3,0,- +BRDA:760,3,1,- +DA:764,0 +DA:765,0 +BRDA:765,4,0,- +BRDA:765,4,1,- +DA:766,0 +DA:768,0 +DA:770,0 +FN:774,StdCheats.dealERC721 +FNDA:0,StdCheats.dealERC721 +DA:776,0 +DA:777,0 +BRDA:777,5,0,- +BRDA:777,5,1,- +DA:780,0 +DA:781,0 +DA:782,0 +DA:785,0 +DA:786,0 +DA:789,0 +DA:790,0 +DA:793,0 +FN:796,StdCheats.deployCodeTo +FNDA:0,StdCheats.deployCodeTo +DA:797,0 +FN:800,StdCheats.deployCodeTo +FNDA:0,StdCheats.deployCodeTo +DA:801,0 +FN:804,StdCheats.deployCodeTo +FNDA:0,StdCheats.deployCodeTo +DA:805,0 +DA:806,0 +DA:807,0 +DA:808,0 +BRDA:808,6,0,- +BRDA:808,6,1,- +DA:809,0 +FN:813,StdCheats.console2_log_StdCheats +FNDA:0,StdCheats.console2_log_StdCheats +DA:814,0 +FNF:59 +FNH:0 +LF:233 +LH:0 +BRF:44 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdInvariant.sol +FN:33,StdInvariant.excludeContract +FNDA:0,StdInvariant.excludeContract +DA:34,0 +FN:37,StdInvariant.excludeSender +FNDA:0,StdInvariant.excludeSender +DA:38,0 +FN:41,StdInvariant.excludeArtifact +FNDA:0,StdInvariant.excludeArtifact +DA:42,0 +FN:45,StdInvariant.targetArtifact +FNDA:0,StdInvariant.targetArtifact +DA:46,0 +FN:49,StdInvariant.targetArtifactSelector +FNDA:0,StdInvariant.targetArtifactSelector +DA:50,0 +FN:53,StdInvariant.targetContract +FNDA:0,StdInvariant.targetContract +DA:54,0 +FN:57,StdInvariant.targetSelector +FNDA:0,StdInvariant.targetSelector +DA:58,0 +FN:61,StdInvariant.targetSender +FNDA:0,StdInvariant.targetSender +DA:62,0 +FN:65,StdInvariant.targetInterface +FNDA:0,StdInvariant.targetInterface +DA:66,0 +FN:72,StdInvariant.excludeArtifacts +FNDA:0,StdInvariant.excludeArtifacts +DA:73,0 +FN:76,StdInvariant.excludeContracts +FNDA:0,StdInvariant.excludeContracts +DA:77,0 +FN:80,StdInvariant.excludeSenders +FNDA:0,StdInvariant.excludeSenders +DA:81,0 +FN:84,StdInvariant.targetArtifacts +FNDA:0,StdInvariant.targetArtifacts +DA:85,0 +FN:88,StdInvariant.targetArtifactSelectors +FNDA:0,StdInvariant.targetArtifactSelectors +DA:89,0 +FN:92,StdInvariant.targetContracts +FNDA:0,StdInvariant.targetContracts +DA:93,0 +FN:96,StdInvariant.targetSelectors +FNDA:0,StdInvariant.targetSelectors +DA:97,0 +FN:100,StdInvariant.targetSenders +FNDA:0,StdInvariant.targetSenders +DA:101,0 +FN:104,StdInvariant.targetInterfaces +FNDA:0,StdInvariant.targetInterfaces +DA:105,0 +FNF:18 +FNH:0 +LF:18 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdJson.sol +FN:32,stdJson.parseRaw +FNDA:0,stdJson.parseRaw +DA:33,0 +FN:36,stdJson.readUint +FNDA:0,stdJson.readUint +DA:37,0 +FN:40,stdJson.readUintArray +FNDA:0,stdJson.readUintArray +DA:41,0 +FN:44,stdJson.readInt +FNDA:0,stdJson.readInt +DA:45,0 +FN:48,stdJson.readIntArray +FNDA:0,stdJson.readIntArray +DA:49,0 +FN:52,stdJson.readBytes32 +FNDA:0,stdJson.readBytes32 +DA:53,0 +FN:56,stdJson.readBytes32Array +FNDA:0,stdJson.readBytes32Array +DA:57,0 +FN:60,stdJson.readString +FNDA:0,stdJson.readString +DA:61,0 +FN:64,stdJson.readStringArray +FNDA:0,stdJson.readStringArray +DA:65,0 +FN:68,stdJson.readAddress +FNDA:0,stdJson.readAddress +DA:69,0 +FN:72,stdJson.readAddressArray +FNDA:0,stdJson.readAddressArray +DA:73,0 +FN:76,stdJson.readBool +FNDA:0,stdJson.readBool +DA:77,0 +FN:80,stdJson.readBoolArray +FNDA:0,stdJson.readBoolArray +DA:81,0 +FN:84,stdJson.readBytes +FNDA:0,stdJson.readBytes +DA:85,0 +FN:88,stdJson.readBytesArray +FNDA:0,stdJson.readBytesArray +DA:89,0 +FN:92,stdJson.serialize +FNDA:0,stdJson.serialize +DA:93,0 +FN:96,stdJson.serialize +FNDA:0,stdJson.serialize +DA:97,0 +FN:100,stdJson.serialize +FNDA:0,stdJson.serialize +DA:104,0 +FN:107,stdJson.serialize +FNDA:0,stdJson.serialize +DA:108,0 +FN:111,stdJson.serialize +FNDA:0,stdJson.serialize +DA:115,0 +FN:118,stdJson.serialize +FNDA:0,stdJson.serialize +DA:119,0 +FN:122,stdJson.serialize +FNDA:0,stdJson.serialize +DA:126,0 +FN:129,stdJson.serialize +FNDA:0,stdJson.serialize +DA:130,0 +FN:133,stdJson.serialize +FNDA:0,stdJson.serialize +DA:137,0 +FN:140,stdJson.serialize +FNDA:0,stdJson.serialize +DA:141,0 +FN:144,stdJson.serialize +FNDA:0,stdJson.serialize +DA:148,0 +FN:151,stdJson.serialize +FNDA:0,stdJson.serialize +DA:152,0 +FN:155,stdJson.serialize +FNDA:0,stdJson.serialize +DA:159,0 +FN:162,stdJson.serialize +FNDA:0,stdJson.serialize +DA:166,0 +FN:169,stdJson.serialize +FNDA:0,stdJson.serialize +DA:173,0 +FN:176,stdJson.write +FNDA:0,stdJson.write +DA:177,0 +FN:180,stdJson.write +FNDA:0,stdJson.write +DA:181,0 +FNF:32 +FNH:0 +LF:32 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdMath.sol +FN:7,stdMath.abs +FNDA:0,stdMath.abs +DA:9,0 +BRDA:9,0,0,- +BRDA:9,0,1,- +DA:10,0 +DA:13,0 +FN:16,stdMath.delta +FNDA:0,stdMath.delta +DA:17,0 +FN:20,stdMath.delta +FNDA:0,stdMath.delta +DA:23,0 +BRDA:23,1,0,- +BRDA:23,1,1,- +DA:24,0 +DA:28,0 +FN:31,stdMath.percentDelta +FNDA:0,stdMath.percentDelta +DA:32,0 +DA:34,0 +FN:37,stdMath.percentDelta +FNDA:0,stdMath.percentDelta +DA:38,0 +DA:39,0 +DA:41,0 +FNF:5 +FNH:0 +LF:12 +LH:0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdStorage.sol +FN:22,stdStorageSafe.sigs +FNDA:0,stdStorageSafe.sigs +DA:23,0 +FN:32,stdStorageSafe.find +FNDA:0,stdStorageSafe.find +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:39,0 +BRDA:39,0,0,- +BRDA:39,0,1,- +DA:40,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:46,0 +DA:47,0 +DA:50,0 +DA:51,0 +BRDA:51,1,0,- +BRDA:51,1,1,- +DA:52,0 +DA:53,0 +BRDA:53,2,0,- +BRDA:53,2,1,- +DA:54,0 +DA:56,0 +BRDA:56,3,0,- +BRDA:56,3,1,- +DA:57,0 +BRDA:57,4,0,- +BRDA:57,4,1,- +DA:62,0 +DA:63,0 +DA:64,0 +DA:65,0 +BRDA:65,5,0,- +BRDA:65,5,1,- +DA:66,0 +DA:67,0 +DA:68,0 +BRDA:68,6,0,- +BRDA:68,6,1,- +DA:69,0 +DA:71,0 +BRDA:71,7,0,- +BRDA:71,7,1,- +DA:72,0 +DA:74,0 +DA:76,0 +DA:77,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:84,0 +BRDA:84,8,0,- +BRDA:84,8,1,- +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:92,0 +DA:95,0 +DA:98,0 +BRDA:98,9,0,- +BRDA:98,9,1,- +DA:103,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:108,0 +FN:111,stdStorageSafe.target +FNDA:0,stdStorageSafe.target +DA:112,0 +DA:113,0 +FN:116,stdStorageSafe.sig +FNDA:0,stdStorageSafe.sig +DA:117,0 +DA:118,0 +FN:121,stdStorageSafe.sig +FNDA:0,stdStorageSafe.sig +DA:122,0 +DA:123,0 +FN:126,stdStorageSafe.with_key +FNDA:0,stdStorageSafe.with_key +DA:127,0 +DA:128,0 +FN:131,stdStorageSafe.with_key +FNDA:0,stdStorageSafe.with_key +DA:132,0 +DA:133,0 +FN:136,stdStorageSafe.with_key +FNDA:0,stdStorageSafe.with_key +DA:137,0 +DA:138,0 +FN:141,stdStorageSafe.depth +FNDA:0,stdStorageSafe.depth +DA:142,0 +DA:143,0 +FN:146,stdStorageSafe.read +FNDA:0,stdStorageSafe.read +DA:147,0 +DA:148,0 +DA:149,0 +FN:152,stdStorageSafe.read_bytes32 +FNDA:0,stdStorageSafe.read_bytes32 +DA:153,0 +FN:156,stdStorageSafe.read_bool +FNDA:0,stdStorageSafe.read_bool +DA:157,0 +DA:158,0 +BRDA:158,10,0,- +BRDA:158,10,1,- +DA:159,0 +BRDA:159,11,0,- +BRDA:159,11,1,- +DA:160,0 +FN:163,stdStorageSafe.read_address +FNDA:0,stdStorageSafe.read_address +DA:164,0 +FN:167,stdStorageSafe.read_uint +FNDA:0,stdStorageSafe.read_uint +DA:168,0 +FN:171,stdStorageSafe.read_int +FNDA:0,stdStorageSafe.read_int +DA:172,0 +FN:175,stdStorageSafe.parent +FNDA:0,stdStorageSafe.parent +DA:176,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:180,0 +DA:181,0 +BRDA:181,12,0,- +BRDA:181,12,1,- +DA:182,0 +DA:186,0 +FN:189,stdStorageSafe.root +FNDA:0,stdStorageSafe.root +DA:190,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:197,0 +DA:198,0 +BRDA:198,13,0,- +BRDA:198,13,1,- +DA:199,0 +DA:204,0 +DA:205,0 +DA:207,0 +FN:210,stdStorageSafe.bytesToBytes32 +FNDA:0,stdStorageSafe.bytesToBytes32 +DA:211,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:217,0 +FN:220,stdStorageSafe.flatten +FNDA:0,stdStorageSafe.flatten +DA:221,0 +DA:222,0 +DA:223,0 +DA:230,0 +FN:237,stdStorage.sigs +FNDA:0,stdStorage.sigs +DA:238,0 +FN:241,stdStorage.find +FNDA:0,stdStorage.find +DA:242,0 +FN:245,stdStorage.target +FNDA:0,stdStorage.target +DA:246,0 +FN:249,stdStorage.sig +FNDA:0,stdStorage.sig +DA:250,0 +FN:253,stdStorage.sig +FNDA:0,stdStorage.sig +DA:254,0 +FN:257,stdStorage.with_key +FNDA:0,stdStorage.with_key +DA:258,0 +FN:261,stdStorage.with_key +FNDA:0,stdStorage.with_key +DA:262,0 +FN:265,stdStorage.with_key +FNDA:0,stdStorage.with_key +DA:266,0 +FN:269,stdStorage.depth +FNDA:0,stdStorage.depth +DA:270,0 +FN:273,stdStorage.checked_write +FNDA:0,stdStorage.checked_write +DA:274,0 +FN:277,stdStorage.checked_write +FNDA:0,stdStorage.checked_write +DA:278,0 +FN:281,stdStorage.checked_write_int +FNDA:0,stdStorage.checked_write_int +DA:282,0 +FN:285,stdStorage.checked_write +FNDA:0,stdStorage.checked_write +DA:286,0 +DA:289,0 +DA:291,0 +FN:294,stdStorage.checked_write +FNDA:0,stdStorage.checked_write +DA:295,0 +DA:296,0 +DA:297,0 +DA:298,0 +DA:300,0 +DA:301,0 +BRDA:301,0,0,- +BRDA:301,0,1,- +DA:302,0 +DA:304,0 +DA:306,0 +DA:308,0 +DA:309,0 +DA:311,0 +DA:313,0 +BRDA:313,1,0,- +BRDA:313,1,1,- +DA:314,0 +BRDA:314,2,0,- +BRDA:314,2,1,- +DA:319,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:323,0 +FN:326,stdStorage.read_bytes32 +FNDA:0,stdStorage.read_bytes32 +DA:327,0 +FN:330,stdStorage.read_bool +FNDA:0,stdStorage.read_bool +DA:331,0 +FN:334,stdStorage.read_address +FNDA:0,stdStorage.read_address +DA:335,0 +FN:338,stdStorage.read_uint +FNDA:0,stdStorage.read_uint +DA:339,0 +FN:342,stdStorage.read_int +FNDA:0,stdStorage.read_int +DA:343,0 +FN:346,stdStorage.parent +FNDA:0,stdStorage.parent +DA:347,0 +FN:350,stdStorage.root +FNDA:0,stdStorage.root +DA:351,0 +FN:355,stdStorage.bytesToBytes32 +FNDA:0,stdStorage.bytesToBytes32 +DA:356,0 +DA:358,0 +DA:359,0 +DA:360,0 +DA:362,0 +FN:366,stdStorage.flatten +FNDA:0,stdStorage.flatten +DA:367,0 +DA:368,0 +DA:369,0 +DA:376,0 +FNF:42 +FNH:0 +LF:154 +LH:0 +BRF:34 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdStyle.sol +FN:22,StdStyle.styleConcat +FNDA:0,StdStyle.styleConcat +DA:23,0 +FN:26,StdStyle.red +FNDA:0,StdStyle.red +DA:27,0 +FN:30,StdStyle.red +FNDA:0,StdStyle.red +DA:31,0 +FN:34,StdStyle.red +FNDA:0,StdStyle.red +DA:35,0 +FN:38,StdStyle.red +FNDA:0,StdStyle.red +DA:39,0 +FN:42,StdStyle.red +FNDA:0,StdStyle.red +DA:43,0 +FN:46,StdStyle.redBytes +FNDA:0,StdStyle.redBytes +DA:47,0 +FN:50,StdStyle.redBytes32 +FNDA:0,StdStyle.redBytes32 +DA:51,0 +FN:54,StdStyle.green +FNDA:0,StdStyle.green +DA:55,0 +FN:58,StdStyle.green +FNDA:0,StdStyle.green +DA:59,0 +FN:62,StdStyle.green +FNDA:0,StdStyle.green +DA:63,0 +FN:66,StdStyle.green +FNDA:0,StdStyle.green +DA:67,0 +FN:70,StdStyle.green +FNDA:0,StdStyle.green +DA:71,0 +FN:74,StdStyle.greenBytes +FNDA:0,StdStyle.greenBytes +DA:75,0 +FN:78,StdStyle.greenBytes32 +FNDA:0,StdStyle.greenBytes32 +DA:79,0 +FN:82,StdStyle.yellow +FNDA:0,StdStyle.yellow +DA:83,0 +FN:86,StdStyle.yellow +FNDA:0,StdStyle.yellow +DA:87,0 +FN:90,StdStyle.yellow +FNDA:0,StdStyle.yellow +DA:91,0 +FN:94,StdStyle.yellow +FNDA:0,StdStyle.yellow +DA:95,0 +FN:98,StdStyle.yellow +FNDA:0,StdStyle.yellow +DA:99,0 +FN:102,StdStyle.yellowBytes +FNDA:0,StdStyle.yellowBytes +DA:103,0 +FN:106,StdStyle.yellowBytes32 +FNDA:0,StdStyle.yellowBytes32 +DA:107,0 +FN:110,StdStyle.blue +FNDA:0,StdStyle.blue +DA:111,0 +FN:114,StdStyle.blue +FNDA:0,StdStyle.blue +DA:115,0 +FN:118,StdStyle.blue +FNDA:0,StdStyle.blue +DA:119,0 +FN:122,StdStyle.blue +FNDA:0,StdStyle.blue +DA:123,0 +FN:126,StdStyle.blue +FNDA:0,StdStyle.blue +DA:127,0 +FN:130,StdStyle.blueBytes +FNDA:0,StdStyle.blueBytes +DA:131,0 +FN:134,StdStyle.blueBytes32 +FNDA:0,StdStyle.blueBytes32 +DA:135,0 +FN:138,StdStyle.magenta +FNDA:0,StdStyle.magenta +DA:139,0 +FN:142,StdStyle.magenta +FNDA:0,StdStyle.magenta +DA:143,0 +FN:146,StdStyle.magenta +FNDA:0,StdStyle.magenta +DA:147,0 +FN:150,StdStyle.magenta +FNDA:0,StdStyle.magenta +DA:151,0 +FN:154,StdStyle.magenta +FNDA:0,StdStyle.magenta +DA:155,0 +FN:158,StdStyle.magentaBytes +FNDA:0,StdStyle.magentaBytes +DA:159,0 +FN:162,StdStyle.magentaBytes32 +FNDA:0,StdStyle.magentaBytes32 +DA:163,0 +FN:166,StdStyle.cyan +FNDA:0,StdStyle.cyan +DA:167,0 +FN:170,StdStyle.cyan +FNDA:0,StdStyle.cyan +DA:171,0 +FN:174,StdStyle.cyan +FNDA:0,StdStyle.cyan +DA:175,0 +FN:178,StdStyle.cyan +FNDA:0,StdStyle.cyan +DA:179,0 +FN:182,StdStyle.cyan +FNDA:0,StdStyle.cyan +DA:183,0 +FN:186,StdStyle.cyanBytes +FNDA:0,StdStyle.cyanBytes +DA:187,0 +FN:190,StdStyle.cyanBytes32 +FNDA:0,StdStyle.cyanBytes32 +DA:191,0 +FN:194,StdStyle.bold +FNDA:0,StdStyle.bold +DA:195,0 +FN:198,StdStyle.bold +FNDA:0,StdStyle.bold +DA:199,0 +FN:202,StdStyle.bold +FNDA:0,StdStyle.bold +DA:203,0 +FN:206,StdStyle.bold +FNDA:0,StdStyle.bold +DA:207,0 +FN:210,StdStyle.bold +FNDA:0,StdStyle.bold +DA:211,0 +FN:214,StdStyle.boldBytes +FNDA:0,StdStyle.boldBytes +DA:215,0 +FN:218,StdStyle.boldBytes32 +FNDA:0,StdStyle.boldBytes32 +DA:219,0 +FN:222,StdStyle.dim +FNDA:0,StdStyle.dim +DA:223,0 +FN:226,StdStyle.dim +FNDA:0,StdStyle.dim +DA:227,0 +FN:230,StdStyle.dim +FNDA:0,StdStyle.dim +DA:231,0 +FN:234,StdStyle.dim +FNDA:0,StdStyle.dim +DA:235,0 +FN:238,StdStyle.dim +FNDA:0,StdStyle.dim +DA:239,0 +FN:242,StdStyle.dimBytes +FNDA:0,StdStyle.dimBytes +DA:243,0 +FN:246,StdStyle.dimBytes32 +FNDA:0,StdStyle.dimBytes32 +DA:247,0 +FN:250,StdStyle.italic +FNDA:0,StdStyle.italic +DA:251,0 +FN:254,StdStyle.italic +FNDA:0,StdStyle.italic +DA:255,0 +FN:258,StdStyle.italic +FNDA:0,StdStyle.italic +DA:259,0 +FN:262,StdStyle.italic +FNDA:0,StdStyle.italic +DA:263,0 +FN:266,StdStyle.italic +FNDA:0,StdStyle.italic +DA:267,0 +FN:270,StdStyle.italicBytes +FNDA:0,StdStyle.italicBytes +DA:271,0 +FN:274,StdStyle.italicBytes32 +FNDA:0,StdStyle.italicBytes32 +DA:275,0 +FN:278,StdStyle.underline +FNDA:0,StdStyle.underline +DA:279,0 +FN:282,StdStyle.underline +FNDA:0,StdStyle.underline +DA:283,0 +FN:286,StdStyle.underline +FNDA:0,StdStyle.underline +DA:287,0 +FN:290,StdStyle.underline +FNDA:0,StdStyle.underline +DA:291,0 +FN:294,StdStyle.underline +FNDA:0,StdStyle.underline +DA:295,0 +FN:298,StdStyle.underlineBytes +FNDA:0,StdStyle.underlineBytes +DA:299,0 +FN:302,StdStyle.underlineBytes32 +FNDA:0,StdStyle.underlineBytes32 +DA:303,0 +FN:306,StdStyle.inverse +FNDA:0,StdStyle.inverse +DA:307,0 +FN:310,StdStyle.inverse +FNDA:0,StdStyle.inverse +DA:311,0 +FN:314,StdStyle.inverse +FNDA:0,StdStyle.inverse +DA:315,0 +FN:318,StdStyle.inverse +FNDA:0,StdStyle.inverse +DA:319,0 +FN:322,StdStyle.inverse +FNDA:0,StdStyle.inverse +DA:323,0 +FN:326,StdStyle.inverseBytes +FNDA:0,StdStyle.inverseBytes +DA:327,0 +FN:330,StdStyle.inverseBytes32 +FNDA:0,StdStyle.inverseBytes32 +DA:331,0 +FNF:78 +FNH:0 +LF:78 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/StdUtils.sol +FN:33,StdUtils._bound +FNDA:0,StdUtils._bound +DA:34,0 +BRDA:34,0,0,- +BRDA:34,0,1,- +DA:37,0 +BRDA:37,1,0,- +BRDA:37,1,1,- +DA:39,0 +DA:43,0 +BRDA:43,2,0,- +BRDA:43,2,1,- +DA:44,0 +BRDA:44,3,0,- +BRDA:44,3,1,- +DA:47,0 +BRDA:47,4,0,- +BRDA:47,4,1,- +DA:48,0 +DA:49,0 +DA:50,0 +BRDA:50,5,0,- +BRDA:50,5,1,- +DA:51,0 +DA:52,0 +BRDA:52,6,0,- +BRDA:52,6,1,- +DA:53,0 +DA:54,0 +DA:55,0 +BRDA:55,7,0,- +BRDA:55,7,1,- +DA:56,0 +FN:60,StdUtils.bound +FNDA:0,StdUtils.bound +DA:61,0 +DA:62,0 +FN:65,StdUtils._bound +FNDA:0,StdUtils._bound +DA:66,0 +BRDA:66,8,0,- +BRDA:66,8,1,- +DA:75,0 +DA:76,0 +DA:77,0 +DA:79,0 +DA:82,0 +FN:85,StdUtils.bound +FNDA:0,StdUtils.bound +DA:86,0 +DA:87,0 +FN:90,StdUtils.boundPrivateKey +FNDA:0,StdUtils.boundPrivateKey +DA:91,0 +FN:94,StdUtils.bytesToUint +FNDA:0,StdUtils.bytesToUint +DA:95,0 +BRDA:95,9,0,- +BRDA:95,9,1,- +DA:96,0 +FN:101,StdUtils.computeCreateAddress +FNDA:0,StdUtils.computeCreateAddress +DA:102,0 +DA:103,0 +FN:106,StdUtils.computeCreate2Address +FNDA:0,StdUtils.computeCreate2Address +DA:112,0 +DA:113,0 +FN:117,StdUtils.computeCreate2Address +FNDA:0,StdUtils.computeCreate2Address +DA:118,0 +DA:119,0 +FN:123,StdUtils.deployMockERC20 +FNDA:0,StdUtils.deployMockERC20 +DA:127,0 +DA:128,0 +FN:132,StdUtils.deployMockERC721 +FNDA:0,StdUtils.deployMockERC721 +DA:133,0 +DA:134,0 +FN:139,StdUtils.hashInitCode +FNDA:0,StdUtils.hashInitCode +DA:140,0 +FN:146,StdUtils.hashInitCode +FNDA:0,StdUtils.hashInitCode +DA:147,0 +FN:151,StdUtils.getTokenBalances +FNDA:0,StdUtils.getTokenBalances +DA:156,0 +DA:158,0 +DA:160,0 +BRDA:160,10,0,- +BRDA:160,10,1,- +DA:163,0 +DA:164,0 +DA:165,0 +DA:167,0 +DA:171,0 +DA:174,0 +DA:175,0 +DA:176,0 +FN:184,StdUtils.addressFromLast20Bytes +FNDA:0,StdUtils.addressFromLast20Bytes +DA:185,0 +FN:191,StdUtils._castLogPayloadViewToPure +FNDA:0,StdUtils._castLogPayloadViewToPure +DA:197,0 +FN:201,StdUtils._sendLogPayload +FNDA:0,StdUtils._sendLogPayload +DA:202,0 +FN:205,StdUtils._sendLogPayloadView +FNDA:0,StdUtils._sendLogPayloadView +DA:206,0 +DA:207,0 +FN:215,StdUtils.console2_log_StdUtils +FNDA:0,StdUtils.console2_log_StdUtils +DA:216,0 +FN:219,StdUtils.console2_log_StdUtils +FNDA:0,StdUtils.console2_log_StdUtils +DA:220,0 +FN:223,StdUtils.console2_log_StdUtils +FNDA:0,StdUtils.console2_log_StdUtils +DA:224,0 +FNF:21 +FNH:0 +LF:59 +LH:0 +BRF:22 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/console.sol +FN:7,console._sendLogPayload +FNDA:0,console._sendLogPayload +DA:8,0 +DA:9,0 +FN:17,console.log +FNDA:0,console.log +DA:18,0 +FN:21,console.logInt +FNDA:0,console.logInt +DA:22,0 +FN:25,console.logUint +FNDA:0,console.logUint +DA:26,0 +FN:29,console.logString +FNDA:0,console.logString +DA:30,0 +FN:33,console.logBool +FNDA:0,console.logBool +DA:34,0 +FN:37,console.logAddress +FNDA:0,console.logAddress +DA:38,0 +FN:41,console.logBytes +FNDA:0,console.logBytes +DA:42,0 +FN:45,console.logBytes1 +FNDA:0,console.logBytes1 +DA:46,0 +FN:49,console.logBytes2 +FNDA:0,console.logBytes2 +DA:50,0 +FN:53,console.logBytes3 +FNDA:0,console.logBytes3 +DA:54,0 +FN:57,console.logBytes4 +FNDA:0,console.logBytes4 +DA:58,0 +FN:61,console.logBytes5 +FNDA:0,console.logBytes5 +DA:62,0 +FN:65,console.logBytes6 +FNDA:0,console.logBytes6 +DA:66,0 +FN:69,console.logBytes7 +FNDA:0,console.logBytes7 +DA:70,0 +FN:73,console.logBytes8 +FNDA:0,console.logBytes8 +DA:74,0 +FN:77,console.logBytes9 +FNDA:0,console.logBytes9 +DA:78,0 +FN:81,console.logBytes10 +FNDA:0,console.logBytes10 +DA:82,0 +FN:85,console.logBytes11 +FNDA:0,console.logBytes11 +DA:86,0 +FN:89,console.logBytes12 +FNDA:0,console.logBytes12 +DA:90,0 +FN:93,console.logBytes13 +FNDA:0,console.logBytes13 +DA:94,0 +FN:97,console.logBytes14 +FNDA:0,console.logBytes14 +DA:98,0 +FN:101,console.logBytes15 +FNDA:0,console.logBytes15 +DA:102,0 +FN:105,console.logBytes16 +FNDA:0,console.logBytes16 +DA:106,0 +FN:109,console.logBytes17 +FNDA:0,console.logBytes17 +DA:110,0 +FN:113,console.logBytes18 +FNDA:0,console.logBytes18 +DA:114,0 +FN:117,console.logBytes19 +FNDA:0,console.logBytes19 +DA:118,0 +FN:121,console.logBytes20 +FNDA:0,console.logBytes20 +DA:122,0 +FN:125,console.logBytes21 +FNDA:0,console.logBytes21 +DA:126,0 +FN:129,console.logBytes22 +FNDA:0,console.logBytes22 +DA:130,0 +FN:133,console.logBytes23 +FNDA:0,console.logBytes23 +DA:134,0 +FN:137,console.logBytes24 +FNDA:0,console.logBytes24 +DA:138,0 +FN:141,console.logBytes25 +FNDA:0,console.logBytes25 +DA:142,0 +FN:145,console.logBytes26 +FNDA:0,console.logBytes26 +DA:146,0 +FN:149,console.logBytes27 +FNDA:0,console.logBytes27 +DA:150,0 +FN:153,console.logBytes28 +FNDA:0,console.logBytes28 +DA:154,0 +FN:157,console.logBytes29 +FNDA:0,console.logBytes29 +DA:158,0 +FN:161,console.logBytes30 +FNDA:0,console.logBytes30 +DA:162,0 +FN:165,console.logBytes31 +FNDA:0,console.logBytes31 +DA:166,0 +FN:169,console.logBytes32 +FNDA:0,console.logBytes32 +DA:170,0 +FN:173,console.log +FNDA:0,console.log +DA:174,0 +FN:177,console.log +FNDA:0,console.log +DA:178,0 +FN:181,console.log +FNDA:0,console.log +DA:182,0 +FN:185,console.log +FNDA:0,console.log +DA:186,0 +FN:189,console.log +FNDA:0,console.log +DA:190,0 +FN:193,console.log +FNDA:0,console.log +DA:194,0 +FN:197,console.log +FNDA:0,console.log +DA:198,0 +FN:201,console.log +FNDA:0,console.log +DA:202,0 +FN:205,console.log +FNDA:0,console.log +DA:206,0 +FN:209,console.log +FNDA:0,console.log +DA:210,0 +FN:213,console.log +FNDA:0,console.log +DA:214,0 +FN:217,console.log +FNDA:0,console.log +DA:218,0 +FN:221,console.log +FNDA:0,console.log +DA:222,0 +FN:225,console.log +FNDA:0,console.log +DA:226,0 +FN:229,console.log +FNDA:0,console.log +DA:230,0 +FN:233,console.log +FNDA:0,console.log +DA:234,0 +FN:237,console.log +FNDA:0,console.log +DA:238,0 +FN:241,console.log +FNDA:0,console.log +DA:242,0 +FN:245,console.log +FNDA:0,console.log +DA:246,0 +FN:249,console.log +FNDA:0,console.log +DA:250,0 +FN:253,console.log +FNDA:0,console.log +DA:254,0 +FN:257,console.log +FNDA:0,console.log +DA:258,0 +FN:261,console.log +FNDA:0,console.log +DA:262,0 +FN:265,console.log +FNDA:0,console.log +DA:266,0 +FN:269,console.log +FNDA:0,console.log +DA:270,0 +FN:273,console.log +FNDA:0,console.log +DA:274,0 +FN:277,console.log +FNDA:0,console.log +DA:278,0 +FN:281,console.log +FNDA:0,console.log +DA:282,0 +FN:285,console.log +FNDA:0,console.log +DA:286,0 +FN:289,console.log +FNDA:0,console.log +DA:290,0 +FN:293,console.log +FNDA:0,console.log +DA:294,0 +FN:297,console.log +FNDA:0,console.log +DA:298,0 +FN:301,console.log +FNDA:0,console.log +DA:302,0 +FN:305,console.log +FNDA:0,console.log +DA:306,0 +FN:309,console.log +FNDA:0,console.log +DA:310,0 +FN:313,console.log +FNDA:0,console.log +DA:314,0 +FN:317,console.log +FNDA:0,console.log +DA:318,0 +FN:321,console.log +FNDA:0,console.log +DA:322,0 +FN:325,console.log +FNDA:0,console.log +DA:326,0 +FN:329,console.log +FNDA:0,console.log +DA:330,0 +FN:333,console.log +FNDA:0,console.log +DA:334,0 +FN:337,console.log +FNDA:0,console.log +DA:338,0 +FN:341,console.log +FNDA:0,console.log +DA:342,0 +FN:345,console.log +FNDA:0,console.log +DA:346,0 +FN:349,console.log +FNDA:0,console.log +DA:350,0 +FN:353,console.log +FNDA:0,console.log +DA:354,0 +FN:357,console.log +FNDA:0,console.log +DA:358,0 +FN:361,console.log +FNDA:0,console.log +DA:362,0 +FN:365,console.log +FNDA:0,console.log +DA:366,0 +FN:369,console.log +FNDA:0,console.log +DA:370,0 +FN:373,console.log +FNDA:0,console.log +DA:374,0 +FN:377,console.log +FNDA:0,console.log +DA:378,0 +FN:381,console.log +FNDA:0,console.log +DA:382,0 +FN:385,console.log +FNDA:0,console.log +DA:386,0 +FN:389,console.log +FNDA:0,console.log +DA:390,0 +FN:393,console.log +FNDA:0,console.log +DA:394,0 +FN:397,console.log +FNDA:0,console.log +DA:398,0 +FN:401,console.log +FNDA:0,console.log +DA:402,0 +FN:405,console.log +FNDA:0,console.log +DA:406,0 +FN:409,console.log +FNDA:0,console.log +DA:410,0 +FN:413,console.log +FNDA:0,console.log +DA:414,0 +FN:417,console.log +FNDA:0,console.log +DA:418,0 +FN:421,console.log +FNDA:0,console.log +DA:422,0 +FN:425,console.log +FNDA:0,console.log +DA:426,0 +FN:429,console.log +FNDA:0,console.log +DA:430,0 +FN:433,console.log +FNDA:0,console.log +DA:434,0 +FN:437,console.log +FNDA:0,console.log +DA:438,0 +FN:441,console.log +FNDA:0,console.log +DA:442,0 +FN:445,console.log +FNDA:0,console.log +DA:446,0 +FN:449,console.log +FNDA:0,console.log +DA:450,0 +FN:453,console.log +FNDA:0,console.log +DA:454,0 +FN:457,console.log +FNDA:0,console.log +DA:458,0 +FN:461,console.log +FNDA:0,console.log +DA:462,0 +FN:465,console.log +FNDA:0,console.log +DA:466,0 +FN:469,console.log +FNDA:0,console.log +DA:470,0 +FN:473,console.log +FNDA:0,console.log +DA:474,0 +FN:477,console.log +FNDA:0,console.log +DA:478,0 +FN:481,console.log +FNDA:0,console.log +DA:482,0 +FN:485,console.log +FNDA:0,console.log +DA:486,0 +FN:489,console.log +FNDA:0,console.log +DA:490,0 +FN:493,console.log +FNDA:0,console.log +DA:494,0 +FN:497,console.log +FNDA:0,console.log +DA:498,0 +FN:501,console.log +FNDA:0,console.log +DA:502,0 +FN:505,console.log +FNDA:0,console.log +DA:506,0 +FN:509,console.log +FNDA:0,console.log +DA:510,0 +FN:513,console.log +FNDA:0,console.log +DA:514,0 +FN:517,console.log +FNDA:0,console.log +DA:518,0 +FN:521,console.log +FNDA:0,console.log +DA:522,0 +FN:525,console.log +FNDA:0,console.log +DA:526,0 +FN:529,console.log +FNDA:0,console.log +DA:530,0 +FN:533,console.log +FNDA:0,console.log +DA:534,0 +FN:537,console.log +FNDA:0,console.log +DA:538,0 +FN:541,console.log +FNDA:0,console.log +DA:542,0 +FN:545,console.log +FNDA:0,console.log +DA:546,0 +FN:549,console.log +FNDA:0,console.log +DA:550,0 +FN:553,console.log +FNDA:0,console.log +DA:554,0 +FN:557,console.log +FNDA:0,console.log +DA:558,0 +FN:561,console.log +FNDA:0,console.log +DA:562,0 +FN:565,console.log +FNDA:0,console.log +DA:566,0 +FN:569,console.log +FNDA:0,console.log +DA:570,0 +FN:573,console.log +FNDA:0,console.log +DA:574,0 +FN:577,console.log +FNDA:0,console.log +DA:578,0 +FN:581,console.log +FNDA:0,console.log +DA:582,0 +FN:585,console.log +FNDA:0,console.log +DA:586,0 +FN:589,console.log +FNDA:0,console.log +DA:590,0 +FN:593,console.log +FNDA:0,console.log +DA:594,0 +FN:597,console.log +FNDA:0,console.log +DA:598,0 +FN:601,console.log +FNDA:0,console.log +DA:602,0 +FN:605,console.log +FNDA:0,console.log +DA:606,0 +FN:609,console.log +FNDA:0,console.log +DA:610,0 +FN:613,console.log +FNDA:0,console.log +DA:614,0 +FN:617,console.log +FNDA:0,console.log +DA:618,0 +FN:621,console.log +FNDA:0,console.log +DA:622,0 +FN:625,console.log +FNDA:0,console.log +DA:626,0 +FN:629,console.log +FNDA:0,console.log +DA:630,0 +FN:633,console.log +FNDA:0,console.log +DA:634,0 +FN:637,console.log +FNDA:0,console.log +DA:638,0 +FN:641,console.log +FNDA:0,console.log +DA:642,0 +FN:645,console.log +FNDA:0,console.log +DA:646,0 +FN:649,console.log +FNDA:0,console.log +DA:650,0 +FN:653,console.log +FNDA:0,console.log +DA:654,0 +FN:657,console.log +FNDA:0,console.log +DA:658,0 +FN:661,console.log +FNDA:0,console.log +DA:662,0 +FN:665,console.log +FNDA:0,console.log +DA:666,0 +FN:669,console.log +FNDA:0,console.log +DA:670,0 +FN:673,console.log +FNDA:0,console.log +DA:674,0 +FN:677,console.log +FNDA:0,console.log +DA:678,0 +FN:681,console.log +FNDA:0,console.log +DA:682,0 +FN:685,console.log +FNDA:0,console.log +DA:686,0 +FN:689,console.log +FNDA:0,console.log +DA:690,0 +FN:693,console.log +FNDA:0,console.log +DA:694,0 +FN:697,console.log +FNDA:0,console.log +DA:698,0 +FN:701,console.log +FNDA:0,console.log +DA:702,0 +FN:705,console.log +FNDA:0,console.log +DA:706,0 +FN:709,console.log +FNDA:0,console.log +DA:710,0 +FN:713,console.log +FNDA:0,console.log +DA:714,0 +FN:717,console.log +FNDA:0,console.log +DA:718,0 +FN:721,console.log +FNDA:0,console.log +DA:722,0 +FN:725,console.log +FNDA:0,console.log +DA:726,0 +FN:729,console.log +FNDA:0,console.log +DA:730,0 +FN:733,console.log +FNDA:0,console.log +DA:734,0 +FN:737,console.log +FNDA:0,console.log +DA:738,0 +FN:741,console.log +FNDA:0,console.log +DA:742,0 +FN:745,console.log +FNDA:0,console.log +DA:746,0 +FN:749,console.log +FNDA:0,console.log +DA:750,0 +FN:753,console.log +FNDA:0,console.log +DA:754,0 +FN:757,console.log +FNDA:0,console.log +DA:758,0 +FN:761,console.log +FNDA:0,console.log +DA:762,0 +FN:765,console.log +FNDA:0,console.log +DA:766,0 +FN:769,console.log +FNDA:0,console.log +DA:770,0 +FN:773,console.log +FNDA:0,console.log +DA:774,0 +FN:777,console.log +FNDA:0,console.log +DA:778,0 +FN:781,console.log +FNDA:0,console.log +DA:782,0 +FN:785,console.log +FNDA:0,console.log +DA:786,0 +FN:789,console.log +FNDA:0,console.log +DA:790,0 +FN:793,console.log +FNDA:0,console.log +DA:794,0 +FN:797,console.log +FNDA:0,console.log +DA:798,0 +FN:801,console.log +FNDA:0,console.log +DA:802,0 +FN:805,console.log +FNDA:0,console.log +DA:806,0 +FN:809,console.log +FNDA:0,console.log +DA:810,0 +FN:813,console.log +FNDA:0,console.log +DA:814,0 +FN:817,console.log +FNDA:0,console.log +DA:818,0 +FN:821,console.log +FNDA:0,console.log +DA:822,0 +FN:825,console.log +FNDA:0,console.log +DA:826,0 +FN:829,console.log +FNDA:0,console.log +DA:830,0 +FN:833,console.log +FNDA:0,console.log +DA:834,0 +FN:837,console.log +FNDA:0,console.log +DA:838,0 +FN:841,console.log +FNDA:0,console.log +DA:842,0 +FN:845,console.log +FNDA:0,console.log +DA:846,0 +FN:849,console.log +FNDA:0,console.log +DA:850,0 +FN:853,console.log +FNDA:0,console.log +DA:854,0 +FN:857,console.log +FNDA:0,console.log +DA:858,0 +FN:861,console.log +FNDA:0,console.log +DA:862,0 +FN:865,console.log +FNDA:0,console.log +DA:866,0 +FN:869,console.log +FNDA:0,console.log +DA:870,0 +FN:873,console.log +FNDA:0,console.log +DA:874,0 +FN:877,console.log +FNDA:0,console.log +DA:878,0 +FN:881,console.log +FNDA:0,console.log +DA:882,0 +FN:885,console.log +FNDA:0,console.log +DA:886,0 +FN:889,console.log +FNDA:0,console.log +DA:890,0 +FN:893,console.log +FNDA:0,console.log +DA:894,0 +FN:897,console.log +FNDA:0,console.log +DA:898,0 +FN:901,console.log +FNDA:0,console.log +DA:902,0 +FN:905,console.log +FNDA:0,console.log +DA:906,0 +FN:909,console.log +FNDA:0,console.log +DA:910,0 +FN:913,console.log +FNDA:0,console.log +DA:914,0 +FN:917,console.log +FNDA:0,console.log +DA:918,0 +FN:921,console.log +FNDA:0,console.log +DA:922,0 +FN:925,console.log +FNDA:0,console.log +DA:926,0 +FN:929,console.log +FNDA:0,console.log +DA:930,0 +FN:933,console.log +FNDA:0,console.log +DA:934,0 +FN:937,console.log +FNDA:0,console.log +DA:938,0 +FN:941,console.log +FNDA:0,console.log +DA:942,0 +FN:945,console.log +FNDA:0,console.log +DA:946,0 +FN:949,console.log +FNDA:0,console.log +DA:950,0 +FN:953,console.log +FNDA:0,console.log +DA:954,0 +FN:957,console.log +FNDA:0,console.log +DA:958,0 +FN:961,console.log +FNDA:0,console.log +DA:962,0 +FN:965,console.log +FNDA:0,console.log +DA:966,0 +FN:969,console.log +FNDA:0,console.log +DA:970,0 +FN:973,console.log +FNDA:0,console.log +DA:974,0 +FN:977,console.log +FNDA:0,console.log +DA:978,0 +FN:981,console.log +FNDA:0,console.log +DA:982,0 +FN:985,console.log +FNDA:0,console.log +DA:986,0 +FN:989,console.log +FNDA:0,console.log +DA:990,0 +FN:993,console.log +FNDA:0,console.log +DA:994,0 +FN:997,console.log +FNDA:0,console.log +DA:998,0 +FN:1001,console.log +FNDA:0,console.log +DA:1002,0 +FN:1005,console.log +FNDA:0,console.log +DA:1006,0 +FN:1009,console.log +FNDA:0,console.log +DA:1010,0 +FN:1013,console.log +FNDA:0,console.log +DA:1014,0 +FN:1017,console.log +FNDA:0,console.log +DA:1018,0 +FN:1021,console.log +FNDA:0,console.log +DA:1022,0 +FN:1025,console.log +FNDA:0,console.log +DA:1026,0 +FN:1029,console.log +FNDA:0,console.log +DA:1030,0 +FN:1033,console.log +FNDA:0,console.log +DA:1034,0 +FN:1037,console.log +FNDA:0,console.log +DA:1038,0 +FN:1041,console.log +FNDA:0,console.log +DA:1042,0 +FN:1045,console.log +FNDA:0,console.log +DA:1046,0 +FN:1049,console.log +FNDA:0,console.log +DA:1050,0 +FN:1053,console.log +FNDA:0,console.log +DA:1054,0 +FN:1057,console.log +FNDA:0,console.log +DA:1058,0 +FN:1061,console.log +FNDA:0,console.log +DA:1062,0 +FN:1065,console.log +FNDA:0,console.log +DA:1066,0 +FN:1069,console.log +FNDA:0,console.log +DA:1070,0 +FN:1073,console.log +FNDA:0,console.log +DA:1074,0 +FN:1077,console.log +FNDA:0,console.log +DA:1078,0 +FN:1081,console.log +FNDA:0,console.log +DA:1082,0 +FN:1085,console.log +FNDA:0,console.log +DA:1086,0 +FN:1089,console.log +FNDA:0,console.log +DA:1090,0 +FN:1093,console.log +FNDA:0,console.log +DA:1094,0 +FN:1097,console.log +FNDA:0,console.log +DA:1098,0 +FN:1101,console.log +FNDA:0,console.log +DA:1102,0 +FN:1105,console.log +FNDA:0,console.log +DA:1106,0 +FN:1109,console.log +FNDA:0,console.log +DA:1110,0 +FN:1113,console.log +FNDA:0,console.log +DA:1114,0 +FN:1117,console.log +FNDA:0,console.log +DA:1118,0 +FN:1121,console.log +FNDA:0,console.log +DA:1122,0 +FN:1125,console.log +FNDA:0,console.log +DA:1126,0 +FN:1129,console.log +FNDA:0,console.log +DA:1130,0 +FN:1133,console.log +FNDA:0,console.log +DA:1134,0 +FN:1137,console.log +FNDA:0,console.log +DA:1138,0 +FN:1141,console.log +FNDA:0,console.log +DA:1142,0 +FN:1145,console.log +FNDA:0,console.log +DA:1146,0 +FN:1149,console.log +FNDA:0,console.log +DA:1150,0 +FN:1153,console.log +FNDA:0,console.log +DA:1154,0 +FN:1157,console.log +FNDA:0,console.log +DA:1158,0 +FN:1161,console.log +FNDA:0,console.log +DA:1162,0 +FN:1165,console.log +FNDA:0,console.log +DA:1166,0 +FN:1169,console.log +FNDA:0,console.log +DA:1170,0 +FN:1173,console.log +FNDA:0,console.log +DA:1174,0 +FN:1177,console.log +FNDA:0,console.log +DA:1178,0 +FN:1181,console.log +FNDA:0,console.log +DA:1182,0 +FN:1185,console.log +FNDA:0,console.log +DA:1186,0 +FN:1189,console.log +FNDA:0,console.log +DA:1190,0 +FN:1193,console.log +FNDA:0,console.log +DA:1194,0 +FN:1197,console.log +FNDA:0,console.log +DA:1198,0 +FN:1201,console.log +FNDA:0,console.log +DA:1202,0 +FN:1205,console.log +FNDA:0,console.log +DA:1206,0 +FN:1209,console.log +FNDA:0,console.log +DA:1210,0 +FN:1213,console.log +FNDA:0,console.log +DA:1214,0 +FN:1217,console.log +FNDA:0,console.log +DA:1218,0 +FN:1221,console.log +FNDA:0,console.log +DA:1222,0 +FN:1225,console.log +FNDA:0,console.log +DA:1226,0 +FN:1229,console.log +FNDA:0,console.log +DA:1230,0 +FN:1233,console.log +FNDA:0,console.log +DA:1234,0 +FN:1237,console.log +FNDA:0,console.log +DA:1238,0 +FN:1241,console.log +FNDA:0,console.log +DA:1242,0 +FN:1245,console.log +FNDA:0,console.log +DA:1246,0 +FN:1249,console.log +FNDA:0,console.log +DA:1250,0 +FN:1253,console.log +FNDA:0,console.log +DA:1254,0 +FN:1257,console.log +FNDA:0,console.log +DA:1258,0 +FN:1261,console.log +FNDA:0,console.log +DA:1262,0 +FN:1265,console.log +FNDA:0,console.log +DA:1266,0 +FN:1269,console.log +FNDA:0,console.log +DA:1270,0 +FN:1273,console.log +FNDA:0,console.log +DA:1274,0 +FN:1277,console.log +FNDA:0,console.log +DA:1278,0 +FN:1281,console.log +FNDA:0,console.log +DA:1282,0 +FN:1285,console.log +FNDA:0,console.log +DA:1286,0 +FN:1289,console.log +FNDA:0,console.log +DA:1290,0 +FN:1293,console.log +FNDA:0,console.log +DA:1294,0 +FN:1297,console.log +FNDA:0,console.log +DA:1298,0 +FN:1301,console.log +FNDA:0,console.log +DA:1302,0 +FN:1305,console.log +FNDA:0,console.log +DA:1306,0 +FN:1309,console.log +FNDA:0,console.log +DA:1310,0 +FN:1313,console.log +FNDA:0,console.log +DA:1314,0 +FN:1317,console.log +FNDA:0,console.log +DA:1318,0 +FN:1321,console.log +FNDA:0,console.log +DA:1322,0 +FN:1325,console.log +FNDA:0,console.log +DA:1326,0 +FN:1329,console.log +FNDA:0,console.log +DA:1330,0 +FN:1333,console.log +FNDA:0,console.log +DA:1334,0 +FN:1337,console.log +FNDA:0,console.log +DA:1338,0 +FN:1341,console.log +FNDA:0,console.log +DA:1342,0 +FN:1345,console.log +FNDA:0,console.log +DA:1346,0 +FN:1349,console.log +FNDA:0,console.log +DA:1350,0 +FN:1353,console.log +FNDA:0,console.log +DA:1354,0 +FN:1357,console.log +FNDA:0,console.log +DA:1358,0 +FN:1361,console.log +FNDA:0,console.log +DA:1362,0 +FN:1365,console.log +FNDA:0,console.log +DA:1366,0 +FN:1369,console.log +FNDA:0,console.log +DA:1370,0 +FN:1373,console.log +FNDA:0,console.log +DA:1374,0 +FN:1377,console.log +FNDA:0,console.log +DA:1378,0 +FN:1381,console.log +FNDA:0,console.log +DA:1382,0 +FN:1385,console.log +FNDA:0,console.log +DA:1386,0 +FN:1389,console.log +FNDA:0,console.log +DA:1390,0 +FN:1393,console.log +FNDA:0,console.log +DA:1394,0 +FN:1397,console.log +FNDA:0,console.log +DA:1398,0 +FN:1401,console.log +FNDA:0,console.log +DA:1402,0 +FN:1405,console.log +FNDA:0,console.log +DA:1406,0 +FN:1409,console.log +FNDA:0,console.log +DA:1410,0 +FN:1413,console.log +FNDA:0,console.log +DA:1414,0 +FN:1417,console.log +FNDA:0,console.log +DA:1418,0 +FN:1421,console.log +FNDA:0,console.log +DA:1422,0 +FN:1425,console.log +FNDA:0,console.log +DA:1426,0 +FN:1429,console.log +FNDA:0,console.log +DA:1430,0 +FN:1433,console.log +FNDA:0,console.log +DA:1434,0 +FN:1437,console.log +FNDA:0,console.log +DA:1438,0 +FN:1441,console.log +FNDA:0,console.log +DA:1442,0 +FN:1445,console.log +FNDA:0,console.log +DA:1446,0 +FN:1449,console.log +FNDA:0,console.log +DA:1450,0 +FN:1453,console.log +FNDA:0,console.log +DA:1454,0 +FN:1457,console.log +FNDA:0,console.log +DA:1458,0 +FN:1461,console.log +FNDA:0,console.log +DA:1462,0 +FN:1465,console.log +FNDA:0,console.log +DA:1466,0 +FN:1469,console.log +FNDA:0,console.log +DA:1470,0 +FN:1473,console.log +FNDA:0,console.log +DA:1474,0 +FN:1477,console.log +FNDA:0,console.log +DA:1478,0 +FN:1481,console.log +FNDA:0,console.log +DA:1482,0 +FN:1485,console.log +FNDA:0,console.log +DA:1486,0 +FN:1489,console.log +FNDA:0,console.log +DA:1490,0 +FN:1493,console.log +FNDA:0,console.log +DA:1494,0 +FN:1497,console.log +FNDA:0,console.log +DA:1498,0 +FN:1501,console.log +FNDA:0,console.log +DA:1502,0 +FN:1505,console.log +FNDA:0,console.log +DA:1506,0 +FN:1509,console.log +FNDA:0,console.log +DA:1510,0 +FN:1513,console.log +FNDA:0,console.log +DA:1514,0 +FN:1517,console.log +FNDA:0,console.log +DA:1518,0 +FN:1521,console.log +FNDA:0,console.log +DA:1522,0 +FN:1525,console.log +FNDA:0,console.log +DA:1526,0 +FN:1529,console.log +FNDA:0,console.log +DA:1530,0 +FNF:380 +FNH:0 +LF:381 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/console2.sol +FN:12,console2._castLogPayloadViewToPure +FNDA:0,console2._castLogPayloadViewToPure +DA:16,0 +FN:20,console2._sendLogPayload +FNDA:0,console2._sendLogPayload +DA:21,0 +FN:24,console2._sendLogPayloadView +FNDA:0,console2._sendLogPayloadView +DA:25,0 +DA:26,0 +FN:34,console2.log +FNDA:0,console2.log +DA:35,0 +FN:38,console2.logInt +FNDA:0,console2.logInt +DA:39,0 +FN:42,console2.logUint +FNDA:0,console2.logUint +DA:43,0 +FN:46,console2.logString +FNDA:0,console2.logString +DA:47,0 +FN:50,console2.logBool +FNDA:0,console2.logBool +DA:51,0 +FN:54,console2.logAddress +FNDA:0,console2.logAddress +DA:55,0 +FN:58,console2.logBytes +FNDA:0,console2.logBytes +DA:59,0 +FN:62,console2.logBytes1 +FNDA:0,console2.logBytes1 +DA:63,0 +FN:66,console2.logBytes2 +FNDA:0,console2.logBytes2 +DA:67,0 +FN:70,console2.logBytes3 +FNDA:0,console2.logBytes3 +DA:71,0 +FN:74,console2.logBytes4 +FNDA:0,console2.logBytes4 +DA:75,0 +FN:78,console2.logBytes5 +FNDA:0,console2.logBytes5 +DA:79,0 +FN:82,console2.logBytes6 +FNDA:0,console2.logBytes6 +DA:83,0 +FN:86,console2.logBytes7 +FNDA:0,console2.logBytes7 +DA:87,0 +FN:90,console2.logBytes8 +FNDA:0,console2.logBytes8 +DA:91,0 +FN:94,console2.logBytes9 +FNDA:0,console2.logBytes9 +DA:95,0 +FN:98,console2.logBytes10 +FNDA:0,console2.logBytes10 +DA:99,0 +FN:102,console2.logBytes11 +FNDA:0,console2.logBytes11 +DA:103,0 +FN:106,console2.logBytes12 +FNDA:0,console2.logBytes12 +DA:107,0 +FN:110,console2.logBytes13 +FNDA:0,console2.logBytes13 +DA:111,0 +FN:114,console2.logBytes14 +FNDA:0,console2.logBytes14 +DA:115,0 +FN:118,console2.logBytes15 +FNDA:0,console2.logBytes15 +DA:119,0 +FN:122,console2.logBytes16 +FNDA:0,console2.logBytes16 +DA:123,0 +FN:126,console2.logBytes17 +FNDA:0,console2.logBytes17 +DA:127,0 +FN:130,console2.logBytes18 +FNDA:0,console2.logBytes18 +DA:131,0 +FN:134,console2.logBytes19 +FNDA:0,console2.logBytes19 +DA:135,0 +FN:138,console2.logBytes20 +FNDA:0,console2.logBytes20 +DA:139,0 +FN:142,console2.logBytes21 +FNDA:0,console2.logBytes21 +DA:143,0 +FN:146,console2.logBytes22 +FNDA:0,console2.logBytes22 +DA:147,0 +FN:150,console2.logBytes23 +FNDA:0,console2.logBytes23 +DA:151,0 +FN:154,console2.logBytes24 +FNDA:0,console2.logBytes24 +DA:155,0 +FN:158,console2.logBytes25 +FNDA:0,console2.logBytes25 +DA:159,0 +FN:162,console2.logBytes26 +FNDA:0,console2.logBytes26 +DA:163,0 +FN:166,console2.logBytes27 +FNDA:0,console2.logBytes27 +DA:167,0 +FN:170,console2.logBytes28 +FNDA:0,console2.logBytes28 +DA:171,0 +FN:174,console2.logBytes29 +FNDA:0,console2.logBytes29 +DA:175,0 +FN:178,console2.logBytes30 +FNDA:0,console2.logBytes30 +DA:179,0 +FN:182,console2.logBytes31 +FNDA:0,console2.logBytes31 +DA:183,0 +FN:186,console2.logBytes32 +FNDA:0,console2.logBytes32 +DA:187,0 +FN:190,console2.log +FNDA:0,console2.log +DA:191,0 +FN:194,console2.log +FNDA:0,console2.log +DA:195,0 +FN:198,console2.log +FNDA:0,console2.log +DA:199,0 +FN:202,console2.log +FNDA:0,console2.log +DA:203,0 +FN:206,console2.log +FNDA:0,console2.log +DA:207,0 +FN:210,console2.log +FNDA:0,console2.log +DA:211,0 +FN:214,console2.log +FNDA:0,console2.log +DA:215,0 +FN:218,console2.log +FNDA:0,console2.log +DA:219,0 +FN:222,console2.log +FNDA:0,console2.log +DA:223,0 +FN:226,console2.log +FNDA:0,console2.log +DA:227,0 +FN:230,console2.log +FNDA:0,console2.log +DA:231,0 +FN:234,console2.log +FNDA:0,console2.log +DA:235,0 +FN:238,console2.log +FNDA:0,console2.log +DA:239,0 +FN:242,console2.log +FNDA:0,console2.log +DA:243,0 +FN:246,console2.log +FNDA:0,console2.log +DA:247,0 +FN:250,console2.log +FNDA:0,console2.log +DA:251,0 +FN:254,console2.log +FNDA:0,console2.log +DA:255,0 +FN:258,console2.log +FNDA:0,console2.log +DA:259,0 +FN:262,console2.log +FNDA:0,console2.log +DA:263,0 +FN:266,console2.log +FNDA:0,console2.log +DA:267,0 +FN:270,console2.log +FNDA:0,console2.log +DA:271,0 +FN:274,console2.log +FNDA:0,console2.log +DA:275,0 +FN:278,console2.log +FNDA:0,console2.log +DA:279,0 +FN:282,console2.log +FNDA:0,console2.log +DA:283,0 +FN:286,console2.log +FNDA:0,console2.log +DA:287,0 +FN:290,console2.log +FNDA:0,console2.log +DA:291,0 +FN:294,console2.log +FNDA:0,console2.log +DA:295,0 +FN:298,console2.log +FNDA:0,console2.log +DA:299,0 +FN:302,console2.log +FNDA:0,console2.log +DA:303,0 +FN:306,console2.log +FNDA:0,console2.log +DA:307,0 +FN:310,console2.log +FNDA:0,console2.log +DA:311,0 +FN:314,console2.log +FNDA:0,console2.log +DA:315,0 +FN:318,console2.log +FNDA:0,console2.log +DA:319,0 +FN:322,console2.log +FNDA:0,console2.log +DA:323,0 +FN:326,console2.log +FNDA:0,console2.log +DA:327,0 +FN:330,console2.log +FNDA:0,console2.log +DA:331,0 +FN:334,console2.log +FNDA:0,console2.log +DA:335,0 +FN:338,console2.log +FNDA:0,console2.log +DA:339,0 +FN:342,console2.log +FNDA:0,console2.log +DA:343,0 +FN:346,console2.log +FNDA:0,console2.log +DA:347,0 +FN:350,console2.log +FNDA:0,console2.log +DA:351,0 +FN:354,console2.log +FNDA:0,console2.log +DA:355,0 +FN:358,console2.log +FNDA:0,console2.log +DA:359,0 +FN:362,console2.log +FNDA:0,console2.log +DA:363,0 +FN:366,console2.log +FNDA:0,console2.log +DA:367,0 +FN:370,console2.log +FNDA:0,console2.log +DA:371,0 +FN:374,console2.log +FNDA:0,console2.log +DA:375,0 +FN:378,console2.log +FNDA:0,console2.log +DA:379,0 +FN:382,console2.log +FNDA:0,console2.log +DA:383,0 +FN:386,console2.log +FNDA:0,console2.log +DA:387,0 +FN:390,console2.log +FNDA:0,console2.log +DA:391,0 +FN:394,console2.log +FNDA:0,console2.log +DA:395,0 +FN:398,console2.log +FNDA:0,console2.log +DA:399,0 +FN:402,console2.log +FNDA:0,console2.log +DA:403,0 +FN:406,console2.log +FNDA:0,console2.log +DA:407,0 +FN:410,console2.log +FNDA:0,console2.log +DA:411,0 +FN:414,console2.log +FNDA:0,console2.log +DA:415,0 +FN:418,console2.log +FNDA:0,console2.log +DA:419,0 +FN:422,console2.log +FNDA:0,console2.log +DA:423,0 +FN:426,console2.log +FNDA:0,console2.log +DA:427,0 +FN:430,console2.log +FNDA:0,console2.log +DA:431,0 +FN:434,console2.log +FNDA:0,console2.log +DA:435,0 +FN:438,console2.log +FNDA:0,console2.log +DA:439,0 +FN:442,console2.log +FNDA:0,console2.log +DA:443,0 +FN:446,console2.log +FNDA:0,console2.log +DA:447,0 +FN:450,console2.log +FNDA:0,console2.log +DA:451,0 +FN:454,console2.log +FNDA:0,console2.log +DA:455,0 +FN:458,console2.log +FNDA:0,console2.log +DA:459,0 +FN:462,console2.log +FNDA:0,console2.log +DA:463,0 +FN:466,console2.log +FNDA:0,console2.log +DA:467,0 +FN:470,console2.log +FNDA:0,console2.log +DA:471,0 +FN:474,console2.log +FNDA:0,console2.log +DA:475,0 +FN:478,console2.log +FNDA:0,console2.log +DA:479,0 +FN:482,console2.log +FNDA:0,console2.log +DA:483,0 +FN:486,console2.log +FNDA:0,console2.log +DA:487,0 +FN:490,console2.log +FNDA:0,console2.log +DA:491,0 +FN:494,console2.log +FNDA:0,console2.log +DA:495,0 +FN:498,console2.log +FNDA:0,console2.log +DA:499,0 +FN:502,console2.log +FNDA:0,console2.log +DA:503,0 +FN:506,console2.log +FNDA:0,console2.log +DA:507,0 +FN:510,console2.log +FNDA:0,console2.log +DA:511,0 +FN:514,console2.log +FNDA:0,console2.log +DA:515,0 +FN:518,console2.log +FNDA:0,console2.log +DA:519,0 +FN:522,console2.log +FNDA:0,console2.log +DA:523,0 +FN:526,console2.log +FNDA:0,console2.log +DA:527,0 +FN:530,console2.log +FNDA:0,console2.log +DA:531,0 +FN:534,console2.log +FNDA:0,console2.log +DA:535,0 +FN:538,console2.log +FNDA:0,console2.log +DA:539,0 +FN:542,console2.log +FNDA:0,console2.log +DA:543,0 +FN:546,console2.log +FNDA:0,console2.log +DA:547,0 +FN:550,console2.log +FNDA:0,console2.log +DA:551,0 +FN:554,console2.log +FNDA:0,console2.log +DA:555,0 +FN:558,console2.log +FNDA:0,console2.log +DA:559,0 +FN:562,console2.log +FNDA:0,console2.log +DA:563,0 +FN:566,console2.log +FNDA:0,console2.log +DA:567,0 +FN:570,console2.log +FNDA:0,console2.log +DA:571,0 +FN:574,console2.log +FNDA:0,console2.log +DA:575,0 +FN:578,console2.log +FNDA:0,console2.log +DA:579,0 +FN:582,console2.log +FNDA:0,console2.log +DA:583,0 +FN:586,console2.log +FNDA:0,console2.log +DA:587,0 +FN:590,console2.log +FNDA:0,console2.log +DA:591,0 +FN:594,console2.log +FNDA:0,console2.log +DA:595,0 +FN:598,console2.log +FNDA:0,console2.log +DA:599,0 +FN:602,console2.log +FNDA:0,console2.log +DA:603,0 +FN:606,console2.log +FNDA:0,console2.log +DA:607,0 +FN:610,console2.log +FNDA:0,console2.log +DA:611,0 +FN:614,console2.log +FNDA:0,console2.log +DA:615,0 +FN:618,console2.log +FNDA:0,console2.log +DA:619,0 +FN:622,console2.log +FNDA:0,console2.log +DA:623,0 +FN:626,console2.log +FNDA:0,console2.log +DA:627,0 +FN:630,console2.log +FNDA:0,console2.log +DA:631,0 +FN:634,console2.log +FNDA:0,console2.log +DA:635,0 +FN:638,console2.log +FNDA:0,console2.log +DA:639,0 +FN:642,console2.log +FNDA:0,console2.log +DA:643,0 +FN:646,console2.log +FNDA:0,console2.log +DA:647,0 +FN:650,console2.log +FNDA:0,console2.log +DA:651,0 +FN:654,console2.log +FNDA:0,console2.log +DA:655,0 +FN:658,console2.log +FNDA:0,console2.log +DA:659,0 +FN:662,console2.log +FNDA:0,console2.log +DA:663,0 +FN:666,console2.log +FNDA:0,console2.log +DA:667,0 +FN:670,console2.log +FNDA:0,console2.log +DA:671,0 +FN:674,console2.log +FNDA:0,console2.log +DA:675,0 +FN:678,console2.log +FNDA:0,console2.log +DA:679,0 +FN:682,console2.log +FNDA:0,console2.log +DA:683,0 +FN:686,console2.log +FNDA:0,console2.log +DA:687,0 +FN:690,console2.log +FNDA:0,console2.log +DA:691,0 +FN:694,console2.log +FNDA:0,console2.log +DA:695,0 +FN:698,console2.log +FNDA:0,console2.log +DA:699,0 +FN:702,console2.log +FNDA:0,console2.log +DA:703,0 +FN:706,console2.log +FNDA:0,console2.log +DA:707,0 +FN:710,console2.log +FNDA:0,console2.log +DA:711,0 +FN:714,console2.log +FNDA:0,console2.log +DA:715,0 +FN:718,console2.log +FNDA:0,console2.log +DA:719,0 +FN:722,console2.log +FNDA:0,console2.log +DA:723,0 +FN:726,console2.log +FNDA:0,console2.log +DA:727,0 +FN:730,console2.log +FNDA:0,console2.log +DA:731,0 +FN:734,console2.log +FNDA:0,console2.log +DA:735,0 +FN:738,console2.log +FNDA:0,console2.log +DA:739,0 +FN:742,console2.log +FNDA:0,console2.log +DA:743,0 +FN:746,console2.log +FNDA:0,console2.log +DA:747,0 +FN:750,console2.log +FNDA:0,console2.log +DA:751,0 +FN:754,console2.log +FNDA:0,console2.log +DA:755,0 +FN:758,console2.log +FNDA:0,console2.log +DA:759,0 +FN:762,console2.log +FNDA:0,console2.log +DA:763,0 +FN:766,console2.log +FNDA:0,console2.log +DA:767,0 +FN:770,console2.log +FNDA:0,console2.log +DA:771,0 +FN:774,console2.log +FNDA:0,console2.log +DA:775,0 +FN:778,console2.log +FNDA:0,console2.log +DA:779,0 +FN:782,console2.log +FNDA:0,console2.log +DA:783,0 +FN:786,console2.log +FNDA:0,console2.log +DA:787,0 +FN:790,console2.log +FNDA:0,console2.log +DA:791,0 +FN:794,console2.log +FNDA:0,console2.log +DA:795,0 +FN:798,console2.log +FNDA:0,console2.log +DA:799,0 +FN:802,console2.log +FNDA:0,console2.log +DA:803,0 +FN:806,console2.log +FNDA:0,console2.log +DA:807,0 +FN:810,console2.log +FNDA:0,console2.log +DA:811,0 +FN:814,console2.log +FNDA:0,console2.log +DA:815,0 +FN:818,console2.log +FNDA:0,console2.log +DA:819,0 +FN:822,console2.log +FNDA:0,console2.log +DA:823,0 +FN:826,console2.log +FNDA:0,console2.log +DA:827,0 +FN:830,console2.log +FNDA:0,console2.log +DA:831,0 +FN:834,console2.log +FNDA:0,console2.log +DA:835,0 +FN:838,console2.log +FNDA:0,console2.log +DA:839,0 +FN:842,console2.log +FNDA:0,console2.log +DA:843,0 +FN:846,console2.log +FNDA:0,console2.log +DA:847,0 +FN:850,console2.log +FNDA:0,console2.log +DA:851,0 +FN:854,console2.log +FNDA:0,console2.log +DA:855,0 +FN:858,console2.log +FNDA:0,console2.log +DA:859,0 +FN:862,console2.log +FNDA:0,console2.log +DA:863,0 +FN:866,console2.log +FNDA:0,console2.log +DA:867,0 +FN:870,console2.log +FNDA:0,console2.log +DA:871,0 +FN:874,console2.log +FNDA:0,console2.log +DA:875,0 +FN:878,console2.log +FNDA:0,console2.log +DA:879,0 +FN:882,console2.log +FNDA:0,console2.log +DA:883,0 +FN:886,console2.log +FNDA:0,console2.log +DA:887,0 +FN:890,console2.log +FNDA:0,console2.log +DA:891,0 +FN:894,console2.log +FNDA:0,console2.log +DA:895,0 +FN:898,console2.log +FNDA:0,console2.log +DA:899,0 +FN:902,console2.log +FNDA:0,console2.log +DA:903,0 +FN:906,console2.log +FNDA:0,console2.log +DA:907,0 +FN:910,console2.log +FNDA:0,console2.log +DA:911,0 +FN:914,console2.log +FNDA:0,console2.log +DA:915,0 +FN:918,console2.log +FNDA:0,console2.log +DA:919,0 +FN:922,console2.log +FNDA:0,console2.log +DA:923,0 +FN:926,console2.log +FNDA:0,console2.log +DA:927,0 +FN:930,console2.log +FNDA:0,console2.log +DA:931,0 +FN:934,console2.log +FNDA:0,console2.log +DA:935,0 +FN:938,console2.log +FNDA:0,console2.log +DA:939,0 +FN:942,console2.log +FNDA:0,console2.log +DA:943,0 +FN:946,console2.log +FNDA:0,console2.log +DA:947,0 +FN:950,console2.log +FNDA:0,console2.log +DA:951,0 +FN:954,console2.log +FNDA:0,console2.log +DA:955,0 +FN:958,console2.log +FNDA:0,console2.log +DA:959,0 +FN:962,console2.log +FNDA:0,console2.log +DA:963,0 +FN:966,console2.log +FNDA:0,console2.log +DA:967,0 +FN:970,console2.log +FNDA:0,console2.log +DA:971,0 +FN:974,console2.log +FNDA:0,console2.log +DA:975,0 +FN:978,console2.log +FNDA:0,console2.log +DA:979,0 +FN:982,console2.log +FNDA:0,console2.log +DA:983,0 +FN:986,console2.log +FNDA:0,console2.log +DA:987,0 +FN:990,console2.log +FNDA:0,console2.log +DA:991,0 +FN:994,console2.log +FNDA:0,console2.log +DA:995,0 +FN:998,console2.log +FNDA:0,console2.log +DA:999,0 +FN:1002,console2.log +FNDA:0,console2.log +DA:1003,0 +FN:1006,console2.log +FNDA:0,console2.log +DA:1007,0 +FN:1010,console2.log +FNDA:0,console2.log +DA:1011,0 +FN:1014,console2.log +FNDA:0,console2.log +DA:1015,0 +FN:1018,console2.log +FNDA:0,console2.log +DA:1019,0 +FN:1022,console2.log +FNDA:0,console2.log +DA:1023,0 +FN:1026,console2.log +FNDA:0,console2.log +DA:1027,0 +FN:1030,console2.log +FNDA:0,console2.log +DA:1031,0 +FN:1034,console2.log +FNDA:0,console2.log +DA:1035,0 +FN:1038,console2.log +FNDA:0,console2.log +DA:1039,0 +FN:1042,console2.log +FNDA:0,console2.log +DA:1043,0 +FN:1046,console2.log +FNDA:0,console2.log +DA:1047,0 +FN:1050,console2.log +FNDA:0,console2.log +DA:1051,0 +FN:1054,console2.log +FNDA:0,console2.log +DA:1055,0 +FN:1058,console2.log +FNDA:0,console2.log +DA:1059,0 +FN:1062,console2.log +FNDA:0,console2.log +DA:1063,0 +FN:1066,console2.log +FNDA:0,console2.log +DA:1067,0 +FN:1070,console2.log +FNDA:0,console2.log +DA:1071,0 +FN:1074,console2.log +FNDA:0,console2.log +DA:1075,0 +FN:1078,console2.log +FNDA:0,console2.log +DA:1079,0 +FN:1082,console2.log +FNDA:0,console2.log +DA:1083,0 +FN:1086,console2.log +FNDA:0,console2.log +DA:1087,0 +FN:1090,console2.log +FNDA:0,console2.log +DA:1091,0 +FN:1094,console2.log +FNDA:0,console2.log +DA:1095,0 +FN:1098,console2.log +FNDA:0,console2.log +DA:1099,0 +FN:1102,console2.log +FNDA:0,console2.log +DA:1103,0 +FN:1106,console2.log +FNDA:0,console2.log +DA:1107,0 +FN:1110,console2.log +FNDA:0,console2.log +DA:1111,0 +FN:1114,console2.log +FNDA:0,console2.log +DA:1115,0 +FN:1118,console2.log +FNDA:0,console2.log +DA:1119,0 +FN:1122,console2.log +FNDA:0,console2.log +DA:1123,0 +FN:1126,console2.log +FNDA:0,console2.log +DA:1127,0 +FN:1130,console2.log +FNDA:0,console2.log +DA:1131,0 +FN:1134,console2.log +FNDA:0,console2.log +DA:1135,0 +FN:1138,console2.log +FNDA:0,console2.log +DA:1139,0 +FN:1142,console2.log +FNDA:0,console2.log +DA:1143,0 +FN:1146,console2.log +FNDA:0,console2.log +DA:1147,0 +FN:1150,console2.log +FNDA:0,console2.log +DA:1151,0 +FN:1154,console2.log +FNDA:0,console2.log +DA:1155,0 +FN:1158,console2.log +FNDA:0,console2.log +DA:1159,0 +FN:1162,console2.log +FNDA:0,console2.log +DA:1163,0 +FN:1166,console2.log +FNDA:0,console2.log +DA:1167,0 +FN:1170,console2.log +FNDA:0,console2.log +DA:1171,0 +FN:1174,console2.log +FNDA:0,console2.log +DA:1175,0 +FN:1178,console2.log +FNDA:0,console2.log +DA:1179,0 +FN:1182,console2.log +FNDA:0,console2.log +DA:1183,0 +FN:1186,console2.log +FNDA:0,console2.log +DA:1187,0 +FN:1190,console2.log +FNDA:0,console2.log +DA:1191,0 +FN:1194,console2.log +FNDA:0,console2.log +DA:1195,0 +FN:1198,console2.log +FNDA:0,console2.log +DA:1199,0 +FN:1202,console2.log +FNDA:0,console2.log +DA:1203,0 +FN:1206,console2.log +FNDA:0,console2.log +DA:1207,0 +FN:1210,console2.log +FNDA:0,console2.log +DA:1211,0 +FN:1214,console2.log +FNDA:0,console2.log +DA:1215,0 +FN:1218,console2.log +FNDA:0,console2.log +DA:1219,0 +FN:1222,console2.log +FNDA:0,console2.log +DA:1223,0 +FN:1226,console2.log +FNDA:0,console2.log +DA:1227,0 +FN:1230,console2.log +FNDA:0,console2.log +DA:1231,0 +FN:1234,console2.log +FNDA:0,console2.log +DA:1235,0 +FN:1238,console2.log +FNDA:0,console2.log +DA:1239,0 +FN:1242,console2.log +FNDA:0,console2.log +DA:1243,0 +FN:1246,console2.log +FNDA:0,console2.log +DA:1247,0 +FN:1250,console2.log +FNDA:0,console2.log +DA:1251,0 +FN:1254,console2.log +FNDA:0,console2.log +DA:1255,0 +FN:1258,console2.log +FNDA:0,console2.log +DA:1259,0 +FN:1262,console2.log +FNDA:0,console2.log +DA:1263,0 +FN:1266,console2.log +FNDA:0,console2.log +DA:1267,0 +FN:1270,console2.log +FNDA:0,console2.log +DA:1271,0 +FN:1274,console2.log +FNDA:0,console2.log +DA:1275,0 +FN:1278,console2.log +FNDA:0,console2.log +DA:1279,0 +FN:1282,console2.log +FNDA:0,console2.log +DA:1283,0 +FN:1286,console2.log +FNDA:0,console2.log +DA:1287,0 +FN:1290,console2.log +FNDA:0,console2.log +DA:1291,0 +FN:1294,console2.log +FNDA:0,console2.log +DA:1295,0 +FN:1298,console2.log +FNDA:0,console2.log +DA:1299,0 +FN:1302,console2.log +FNDA:0,console2.log +DA:1303,0 +FN:1306,console2.log +FNDA:0,console2.log +DA:1307,0 +FN:1310,console2.log +FNDA:0,console2.log +DA:1311,0 +FN:1314,console2.log +FNDA:0,console2.log +DA:1315,0 +FN:1318,console2.log +FNDA:0,console2.log +DA:1319,0 +FN:1322,console2.log +FNDA:0,console2.log +DA:1323,0 +FN:1326,console2.log +FNDA:0,console2.log +DA:1327,0 +FN:1330,console2.log +FNDA:0,console2.log +DA:1331,0 +FN:1334,console2.log +FNDA:0,console2.log +DA:1335,0 +FN:1338,console2.log +FNDA:0,console2.log +DA:1339,0 +FN:1342,console2.log +FNDA:0,console2.log +DA:1343,0 +FN:1346,console2.log +FNDA:0,console2.log +DA:1347,0 +FN:1350,console2.log +FNDA:0,console2.log +DA:1351,0 +FN:1354,console2.log +FNDA:0,console2.log +DA:1355,0 +FN:1358,console2.log +FNDA:0,console2.log +DA:1359,0 +FN:1362,console2.log +FNDA:0,console2.log +DA:1363,0 +FN:1366,console2.log +FNDA:0,console2.log +DA:1367,0 +FN:1370,console2.log +FNDA:0,console2.log +DA:1371,0 +FN:1374,console2.log +FNDA:0,console2.log +DA:1375,0 +FN:1378,console2.log +FNDA:0,console2.log +DA:1379,0 +FN:1382,console2.log +FNDA:0,console2.log +DA:1383,0 +FN:1386,console2.log +FNDA:0,console2.log +DA:1387,0 +FN:1390,console2.log +FNDA:0,console2.log +DA:1391,0 +FN:1394,console2.log +FNDA:0,console2.log +DA:1395,0 +FN:1398,console2.log +FNDA:0,console2.log +DA:1399,0 +FN:1402,console2.log +FNDA:0,console2.log +DA:1403,0 +FN:1406,console2.log +FNDA:0,console2.log +DA:1407,0 +FN:1410,console2.log +FNDA:0,console2.log +DA:1411,0 +FN:1414,console2.log +FNDA:0,console2.log +DA:1415,0 +FN:1418,console2.log +FNDA:0,console2.log +DA:1419,0 +FN:1422,console2.log +FNDA:0,console2.log +DA:1423,0 +FN:1426,console2.log +FNDA:0,console2.log +DA:1427,0 +FN:1430,console2.log +FNDA:0,console2.log +DA:1431,0 +FN:1434,console2.log +FNDA:0,console2.log +DA:1435,0 +FN:1438,console2.log +FNDA:0,console2.log +DA:1439,0 +FN:1442,console2.log +FNDA:0,console2.log +DA:1443,0 +FN:1446,console2.log +FNDA:0,console2.log +DA:1447,0 +FN:1450,console2.log +FNDA:0,console2.log +DA:1451,0 +FN:1454,console2.log +FNDA:0,console2.log +DA:1455,0 +FN:1458,console2.log +FNDA:0,console2.log +DA:1459,0 +FN:1462,console2.log +FNDA:0,console2.log +DA:1463,0 +FN:1466,console2.log +FNDA:0,console2.log +DA:1467,0 +FN:1470,console2.log +FNDA:0,console2.log +DA:1471,0 +FN:1474,console2.log +FNDA:0,console2.log +DA:1475,0 +FN:1478,console2.log +FNDA:0,console2.log +DA:1479,0 +FN:1482,console2.log +FNDA:0,console2.log +DA:1483,0 +FN:1486,console2.log +FNDA:0,console2.log +DA:1487,0 +FN:1490,console2.log +FNDA:0,console2.log +DA:1491,0 +FN:1494,console2.log +FNDA:0,console2.log +DA:1495,0 +FN:1498,console2.log +FNDA:0,console2.log +DA:1499,0 +FN:1502,console2.log +FNDA:0,console2.log +DA:1503,0 +FN:1506,console2.log +FNDA:0,console2.log +DA:1507,0 +FN:1510,console2.log +FNDA:0,console2.log +DA:1511,0 +FN:1514,console2.log +FNDA:0,console2.log +DA:1515,0 +FN:1518,console2.log +FNDA:0,console2.log +DA:1519,0 +FN:1522,console2.log +FNDA:0,console2.log +DA:1523,0 +FN:1526,console2.log +FNDA:0,console2.log +DA:1527,0 +FN:1530,console2.log +FNDA:0,console2.log +DA:1531,0 +FN:1534,console2.log +FNDA:0,console2.log +DA:1535,0 +FN:1538,console2.log +FNDA:0,console2.log +DA:1539,0 +FN:1542,console2.log +FNDA:0,console2.log +DA:1543,0 +FN:1546,console2.log +FNDA:0,console2.log +DA:1547,0 +FN:1550,console2.log +FNDA:0,console2.log +DA:1551,0 +FN:1554,console2.log +FNDA:0,console2.log +DA:1555,0 +FNF:384 +FNH:0 +LF:385 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/mocks/MockERC20.sol +FN:54,MockERC20.initialize +FNDA:0,MockERC20.initialize +DA:55,0 +BRDA:55,0,0,- +BRDA:55,0,1,- +DA:57,0 +DA:58,0 +DA:59,0 +DA:61,0 +DA:62,0 +DA:64,0 +FN:71,MockERC20.approve +FNDA:0,MockERC20.approve +DA:72,0 +DA:74,0 +DA:76,0 +FN:79,MockERC20.transfer +FNDA:0,MockERC20.transfer +DA:80,0 +DA:81,0 +DA:83,0 +DA:85,0 +FN:88,MockERC20.transferFrom +FNDA:0,MockERC20.transferFrom +DA:89,0 +DA:91,0 +BRDA:91,1,0,- +BRDA:91,1,1,- +DA:93,0 +DA:94,0 +DA:96,0 +DA:98,0 +FN:105,MockERC20.permit +FNDA:0,MockERC20.permit +DA:109,0 +BRDA:109,2,0,- +BRDA:109,2,1,- +DA:111,0 +DA:135,0 +BRDA:135,3,0,- +BRDA:135,3,1,- +DA:137,0 +DA:139,0 +FN:142,MockERC20.DOMAIN_SEPARATOR +FNDA:0,MockERC20.DOMAIN_SEPARATOR +DA:143,0 +FN:146,MockERC20.computeDomainSeparator +FNDA:0,MockERC20.computeDomainSeparator +DA:147,0 +FN:162,MockERC20._mint +FNDA:0,MockERC20._mint +DA:163,0 +DA:164,0 +DA:166,0 +FN:169,MockERC20._burn +FNDA:0,MockERC20._burn +DA:170,0 +DA:171,0 +DA:173,0 +FN:180,MockERC20._add +FNDA:0,MockERC20._add +DA:181,0 +DA:182,0 +BRDA:182,4,0,- +BRDA:182,4,1,- +DA:183,0 +FN:186,MockERC20._sub +FNDA:0,MockERC20._sub +DA:187,0 +BRDA:187,5,0,- +BRDA:187,5,1,- +DA:188,0 +FN:199,MockERC20._viewChainId +FNDA:0,MockERC20._viewChainId +DA:202,0 +DA:205,0 +FN:208,MockERC20._pureChainId +FNDA:0,MockERC20._pureChainId +DA:209,0 +DA:210,0 +DA:212,0 +DA:214,0 +FNF:13 +FNH:0 +LF:44 +LH:0 +BRF:12 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/mocks/MockERC721.sol +FN:25,MockERC721.tokenURI +FNDA:0,MockERC721.tokenURI +FN:35,MockERC721.ownerOf +FNDA:0,MockERC721.ownerOf +DA:36,0 +BRDA:36,0,0,- +BRDA:36,0,1,- +FN:39,MockERC721.balanceOf +FNDA:0,MockERC721.balanceOf +DA:40,0 +BRDA:40,1,0,- +BRDA:40,1,1,- +DA:42,0 +FN:62,MockERC721.initialize +FNDA:0,MockERC721.initialize +DA:63,0 +BRDA:63,2,0,- +BRDA:63,2,1,- +DA:65,0 +DA:66,0 +DA:68,0 +FN:75,MockERC721.approve +FNDA:0,MockERC721.approve +DA:76,0 +DA:78,0 +BRDA:78,3,0,- +BRDA:78,3,1,- +DA:80,0 +DA:82,0 +FN:85,MockERC721.setApprovalForAll +FNDA:0,MockERC721.setApprovalForAll +DA:86,0 +DA:88,0 +FN:91,MockERC721.transferFrom +FNDA:0,MockERC721.transferFrom +DA:92,0 +BRDA:92,4,0,- +BRDA:92,4,1,- +DA:94,0 +BRDA:94,5,0,- +BRDA:94,5,1,- +DA:96,0 +BRDA:96,6,0,- +BRDA:96,6,1,- +DA:102,0 +DA:104,0 +DA:106,0 +DA:108,0 +DA:110,0 +FN:113,MockERC721.safeTransferFrom +FNDA:0,MockERC721.safeTransferFrom +DA:114,0 +DA:116,0 +BRDA:116,7,0,- +BRDA:116,7,1,- +FN:124,MockERC721.safeTransferFrom +FNDA:0,MockERC721.safeTransferFrom +DA:125,0 +DA:127,0 +BRDA:127,8,0,- +BRDA:127,8,1,- +FN:139,MockERC721.supportsInterface +FNDA:0,MockERC721.supportsInterface +DA:140,0 +DA:141,0 +DA:142,0 +FN:149,MockERC721._mint +FNDA:0,MockERC721._mint +DA:150,0 +BRDA:150,9,0,- +BRDA:150,9,1,- +DA:152,0 +BRDA:152,10,0,- +BRDA:152,10,1,- +DA:156,0 +DA:158,0 +DA:160,0 +FN:163,MockERC721._burn +FNDA:0,MockERC721._burn +DA:164,0 +DA:166,0 +BRDA:166,11,0,- +BRDA:166,11,1,- +DA:168,0 +DA:170,0 +DA:172,0 +DA:174,0 +FN:181,MockERC721._safeMint +FNDA:0,MockERC721._safeMint +DA:182,0 +DA:184,0 +BRDA:184,12,0,- +BRDA:184,12,1,- +FN:192,MockERC721._safeMint +FNDA:0,MockERC721._safeMint +DA:193,0 +DA:195,0 +BRDA:195,13,0,- +BRDA:195,13,1,- +FN:207,MockERC721._isContract +FNDA:0,MockERC721._isContract +DA:208,0 +DA:212,0 +DA:215,0 +FNF:15 +FNH:0 +LF:46 +LH:0 +BRF:28 +BRH:0 +end_of_record +TN: +SF:node_modules/forge-std/src/safeconsole.sol +FN:11,safeconsole._sendLogPayload +FNDA:0,safeconsole._sendLogPayload +DA:12,0 +DA:13,0 +DA:15,0 +DA:17,0 +FN:20,safeconsole._sendLogPayloadView +FNDA:0,safeconsole._sendLogPayloadView +FN:26,safeconsole._memcopy +FNDA:0,safeconsole._memcopy +DA:27,0 +DA:28,0 +DA:30,0 +DA:32,0 +FN:35,safeconsole._memcopyView +FNDA:0,safeconsole._memcopyView +FN:41,safeconsole.logMemory +FNDA:0,safeconsole.logMemory +DA:42,0 +BRDA:42,0,0,- +BRDA:42,0,1,- +DA:44,0 +DA:45,0 +DA:46,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:56,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:73,0 +DA:80,0 +DA:81,0 +FN:90,safeconsole.log +FNDA:0,safeconsole.log +DA:91,0 +DA:92,0 +DA:94,0 +DA:95,0 +DA:100,0 +FN:107,safeconsole.log +FNDA:0,safeconsole.log +DA:108,0 +DA:109,0 +DA:111,0 +DA:112,0 +DA:117,0 +FN:124,safeconsole.log +FNDA:0,safeconsole.log +DA:125,0 +DA:126,0 +DA:128,0 +DA:129,0 +DA:134,0 +FN:141,safeconsole.log +FNDA:0,safeconsole.log +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:154,0 +DA:155,0 +DA:156,0 +DA:157,0 +DA:163,0 +FN:172,safeconsole.log +FNDA:0,safeconsole.log +DA:173,0 +DA:174,0 +DA:175,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:185,0 +FN:193,safeconsole.log +FNDA:0,safeconsole.log +DA:194,0 +DA:195,0 +DA:196,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:206,0 +FN:214,safeconsole.log +FNDA:0,safeconsole.log +DA:215,0 +DA:216,0 +DA:217,0 +DA:219,0 +DA:220,0 +DA:221,0 +DA:227,0 +FN:235,safeconsole.log +FNDA:0,safeconsole.log +DA:236,0 +DA:237,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:260,0 +FN:270,safeconsole.log +FNDA:0,safeconsole.log +DA:271,0 +DA:272,0 +DA:273,0 +DA:275,0 +DA:276,0 +DA:277,0 +DA:283,0 +FN:291,safeconsole.log +FNDA:0,safeconsole.log +DA:292,0 +DA:293,0 +DA:294,0 +DA:296,0 +DA:297,0 +DA:298,0 +DA:304,0 +FN:312,safeconsole.log +FNDA:0,safeconsole.log +DA:313,0 +DA:314,0 +DA:315,0 +DA:317,0 +DA:318,0 +DA:319,0 +DA:325,0 +FN:333,safeconsole.log +FNDA:0,safeconsole.log +DA:334,0 +DA:335,0 +DA:336,0 +DA:337,0 +DA:338,0 +DA:347,0 +DA:348,0 +DA:349,0 +DA:350,0 +DA:351,0 +DA:358,0 +FN:368,safeconsole.log +FNDA:0,safeconsole.log +DA:369,0 +DA:370,0 +DA:371,0 +DA:373,0 +DA:374,0 +DA:375,0 +DA:381,0 +FN:389,safeconsole.log +FNDA:0,safeconsole.log +DA:390,0 +DA:391,0 +DA:392,0 +DA:394,0 +DA:395,0 +DA:396,0 +DA:402,0 +FN:410,safeconsole.log +FNDA:0,safeconsole.log +DA:411,0 +DA:412,0 +DA:413,0 +DA:415,0 +DA:416,0 +DA:417,0 +DA:423,0 +FN:431,safeconsole.log +FNDA:0,safeconsole.log +DA:432,0 +DA:433,0 +DA:434,0 +DA:435,0 +DA:436,0 +DA:445,0 +DA:446,0 +DA:447,0 +DA:448,0 +DA:449,0 +DA:456,0 +FN:466,safeconsole.log +FNDA:0,safeconsole.log +DA:467,0 +DA:468,0 +DA:469,0 +DA:470,0 +DA:471,0 +DA:480,0 +DA:481,0 +DA:482,0 +DA:483,0 +DA:484,0 +DA:491,0 +FN:501,safeconsole.log +FNDA:0,safeconsole.log +DA:502,0 +DA:503,0 +DA:504,0 +DA:505,0 +DA:506,0 +DA:515,0 +DA:516,0 +DA:517,0 +DA:518,0 +DA:519,0 +DA:526,0 +FN:536,safeconsole.log +FNDA:0,safeconsole.log +DA:537,0 +DA:538,0 +DA:539,0 +DA:540,0 +DA:541,0 +DA:550,0 +DA:551,0 +DA:552,0 +DA:553,0 +DA:554,0 +DA:561,0 +FN:571,safeconsole.log +FNDA:0,safeconsole.log +DA:572,0 +DA:573,0 +DA:574,0 +DA:575,0 +DA:576,0 +DA:577,0 +DA:578,0 +DA:587,0 +DA:588,0 +DA:589,0 +DA:590,0 +DA:591,0 +DA:592,0 +DA:593,0 +DA:601,0 +FN:613,safeconsole.log +FNDA:0,safeconsole.log +DA:614,0 +DA:615,0 +DA:616,0 +DA:617,0 +DA:619,0 +DA:620,0 +DA:621,0 +DA:622,0 +DA:629,0 +FN:638,safeconsole.log +FNDA:0,safeconsole.log +DA:639,0 +DA:640,0 +DA:641,0 +DA:642,0 +DA:644,0 +DA:645,0 +DA:646,0 +DA:647,0 +DA:654,0 +FN:663,safeconsole.log +FNDA:0,safeconsole.log +DA:664,0 +DA:665,0 +DA:666,0 +DA:667,0 +DA:669,0 +DA:670,0 +DA:671,0 +DA:672,0 +DA:679,0 +FN:688,safeconsole.log +FNDA:0,safeconsole.log +DA:689,0 +DA:690,0 +DA:691,0 +DA:692,0 +DA:693,0 +DA:694,0 +DA:703,0 +DA:704,0 +DA:705,0 +DA:706,0 +DA:707,0 +DA:708,0 +DA:716,0 +FN:727,safeconsole.log +FNDA:0,safeconsole.log +DA:728,0 +DA:729,0 +DA:730,0 +DA:731,0 +DA:733,0 +DA:734,0 +DA:735,0 +DA:736,0 +DA:743,0 +FN:752,safeconsole.log +FNDA:0,safeconsole.log +DA:753,0 +DA:754,0 +DA:755,0 +DA:756,0 +DA:758,0 +DA:759,0 +DA:760,0 +DA:761,0 +DA:768,0 +FN:777,safeconsole.log +FNDA:0,safeconsole.log +DA:778,0 +DA:779,0 +DA:780,0 +DA:781,0 +DA:783,0 +DA:784,0 +DA:785,0 +DA:786,0 +DA:793,0 +FN:802,safeconsole.log +FNDA:0,safeconsole.log +DA:803,0 +DA:804,0 +DA:805,0 +DA:806,0 +DA:807,0 +DA:808,0 +DA:817,0 +DA:818,0 +DA:819,0 +DA:820,0 +DA:821,0 +DA:822,0 +DA:830,0 +FN:841,safeconsole.log +FNDA:0,safeconsole.log +DA:842,0 +DA:843,0 +DA:844,0 +DA:845,0 +DA:847,0 +DA:848,0 +DA:849,0 +DA:850,0 +DA:857,0 +FN:866,safeconsole.log +FNDA:0,safeconsole.log +DA:867,0 +DA:868,0 +DA:869,0 +DA:870,0 +DA:872,0 +DA:873,0 +DA:874,0 +DA:875,0 +DA:882,0 +FN:891,safeconsole.log +FNDA:0,safeconsole.log +DA:892,0 +DA:893,0 +DA:894,0 +DA:895,0 +DA:897,0 +DA:898,0 +DA:899,0 +DA:900,0 +DA:907,0 +FN:916,safeconsole.log +FNDA:0,safeconsole.log +DA:917,0 +DA:918,0 +DA:919,0 +DA:920,0 +DA:921,0 +DA:922,0 +DA:931,0 +DA:932,0 +DA:933,0 +DA:934,0 +DA:935,0 +DA:936,0 +DA:944,0 +FN:955,safeconsole.log +FNDA:0,safeconsole.log +DA:956,0 +DA:957,0 +DA:958,0 +DA:959,0 +DA:960,0 +DA:961,0 +DA:970,0 +DA:971,0 +DA:972,0 +DA:973,0 +DA:974,0 +DA:975,0 +DA:983,0 +FN:994,safeconsole.log +FNDA:0,safeconsole.log +DA:995,0 +DA:996,0 +DA:997,0 +DA:998,0 +DA:999,0 +DA:1000,0 +DA:1009,0 +DA:1010,0 +DA:1011,0 +DA:1012,0 +DA:1013,0 +DA:1014,0 +DA:1022,0 +FN:1033,safeconsole.log +FNDA:0,safeconsole.log +DA:1034,0 +DA:1035,0 +DA:1036,0 +DA:1037,0 +DA:1038,0 +DA:1039,0 +DA:1048,0 +DA:1049,0 +DA:1050,0 +DA:1051,0 +DA:1052,0 +DA:1053,0 +DA:1061,0 +FN:1072,safeconsole.log +FNDA:0,safeconsole.log +DA:1073,0 +DA:1074,0 +DA:1075,0 +DA:1076,0 +DA:1077,0 +DA:1078,0 +DA:1079,0 +DA:1080,0 +DA:1089,0 +DA:1090,0 +DA:1091,0 +DA:1092,0 +DA:1093,0 +DA:1094,0 +DA:1095,0 +DA:1096,0 +DA:1105,0 +FN:1118,safeconsole.log +FNDA:0,safeconsole.log +DA:1119,0 +DA:1120,0 +DA:1121,0 +DA:1122,0 +DA:1124,0 +DA:1125,0 +DA:1126,0 +DA:1127,0 +DA:1134,0 +FN:1143,safeconsole.log +FNDA:0,safeconsole.log +DA:1144,0 +DA:1145,0 +DA:1146,0 +DA:1147,0 +DA:1149,0 +DA:1150,0 +DA:1151,0 +DA:1152,0 +DA:1159,0 +FN:1168,safeconsole.log +FNDA:0,safeconsole.log +DA:1169,0 +DA:1170,0 +DA:1171,0 +DA:1172,0 +DA:1174,0 +DA:1175,0 +DA:1176,0 +DA:1177,0 +DA:1184,0 +FN:1193,safeconsole.log +FNDA:0,safeconsole.log +DA:1194,0 +DA:1195,0 +DA:1196,0 +DA:1197,0 +DA:1198,0 +DA:1199,0 +DA:1208,0 +DA:1209,0 +DA:1210,0 +DA:1211,0 +DA:1212,0 +DA:1213,0 +DA:1221,0 +FN:1232,safeconsole.log +FNDA:0,safeconsole.log +DA:1233,0 +DA:1234,0 +DA:1235,0 +DA:1236,0 +DA:1238,0 +DA:1239,0 +DA:1240,0 +DA:1241,0 +DA:1248,0 +FN:1257,safeconsole.log +FNDA:0,safeconsole.log +DA:1258,0 +DA:1259,0 +DA:1260,0 +DA:1261,0 +DA:1263,0 +DA:1264,0 +DA:1265,0 +DA:1266,0 +DA:1273,0 +FN:1282,safeconsole.log +FNDA:0,safeconsole.log +DA:1283,0 +DA:1284,0 +DA:1285,0 +DA:1286,0 +DA:1288,0 +DA:1289,0 +DA:1290,0 +DA:1291,0 +DA:1298,0 +FN:1307,safeconsole.log +FNDA:0,safeconsole.log +DA:1308,0 +DA:1309,0 +DA:1310,0 +DA:1311,0 +DA:1312,0 +DA:1313,0 +DA:1322,0 +DA:1323,0 +DA:1324,0 +DA:1325,0 +DA:1326,0 +DA:1327,0 +DA:1335,0 +FN:1346,safeconsole.log +FNDA:0,safeconsole.log +DA:1347,0 +DA:1348,0 +DA:1349,0 +DA:1350,0 +DA:1352,0 +DA:1353,0 +DA:1354,0 +DA:1355,0 +DA:1362,0 +FN:1371,safeconsole.log +FNDA:0,safeconsole.log +DA:1372,0 +DA:1373,0 +DA:1374,0 +DA:1375,0 +DA:1377,0 +DA:1378,0 +DA:1379,0 +DA:1380,0 +DA:1387,0 +FN:1396,safeconsole.log +FNDA:0,safeconsole.log +DA:1397,0 +DA:1398,0 +DA:1399,0 +DA:1400,0 +DA:1402,0 +DA:1403,0 +DA:1404,0 +DA:1405,0 +DA:1412,0 +FN:1421,safeconsole.log +FNDA:0,safeconsole.log +DA:1422,0 +DA:1423,0 +DA:1424,0 +DA:1425,0 +DA:1426,0 +DA:1427,0 +DA:1436,0 +DA:1437,0 +DA:1438,0 +DA:1439,0 +DA:1440,0 +DA:1441,0 +DA:1449,0 +FN:1460,safeconsole.log +FNDA:0,safeconsole.log +DA:1461,0 +DA:1462,0 +DA:1463,0 +DA:1464,0 +DA:1465,0 +DA:1466,0 +DA:1475,0 +DA:1476,0 +DA:1477,0 +DA:1478,0 +DA:1479,0 +DA:1480,0 +DA:1488,0 +FN:1499,safeconsole.log +FNDA:0,safeconsole.log +DA:1500,0 +DA:1501,0 +DA:1502,0 +DA:1503,0 +DA:1504,0 +DA:1505,0 +DA:1514,0 +DA:1515,0 +DA:1516,0 +DA:1517,0 +DA:1518,0 +DA:1519,0 +DA:1527,0 +FN:1538,safeconsole.log +FNDA:0,safeconsole.log +DA:1539,0 +DA:1540,0 +DA:1541,0 +DA:1542,0 +DA:1543,0 +DA:1544,0 +DA:1553,0 +DA:1554,0 +DA:1555,0 +DA:1556,0 +DA:1557,0 +DA:1558,0 +DA:1566,0 +FN:1577,safeconsole.log +FNDA:0,safeconsole.log +DA:1578,0 +DA:1579,0 +DA:1580,0 +DA:1581,0 +DA:1582,0 +DA:1583,0 +DA:1584,0 +DA:1585,0 +DA:1594,0 +DA:1595,0 +DA:1596,0 +DA:1597,0 +DA:1598,0 +DA:1599,0 +DA:1600,0 +DA:1601,0 +DA:1610,0 +FN:1623,safeconsole.log +FNDA:0,safeconsole.log +DA:1624,0 +DA:1625,0 +DA:1626,0 +DA:1627,0 +DA:1629,0 +DA:1630,0 +DA:1631,0 +DA:1632,0 +DA:1639,0 +FN:1648,safeconsole.log +FNDA:0,safeconsole.log +DA:1649,0 +DA:1650,0 +DA:1651,0 +DA:1652,0 +DA:1654,0 +DA:1655,0 +DA:1656,0 +DA:1657,0 +DA:1664,0 +FN:1673,safeconsole.log +FNDA:0,safeconsole.log +DA:1674,0 +DA:1675,0 +DA:1676,0 +DA:1677,0 +DA:1679,0 +DA:1680,0 +DA:1681,0 +DA:1682,0 +DA:1689,0 +FN:1698,safeconsole.log +FNDA:0,safeconsole.log +DA:1699,0 +DA:1700,0 +DA:1701,0 +DA:1702,0 +DA:1703,0 +DA:1704,0 +DA:1713,0 +DA:1714,0 +DA:1715,0 +DA:1716,0 +DA:1717,0 +DA:1718,0 +DA:1726,0 +FN:1737,safeconsole.log +FNDA:0,safeconsole.log +DA:1738,0 +DA:1739,0 +DA:1740,0 +DA:1741,0 +DA:1743,0 +DA:1744,0 +DA:1745,0 +DA:1746,0 +DA:1753,0 +FN:1762,safeconsole.log +FNDA:0,safeconsole.log +DA:1763,0 +DA:1764,0 +DA:1765,0 +DA:1766,0 +DA:1768,0 +DA:1769,0 +DA:1770,0 +DA:1771,0 +DA:1778,0 +FN:1787,safeconsole.log +FNDA:0,safeconsole.log +DA:1788,0 +DA:1789,0 +DA:1790,0 +DA:1791,0 +DA:1793,0 +DA:1794,0 +DA:1795,0 +DA:1796,0 +DA:1803,0 +FN:1812,safeconsole.log +FNDA:0,safeconsole.log +DA:1813,0 +DA:1814,0 +DA:1815,0 +DA:1816,0 +DA:1817,0 +DA:1818,0 +DA:1827,0 +DA:1828,0 +DA:1829,0 +DA:1830,0 +DA:1831,0 +DA:1832,0 +DA:1840,0 +FN:1851,safeconsole.log +FNDA:0,safeconsole.log +DA:1852,0 +DA:1853,0 +DA:1854,0 +DA:1855,0 +DA:1857,0 +DA:1858,0 +DA:1859,0 +DA:1860,0 +DA:1867,0 +FN:1876,safeconsole.log +FNDA:0,safeconsole.log +DA:1877,0 +DA:1878,0 +DA:1879,0 +DA:1880,0 +DA:1882,0 +DA:1883,0 +DA:1884,0 +DA:1885,0 +DA:1892,0 +FN:1901,safeconsole.log +FNDA:0,safeconsole.log +DA:1902,0 +DA:1903,0 +DA:1904,0 +DA:1905,0 +DA:1907,0 +DA:1908,0 +DA:1909,0 +DA:1910,0 +DA:1917,0 +FN:1926,safeconsole.log +FNDA:0,safeconsole.log +DA:1927,0 +DA:1928,0 +DA:1929,0 +DA:1930,0 +DA:1931,0 +DA:1932,0 +DA:1941,0 +DA:1942,0 +DA:1943,0 +DA:1944,0 +DA:1945,0 +DA:1946,0 +DA:1954,0 +FN:1965,safeconsole.log +FNDA:0,safeconsole.log +DA:1966,0 +DA:1967,0 +DA:1968,0 +DA:1969,0 +DA:1970,0 +DA:1971,0 +DA:1980,0 +DA:1981,0 +DA:1982,0 +DA:1983,0 +DA:1984,0 +DA:1985,0 +DA:1993,0 +FN:2004,safeconsole.log +FNDA:0,safeconsole.log +DA:2005,0 +DA:2006,0 +DA:2007,0 +DA:2008,0 +DA:2009,0 +DA:2010,0 +DA:2019,0 +DA:2020,0 +DA:2021,0 +DA:2022,0 +DA:2023,0 +DA:2024,0 +DA:2032,0 +FN:2043,safeconsole.log +FNDA:0,safeconsole.log +DA:2044,0 +DA:2045,0 +DA:2046,0 +DA:2047,0 +DA:2048,0 +DA:2049,0 +DA:2058,0 +DA:2059,0 +DA:2060,0 +DA:2061,0 +DA:2062,0 +DA:2063,0 +DA:2071,0 +FN:2082,safeconsole.log +FNDA:0,safeconsole.log +DA:2083,0 +DA:2084,0 +DA:2085,0 +DA:2086,0 +DA:2087,0 +DA:2088,0 +DA:2089,0 +DA:2090,0 +DA:2099,0 +DA:2100,0 +DA:2101,0 +DA:2102,0 +DA:2103,0 +DA:2104,0 +DA:2105,0 +DA:2106,0 +DA:2115,0 +FN:2128,safeconsole.log +FNDA:0,safeconsole.log +DA:2129,0 +DA:2130,0 +DA:2131,0 +DA:2132,0 +DA:2133,0 +DA:2134,0 +DA:2143,0 +DA:2144,0 +DA:2145,0 +DA:2146,0 +DA:2147,0 +DA:2148,0 +DA:2156,0 +FN:2167,safeconsole.log +FNDA:0,safeconsole.log +DA:2168,0 +DA:2169,0 +DA:2170,0 +DA:2171,0 +DA:2172,0 +DA:2173,0 +DA:2182,0 +DA:2183,0 +DA:2184,0 +DA:2185,0 +DA:2186,0 +DA:2187,0 +DA:2195,0 +FN:2206,safeconsole.log +FNDA:0,safeconsole.log +DA:2207,0 +DA:2208,0 +DA:2209,0 +DA:2210,0 +DA:2211,0 +DA:2212,0 +DA:2221,0 +DA:2222,0 +DA:2223,0 +DA:2224,0 +DA:2225,0 +DA:2226,0 +DA:2234,0 +FN:2245,safeconsole.log +FNDA:0,safeconsole.log +DA:2246,0 +DA:2247,0 +DA:2248,0 +DA:2249,0 +DA:2250,0 +DA:2251,0 +DA:2252,0 +DA:2253,0 +DA:2262,0 +DA:2263,0 +DA:2264,0 +DA:2265,0 +DA:2266,0 +DA:2267,0 +DA:2268,0 +DA:2269,0 +DA:2278,0 +FN:2291,safeconsole.log +FNDA:0,safeconsole.log +DA:2292,0 +DA:2293,0 +DA:2294,0 +DA:2295,0 +DA:2296,0 +DA:2297,0 +DA:2306,0 +DA:2307,0 +DA:2308,0 +DA:2309,0 +DA:2310,0 +DA:2311,0 +DA:2319,0 +FN:2330,safeconsole.log +FNDA:0,safeconsole.log +DA:2331,0 +DA:2332,0 +DA:2333,0 +DA:2334,0 +DA:2335,0 +DA:2336,0 +DA:2345,0 +DA:2346,0 +DA:2347,0 +DA:2348,0 +DA:2349,0 +DA:2350,0 +DA:2358,0 +FN:2369,safeconsole.log +FNDA:0,safeconsole.log +DA:2370,0 +DA:2371,0 +DA:2372,0 +DA:2373,0 +DA:2374,0 +DA:2375,0 +DA:2384,0 +DA:2385,0 +DA:2386,0 +DA:2387,0 +DA:2388,0 +DA:2389,0 +DA:2397,0 +FN:2408,safeconsole.log +FNDA:0,safeconsole.log +DA:2409,0 +DA:2410,0 +DA:2411,0 +DA:2412,0 +DA:2413,0 +DA:2414,0 +DA:2415,0 +DA:2416,0 +DA:2425,0 +DA:2426,0 +DA:2427,0 +DA:2428,0 +DA:2429,0 +DA:2430,0 +DA:2431,0 +DA:2432,0 +DA:2441,0 +FN:2454,safeconsole.log +FNDA:0,safeconsole.log +DA:2455,0 +DA:2456,0 +DA:2457,0 +DA:2458,0 +DA:2459,0 +DA:2460,0 +DA:2469,0 +DA:2470,0 +DA:2471,0 +DA:2472,0 +DA:2473,0 +DA:2474,0 +DA:2482,0 +FN:2493,safeconsole.log +FNDA:0,safeconsole.log +DA:2494,0 +DA:2495,0 +DA:2496,0 +DA:2497,0 +DA:2498,0 +DA:2499,0 +DA:2508,0 +DA:2509,0 +DA:2510,0 +DA:2511,0 +DA:2512,0 +DA:2513,0 +DA:2521,0 +FN:2532,safeconsole.log +FNDA:0,safeconsole.log +DA:2533,0 +DA:2534,0 +DA:2535,0 +DA:2536,0 +DA:2537,0 +DA:2538,0 +DA:2547,0 +DA:2548,0 +DA:2549,0 +DA:2550,0 +DA:2551,0 +DA:2552,0 +DA:2560,0 +FN:2571,safeconsole.log +FNDA:0,safeconsole.log +DA:2572,0 +DA:2573,0 +DA:2574,0 +DA:2575,0 +DA:2576,0 +DA:2577,0 +DA:2578,0 +DA:2579,0 +DA:2588,0 +DA:2589,0 +DA:2590,0 +DA:2591,0 +DA:2592,0 +DA:2593,0 +DA:2594,0 +DA:2595,0 +DA:2604,0 +FN:2617,safeconsole.log +FNDA:0,safeconsole.log +DA:2618,0 +DA:2619,0 +DA:2620,0 +DA:2621,0 +DA:2622,0 +DA:2623,0 +DA:2624,0 +DA:2625,0 +DA:2634,0 +DA:2635,0 +DA:2636,0 +DA:2637,0 +DA:2638,0 +DA:2639,0 +DA:2640,0 +DA:2641,0 +DA:2650,0 +FN:2663,safeconsole.log +FNDA:0,safeconsole.log +DA:2664,0 +DA:2665,0 +DA:2666,0 +DA:2667,0 +DA:2668,0 +DA:2669,0 +DA:2670,0 +DA:2671,0 +DA:2680,0 +DA:2681,0 +DA:2682,0 +DA:2683,0 +DA:2684,0 +DA:2685,0 +DA:2686,0 +DA:2687,0 +DA:2696,0 +FN:2709,safeconsole.log +FNDA:0,safeconsole.log +DA:2710,0 +DA:2711,0 +DA:2712,0 +DA:2713,0 +DA:2714,0 +DA:2715,0 +DA:2716,0 +DA:2717,0 +DA:2726,0 +DA:2727,0 +DA:2728,0 +DA:2729,0 +DA:2730,0 +DA:2731,0 +DA:2732,0 +DA:2733,0 +DA:2742,0 +FN:2755,safeconsole.log +FNDA:0,safeconsole.log +DA:2756,0 +DA:2757,0 +DA:2758,0 +DA:2759,0 +DA:2760,0 +DA:2761,0 +DA:2762,0 +DA:2763,0 +DA:2764,0 +DA:2765,0 +DA:2774,0 +DA:2775,0 +DA:2776,0 +DA:2777,0 +DA:2778,0 +DA:2779,0 +DA:2780,0 +DA:2781,0 +DA:2782,0 +DA:2783,0 +DA:2793,0 +FN:2808,safeconsole.log +FNDA:0,safeconsole.log +DA:2809,0 +DA:2810,0 +DA:2811,0 +DA:2812,0 +DA:2813,0 +DA:2815,0 +DA:2816,0 +DA:2817,0 +DA:2818,0 +DA:2819,0 +DA:2827,0 +FN:2837,safeconsole.log +FNDA:0,safeconsole.log +DA:2838,0 +DA:2839,0 +DA:2840,0 +DA:2841,0 +DA:2842,0 +DA:2844,0 +DA:2845,0 +DA:2846,0 +DA:2847,0 +DA:2848,0 +DA:2856,0 +FN:2866,safeconsole.log +FNDA:0,safeconsole.log +DA:2867,0 +DA:2868,0 +DA:2869,0 +DA:2870,0 +DA:2871,0 +DA:2873,0 +DA:2874,0 +DA:2875,0 +DA:2876,0 +DA:2877,0 +DA:2885,0 +FN:2895,safeconsole.log +FNDA:0,safeconsole.log +DA:2896,0 +DA:2897,0 +DA:2898,0 +DA:2899,0 +DA:2900,0 +DA:2901,0 +DA:2902,0 +DA:2911,0 +DA:2912,0 +DA:2913,0 +DA:2914,0 +DA:2915,0 +DA:2916,0 +DA:2917,0 +DA:2926,0 +FN:2938,safeconsole.log +FNDA:0,safeconsole.log +DA:2939,0 +DA:2940,0 +DA:2941,0 +DA:2942,0 +DA:2943,0 +DA:2945,0 +DA:2946,0 +DA:2947,0 +DA:2948,0 +DA:2949,0 +DA:2957,0 +FN:2967,safeconsole.log +FNDA:0,safeconsole.log +DA:2968,0 +DA:2969,0 +DA:2970,0 +DA:2971,0 +DA:2972,0 +DA:2974,0 +DA:2975,0 +DA:2976,0 +DA:2977,0 +DA:2978,0 +DA:2986,0 +FN:2996,safeconsole.log +FNDA:0,safeconsole.log +DA:2997,0 +DA:2998,0 +DA:2999,0 +DA:3000,0 +DA:3001,0 +DA:3003,0 +DA:3004,0 +DA:3005,0 +DA:3006,0 +DA:3007,0 +DA:3015,0 +FN:3025,safeconsole.log +FNDA:0,safeconsole.log +DA:3026,0 +DA:3027,0 +DA:3028,0 +DA:3029,0 +DA:3030,0 +DA:3031,0 +DA:3032,0 +DA:3041,0 +DA:3042,0 +DA:3043,0 +DA:3044,0 +DA:3045,0 +DA:3046,0 +DA:3047,0 +DA:3056,0 +FN:3068,safeconsole.log +FNDA:0,safeconsole.log +DA:3069,0 +DA:3070,0 +DA:3071,0 +DA:3072,0 +DA:3073,0 +DA:3075,0 +DA:3076,0 +DA:3077,0 +DA:3078,0 +DA:3079,0 +DA:3087,0 +FN:3097,safeconsole.log +FNDA:0,safeconsole.log +DA:3098,0 +DA:3099,0 +DA:3100,0 +DA:3101,0 +DA:3102,0 +DA:3104,0 +DA:3105,0 +DA:3106,0 +DA:3107,0 +DA:3108,0 +DA:3116,0 +FN:3126,safeconsole.log +FNDA:0,safeconsole.log +DA:3127,0 +DA:3128,0 +DA:3129,0 +DA:3130,0 +DA:3131,0 +DA:3133,0 +DA:3134,0 +DA:3135,0 +DA:3136,0 +DA:3137,0 +DA:3145,0 +FN:3155,safeconsole.log +FNDA:0,safeconsole.log +DA:3156,0 +DA:3157,0 +DA:3158,0 +DA:3159,0 +DA:3160,0 +DA:3161,0 +DA:3162,0 +DA:3171,0 +DA:3172,0 +DA:3173,0 +DA:3174,0 +DA:3175,0 +DA:3176,0 +DA:3177,0 +DA:3186,0 +FN:3198,safeconsole.log +FNDA:0,safeconsole.log +DA:3199,0 +DA:3200,0 +DA:3201,0 +DA:3202,0 +DA:3203,0 +DA:3204,0 +DA:3205,0 +DA:3214,0 +DA:3215,0 +DA:3216,0 +DA:3217,0 +DA:3218,0 +DA:3219,0 +DA:3220,0 +DA:3229,0 +FN:3241,safeconsole.log +FNDA:0,safeconsole.log +DA:3242,0 +DA:3243,0 +DA:3244,0 +DA:3245,0 +DA:3246,0 +DA:3247,0 +DA:3248,0 +DA:3257,0 +DA:3258,0 +DA:3259,0 +DA:3260,0 +DA:3261,0 +DA:3262,0 +DA:3263,0 +DA:3272,0 +FN:3284,safeconsole.log +FNDA:0,safeconsole.log +DA:3285,0 +DA:3286,0 +DA:3287,0 +DA:3288,0 +DA:3289,0 +DA:3290,0 +DA:3291,0 +DA:3300,0 +DA:3301,0 +DA:3302,0 +DA:3303,0 +DA:3304,0 +DA:3305,0 +DA:3306,0 +DA:3315,0 +FN:3327,safeconsole.log +FNDA:0,safeconsole.log +DA:3328,0 +DA:3329,0 +DA:3330,0 +DA:3331,0 +DA:3332,0 +DA:3333,0 +DA:3334,0 +DA:3335,0 +DA:3336,0 +DA:3345,0 +DA:3346,0 +DA:3347,0 +DA:3348,0 +DA:3349,0 +DA:3350,0 +DA:3351,0 +DA:3352,0 +DA:3353,0 +DA:3363,0 +FN:3377,safeconsole.log +FNDA:0,safeconsole.log +DA:3378,0 +DA:3379,0 +DA:3380,0 +DA:3381,0 +DA:3382,0 +DA:3384,0 +DA:3385,0 +DA:3386,0 +DA:3387,0 +DA:3388,0 +DA:3396,0 +FN:3406,safeconsole.log +FNDA:0,safeconsole.log +DA:3407,0 +DA:3408,0 +DA:3409,0 +DA:3410,0 +DA:3411,0 +DA:3413,0 +DA:3414,0 +DA:3415,0 +DA:3416,0 +DA:3417,0 +DA:3425,0 +FN:3435,safeconsole.log +FNDA:0,safeconsole.log +DA:3436,0 +DA:3437,0 +DA:3438,0 +DA:3439,0 +DA:3440,0 +DA:3442,0 +DA:3443,0 +DA:3444,0 +DA:3445,0 +DA:3446,0 +DA:3454,0 +FN:3464,safeconsole.log +FNDA:0,safeconsole.log +DA:3465,0 +DA:3466,0 +DA:3467,0 +DA:3468,0 +DA:3469,0 +DA:3470,0 +DA:3471,0 +DA:3480,0 +DA:3481,0 +DA:3482,0 +DA:3483,0 +DA:3484,0 +DA:3485,0 +DA:3486,0 +DA:3495,0 +FN:3507,safeconsole.log +FNDA:0,safeconsole.log +DA:3508,0 +DA:3509,0 +DA:3510,0 +DA:3511,0 +DA:3512,0 +DA:3514,0 +DA:3515,0 +DA:3516,0 +DA:3517,0 +DA:3518,0 +DA:3526,0 +FN:3536,safeconsole.log +FNDA:0,safeconsole.log +DA:3537,0 +DA:3538,0 +DA:3539,0 +DA:3540,0 +DA:3541,0 +DA:3543,0 +DA:3544,0 +DA:3545,0 +DA:3546,0 +DA:3547,0 +DA:3555,0 +FN:3565,safeconsole.log +FNDA:0,safeconsole.log +DA:3566,0 +DA:3567,0 +DA:3568,0 +DA:3569,0 +DA:3570,0 +DA:3572,0 +DA:3573,0 +DA:3574,0 +DA:3575,0 +DA:3576,0 +DA:3584,0 +FN:3594,safeconsole.log +FNDA:0,safeconsole.log +DA:3595,0 +DA:3596,0 +DA:3597,0 +DA:3598,0 +DA:3599,0 +DA:3600,0 +DA:3601,0 +DA:3610,0 +DA:3611,0 +DA:3612,0 +DA:3613,0 +DA:3614,0 +DA:3615,0 +DA:3616,0 +DA:3625,0 +FN:3637,safeconsole.log +FNDA:0,safeconsole.log +DA:3638,0 +DA:3639,0 +DA:3640,0 +DA:3641,0 +DA:3642,0 +DA:3644,0 +DA:3645,0 +DA:3646,0 +DA:3647,0 +DA:3648,0 +DA:3656,0 +FN:3666,safeconsole.log +FNDA:0,safeconsole.log +DA:3667,0 +DA:3668,0 +DA:3669,0 +DA:3670,0 +DA:3671,0 +DA:3673,0 +DA:3674,0 +DA:3675,0 +DA:3676,0 +DA:3677,0 +DA:3685,0 +FN:3695,safeconsole.log +FNDA:0,safeconsole.log +DA:3696,0 +DA:3697,0 +DA:3698,0 +DA:3699,0 +DA:3700,0 +DA:3702,0 +DA:3703,0 +DA:3704,0 +DA:3705,0 +DA:3706,0 +DA:3714,0 +FN:3724,safeconsole.log +FNDA:0,safeconsole.log +DA:3725,0 +DA:3726,0 +DA:3727,0 +DA:3728,0 +DA:3729,0 +DA:3730,0 +DA:3731,0 +DA:3740,0 +DA:3741,0 +DA:3742,0 +DA:3743,0 +DA:3744,0 +DA:3745,0 +DA:3746,0 +DA:3755,0 +FN:3767,safeconsole.log +FNDA:0,safeconsole.log +DA:3768,0 +DA:3769,0 +DA:3770,0 +DA:3771,0 +DA:3772,0 +DA:3773,0 +DA:3774,0 +DA:3783,0 +DA:3784,0 +DA:3785,0 +DA:3786,0 +DA:3787,0 +DA:3788,0 +DA:3789,0 +DA:3798,0 +FN:3810,safeconsole.log +FNDA:0,safeconsole.log +DA:3811,0 +DA:3812,0 +DA:3813,0 +DA:3814,0 +DA:3815,0 +DA:3816,0 +DA:3817,0 +DA:3826,0 +DA:3827,0 +DA:3828,0 +DA:3829,0 +DA:3830,0 +DA:3831,0 +DA:3832,0 +DA:3841,0 +FN:3853,safeconsole.log +FNDA:0,safeconsole.log +DA:3854,0 +DA:3855,0 +DA:3856,0 +DA:3857,0 +DA:3858,0 +DA:3859,0 +DA:3860,0 +DA:3869,0 +DA:3870,0 +DA:3871,0 +DA:3872,0 +DA:3873,0 +DA:3874,0 +DA:3875,0 +DA:3884,0 +FN:3896,safeconsole.log +FNDA:0,safeconsole.log +DA:3897,0 +DA:3898,0 +DA:3899,0 +DA:3900,0 +DA:3901,0 +DA:3902,0 +DA:3903,0 +DA:3904,0 +DA:3905,0 +DA:3914,0 +DA:3915,0 +DA:3916,0 +DA:3917,0 +DA:3918,0 +DA:3919,0 +DA:3920,0 +DA:3921,0 +DA:3922,0 +DA:3932,0 +FN:3946,safeconsole.log +FNDA:0,safeconsole.log +DA:3947,0 +DA:3948,0 +DA:3949,0 +DA:3950,0 +DA:3951,0 +DA:3953,0 +DA:3954,0 +DA:3955,0 +DA:3956,0 +DA:3957,0 +DA:3965,0 +FN:3975,safeconsole.log +FNDA:0,safeconsole.log +DA:3976,0 +DA:3977,0 +DA:3978,0 +DA:3979,0 +DA:3980,0 +DA:3982,0 +DA:3983,0 +DA:3984,0 +DA:3985,0 +DA:3986,0 +DA:3994,0 +FN:4004,safeconsole.log +FNDA:0,safeconsole.log +DA:4005,0 +DA:4006,0 +DA:4007,0 +DA:4008,0 +DA:4009,0 +DA:4011,0 +DA:4012,0 +DA:4013,0 +DA:4014,0 +DA:4015,0 +DA:4023,0 +FN:4033,safeconsole.log +FNDA:0,safeconsole.log +DA:4034,0 +DA:4035,0 +DA:4036,0 +DA:4037,0 +DA:4038,0 +DA:4039,0 +DA:4040,0 +DA:4049,0 +DA:4050,0 +DA:4051,0 +DA:4052,0 +DA:4053,0 +DA:4054,0 +DA:4055,0 +DA:4064,0 +FN:4076,safeconsole.log +FNDA:0,safeconsole.log +DA:4077,0 +DA:4078,0 +DA:4079,0 +DA:4080,0 +DA:4081,0 +DA:4083,0 +DA:4084,0 +DA:4085,0 +DA:4086,0 +DA:4087,0 +DA:4095,0 +FN:4105,safeconsole.log +FNDA:0,safeconsole.log +DA:4106,0 +DA:4107,0 +DA:4108,0 +DA:4109,0 +DA:4110,0 +DA:4112,0 +DA:4113,0 +DA:4114,0 +DA:4115,0 +DA:4116,0 +DA:4124,0 +FN:4134,safeconsole.log +FNDA:0,safeconsole.log +DA:4135,0 +DA:4136,0 +DA:4137,0 +DA:4138,0 +DA:4139,0 +DA:4141,0 +DA:4142,0 +DA:4143,0 +DA:4144,0 +DA:4145,0 +DA:4153,0 +FN:4163,safeconsole.log +FNDA:0,safeconsole.log +DA:4164,0 +DA:4165,0 +DA:4166,0 +DA:4167,0 +DA:4168,0 +DA:4169,0 +DA:4170,0 +DA:4179,0 +DA:4180,0 +DA:4181,0 +DA:4182,0 +DA:4183,0 +DA:4184,0 +DA:4185,0 +DA:4194,0 +FN:4206,safeconsole.log +FNDA:0,safeconsole.log +DA:4207,0 +DA:4208,0 +DA:4209,0 +DA:4210,0 +DA:4211,0 +DA:4213,0 +DA:4214,0 +DA:4215,0 +DA:4216,0 +DA:4217,0 +DA:4225,0 +FN:4235,safeconsole.log +FNDA:0,safeconsole.log +DA:4236,0 +DA:4237,0 +DA:4238,0 +DA:4239,0 +DA:4240,0 +DA:4242,0 +DA:4243,0 +DA:4244,0 +DA:4245,0 +DA:4246,0 +DA:4254,0 +FN:4264,safeconsole.log +FNDA:0,safeconsole.log +DA:4265,0 +DA:4266,0 +DA:4267,0 +DA:4268,0 +DA:4269,0 +DA:4271,0 +DA:4272,0 +DA:4273,0 +DA:4274,0 +DA:4275,0 +DA:4283,0 +FN:4293,safeconsole.log +FNDA:0,safeconsole.log +DA:4294,0 +DA:4295,0 +DA:4296,0 +DA:4297,0 +DA:4298,0 +DA:4299,0 +DA:4300,0 +DA:4309,0 +DA:4310,0 +DA:4311,0 +DA:4312,0 +DA:4313,0 +DA:4314,0 +DA:4315,0 +DA:4324,0 +FN:4336,safeconsole.log +FNDA:0,safeconsole.log +DA:4337,0 +DA:4338,0 +DA:4339,0 +DA:4340,0 +DA:4341,0 +DA:4342,0 +DA:4343,0 +DA:4352,0 +DA:4353,0 +DA:4354,0 +DA:4355,0 +DA:4356,0 +DA:4357,0 +DA:4358,0 +DA:4367,0 +FN:4379,safeconsole.log +FNDA:0,safeconsole.log +DA:4380,0 +DA:4381,0 +DA:4382,0 +DA:4383,0 +DA:4384,0 +DA:4385,0 +DA:4386,0 +DA:4395,0 +DA:4396,0 +DA:4397,0 +DA:4398,0 +DA:4399,0 +DA:4400,0 +DA:4401,0 +DA:4410,0 +FN:4422,safeconsole.log +FNDA:0,safeconsole.log +DA:4423,0 +DA:4424,0 +DA:4425,0 +DA:4426,0 +DA:4427,0 +DA:4428,0 +DA:4429,0 +DA:4438,0 +DA:4439,0 +DA:4440,0 +DA:4441,0 +DA:4442,0 +DA:4443,0 +DA:4444,0 +DA:4453,0 +FN:4465,safeconsole.log +FNDA:0,safeconsole.log +DA:4466,0 +DA:4467,0 +DA:4468,0 +DA:4469,0 +DA:4470,0 +DA:4471,0 +DA:4472,0 +DA:4473,0 +DA:4474,0 +DA:4483,0 +DA:4484,0 +DA:4485,0 +DA:4486,0 +DA:4487,0 +DA:4488,0 +DA:4489,0 +DA:4490,0 +DA:4491,0 +DA:4501,0 +FN:4515,safeconsole.log +FNDA:0,safeconsole.log +DA:4516,0 +DA:4517,0 +DA:4518,0 +DA:4519,0 +DA:4520,0 +DA:4521,0 +DA:4522,0 +DA:4531,0 +DA:4532,0 +DA:4533,0 +DA:4534,0 +DA:4535,0 +DA:4536,0 +DA:4537,0 +DA:4546,0 +FN:4558,safeconsole.log +FNDA:0,safeconsole.log +DA:4559,0 +DA:4560,0 +DA:4561,0 +DA:4562,0 +DA:4563,0 +DA:4564,0 +DA:4565,0 +DA:4574,0 +DA:4575,0 +DA:4576,0 +DA:4577,0 +DA:4578,0 +DA:4579,0 +DA:4580,0 +DA:4589,0 +FN:4601,safeconsole.log +FNDA:0,safeconsole.log +DA:4602,0 +DA:4603,0 +DA:4604,0 +DA:4605,0 +DA:4606,0 +DA:4607,0 +DA:4608,0 +DA:4617,0 +DA:4618,0 +DA:4619,0 +DA:4620,0 +DA:4621,0 +DA:4622,0 +DA:4623,0 +DA:4632,0 +FN:4644,safeconsole.log +FNDA:0,safeconsole.log +DA:4645,0 +DA:4646,0 +DA:4647,0 +DA:4648,0 +DA:4649,0 +DA:4650,0 +DA:4651,0 +DA:4652,0 +DA:4653,0 +DA:4662,0 +DA:4663,0 +DA:4664,0 +DA:4665,0 +DA:4666,0 +DA:4667,0 +DA:4668,0 +DA:4669,0 +DA:4670,0 +DA:4680,0 +FN:4694,safeconsole.log +FNDA:0,safeconsole.log +DA:4695,0 +DA:4696,0 +DA:4697,0 +DA:4698,0 +DA:4699,0 +DA:4700,0 +DA:4701,0 +DA:4710,0 +DA:4711,0 +DA:4712,0 +DA:4713,0 +DA:4714,0 +DA:4715,0 +DA:4716,0 +DA:4725,0 +FN:4737,safeconsole.log +FNDA:0,safeconsole.log +DA:4738,0 +DA:4739,0 +DA:4740,0 +DA:4741,0 +DA:4742,0 +DA:4743,0 +DA:4744,0 +DA:4753,0 +DA:4754,0 +DA:4755,0 +DA:4756,0 +DA:4757,0 +DA:4758,0 +DA:4759,0 +DA:4768,0 +FN:4780,safeconsole.log +FNDA:0,safeconsole.log +DA:4781,0 +DA:4782,0 +DA:4783,0 +DA:4784,0 +DA:4785,0 +DA:4786,0 +DA:4787,0 +DA:4796,0 +DA:4797,0 +DA:4798,0 +DA:4799,0 +DA:4800,0 +DA:4801,0 +DA:4802,0 +DA:4811,0 +FN:4823,safeconsole.log +FNDA:0,safeconsole.log +DA:4824,0 +DA:4825,0 +DA:4826,0 +DA:4827,0 +DA:4828,0 +DA:4829,0 +DA:4830,0 +DA:4831,0 +DA:4832,0 +DA:4841,0 +DA:4842,0 +DA:4843,0 +DA:4844,0 +DA:4845,0 +DA:4846,0 +DA:4847,0 +DA:4848,0 +DA:4849,0 +DA:4859,0 +FN:4873,safeconsole.log +FNDA:0,safeconsole.log +DA:4874,0 +DA:4875,0 +DA:4876,0 +DA:4877,0 +DA:4878,0 +DA:4879,0 +DA:4880,0 +DA:4889,0 +DA:4890,0 +DA:4891,0 +DA:4892,0 +DA:4893,0 +DA:4894,0 +DA:4895,0 +DA:4904,0 +FN:4916,safeconsole.log +FNDA:0,safeconsole.log +DA:4917,0 +DA:4918,0 +DA:4919,0 +DA:4920,0 +DA:4921,0 +DA:4922,0 +DA:4923,0 +DA:4932,0 +DA:4933,0 +DA:4934,0 +DA:4935,0 +DA:4936,0 +DA:4937,0 +DA:4938,0 +DA:4947,0 +FN:4959,safeconsole.log +FNDA:0,safeconsole.log +DA:4960,0 +DA:4961,0 +DA:4962,0 +DA:4963,0 +DA:4964,0 +DA:4965,0 +DA:4966,0 +DA:4975,0 +DA:4976,0 +DA:4977,0 +DA:4978,0 +DA:4979,0 +DA:4980,0 +DA:4981,0 +DA:4990,0 +FN:5002,safeconsole.log +FNDA:0,safeconsole.log +DA:5003,0 +DA:5004,0 +DA:5005,0 +DA:5006,0 +DA:5007,0 +DA:5008,0 +DA:5009,0 +DA:5010,0 +DA:5011,0 +DA:5020,0 +DA:5021,0 +DA:5022,0 +DA:5023,0 +DA:5024,0 +DA:5025,0 +DA:5026,0 +DA:5027,0 +DA:5028,0 +DA:5038,0 +FN:5052,safeconsole.log +FNDA:0,safeconsole.log +DA:5053,0 +DA:5054,0 +DA:5055,0 +DA:5056,0 +DA:5057,0 +DA:5058,0 +DA:5059,0 +DA:5060,0 +DA:5061,0 +DA:5070,0 +DA:5071,0 +DA:5072,0 +DA:5073,0 +DA:5074,0 +DA:5075,0 +DA:5076,0 +DA:5077,0 +DA:5078,0 +DA:5088,0 +FN:5102,safeconsole.log +FNDA:0,safeconsole.log +DA:5103,0 +DA:5104,0 +DA:5105,0 +DA:5106,0 +DA:5107,0 +DA:5108,0 +DA:5109,0 +DA:5110,0 +DA:5111,0 +DA:5120,0 +DA:5121,0 +DA:5122,0 +DA:5123,0 +DA:5124,0 +DA:5125,0 +DA:5126,0 +DA:5127,0 +DA:5128,0 +DA:5138,0 +FN:5152,safeconsole.log +FNDA:0,safeconsole.log +DA:5153,0 +DA:5154,0 +DA:5155,0 +DA:5156,0 +DA:5157,0 +DA:5158,0 +DA:5159,0 +DA:5160,0 +DA:5161,0 +DA:5170,0 +DA:5171,0 +DA:5172,0 +DA:5173,0 +DA:5174,0 +DA:5175,0 +DA:5176,0 +DA:5177,0 +DA:5178,0 +DA:5188,0 +FN:5202,safeconsole.log +FNDA:0,safeconsole.log +DA:5203,0 +DA:5204,0 +DA:5205,0 +DA:5206,0 +DA:5207,0 +DA:5208,0 +DA:5209,0 +DA:5210,0 +DA:5211,0 +DA:5212,0 +DA:5213,0 +DA:5222,0 +DA:5223,0 +DA:5224,0 +DA:5225,0 +DA:5226,0 +DA:5227,0 +DA:5228,0 +DA:5229,0 +DA:5230,0 +DA:5231,0 +DA:5232,0 +DA:5243,0 +FN:5259,safeconsole.log +FNDA:0,safeconsole.log +DA:5260,0 +DA:5261,0 +DA:5262,0 +DA:5263,0 +DA:5264,0 +DA:5266,0 +DA:5267,0 +DA:5268,0 +DA:5269,0 +DA:5270,0 +DA:5278,0 +FN:5288,safeconsole.log +FNDA:0,safeconsole.log +DA:5289,0 +DA:5290,0 +DA:5291,0 +DA:5292,0 +DA:5293,0 +DA:5295,0 +DA:5296,0 +DA:5297,0 +DA:5298,0 +DA:5299,0 +DA:5307,0 +FN:5317,safeconsole.log +FNDA:0,safeconsole.log +DA:5318,0 +DA:5319,0 +DA:5320,0 +DA:5321,0 +DA:5322,0 +DA:5324,0 +DA:5325,0 +DA:5326,0 +DA:5327,0 +DA:5328,0 +DA:5336,0 +FN:5346,safeconsole.log +FNDA:0,safeconsole.log +DA:5347,0 +DA:5348,0 +DA:5349,0 +DA:5350,0 +DA:5351,0 +DA:5352,0 +DA:5353,0 +DA:5362,0 +DA:5363,0 +DA:5364,0 +DA:5365,0 +DA:5366,0 +DA:5367,0 +DA:5368,0 +DA:5377,0 +FN:5389,safeconsole.log +FNDA:0,safeconsole.log +DA:5390,0 +DA:5391,0 +DA:5392,0 +DA:5393,0 +DA:5394,0 +DA:5396,0 +DA:5397,0 +DA:5398,0 +DA:5399,0 +DA:5400,0 +DA:5408,0 +FN:5418,safeconsole.log +FNDA:0,safeconsole.log +DA:5419,0 +DA:5420,0 +DA:5421,0 +DA:5422,0 +DA:5423,0 +DA:5425,0 +DA:5426,0 +DA:5427,0 +DA:5428,0 +DA:5429,0 +DA:5437,0 +FN:5447,safeconsole.log +FNDA:0,safeconsole.log +DA:5448,0 +DA:5449,0 +DA:5450,0 +DA:5451,0 +DA:5452,0 +DA:5454,0 +DA:5455,0 +DA:5456,0 +DA:5457,0 +DA:5458,0 +DA:5466,0 +FN:5476,safeconsole.log +FNDA:0,safeconsole.log +DA:5477,0 +DA:5478,0 +DA:5479,0 +DA:5480,0 +DA:5481,0 +DA:5482,0 +DA:5483,0 +DA:5492,0 +DA:5493,0 +DA:5494,0 +DA:5495,0 +DA:5496,0 +DA:5497,0 +DA:5498,0 +DA:5507,0 +FN:5519,safeconsole.log +FNDA:0,safeconsole.log +DA:5520,0 +DA:5521,0 +DA:5522,0 +DA:5523,0 +DA:5524,0 +DA:5526,0 +DA:5527,0 +DA:5528,0 +DA:5529,0 +DA:5530,0 +DA:5538,0 +FN:5548,safeconsole.log +FNDA:0,safeconsole.log +DA:5549,0 +DA:5550,0 +DA:5551,0 +DA:5552,0 +DA:5553,0 +DA:5555,0 +DA:5556,0 +DA:5557,0 +DA:5558,0 +DA:5559,0 +DA:5567,0 +FN:5577,safeconsole.log +FNDA:0,safeconsole.log +DA:5578,0 +DA:5579,0 +DA:5580,0 +DA:5581,0 +DA:5582,0 +DA:5584,0 +DA:5585,0 +DA:5586,0 +DA:5587,0 +DA:5588,0 +DA:5596,0 +FN:5606,safeconsole.log +FNDA:0,safeconsole.log +DA:5607,0 +DA:5608,0 +DA:5609,0 +DA:5610,0 +DA:5611,0 +DA:5612,0 +DA:5613,0 +DA:5622,0 +DA:5623,0 +DA:5624,0 +DA:5625,0 +DA:5626,0 +DA:5627,0 +DA:5628,0 +DA:5637,0 +FN:5649,safeconsole.log +FNDA:0,safeconsole.log +DA:5650,0 +DA:5651,0 +DA:5652,0 +DA:5653,0 +DA:5654,0 +DA:5655,0 +DA:5656,0 +DA:5665,0 +DA:5666,0 +DA:5667,0 +DA:5668,0 +DA:5669,0 +DA:5670,0 +DA:5671,0 +DA:5680,0 +FN:5692,safeconsole.log +FNDA:0,safeconsole.log +DA:5693,0 +DA:5694,0 +DA:5695,0 +DA:5696,0 +DA:5697,0 +DA:5698,0 +DA:5699,0 +DA:5708,0 +DA:5709,0 +DA:5710,0 +DA:5711,0 +DA:5712,0 +DA:5713,0 +DA:5714,0 +DA:5723,0 +FN:5735,safeconsole.log +FNDA:0,safeconsole.log +DA:5736,0 +DA:5737,0 +DA:5738,0 +DA:5739,0 +DA:5740,0 +DA:5741,0 +DA:5742,0 +DA:5751,0 +DA:5752,0 +DA:5753,0 +DA:5754,0 +DA:5755,0 +DA:5756,0 +DA:5757,0 +DA:5766,0 +FN:5778,safeconsole.log +FNDA:0,safeconsole.log +DA:5779,0 +DA:5780,0 +DA:5781,0 +DA:5782,0 +DA:5783,0 +DA:5784,0 +DA:5785,0 +DA:5786,0 +DA:5787,0 +DA:5796,0 +DA:5797,0 +DA:5798,0 +DA:5799,0 +DA:5800,0 +DA:5801,0 +DA:5802,0 +DA:5803,0 +DA:5804,0 +DA:5814,0 +FN:5828,safeconsole.log +FNDA:0,safeconsole.log +DA:5829,0 +DA:5830,0 +DA:5831,0 +DA:5832,0 +DA:5833,0 +DA:5835,0 +DA:5836,0 +DA:5837,0 +DA:5838,0 +DA:5839,0 +DA:5847,0 +FN:5857,safeconsole.log +FNDA:0,safeconsole.log +DA:5858,0 +DA:5859,0 +DA:5860,0 +DA:5861,0 +DA:5862,0 +DA:5864,0 +DA:5865,0 +DA:5866,0 +DA:5867,0 +DA:5868,0 +DA:5876,0 +FN:5886,safeconsole.log +FNDA:0,safeconsole.log +DA:5887,0 +DA:5888,0 +DA:5889,0 +DA:5890,0 +DA:5891,0 +DA:5893,0 +DA:5894,0 +DA:5895,0 +DA:5896,0 +DA:5897,0 +DA:5905,0 +FN:5915,safeconsole.log +FNDA:0,safeconsole.log +DA:5916,0 +DA:5917,0 +DA:5918,0 +DA:5919,0 +DA:5920,0 +DA:5921,0 +DA:5922,0 +DA:5931,0 +DA:5932,0 +DA:5933,0 +DA:5934,0 +DA:5935,0 +DA:5936,0 +DA:5937,0 +DA:5946,0 +FN:5958,safeconsole.log +FNDA:0,safeconsole.log +DA:5959,0 +DA:5960,0 +DA:5961,0 +DA:5962,0 +DA:5963,0 +DA:5965,0 +DA:5966,0 +DA:5967,0 +DA:5968,0 +DA:5969,0 +DA:5977,0 +FN:5987,safeconsole.log +FNDA:0,safeconsole.log +DA:5988,0 +DA:5989,0 +DA:5990,0 +DA:5991,0 +DA:5992,0 +DA:5994,0 +DA:5995,0 +DA:5996,0 +DA:5997,0 +DA:5998,0 +DA:6006,0 +FN:6016,safeconsole.log +FNDA:0,safeconsole.log +DA:6017,0 +DA:6018,0 +DA:6019,0 +DA:6020,0 +DA:6021,0 +DA:6023,0 +DA:6024,0 +DA:6025,0 +DA:6026,0 +DA:6027,0 +DA:6035,0 +FN:6045,safeconsole.log +FNDA:0,safeconsole.log +DA:6046,0 +DA:6047,0 +DA:6048,0 +DA:6049,0 +DA:6050,0 +DA:6051,0 +DA:6052,0 +DA:6061,0 +DA:6062,0 +DA:6063,0 +DA:6064,0 +DA:6065,0 +DA:6066,0 +DA:6067,0 +DA:6076,0 +FN:6088,safeconsole.log +FNDA:0,safeconsole.log +DA:6089,0 +DA:6090,0 +DA:6091,0 +DA:6092,0 +DA:6093,0 +DA:6095,0 +DA:6096,0 +DA:6097,0 +DA:6098,0 +DA:6099,0 +DA:6107,0 +FN:6117,safeconsole.log +FNDA:0,safeconsole.log +DA:6118,0 +DA:6119,0 +DA:6120,0 +DA:6121,0 +DA:6122,0 +DA:6124,0 +DA:6125,0 +DA:6126,0 +DA:6127,0 +DA:6128,0 +DA:6136,0 +FN:6146,safeconsole.log +FNDA:0,safeconsole.log +DA:6147,0 +DA:6148,0 +DA:6149,0 +DA:6150,0 +DA:6151,0 +DA:6153,0 +DA:6154,0 +DA:6155,0 +DA:6156,0 +DA:6157,0 +DA:6165,0 +FN:6175,safeconsole.log +FNDA:0,safeconsole.log +DA:6176,0 +DA:6177,0 +DA:6178,0 +DA:6179,0 +DA:6180,0 +DA:6181,0 +DA:6182,0 +DA:6191,0 +DA:6192,0 +DA:6193,0 +DA:6194,0 +DA:6195,0 +DA:6196,0 +DA:6197,0 +DA:6206,0 +FN:6218,safeconsole.log +FNDA:0,safeconsole.log +DA:6219,0 +DA:6220,0 +DA:6221,0 +DA:6222,0 +DA:6223,0 +DA:6224,0 +DA:6225,0 +DA:6234,0 +DA:6235,0 +DA:6236,0 +DA:6237,0 +DA:6238,0 +DA:6239,0 +DA:6240,0 +DA:6249,0 +FN:6261,safeconsole.log +FNDA:0,safeconsole.log +DA:6262,0 +DA:6263,0 +DA:6264,0 +DA:6265,0 +DA:6266,0 +DA:6267,0 +DA:6268,0 +DA:6277,0 +DA:6278,0 +DA:6279,0 +DA:6280,0 +DA:6281,0 +DA:6282,0 +DA:6283,0 +DA:6292,0 +FN:6304,safeconsole.log +FNDA:0,safeconsole.log +DA:6305,0 +DA:6306,0 +DA:6307,0 +DA:6308,0 +DA:6309,0 +DA:6310,0 +DA:6311,0 +DA:6320,0 +DA:6321,0 +DA:6322,0 +DA:6323,0 +DA:6324,0 +DA:6325,0 +DA:6326,0 +DA:6335,0 +FN:6347,safeconsole.log +FNDA:0,safeconsole.log +DA:6348,0 +DA:6349,0 +DA:6350,0 +DA:6351,0 +DA:6352,0 +DA:6353,0 +DA:6354,0 +DA:6355,0 +DA:6356,0 +DA:6365,0 +DA:6366,0 +DA:6367,0 +DA:6368,0 +DA:6369,0 +DA:6370,0 +DA:6371,0 +DA:6372,0 +DA:6373,0 +DA:6383,0 +FN:6397,safeconsole.log +FNDA:0,safeconsole.log +DA:6398,0 +DA:6399,0 +DA:6400,0 +DA:6401,0 +DA:6402,0 +DA:6404,0 +DA:6405,0 +DA:6406,0 +DA:6407,0 +DA:6408,0 +DA:6416,0 +FN:6426,safeconsole.log +FNDA:0,safeconsole.log +DA:6427,0 +DA:6428,0 +DA:6429,0 +DA:6430,0 +DA:6431,0 +DA:6433,0 +DA:6434,0 +DA:6435,0 +DA:6436,0 +DA:6437,0 +DA:6445,0 +FN:6455,safeconsole.log +FNDA:0,safeconsole.log +DA:6456,0 +DA:6457,0 +DA:6458,0 +DA:6459,0 +DA:6460,0 +DA:6462,0 +DA:6463,0 +DA:6464,0 +DA:6465,0 +DA:6466,0 +DA:6474,0 +FN:6484,safeconsole.log +FNDA:0,safeconsole.log +DA:6485,0 +DA:6486,0 +DA:6487,0 +DA:6488,0 +DA:6489,0 +DA:6490,0 +DA:6491,0 +DA:6500,0 +DA:6501,0 +DA:6502,0 +DA:6503,0 +DA:6504,0 +DA:6505,0 +DA:6506,0 +DA:6515,0 +FN:6527,safeconsole.log +FNDA:0,safeconsole.log +DA:6528,0 +DA:6529,0 +DA:6530,0 +DA:6531,0 +DA:6532,0 +DA:6534,0 +DA:6535,0 +DA:6536,0 +DA:6537,0 +DA:6538,0 +DA:6546,0 +FN:6556,safeconsole.log +FNDA:0,safeconsole.log +DA:6557,0 +DA:6558,0 +DA:6559,0 +DA:6560,0 +DA:6561,0 +DA:6563,0 +DA:6564,0 +DA:6565,0 +DA:6566,0 +DA:6567,0 +DA:6575,0 +FN:6585,safeconsole.log +FNDA:0,safeconsole.log +DA:6586,0 +DA:6587,0 +DA:6588,0 +DA:6589,0 +DA:6590,0 +DA:6592,0 +DA:6593,0 +DA:6594,0 +DA:6595,0 +DA:6596,0 +DA:6604,0 +FN:6614,safeconsole.log +FNDA:0,safeconsole.log +DA:6615,0 +DA:6616,0 +DA:6617,0 +DA:6618,0 +DA:6619,0 +DA:6620,0 +DA:6621,0 +DA:6630,0 +DA:6631,0 +DA:6632,0 +DA:6633,0 +DA:6634,0 +DA:6635,0 +DA:6636,0 +DA:6645,0 +FN:6657,safeconsole.log +FNDA:0,safeconsole.log +DA:6658,0 +DA:6659,0 +DA:6660,0 +DA:6661,0 +DA:6662,0 +DA:6664,0 +DA:6665,0 +DA:6666,0 +DA:6667,0 +DA:6668,0 +DA:6676,0 +FN:6686,safeconsole.log +FNDA:0,safeconsole.log +DA:6687,0 +DA:6688,0 +DA:6689,0 +DA:6690,0 +DA:6691,0 +DA:6693,0 +DA:6694,0 +DA:6695,0 +DA:6696,0 +DA:6697,0 +DA:6705,0 +FN:6715,safeconsole.log +FNDA:0,safeconsole.log +DA:6716,0 +DA:6717,0 +DA:6718,0 +DA:6719,0 +DA:6720,0 +DA:6722,0 +DA:6723,0 +DA:6724,0 +DA:6725,0 +DA:6726,0 +DA:6734,0 +FN:6744,safeconsole.log +FNDA:0,safeconsole.log +DA:6745,0 +DA:6746,0 +DA:6747,0 +DA:6748,0 +DA:6749,0 +DA:6750,0 +DA:6751,0 +DA:6760,0 +DA:6761,0 +DA:6762,0 +DA:6763,0 +DA:6764,0 +DA:6765,0 +DA:6766,0 +DA:6775,0 +FN:6787,safeconsole.log +FNDA:0,safeconsole.log +DA:6788,0 +DA:6789,0 +DA:6790,0 +DA:6791,0 +DA:6792,0 +DA:6793,0 +DA:6794,0 +DA:6803,0 +DA:6804,0 +DA:6805,0 +DA:6806,0 +DA:6807,0 +DA:6808,0 +DA:6809,0 +DA:6818,0 +FN:6830,safeconsole.log +FNDA:0,safeconsole.log +DA:6831,0 +DA:6832,0 +DA:6833,0 +DA:6834,0 +DA:6835,0 +DA:6836,0 +DA:6837,0 +DA:6846,0 +DA:6847,0 +DA:6848,0 +DA:6849,0 +DA:6850,0 +DA:6851,0 +DA:6852,0 +DA:6861,0 +FN:6873,safeconsole.log +FNDA:0,safeconsole.log +DA:6874,0 +DA:6875,0 +DA:6876,0 +DA:6877,0 +DA:6878,0 +DA:6879,0 +DA:6880,0 +DA:6889,0 +DA:6890,0 +DA:6891,0 +DA:6892,0 +DA:6893,0 +DA:6894,0 +DA:6895,0 +DA:6904,0 +FN:6916,safeconsole.log +FNDA:0,safeconsole.log +DA:6917,0 +DA:6918,0 +DA:6919,0 +DA:6920,0 +DA:6921,0 +DA:6922,0 +DA:6923,0 +DA:6924,0 +DA:6925,0 +DA:6934,0 +DA:6935,0 +DA:6936,0 +DA:6937,0 +DA:6938,0 +DA:6939,0 +DA:6940,0 +DA:6941,0 +DA:6942,0 +DA:6952,0 +FN:6966,safeconsole.log +FNDA:0,safeconsole.log +DA:6967,0 +DA:6968,0 +DA:6969,0 +DA:6970,0 +DA:6971,0 +DA:6972,0 +DA:6973,0 +DA:6982,0 +DA:6983,0 +DA:6984,0 +DA:6985,0 +DA:6986,0 +DA:6987,0 +DA:6988,0 +DA:6997,0 +FN:7009,safeconsole.log +FNDA:0,safeconsole.log +DA:7010,0 +DA:7011,0 +DA:7012,0 +DA:7013,0 +DA:7014,0 +DA:7015,0 +DA:7016,0 +DA:7025,0 +DA:7026,0 +DA:7027,0 +DA:7028,0 +DA:7029,0 +DA:7030,0 +DA:7031,0 +DA:7040,0 +FN:7052,safeconsole.log +FNDA:0,safeconsole.log +DA:7053,0 +DA:7054,0 +DA:7055,0 +DA:7056,0 +DA:7057,0 +DA:7058,0 +DA:7059,0 +DA:7068,0 +DA:7069,0 +DA:7070,0 +DA:7071,0 +DA:7072,0 +DA:7073,0 +DA:7074,0 +DA:7083,0 +FN:7095,safeconsole.log +FNDA:0,safeconsole.log +DA:7096,0 +DA:7097,0 +DA:7098,0 +DA:7099,0 +DA:7100,0 +DA:7101,0 +DA:7102,0 +DA:7103,0 +DA:7104,0 +DA:7113,0 +DA:7114,0 +DA:7115,0 +DA:7116,0 +DA:7117,0 +DA:7118,0 +DA:7119,0 +DA:7120,0 +DA:7121,0 +DA:7131,0 +FN:7145,safeconsole.log +FNDA:0,safeconsole.log +DA:7146,0 +DA:7147,0 +DA:7148,0 +DA:7149,0 +DA:7150,0 +DA:7151,0 +DA:7152,0 +DA:7161,0 +DA:7162,0 +DA:7163,0 +DA:7164,0 +DA:7165,0 +DA:7166,0 +DA:7167,0 +DA:7176,0 +FN:7188,safeconsole.log +FNDA:0,safeconsole.log +DA:7189,0 +DA:7190,0 +DA:7191,0 +DA:7192,0 +DA:7193,0 +DA:7194,0 +DA:7195,0 +DA:7204,0 +DA:7205,0 +DA:7206,0 +DA:7207,0 +DA:7208,0 +DA:7209,0 +DA:7210,0 +DA:7219,0 +FN:7231,safeconsole.log +FNDA:0,safeconsole.log +DA:7232,0 +DA:7233,0 +DA:7234,0 +DA:7235,0 +DA:7236,0 +DA:7237,0 +DA:7238,0 +DA:7247,0 +DA:7248,0 +DA:7249,0 +DA:7250,0 +DA:7251,0 +DA:7252,0 +DA:7253,0 +DA:7262,0 +FN:7274,safeconsole.log +FNDA:0,safeconsole.log +DA:7275,0 +DA:7276,0 +DA:7277,0 +DA:7278,0 +DA:7279,0 +DA:7280,0 +DA:7281,0 +DA:7282,0 +DA:7283,0 +DA:7292,0 +DA:7293,0 +DA:7294,0 +DA:7295,0 +DA:7296,0 +DA:7297,0 +DA:7298,0 +DA:7299,0 +DA:7300,0 +DA:7310,0 +FN:7324,safeconsole.log +FNDA:0,safeconsole.log +DA:7325,0 +DA:7326,0 +DA:7327,0 +DA:7328,0 +DA:7329,0 +DA:7330,0 +DA:7331,0 +DA:7340,0 +DA:7341,0 +DA:7342,0 +DA:7343,0 +DA:7344,0 +DA:7345,0 +DA:7346,0 +DA:7355,0 +FN:7367,safeconsole.log +FNDA:0,safeconsole.log +DA:7368,0 +DA:7369,0 +DA:7370,0 +DA:7371,0 +DA:7372,0 +DA:7373,0 +DA:7374,0 +DA:7383,0 +DA:7384,0 +DA:7385,0 +DA:7386,0 +DA:7387,0 +DA:7388,0 +DA:7389,0 +DA:7398,0 +FN:7410,safeconsole.log +FNDA:0,safeconsole.log +DA:7411,0 +DA:7412,0 +DA:7413,0 +DA:7414,0 +DA:7415,0 +DA:7416,0 +DA:7417,0 +DA:7426,0 +DA:7427,0 +DA:7428,0 +DA:7429,0 +DA:7430,0 +DA:7431,0 +DA:7432,0 +DA:7441,0 +FN:7453,safeconsole.log +FNDA:0,safeconsole.log +DA:7454,0 +DA:7455,0 +DA:7456,0 +DA:7457,0 +DA:7458,0 +DA:7459,0 +DA:7460,0 +DA:7461,0 +DA:7462,0 +DA:7471,0 +DA:7472,0 +DA:7473,0 +DA:7474,0 +DA:7475,0 +DA:7476,0 +DA:7477,0 +DA:7478,0 +DA:7479,0 +DA:7489,0 +FN:7503,safeconsole.log +FNDA:0,safeconsole.log +DA:7504,0 +DA:7505,0 +DA:7506,0 +DA:7507,0 +DA:7508,0 +DA:7509,0 +DA:7510,0 +DA:7511,0 +DA:7512,0 +DA:7521,0 +DA:7522,0 +DA:7523,0 +DA:7524,0 +DA:7525,0 +DA:7526,0 +DA:7527,0 +DA:7528,0 +DA:7529,0 +DA:7539,0 +FN:7553,safeconsole.log +FNDA:0,safeconsole.log +DA:7554,0 +DA:7555,0 +DA:7556,0 +DA:7557,0 +DA:7558,0 +DA:7559,0 +DA:7560,0 +DA:7561,0 +DA:7562,0 +DA:7571,0 +DA:7572,0 +DA:7573,0 +DA:7574,0 +DA:7575,0 +DA:7576,0 +DA:7577,0 +DA:7578,0 +DA:7579,0 +DA:7589,0 +FN:7603,safeconsole.log +FNDA:0,safeconsole.log +DA:7604,0 +DA:7605,0 +DA:7606,0 +DA:7607,0 +DA:7608,0 +DA:7609,0 +DA:7610,0 +DA:7611,0 +DA:7612,0 +DA:7621,0 +DA:7622,0 +DA:7623,0 +DA:7624,0 +DA:7625,0 +DA:7626,0 +DA:7627,0 +DA:7628,0 +DA:7629,0 +DA:7639,0 +FN:7653,safeconsole.log +FNDA:0,safeconsole.log +DA:7654,0 +DA:7655,0 +DA:7656,0 +DA:7657,0 +DA:7658,0 +DA:7659,0 +DA:7660,0 +DA:7661,0 +DA:7662,0 +DA:7663,0 +DA:7664,0 +DA:7673,0 +DA:7674,0 +DA:7675,0 +DA:7676,0 +DA:7677,0 +DA:7678,0 +DA:7679,0 +DA:7680,0 +DA:7681,0 +DA:7682,0 +DA:7683,0 +DA:7694,0 +FN:7710,safeconsole.log +FNDA:0,safeconsole.log +DA:7711,0 +DA:7712,0 +DA:7713,0 +DA:7714,0 +DA:7715,0 +DA:7717,0 +DA:7718,0 +DA:7719,0 +DA:7720,0 +DA:7721,0 +DA:7729,0 +FN:7739,safeconsole.log +FNDA:0,safeconsole.log +DA:7740,0 +DA:7741,0 +DA:7742,0 +DA:7743,0 +DA:7744,0 +DA:7746,0 +DA:7747,0 +DA:7748,0 +DA:7749,0 +DA:7750,0 +DA:7758,0 +FN:7768,safeconsole.log +FNDA:0,safeconsole.log +DA:7769,0 +DA:7770,0 +DA:7771,0 +DA:7772,0 +DA:7773,0 +DA:7775,0 +DA:7776,0 +DA:7777,0 +DA:7778,0 +DA:7779,0 +DA:7787,0 +FN:7797,safeconsole.log +FNDA:0,safeconsole.log +DA:7798,0 +DA:7799,0 +DA:7800,0 +DA:7801,0 +DA:7802,0 +DA:7803,0 +DA:7804,0 +DA:7813,0 +DA:7814,0 +DA:7815,0 +DA:7816,0 +DA:7817,0 +DA:7818,0 +DA:7819,0 +DA:7828,0 +FN:7840,safeconsole.log +FNDA:0,safeconsole.log +DA:7841,0 +DA:7842,0 +DA:7843,0 +DA:7844,0 +DA:7845,0 +DA:7847,0 +DA:7848,0 +DA:7849,0 +DA:7850,0 +DA:7851,0 +DA:7859,0 +FN:7869,safeconsole.log +FNDA:0,safeconsole.log +DA:7870,0 +DA:7871,0 +DA:7872,0 +DA:7873,0 +DA:7874,0 +DA:7876,0 +DA:7877,0 +DA:7878,0 +DA:7879,0 +DA:7880,0 +DA:7888,0 +FN:7898,safeconsole.log +FNDA:0,safeconsole.log +DA:7899,0 +DA:7900,0 +DA:7901,0 +DA:7902,0 +DA:7903,0 +DA:7905,0 +DA:7906,0 +DA:7907,0 +DA:7908,0 +DA:7909,0 +DA:7917,0 +FN:7927,safeconsole.log +FNDA:0,safeconsole.log +DA:7928,0 +DA:7929,0 +DA:7930,0 +DA:7931,0 +DA:7932,0 +DA:7933,0 +DA:7934,0 +DA:7943,0 +DA:7944,0 +DA:7945,0 +DA:7946,0 +DA:7947,0 +DA:7948,0 +DA:7949,0 +DA:7958,0 +FN:7970,safeconsole.log +FNDA:0,safeconsole.log +DA:7971,0 +DA:7972,0 +DA:7973,0 +DA:7974,0 +DA:7975,0 +DA:7977,0 +DA:7978,0 +DA:7979,0 +DA:7980,0 +DA:7981,0 +DA:7989,0 +FN:7999,safeconsole.log +FNDA:0,safeconsole.log +DA:8000,0 +DA:8001,0 +DA:8002,0 +DA:8003,0 +DA:8004,0 +DA:8006,0 +DA:8007,0 +DA:8008,0 +DA:8009,0 +DA:8010,0 +DA:8018,0 +FN:8028,safeconsole.log +FNDA:0,safeconsole.log +DA:8029,0 +DA:8030,0 +DA:8031,0 +DA:8032,0 +DA:8033,0 +DA:8035,0 +DA:8036,0 +DA:8037,0 +DA:8038,0 +DA:8039,0 +DA:8047,0 +FN:8057,safeconsole.log +FNDA:0,safeconsole.log +DA:8058,0 +DA:8059,0 +DA:8060,0 +DA:8061,0 +DA:8062,0 +DA:8063,0 +DA:8064,0 +DA:8073,0 +DA:8074,0 +DA:8075,0 +DA:8076,0 +DA:8077,0 +DA:8078,0 +DA:8079,0 +DA:8088,0 +FN:8100,safeconsole.log +FNDA:0,safeconsole.log +DA:8101,0 +DA:8102,0 +DA:8103,0 +DA:8104,0 +DA:8105,0 +DA:8106,0 +DA:8107,0 +DA:8116,0 +DA:8117,0 +DA:8118,0 +DA:8119,0 +DA:8120,0 +DA:8121,0 +DA:8122,0 +DA:8131,0 +FN:8143,safeconsole.log +FNDA:0,safeconsole.log +DA:8144,0 +DA:8145,0 +DA:8146,0 +DA:8147,0 +DA:8148,0 +DA:8149,0 +DA:8150,0 +DA:8159,0 +DA:8160,0 +DA:8161,0 +DA:8162,0 +DA:8163,0 +DA:8164,0 +DA:8165,0 +DA:8174,0 +FN:8186,safeconsole.log +FNDA:0,safeconsole.log +DA:8187,0 +DA:8188,0 +DA:8189,0 +DA:8190,0 +DA:8191,0 +DA:8192,0 +DA:8193,0 +DA:8202,0 +DA:8203,0 +DA:8204,0 +DA:8205,0 +DA:8206,0 +DA:8207,0 +DA:8208,0 +DA:8217,0 +FN:8229,safeconsole.log +FNDA:0,safeconsole.log +DA:8230,0 +DA:8231,0 +DA:8232,0 +DA:8233,0 +DA:8234,0 +DA:8235,0 +DA:8236,0 +DA:8237,0 +DA:8238,0 +DA:8247,0 +DA:8248,0 +DA:8249,0 +DA:8250,0 +DA:8251,0 +DA:8252,0 +DA:8253,0 +DA:8254,0 +DA:8255,0 +DA:8265,0 +FN:8279,safeconsole.log +FNDA:0,safeconsole.log +DA:8280,0 +DA:8281,0 +DA:8282,0 +DA:8283,0 +DA:8284,0 +DA:8286,0 +DA:8287,0 +DA:8288,0 +DA:8289,0 +DA:8290,0 +DA:8298,0 +FN:8308,safeconsole.log +FNDA:0,safeconsole.log +DA:8309,0 +DA:8310,0 +DA:8311,0 +DA:8312,0 +DA:8313,0 +DA:8315,0 +DA:8316,0 +DA:8317,0 +DA:8318,0 +DA:8319,0 +DA:8327,0 +FN:8337,safeconsole.log +FNDA:0,safeconsole.log +DA:8338,0 +DA:8339,0 +DA:8340,0 +DA:8341,0 +DA:8342,0 +DA:8344,0 +DA:8345,0 +DA:8346,0 +DA:8347,0 +DA:8348,0 +DA:8356,0 +FN:8366,safeconsole.log +FNDA:0,safeconsole.log +DA:8367,0 +DA:8368,0 +DA:8369,0 +DA:8370,0 +DA:8371,0 +DA:8372,0 +DA:8373,0 +DA:8382,0 +DA:8383,0 +DA:8384,0 +DA:8385,0 +DA:8386,0 +DA:8387,0 +DA:8388,0 +DA:8397,0 +FN:8409,safeconsole.log +FNDA:0,safeconsole.log +DA:8410,0 +DA:8411,0 +DA:8412,0 +DA:8413,0 +DA:8414,0 +DA:8416,0 +DA:8417,0 +DA:8418,0 +DA:8419,0 +DA:8420,0 +DA:8428,0 +FN:8438,safeconsole.log +FNDA:0,safeconsole.log +DA:8439,0 +DA:8440,0 +DA:8441,0 +DA:8442,0 +DA:8443,0 +DA:8445,0 +DA:8446,0 +DA:8447,0 +DA:8448,0 +DA:8449,0 +DA:8457,0 +FN:8467,safeconsole.log +FNDA:0,safeconsole.log +DA:8468,0 +DA:8469,0 +DA:8470,0 +DA:8471,0 +DA:8472,0 +DA:8474,0 +DA:8475,0 +DA:8476,0 +DA:8477,0 +DA:8478,0 +DA:8486,0 +FN:8496,safeconsole.log +FNDA:0,safeconsole.log +DA:8497,0 +DA:8498,0 +DA:8499,0 +DA:8500,0 +DA:8501,0 +DA:8502,0 +DA:8503,0 +DA:8512,0 +DA:8513,0 +DA:8514,0 +DA:8515,0 +DA:8516,0 +DA:8517,0 +DA:8518,0 +DA:8527,0 +FN:8539,safeconsole.log +FNDA:0,safeconsole.log +DA:8540,0 +DA:8541,0 +DA:8542,0 +DA:8543,0 +DA:8544,0 +DA:8546,0 +DA:8547,0 +DA:8548,0 +DA:8549,0 +DA:8550,0 +DA:8558,0 +FN:8568,safeconsole.log +FNDA:0,safeconsole.log +DA:8569,0 +DA:8570,0 +DA:8571,0 +DA:8572,0 +DA:8573,0 +DA:8575,0 +DA:8576,0 +DA:8577,0 +DA:8578,0 +DA:8579,0 +DA:8587,0 +FN:8597,safeconsole.log +FNDA:0,safeconsole.log +DA:8598,0 +DA:8599,0 +DA:8600,0 +DA:8601,0 +DA:8602,0 +DA:8604,0 +DA:8605,0 +DA:8606,0 +DA:8607,0 +DA:8608,0 +DA:8616,0 +FN:8626,safeconsole.log +FNDA:0,safeconsole.log +DA:8627,0 +DA:8628,0 +DA:8629,0 +DA:8630,0 +DA:8631,0 +DA:8632,0 +DA:8633,0 +DA:8642,0 +DA:8643,0 +DA:8644,0 +DA:8645,0 +DA:8646,0 +DA:8647,0 +DA:8648,0 +DA:8657,0 +FN:8669,safeconsole.log +FNDA:0,safeconsole.log +DA:8670,0 +DA:8671,0 +DA:8672,0 +DA:8673,0 +DA:8674,0 +DA:8675,0 +DA:8676,0 +DA:8685,0 +DA:8686,0 +DA:8687,0 +DA:8688,0 +DA:8689,0 +DA:8690,0 +DA:8691,0 +DA:8700,0 +FN:8712,safeconsole.log +FNDA:0,safeconsole.log +DA:8713,0 +DA:8714,0 +DA:8715,0 +DA:8716,0 +DA:8717,0 +DA:8718,0 +DA:8719,0 +DA:8728,0 +DA:8729,0 +DA:8730,0 +DA:8731,0 +DA:8732,0 +DA:8733,0 +DA:8734,0 +DA:8743,0 +FN:8755,safeconsole.log +FNDA:0,safeconsole.log +DA:8756,0 +DA:8757,0 +DA:8758,0 +DA:8759,0 +DA:8760,0 +DA:8761,0 +DA:8762,0 +DA:8771,0 +DA:8772,0 +DA:8773,0 +DA:8774,0 +DA:8775,0 +DA:8776,0 +DA:8777,0 +DA:8786,0 +FN:8798,safeconsole.log +FNDA:0,safeconsole.log +DA:8799,0 +DA:8800,0 +DA:8801,0 +DA:8802,0 +DA:8803,0 +DA:8804,0 +DA:8805,0 +DA:8806,0 +DA:8807,0 +DA:8816,0 +DA:8817,0 +DA:8818,0 +DA:8819,0 +DA:8820,0 +DA:8821,0 +DA:8822,0 +DA:8823,0 +DA:8824,0 +DA:8834,0 +FN:8848,safeconsole.log +FNDA:0,safeconsole.log +DA:8849,0 +DA:8850,0 +DA:8851,0 +DA:8852,0 +DA:8853,0 +DA:8855,0 +DA:8856,0 +DA:8857,0 +DA:8858,0 +DA:8859,0 +DA:8867,0 +FN:8877,safeconsole.log +FNDA:0,safeconsole.log +DA:8878,0 +DA:8879,0 +DA:8880,0 +DA:8881,0 +DA:8882,0 +DA:8884,0 +DA:8885,0 +DA:8886,0 +DA:8887,0 +DA:8888,0 +DA:8896,0 +FN:8906,safeconsole.log +FNDA:0,safeconsole.log +DA:8907,0 +DA:8908,0 +DA:8909,0 +DA:8910,0 +DA:8911,0 +DA:8913,0 +DA:8914,0 +DA:8915,0 +DA:8916,0 +DA:8917,0 +DA:8925,0 +FN:8935,safeconsole.log +FNDA:0,safeconsole.log +DA:8936,0 +DA:8937,0 +DA:8938,0 +DA:8939,0 +DA:8940,0 +DA:8941,0 +DA:8942,0 +DA:8951,0 +DA:8952,0 +DA:8953,0 +DA:8954,0 +DA:8955,0 +DA:8956,0 +DA:8957,0 +DA:8966,0 +FN:8978,safeconsole.log +FNDA:0,safeconsole.log +DA:8979,0 +DA:8980,0 +DA:8981,0 +DA:8982,0 +DA:8983,0 +DA:8985,0 +DA:8986,0 +DA:8987,0 +DA:8988,0 +DA:8989,0 +DA:8997,0 +FN:9007,safeconsole.log +FNDA:0,safeconsole.log +DA:9008,0 +DA:9009,0 +DA:9010,0 +DA:9011,0 +DA:9012,0 +DA:9014,0 +DA:9015,0 +DA:9016,0 +DA:9017,0 +DA:9018,0 +DA:9026,0 +FN:9036,safeconsole.log +FNDA:0,safeconsole.log +DA:9037,0 +DA:9038,0 +DA:9039,0 +DA:9040,0 +DA:9041,0 +DA:9043,0 +DA:9044,0 +DA:9045,0 +DA:9046,0 +DA:9047,0 +DA:9055,0 +FN:9065,safeconsole.log +FNDA:0,safeconsole.log +DA:9066,0 +DA:9067,0 +DA:9068,0 +DA:9069,0 +DA:9070,0 +DA:9071,0 +DA:9072,0 +DA:9081,0 +DA:9082,0 +DA:9083,0 +DA:9084,0 +DA:9085,0 +DA:9086,0 +DA:9087,0 +DA:9096,0 +FN:9108,safeconsole.log +FNDA:0,safeconsole.log +DA:9109,0 +DA:9110,0 +DA:9111,0 +DA:9112,0 +DA:9113,0 +DA:9115,0 +DA:9116,0 +DA:9117,0 +DA:9118,0 +DA:9119,0 +DA:9127,0 +FN:9137,safeconsole.log +FNDA:0,safeconsole.log +DA:9138,0 +DA:9139,0 +DA:9140,0 +DA:9141,0 +DA:9142,0 +DA:9144,0 +DA:9145,0 +DA:9146,0 +DA:9147,0 +DA:9148,0 +DA:9156,0 +FN:9166,safeconsole.log +FNDA:0,safeconsole.log +DA:9167,0 +DA:9168,0 +DA:9169,0 +DA:9170,0 +DA:9171,0 +DA:9173,0 +DA:9174,0 +DA:9175,0 +DA:9176,0 +DA:9177,0 +DA:9185,0 +FN:9195,safeconsole.log +FNDA:0,safeconsole.log +DA:9196,0 +DA:9197,0 +DA:9198,0 +DA:9199,0 +DA:9200,0 +DA:9201,0 +DA:9202,0 +DA:9211,0 +DA:9212,0 +DA:9213,0 +DA:9214,0 +DA:9215,0 +DA:9216,0 +DA:9217,0 +DA:9226,0 +FN:9238,safeconsole.log +FNDA:0,safeconsole.log +DA:9239,0 +DA:9240,0 +DA:9241,0 +DA:9242,0 +DA:9243,0 +DA:9244,0 +DA:9245,0 +DA:9254,0 +DA:9255,0 +DA:9256,0 +DA:9257,0 +DA:9258,0 +DA:9259,0 +DA:9260,0 +DA:9269,0 +FN:9281,safeconsole.log +FNDA:0,safeconsole.log +DA:9282,0 +DA:9283,0 +DA:9284,0 +DA:9285,0 +DA:9286,0 +DA:9287,0 +DA:9288,0 +DA:9297,0 +DA:9298,0 +DA:9299,0 +DA:9300,0 +DA:9301,0 +DA:9302,0 +DA:9303,0 +DA:9312,0 +FN:9324,safeconsole.log +FNDA:0,safeconsole.log +DA:9325,0 +DA:9326,0 +DA:9327,0 +DA:9328,0 +DA:9329,0 +DA:9330,0 +DA:9331,0 +DA:9340,0 +DA:9341,0 +DA:9342,0 +DA:9343,0 +DA:9344,0 +DA:9345,0 +DA:9346,0 +DA:9355,0 +FN:9367,safeconsole.log +FNDA:0,safeconsole.log +DA:9368,0 +DA:9369,0 +DA:9370,0 +DA:9371,0 +DA:9372,0 +DA:9373,0 +DA:9374,0 +DA:9375,0 +DA:9376,0 +DA:9385,0 +DA:9386,0 +DA:9387,0 +DA:9388,0 +DA:9389,0 +DA:9390,0 +DA:9391,0 +DA:9392,0 +DA:9393,0 +DA:9403,0 +FN:9417,safeconsole.log +FNDA:0,safeconsole.log +DA:9418,0 +DA:9419,0 +DA:9420,0 +DA:9421,0 +DA:9422,0 +DA:9423,0 +DA:9424,0 +DA:9433,0 +DA:9434,0 +DA:9435,0 +DA:9436,0 +DA:9437,0 +DA:9438,0 +DA:9439,0 +DA:9448,0 +FN:9460,safeconsole.log +FNDA:0,safeconsole.log +DA:9461,0 +DA:9462,0 +DA:9463,0 +DA:9464,0 +DA:9465,0 +DA:9466,0 +DA:9467,0 +DA:9476,0 +DA:9477,0 +DA:9478,0 +DA:9479,0 +DA:9480,0 +DA:9481,0 +DA:9482,0 +DA:9491,0 +FN:9503,safeconsole.log +FNDA:0,safeconsole.log +DA:9504,0 +DA:9505,0 +DA:9506,0 +DA:9507,0 +DA:9508,0 +DA:9509,0 +DA:9510,0 +DA:9519,0 +DA:9520,0 +DA:9521,0 +DA:9522,0 +DA:9523,0 +DA:9524,0 +DA:9525,0 +DA:9534,0 +FN:9546,safeconsole.log +FNDA:0,safeconsole.log +DA:9547,0 +DA:9548,0 +DA:9549,0 +DA:9550,0 +DA:9551,0 +DA:9552,0 +DA:9553,0 +DA:9554,0 +DA:9555,0 +DA:9564,0 +DA:9565,0 +DA:9566,0 +DA:9567,0 +DA:9568,0 +DA:9569,0 +DA:9570,0 +DA:9571,0 +DA:9572,0 +DA:9582,0 +FN:9596,safeconsole.log +FNDA:0,safeconsole.log +DA:9597,0 +DA:9598,0 +DA:9599,0 +DA:9600,0 +DA:9601,0 +DA:9602,0 +DA:9603,0 +DA:9612,0 +DA:9613,0 +DA:9614,0 +DA:9615,0 +DA:9616,0 +DA:9617,0 +DA:9618,0 +DA:9627,0 +FN:9639,safeconsole.log +FNDA:0,safeconsole.log +DA:9640,0 +DA:9641,0 +DA:9642,0 +DA:9643,0 +DA:9644,0 +DA:9645,0 +DA:9646,0 +DA:9655,0 +DA:9656,0 +DA:9657,0 +DA:9658,0 +DA:9659,0 +DA:9660,0 +DA:9661,0 +DA:9670,0 +FN:9682,safeconsole.log +FNDA:0,safeconsole.log +DA:9683,0 +DA:9684,0 +DA:9685,0 +DA:9686,0 +DA:9687,0 +DA:9688,0 +DA:9689,0 +DA:9698,0 +DA:9699,0 +DA:9700,0 +DA:9701,0 +DA:9702,0 +DA:9703,0 +DA:9704,0 +DA:9713,0 +FN:9725,safeconsole.log +FNDA:0,safeconsole.log +DA:9726,0 +DA:9727,0 +DA:9728,0 +DA:9729,0 +DA:9730,0 +DA:9731,0 +DA:9732,0 +DA:9733,0 +DA:9734,0 +DA:9743,0 +DA:9744,0 +DA:9745,0 +DA:9746,0 +DA:9747,0 +DA:9748,0 +DA:9749,0 +DA:9750,0 +DA:9751,0 +DA:9761,0 +FN:9775,safeconsole.log +FNDA:0,safeconsole.log +DA:9776,0 +DA:9777,0 +DA:9778,0 +DA:9779,0 +DA:9780,0 +DA:9781,0 +DA:9782,0 +DA:9791,0 +DA:9792,0 +DA:9793,0 +DA:9794,0 +DA:9795,0 +DA:9796,0 +DA:9797,0 +DA:9806,0 +FN:9818,safeconsole.log +FNDA:0,safeconsole.log +DA:9819,0 +DA:9820,0 +DA:9821,0 +DA:9822,0 +DA:9823,0 +DA:9824,0 +DA:9825,0 +DA:9834,0 +DA:9835,0 +DA:9836,0 +DA:9837,0 +DA:9838,0 +DA:9839,0 +DA:9840,0 +DA:9849,0 +FN:9861,safeconsole.log +FNDA:0,safeconsole.log +DA:9862,0 +DA:9863,0 +DA:9864,0 +DA:9865,0 +DA:9866,0 +DA:9867,0 +DA:9868,0 +DA:9877,0 +DA:9878,0 +DA:9879,0 +DA:9880,0 +DA:9881,0 +DA:9882,0 +DA:9883,0 +DA:9892,0 +FN:9904,safeconsole.log +FNDA:0,safeconsole.log +DA:9905,0 +DA:9906,0 +DA:9907,0 +DA:9908,0 +DA:9909,0 +DA:9910,0 +DA:9911,0 +DA:9912,0 +DA:9913,0 +DA:9922,0 +DA:9923,0 +DA:9924,0 +DA:9925,0 +DA:9926,0 +DA:9927,0 +DA:9928,0 +DA:9929,0 +DA:9930,0 +DA:9940,0 +FN:9954,safeconsole.log +FNDA:0,safeconsole.log +DA:9955,0 +DA:9956,0 +DA:9957,0 +DA:9958,0 +DA:9959,0 +DA:9960,0 +DA:9961,0 +DA:9962,0 +DA:9963,0 +DA:9972,0 +DA:9973,0 +DA:9974,0 +DA:9975,0 +DA:9976,0 +DA:9977,0 +DA:9978,0 +DA:9979,0 +DA:9980,0 +DA:9990,0 +FN:10004,safeconsole.log +FNDA:0,safeconsole.log +DA:10005,0 +DA:10006,0 +DA:10007,0 +DA:10008,0 +DA:10009,0 +DA:10010,0 +DA:10011,0 +DA:10012,0 +DA:10013,0 +DA:10022,0 +DA:10023,0 +DA:10024,0 +DA:10025,0 +DA:10026,0 +DA:10027,0 +DA:10028,0 +DA:10029,0 +DA:10030,0 +DA:10040,0 +FN:10054,safeconsole.log +FNDA:0,safeconsole.log +DA:10055,0 +DA:10056,0 +DA:10057,0 +DA:10058,0 +DA:10059,0 +DA:10060,0 +DA:10061,0 +DA:10062,0 +DA:10063,0 +DA:10072,0 +DA:10073,0 +DA:10074,0 +DA:10075,0 +DA:10076,0 +DA:10077,0 +DA:10078,0 +DA:10079,0 +DA:10080,0 +DA:10090,0 +FN:10104,safeconsole.log +FNDA:0,safeconsole.log +DA:10105,0 +DA:10106,0 +DA:10107,0 +DA:10108,0 +DA:10109,0 +DA:10110,0 +DA:10111,0 +DA:10112,0 +DA:10113,0 +DA:10114,0 +DA:10115,0 +DA:10124,0 +DA:10125,0 +DA:10126,0 +DA:10127,0 +DA:10128,0 +DA:10129,0 +DA:10130,0 +DA:10131,0 +DA:10132,0 +DA:10133,0 +DA:10134,0 +DA:10145,0 +FN:10161,safeconsole.log +FNDA:0,safeconsole.log +DA:10162,0 +DA:10163,0 +DA:10164,0 +DA:10165,0 +DA:10166,0 +DA:10167,0 +DA:10168,0 +DA:10177,0 +DA:10178,0 +DA:10179,0 +DA:10180,0 +DA:10181,0 +DA:10182,0 +DA:10183,0 +DA:10192,0 +FN:10204,safeconsole.log +FNDA:0,safeconsole.log +DA:10205,0 +DA:10206,0 +DA:10207,0 +DA:10208,0 +DA:10209,0 +DA:10210,0 +DA:10211,0 +DA:10220,0 +DA:10221,0 +DA:10222,0 +DA:10223,0 +DA:10224,0 +DA:10225,0 +DA:10226,0 +DA:10235,0 +FN:10247,safeconsole.log +FNDA:0,safeconsole.log +DA:10248,0 +DA:10249,0 +DA:10250,0 +DA:10251,0 +DA:10252,0 +DA:10253,0 +DA:10254,0 +DA:10263,0 +DA:10264,0 +DA:10265,0 +DA:10266,0 +DA:10267,0 +DA:10268,0 +DA:10269,0 +DA:10278,0 +FN:10290,safeconsole.log +FNDA:0,safeconsole.log +DA:10291,0 +DA:10292,0 +DA:10293,0 +DA:10294,0 +DA:10295,0 +DA:10296,0 +DA:10297,0 +DA:10298,0 +DA:10299,0 +DA:10308,0 +DA:10309,0 +DA:10310,0 +DA:10311,0 +DA:10312,0 +DA:10313,0 +DA:10314,0 +DA:10315,0 +DA:10316,0 +DA:10326,0 +FN:10340,safeconsole.log +FNDA:0,safeconsole.log +DA:10341,0 +DA:10342,0 +DA:10343,0 +DA:10344,0 +DA:10345,0 +DA:10346,0 +DA:10347,0 +DA:10356,0 +DA:10357,0 +DA:10358,0 +DA:10359,0 +DA:10360,0 +DA:10361,0 +DA:10362,0 +DA:10371,0 +FN:10383,safeconsole.log +FNDA:0,safeconsole.log +DA:10384,0 +DA:10385,0 +DA:10386,0 +DA:10387,0 +DA:10388,0 +DA:10389,0 +DA:10390,0 +DA:10399,0 +DA:10400,0 +DA:10401,0 +DA:10402,0 +DA:10403,0 +DA:10404,0 +DA:10405,0 +DA:10414,0 +FN:10426,safeconsole.log +FNDA:0,safeconsole.log +DA:10427,0 +DA:10428,0 +DA:10429,0 +DA:10430,0 +DA:10431,0 +DA:10432,0 +DA:10433,0 +DA:10442,0 +DA:10443,0 +DA:10444,0 +DA:10445,0 +DA:10446,0 +DA:10447,0 +DA:10448,0 +DA:10457,0 +FN:10469,safeconsole.log +FNDA:0,safeconsole.log +DA:10470,0 +DA:10471,0 +DA:10472,0 +DA:10473,0 +DA:10474,0 +DA:10475,0 +DA:10476,0 +DA:10477,0 +DA:10478,0 +DA:10487,0 +DA:10488,0 +DA:10489,0 +DA:10490,0 +DA:10491,0 +DA:10492,0 +DA:10493,0 +DA:10494,0 +DA:10495,0 +DA:10505,0 +FN:10519,safeconsole.log +FNDA:0,safeconsole.log +DA:10520,0 +DA:10521,0 +DA:10522,0 +DA:10523,0 +DA:10524,0 +DA:10525,0 +DA:10526,0 +DA:10535,0 +DA:10536,0 +DA:10537,0 +DA:10538,0 +DA:10539,0 +DA:10540,0 +DA:10541,0 +DA:10550,0 +FN:10562,safeconsole.log +FNDA:0,safeconsole.log +DA:10563,0 +DA:10564,0 +DA:10565,0 +DA:10566,0 +DA:10567,0 +DA:10568,0 +DA:10569,0 +DA:10578,0 +DA:10579,0 +DA:10580,0 +DA:10581,0 +DA:10582,0 +DA:10583,0 +DA:10584,0 +DA:10593,0 +FN:10605,safeconsole.log +FNDA:0,safeconsole.log +DA:10606,0 +DA:10607,0 +DA:10608,0 +DA:10609,0 +DA:10610,0 +DA:10611,0 +DA:10612,0 +DA:10621,0 +DA:10622,0 +DA:10623,0 +DA:10624,0 +DA:10625,0 +DA:10626,0 +DA:10627,0 +DA:10636,0 +FN:10648,safeconsole.log +FNDA:0,safeconsole.log +DA:10649,0 +DA:10650,0 +DA:10651,0 +DA:10652,0 +DA:10653,0 +DA:10654,0 +DA:10655,0 +DA:10656,0 +DA:10657,0 +DA:10666,0 +DA:10667,0 +DA:10668,0 +DA:10669,0 +DA:10670,0 +DA:10671,0 +DA:10672,0 +DA:10673,0 +DA:10674,0 +DA:10684,0 +FN:10698,safeconsole.log +FNDA:0,safeconsole.log +DA:10699,0 +DA:10700,0 +DA:10701,0 +DA:10702,0 +DA:10703,0 +DA:10704,0 +DA:10705,0 +DA:10706,0 +DA:10707,0 +DA:10716,0 +DA:10717,0 +DA:10718,0 +DA:10719,0 +DA:10720,0 +DA:10721,0 +DA:10722,0 +DA:10723,0 +DA:10724,0 +DA:10734,0 +FN:10748,safeconsole.log +FNDA:0,safeconsole.log +DA:10749,0 +DA:10750,0 +DA:10751,0 +DA:10752,0 +DA:10753,0 +DA:10754,0 +DA:10755,0 +DA:10756,0 +DA:10757,0 +DA:10766,0 +DA:10767,0 +DA:10768,0 +DA:10769,0 +DA:10770,0 +DA:10771,0 +DA:10772,0 +DA:10773,0 +DA:10774,0 +DA:10784,0 +FN:10798,safeconsole.log +FNDA:0,safeconsole.log +DA:10799,0 +DA:10800,0 +DA:10801,0 +DA:10802,0 +DA:10803,0 +DA:10804,0 +DA:10805,0 +DA:10806,0 +DA:10807,0 +DA:10816,0 +DA:10817,0 +DA:10818,0 +DA:10819,0 +DA:10820,0 +DA:10821,0 +DA:10822,0 +DA:10823,0 +DA:10824,0 +DA:10834,0 +FN:10848,safeconsole.log +FNDA:0,safeconsole.log +DA:10849,0 +DA:10850,0 +DA:10851,0 +DA:10852,0 +DA:10853,0 +DA:10854,0 +DA:10855,0 +DA:10856,0 +DA:10857,0 +DA:10858,0 +DA:10859,0 +DA:10868,0 +DA:10869,0 +DA:10870,0 +DA:10871,0 +DA:10872,0 +DA:10873,0 +DA:10874,0 +DA:10875,0 +DA:10876,0 +DA:10877,0 +DA:10878,0 +DA:10889,0 +FN:10905,safeconsole.log +FNDA:0,safeconsole.log +DA:10906,0 +DA:10907,0 +DA:10908,0 +DA:10909,0 +DA:10910,0 +DA:10911,0 +DA:10912,0 +DA:10921,0 +DA:10922,0 +DA:10923,0 +DA:10924,0 +DA:10925,0 +DA:10926,0 +DA:10927,0 +DA:10936,0 +FN:10948,safeconsole.log +FNDA:0,safeconsole.log +DA:10949,0 +DA:10950,0 +DA:10951,0 +DA:10952,0 +DA:10953,0 +DA:10954,0 +DA:10955,0 +DA:10964,0 +DA:10965,0 +DA:10966,0 +DA:10967,0 +DA:10968,0 +DA:10969,0 +DA:10970,0 +DA:10979,0 +FN:10991,safeconsole.log +FNDA:0,safeconsole.log +DA:10992,0 +DA:10993,0 +DA:10994,0 +DA:10995,0 +DA:10996,0 +DA:10997,0 +DA:10998,0 +DA:11007,0 +DA:11008,0 +DA:11009,0 +DA:11010,0 +DA:11011,0 +DA:11012,0 +DA:11013,0 +DA:11022,0 +FN:11034,safeconsole.log +FNDA:0,safeconsole.log +DA:11035,0 +DA:11036,0 +DA:11037,0 +DA:11038,0 +DA:11039,0 +DA:11040,0 +DA:11041,0 +DA:11042,0 +DA:11043,0 +DA:11052,0 +DA:11053,0 +DA:11054,0 +DA:11055,0 +DA:11056,0 +DA:11057,0 +DA:11058,0 +DA:11059,0 +DA:11060,0 +DA:11070,0 +FN:11084,safeconsole.log +FNDA:0,safeconsole.log +DA:11085,0 +DA:11086,0 +DA:11087,0 +DA:11088,0 +DA:11089,0 +DA:11090,0 +DA:11091,0 +DA:11100,0 +DA:11101,0 +DA:11102,0 +DA:11103,0 +DA:11104,0 +DA:11105,0 +DA:11106,0 +DA:11115,0 +FN:11127,safeconsole.log +FNDA:0,safeconsole.log +DA:11128,0 +DA:11129,0 +DA:11130,0 +DA:11131,0 +DA:11132,0 +DA:11133,0 +DA:11134,0 +DA:11143,0 +DA:11144,0 +DA:11145,0 +DA:11146,0 +DA:11147,0 +DA:11148,0 +DA:11149,0 +DA:11158,0 +FN:11170,safeconsole.log +FNDA:0,safeconsole.log +DA:11171,0 +DA:11172,0 +DA:11173,0 +DA:11174,0 +DA:11175,0 +DA:11176,0 +DA:11177,0 +DA:11186,0 +DA:11187,0 +DA:11188,0 +DA:11189,0 +DA:11190,0 +DA:11191,0 +DA:11192,0 +DA:11201,0 +FN:11213,safeconsole.log +FNDA:0,safeconsole.log +DA:11214,0 +DA:11215,0 +DA:11216,0 +DA:11217,0 +DA:11218,0 +DA:11219,0 +DA:11220,0 +DA:11221,0 +DA:11222,0 +DA:11231,0 +DA:11232,0 +DA:11233,0 +DA:11234,0 +DA:11235,0 +DA:11236,0 +DA:11237,0 +DA:11238,0 +DA:11239,0 +DA:11249,0 +FN:11263,safeconsole.log +FNDA:0,safeconsole.log +DA:11264,0 +DA:11265,0 +DA:11266,0 +DA:11267,0 +DA:11268,0 +DA:11269,0 +DA:11270,0 +DA:11279,0 +DA:11280,0 +DA:11281,0 +DA:11282,0 +DA:11283,0 +DA:11284,0 +DA:11285,0 +DA:11294,0 +FN:11306,safeconsole.log +FNDA:0,safeconsole.log +DA:11307,0 +DA:11308,0 +DA:11309,0 +DA:11310,0 +DA:11311,0 +DA:11312,0 +DA:11313,0 +DA:11322,0 +DA:11323,0 +DA:11324,0 +DA:11325,0 +DA:11326,0 +DA:11327,0 +DA:11328,0 +DA:11337,0 +FN:11349,safeconsole.log +FNDA:0,safeconsole.log +DA:11350,0 +DA:11351,0 +DA:11352,0 +DA:11353,0 +DA:11354,0 +DA:11355,0 +DA:11356,0 +DA:11365,0 +DA:11366,0 +DA:11367,0 +DA:11368,0 +DA:11369,0 +DA:11370,0 +DA:11371,0 +DA:11380,0 +FN:11392,safeconsole.log +FNDA:0,safeconsole.log +DA:11393,0 +DA:11394,0 +DA:11395,0 +DA:11396,0 +DA:11397,0 +DA:11398,0 +DA:11399,0 +DA:11400,0 +DA:11401,0 +DA:11410,0 +DA:11411,0 +DA:11412,0 +DA:11413,0 +DA:11414,0 +DA:11415,0 +DA:11416,0 +DA:11417,0 +DA:11418,0 +DA:11428,0 +FN:11442,safeconsole.log +FNDA:0,safeconsole.log +DA:11443,0 +DA:11444,0 +DA:11445,0 +DA:11446,0 +DA:11447,0 +DA:11448,0 +DA:11449,0 +DA:11450,0 +DA:11451,0 +DA:11460,0 +DA:11461,0 +DA:11462,0 +DA:11463,0 +DA:11464,0 +DA:11465,0 +DA:11466,0 +DA:11467,0 +DA:11468,0 +DA:11478,0 +FN:11492,safeconsole.log +FNDA:0,safeconsole.log +DA:11493,0 +DA:11494,0 +DA:11495,0 +DA:11496,0 +DA:11497,0 +DA:11498,0 +DA:11499,0 +DA:11500,0 +DA:11501,0 +DA:11510,0 +DA:11511,0 +DA:11512,0 +DA:11513,0 +DA:11514,0 +DA:11515,0 +DA:11516,0 +DA:11517,0 +DA:11518,0 +DA:11528,0 +FN:11542,safeconsole.log +FNDA:0,safeconsole.log +DA:11543,0 +DA:11544,0 +DA:11545,0 +DA:11546,0 +DA:11547,0 +DA:11548,0 +DA:11549,0 +DA:11550,0 +DA:11551,0 +DA:11560,0 +DA:11561,0 +DA:11562,0 +DA:11563,0 +DA:11564,0 +DA:11565,0 +DA:11566,0 +DA:11567,0 +DA:11568,0 +DA:11578,0 +FN:11592,safeconsole.log +FNDA:0,safeconsole.log +DA:11593,0 +DA:11594,0 +DA:11595,0 +DA:11596,0 +DA:11597,0 +DA:11598,0 +DA:11599,0 +DA:11600,0 +DA:11601,0 +DA:11602,0 +DA:11603,0 +DA:11612,0 +DA:11613,0 +DA:11614,0 +DA:11615,0 +DA:11616,0 +DA:11617,0 +DA:11618,0 +DA:11619,0 +DA:11620,0 +DA:11621,0 +DA:11622,0 +DA:11633,0 +FN:11649,safeconsole.log +FNDA:0,safeconsole.log +DA:11650,0 +DA:11651,0 +DA:11652,0 +DA:11653,0 +DA:11654,0 +DA:11655,0 +DA:11656,0 +DA:11665,0 +DA:11666,0 +DA:11667,0 +DA:11668,0 +DA:11669,0 +DA:11670,0 +DA:11671,0 +DA:11680,0 +FN:11692,safeconsole.log +FNDA:0,safeconsole.log +DA:11693,0 +DA:11694,0 +DA:11695,0 +DA:11696,0 +DA:11697,0 +DA:11698,0 +DA:11699,0 +DA:11708,0 +DA:11709,0 +DA:11710,0 +DA:11711,0 +DA:11712,0 +DA:11713,0 +DA:11714,0 +DA:11723,0 +FN:11735,safeconsole.log +FNDA:0,safeconsole.log +DA:11736,0 +DA:11737,0 +DA:11738,0 +DA:11739,0 +DA:11740,0 +DA:11741,0 +DA:11742,0 +DA:11751,0 +DA:11752,0 +DA:11753,0 +DA:11754,0 +DA:11755,0 +DA:11756,0 +DA:11757,0 +DA:11766,0 +FN:11778,safeconsole.log +FNDA:0,safeconsole.log +DA:11779,0 +DA:11780,0 +DA:11781,0 +DA:11782,0 +DA:11783,0 +DA:11784,0 +DA:11785,0 +DA:11786,0 +DA:11787,0 +DA:11796,0 +DA:11797,0 +DA:11798,0 +DA:11799,0 +DA:11800,0 +DA:11801,0 +DA:11802,0 +DA:11803,0 +DA:11804,0 +DA:11814,0 +FN:11828,safeconsole.log +FNDA:0,safeconsole.log +DA:11829,0 +DA:11830,0 +DA:11831,0 +DA:11832,0 +DA:11833,0 +DA:11834,0 +DA:11835,0 +DA:11844,0 +DA:11845,0 +DA:11846,0 +DA:11847,0 +DA:11848,0 +DA:11849,0 +DA:11850,0 +DA:11859,0 +FN:11871,safeconsole.log +FNDA:0,safeconsole.log +DA:11872,0 +DA:11873,0 +DA:11874,0 +DA:11875,0 +DA:11876,0 +DA:11877,0 +DA:11878,0 +DA:11887,0 +DA:11888,0 +DA:11889,0 +DA:11890,0 +DA:11891,0 +DA:11892,0 +DA:11893,0 +DA:11902,0 +FN:11914,safeconsole.log +FNDA:0,safeconsole.log +DA:11915,0 +DA:11916,0 +DA:11917,0 +DA:11918,0 +DA:11919,0 +DA:11920,0 +DA:11921,0 +DA:11930,0 +DA:11931,0 +DA:11932,0 +DA:11933,0 +DA:11934,0 +DA:11935,0 +DA:11936,0 +DA:11945,0 +FN:11957,safeconsole.log +FNDA:0,safeconsole.log +DA:11958,0 +DA:11959,0 +DA:11960,0 +DA:11961,0 +DA:11962,0 +DA:11963,0 +DA:11964,0 +DA:11965,0 +DA:11966,0 +DA:11975,0 +DA:11976,0 +DA:11977,0 +DA:11978,0 +DA:11979,0 +DA:11980,0 +DA:11981,0 +DA:11982,0 +DA:11983,0 +DA:11993,0 +FN:12007,safeconsole.log +FNDA:0,safeconsole.log +DA:12008,0 +DA:12009,0 +DA:12010,0 +DA:12011,0 +DA:12012,0 +DA:12013,0 +DA:12014,0 +DA:12023,0 +DA:12024,0 +DA:12025,0 +DA:12026,0 +DA:12027,0 +DA:12028,0 +DA:12029,0 +DA:12038,0 +FN:12050,safeconsole.log +FNDA:0,safeconsole.log +DA:12051,0 +DA:12052,0 +DA:12053,0 +DA:12054,0 +DA:12055,0 +DA:12056,0 +DA:12057,0 +DA:12066,0 +DA:12067,0 +DA:12068,0 +DA:12069,0 +DA:12070,0 +DA:12071,0 +DA:12072,0 +DA:12081,0 +FN:12093,safeconsole.log +FNDA:0,safeconsole.log +DA:12094,0 +DA:12095,0 +DA:12096,0 +DA:12097,0 +DA:12098,0 +DA:12099,0 +DA:12100,0 +DA:12109,0 +DA:12110,0 +DA:12111,0 +DA:12112,0 +DA:12113,0 +DA:12114,0 +DA:12115,0 +DA:12124,0 +FN:12136,safeconsole.log +FNDA:0,safeconsole.log +DA:12137,0 +DA:12138,0 +DA:12139,0 +DA:12140,0 +DA:12141,0 +DA:12142,0 +DA:12143,0 +DA:12144,0 +DA:12145,0 +DA:12154,0 +DA:12155,0 +DA:12156,0 +DA:12157,0 +DA:12158,0 +DA:12159,0 +DA:12160,0 +DA:12161,0 +DA:12162,0 +DA:12172,0 +FN:12186,safeconsole.log +FNDA:0,safeconsole.log +DA:12187,0 +DA:12188,0 +DA:12189,0 +DA:12190,0 +DA:12191,0 +DA:12192,0 +DA:12193,0 +DA:12194,0 +DA:12195,0 +DA:12204,0 +DA:12205,0 +DA:12206,0 +DA:12207,0 +DA:12208,0 +DA:12209,0 +DA:12210,0 +DA:12211,0 +DA:12212,0 +DA:12222,0 +FN:12236,safeconsole.log +FNDA:0,safeconsole.log +DA:12237,0 +DA:12238,0 +DA:12239,0 +DA:12240,0 +DA:12241,0 +DA:12242,0 +DA:12243,0 +DA:12244,0 +DA:12245,0 +DA:12254,0 +DA:12255,0 +DA:12256,0 +DA:12257,0 +DA:12258,0 +DA:12259,0 +DA:12260,0 +DA:12261,0 +DA:12262,0 +DA:12272,0 +FN:12286,safeconsole.log +FNDA:0,safeconsole.log +DA:12287,0 +DA:12288,0 +DA:12289,0 +DA:12290,0 +DA:12291,0 +DA:12292,0 +DA:12293,0 +DA:12294,0 +DA:12295,0 +DA:12304,0 +DA:12305,0 +DA:12306,0 +DA:12307,0 +DA:12308,0 +DA:12309,0 +DA:12310,0 +DA:12311,0 +DA:12312,0 +DA:12322,0 +FN:12336,safeconsole.log +FNDA:0,safeconsole.log +DA:12337,0 +DA:12338,0 +DA:12339,0 +DA:12340,0 +DA:12341,0 +DA:12342,0 +DA:12343,0 +DA:12344,0 +DA:12345,0 +DA:12346,0 +DA:12347,0 +DA:12356,0 +DA:12357,0 +DA:12358,0 +DA:12359,0 +DA:12360,0 +DA:12361,0 +DA:12362,0 +DA:12363,0 +DA:12364,0 +DA:12365,0 +DA:12366,0 +DA:12377,0 +FN:12393,safeconsole.log +FNDA:0,safeconsole.log +DA:12394,0 +DA:12395,0 +DA:12396,0 +DA:12397,0 +DA:12398,0 +DA:12399,0 +DA:12400,0 +DA:12401,0 +DA:12402,0 +DA:12411,0 +DA:12412,0 +DA:12413,0 +DA:12414,0 +DA:12415,0 +DA:12416,0 +DA:12417,0 +DA:12418,0 +DA:12419,0 +DA:12429,0 +FN:12443,safeconsole.log +FNDA:0,safeconsole.log +DA:12444,0 +DA:12445,0 +DA:12446,0 +DA:12447,0 +DA:12448,0 +DA:12449,0 +DA:12450,0 +DA:12451,0 +DA:12452,0 +DA:12461,0 +DA:12462,0 +DA:12463,0 +DA:12464,0 +DA:12465,0 +DA:12466,0 +DA:12467,0 +DA:12468,0 +DA:12469,0 +DA:12479,0 +FN:12493,safeconsole.log +FNDA:0,safeconsole.log +DA:12494,0 +DA:12495,0 +DA:12496,0 +DA:12497,0 +DA:12498,0 +DA:12499,0 +DA:12500,0 +DA:12501,0 +DA:12502,0 +DA:12511,0 +DA:12512,0 +DA:12513,0 +DA:12514,0 +DA:12515,0 +DA:12516,0 +DA:12517,0 +DA:12518,0 +DA:12519,0 +DA:12529,0 +FN:12543,safeconsole.log +FNDA:0,safeconsole.log +DA:12544,0 +DA:12545,0 +DA:12546,0 +DA:12547,0 +DA:12548,0 +DA:12549,0 +DA:12550,0 +DA:12551,0 +DA:12552,0 +DA:12553,0 +DA:12554,0 +DA:12563,0 +DA:12564,0 +DA:12565,0 +DA:12566,0 +DA:12567,0 +DA:12568,0 +DA:12569,0 +DA:12570,0 +DA:12571,0 +DA:12572,0 +DA:12573,0 +DA:12584,0 +FN:12600,safeconsole.log +FNDA:0,safeconsole.log +DA:12601,0 +DA:12602,0 +DA:12603,0 +DA:12604,0 +DA:12605,0 +DA:12606,0 +DA:12607,0 +DA:12608,0 +DA:12609,0 +DA:12618,0 +DA:12619,0 +DA:12620,0 +DA:12621,0 +DA:12622,0 +DA:12623,0 +DA:12624,0 +DA:12625,0 +DA:12626,0 +DA:12636,0 +FN:12650,safeconsole.log +FNDA:0,safeconsole.log +DA:12651,0 +DA:12652,0 +DA:12653,0 +DA:12654,0 +DA:12655,0 +DA:12656,0 +DA:12657,0 +DA:12658,0 +DA:12659,0 +DA:12668,0 +DA:12669,0 +DA:12670,0 +DA:12671,0 +DA:12672,0 +DA:12673,0 +DA:12674,0 +DA:12675,0 +DA:12676,0 +DA:12686,0 +FN:12700,safeconsole.log +FNDA:0,safeconsole.log +DA:12701,0 +DA:12702,0 +DA:12703,0 +DA:12704,0 +DA:12705,0 +DA:12706,0 +DA:12707,0 +DA:12708,0 +DA:12709,0 +DA:12718,0 +DA:12719,0 +DA:12720,0 +DA:12721,0 +DA:12722,0 +DA:12723,0 +DA:12724,0 +DA:12725,0 +DA:12726,0 +DA:12736,0 +FN:12750,safeconsole.log +FNDA:0,safeconsole.log +DA:12751,0 +DA:12752,0 +DA:12753,0 +DA:12754,0 +DA:12755,0 +DA:12756,0 +DA:12757,0 +DA:12758,0 +DA:12759,0 +DA:12760,0 +DA:12761,0 +DA:12770,0 +DA:12771,0 +DA:12772,0 +DA:12773,0 +DA:12774,0 +DA:12775,0 +DA:12776,0 +DA:12777,0 +DA:12778,0 +DA:12779,0 +DA:12780,0 +DA:12791,0 +FN:12807,safeconsole.log +FNDA:0,safeconsole.log +DA:12808,0 +DA:12809,0 +DA:12810,0 +DA:12811,0 +DA:12812,0 +DA:12813,0 +DA:12814,0 +DA:12815,0 +DA:12816,0 +DA:12825,0 +DA:12826,0 +DA:12827,0 +DA:12828,0 +DA:12829,0 +DA:12830,0 +DA:12831,0 +DA:12832,0 +DA:12833,0 +DA:12843,0 +FN:12857,safeconsole.log +FNDA:0,safeconsole.log +DA:12858,0 +DA:12859,0 +DA:12860,0 +DA:12861,0 +DA:12862,0 +DA:12863,0 +DA:12864,0 +DA:12865,0 +DA:12866,0 +DA:12875,0 +DA:12876,0 +DA:12877,0 +DA:12878,0 +DA:12879,0 +DA:12880,0 +DA:12881,0 +DA:12882,0 +DA:12883,0 +DA:12893,0 +FN:12907,safeconsole.log +FNDA:0,safeconsole.log +DA:12908,0 +DA:12909,0 +DA:12910,0 +DA:12911,0 +DA:12912,0 +DA:12913,0 +DA:12914,0 +DA:12915,0 +DA:12916,0 +DA:12925,0 +DA:12926,0 +DA:12927,0 +DA:12928,0 +DA:12929,0 +DA:12930,0 +DA:12931,0 +DA:12932,0 +DA:12933,0 +DA:12943,0 +FN:12957,safeconsole.log +FNDA:0,safeconsole.log +DA:12958,0 +DA:12959,0 +DA:12960,0 +DA:12961,0 +DA:12962,0 +DA:12963,0 +DA:12964,0 +DA:12965,0 +DA:12966,0 +DA:12967,0 +DA:12968,0 +DA:12977,0 +DA:12978,0 +DA:12979,0 +DA:12980,0 +DA:12981,0 +DA:12982,0 +DA:12983,0 +DA:12984,0 +DA:12985,0 +DA:12986,0 +DA:12987,0 +DA:12998,0 +FN:13014,safeconsole.log +FNDA:0,safeconsole.log +DA:13015,0 +DA:13016,0 +DA:13017,0 +DA:13018,0 +DA:13019,0 +DA:13020,0 +DA:13021,0 +DA:13022,0 +DA:13023,0 +DA:13024,0 +DA:13025,0 +DA:13034,0 +DA:13035,0 +DA:13036,0 +DA:13037,0 +DA:13038,0 +DA:13039,0 +DA:13040,0 +DA:13041,0 +DA:13042,0 +DA:13043,0 +DA:13044,0 +DA:13055,0 +FN:13071,safeconsole.log +FNDA:0,safeconsole.log +DA:13072,0 +DA:13073,0 +DA:13074,0 +DA:13075,0 +DA:13076,0 +DA:13077,0 +DA:13078,0 +DA:13079,0 +DA:13080,0 +DA:13081,0 +DA:13082,0 +DA:13091,0 +DA:13092,0 +DA:13093,0 +DA:13094,0 +DA:13095,0 +DA:13096,0 +DA:13097,0 +DA:13098,0 +DA:13099,0 +DA:13100,0 +DA:13101,0 +DA:13112,0 +FN:13128,safeconsole.log +FNDA:0,safeconsole.log +DA:13129,0 +DA:13130,0 +DA:13131,0 +DA:13132,0 +DA:13133,0 +DA:13134,0 +DA:13135,0 +DA:13136,0 +DA:13137,0 +DA:13138,0 +DA:13139,0 +DA:13148,0 +DA:13149,0 +DA:13150,0 +DA:13151,0 +DA:13152,0 +DA:13153,0 +DA:13154,0 +DA:13155,0 +DA:13156,0 +DA:13157,0 +DA:13158,0 +DA:13169,0 +FN:13185,safeconsole.log +FNDA:0,safeconsole.log +DA:13186,0 +DA:13187,0 +DA:13188,0 +DA:13189,0 +DA:13190,0 +DA:13191,0 +DA:13192,0 +DA:13193,0 +DA:13194,0 +DA:13195,0 +DA:13196,0 +DA:13197,0 +DA:13198,0 +DA:13207,0 +DA:13208,0 +DA:13209,0 +DA:13210,0 +DA:13211,0 +DA:13212,0 +DA:13213,0 +DA:13214,0 +DA:13215,0 +DA:13216,0 +DA:13217,0 +DA:13218,0 +DA:13219,0 +DA:13231,0 +FNF:345 +FNH:0 +LF:4802 +LH:0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:node_modules/solady/src/utils/EIP712.sol +FN:95,EIP712._domainNameAndVersionMayChange +FNDA:24,EIP712._domainNameAndVersionMayChange +FN:102,EIP712._domainSeparator +FNDA:0,EIP712._domainSeparator +DA:103,0 +BRDA:103,0,0,- +BRDA:103,0,1,- +DA:104,0 +DA:106,0 +DA:107,0 +BRDA:107,1,0,- +BRDA:107,1,1,- +FN:124,EIP712._hashTypedData +FNDA:24,EIP712._hashTypedData +DA:126,24 +BRDA:126,2,0,- +BRDA:126,2,1,24 +DA:127,0 +DA:129,24 +DA:130,24 +BRDA:130,3,0,- +BRDA:130,3,1,24 +DA:138,24 +FN:149,EIP712.eip712Domain +FNDA:0,EIP712.eip712Domain +DA:163,0 +DA:164,0 +DA:165,0 +DA:166,0 +DA:167,0 +DA:168,0 +FN:176,EIP712._buildDomainSeparator +FNDA:0,EIP712._buildDomainSeparator +DA:178,0 +DA:179,0 +BRDA:179,4,0,- +BRDA:179,4,1,- +DA:180,0 +DA:181,0 +DA:182,0 +DA:184,0 +DA:185,0 +DA:195,0 +FN:200,EIP712._cachedDomainSeparatorInvalidated +FNDA:24,EIP712._cachedDomainSeparatorInvalidated +DA:201,24 +DA:202,24 +DA:205,24 +FNF:6 +FNH:3 +LF:26 +LH:7 +BRF:10 +BRH:2 +end_of_record +TN: +SF:node_modules/solady/src/utils/LibSort.sol +FN:17,LibSort.insertionSort +FNDA:0,LibSort.insertionSort +FN:45,LibSort.insertionSort +FNDA:0,LibSort.insertionSort +DA:46,0 +DA:47,0 +DA:48,0 +FN:52,LibSort.insertionSort +FNDA:0,LibSort.insertionSort +DA:53,0 +FN:65,LibSort.sort +FNDA:0,LibSort.sort +FN:207,LibSort.sort +FNDA:0,LibSort.sort +DA:208,0 +DA:209,0 +DA:210,0 +FN:214,LibSort.sort +FNDA:0,LibSort.sort +DA:215,0 +FN:226,LibSort.uniquifySorted +FNDA:0,LibSort.uniquifySorted +DA:230,0 +BRDA:230,0,0,- +FN:248,LibSort.uniquifySorted +FNDA:0,LibSort.uniquifySorted +DA:249,0 +FN:253,LibSort.uniquifySorted +FNDA:0,LibSort.uniquifySorted +DA:254,0 +FN:259,LibSort.searchSorted +FNDA:0,LibSort.searchSorted +DA:264,0 +FN:269,LibSort.searchSorted +FNDA:0,LibSort.searchSorted +DA:274,0 +FN:279,LibSort.searchSorted +FNDA:0,LibSort.searchSorted +DA:284,0 +FN:288,LibSort.reverse +FNDA:0,LibSort.reverse +DA:291,0 +BRDA:291,1,0,- +FN:308,LibSort.reverse +FNDA:0,LibSort.reverse +DA:309,0 +FN:313,LibSort.reverse +FNDA:0,LibSort.reverse +DA:314,0 +FN:318,LibSort.copy +FNDA:0,LibSort.copy +DA:321,0 +FN:334,LibSort.copy +FNDA:0,LibSort.copy +DA:335,0 +FN:339,LibSort.copy +FNDA:0,LibSort.copy +DA:340,0 +FN:344,LibSort.isSorted +FNDA:0,LibSort.isSorted +DA:347,0 +DA:348,0 +BRDA:348,2,0,- +FN:361,LibSort.isSorted +FNDA:0,LibSort.isSorted +DA:364,0 +DA:365,0 +BRDA:365,3,0,- +FN:378,LibSort.isSorted +FNDA:0,LibSort.isSorted +DA:379,0 +FN:383,LibSort.isSortedAndUniquified +FNDA:0,LibSort.isSortedAndUniquified +DA:386,0 +DA:387,0 +BRDA:387,4,0,- +FN:400,LibSort.isSortedAndUniquified +FNDA:0,LibSort.isSortedAndUniquified +DA:403,0 +DA:404,0 +BRDA:404,5,0,- +FN:417,LibSort.isSortedAndUniquified +FNDA:0,LibSort.isSortedAndUniquified +DA:418,0 +FN:423,LibSort.difference +FNDA:0,LibSort.difference +DA:428,0 +FN:433,LibSort.difference +FNDA:0,LibSort.difference +DA:438,0 +FN:443,LibSort.difference +FNDA:0,LibSort.difference +DA:448,0 +FN:453,LibSort.intersection +FNDA:0,LibSort.intersection +DA:458,0 +FN:463,LibSort.intersection +FNDA:0,LibSort.intersection +DA:468,0 +FN:473,LibSort.intersection +FNDA:0,LibSort.intersection +DA:478,0 +FN:483,LibSort.union +FNDA:0,LibSort.union +DA:488,0 +FN:493,LibSort.union +FNDA:0,LibSort.union +DA:498,0 +FN:503,LibSort.union +FNDA:0,LibSort.union +DA:508,0 +FN:516,LibSort._toUints +FNDA:0,LibSort._toUints +DA:519,0 +FN:524,LibSort._toUints +FNDA:0,LibSort._toUints +DA:530,0 +FN:535,LibSort._toInts +FNDA:0,LibSort._toInts +DA:538,0 +FN:543,LibSort._toAddresses +FNDA:0,LibSort._toAddresses +DA:546,0 +FN:552,LibSort._flipSign +FNDA:0,LibSort._flipSign +FN:565,LibSort._searchSorted +FNDA:0,LibSort._searchSorted +DA:589,0 +DA:590,0 +DA:591,0 +DA:592,0 +FN:598,LibSort._difference +FNDA:0,LibSort._difference +DA:608,0 +DA:609,0 +DA:610,0 +FN:640,LibSort._intersection +FNDA:0,LibSort._intersection +DA:650,0 +DA:651,0 +DA:652,0 +FN:677,LibSort._union +FNDA:0,LibSort._union +DA:687,0 +DA:688,0 +DA:689,0 +FNF:42 +FNH:0 +LF:56 +LH:0 +BRF:6 +BRH:0 +end_of_record +TN: +SF:node_modules/solady/src/utils/SSTORE2.sol +FN:36,SSTORE2.write +FNDA:0,SSTORE2.write +DA:69,0 +DA:72,0 +BRDA:72,0,0,- +FN:86,SSTORE2.writeDeterministic +FNDA:0,SSTORE2.writeDeterministic +DA:104,0 +DA:107,0 +BRDA:107,1,0,- +FN:121,SSTORE2.initCodeHash +FNDA:0,SSTORE2.initCodeHash +DA:133,0 +FN:143,SSTORE2.predictDeterministicAddress +FNDA:0,SSTORE2.predictDeterministicAddress +DA:148,0 +DA:156,0 +FN:167,SSTORE2.read +FNDA:0,SSTORE2.read +DA:171,0 +BRDA:171,2,0,- +DA:184,0 +FN:194,SSTORE2.read +FNDA:0,SSTORE2.read +DA:198,0 +BRDA:198,3,0,- +DA:207,0 +BRDA:207,4,0,- +DA:219,0 +FN:229,SSTORE2.read +FNDA:0,SSTORE2.read +DA:237,0 +BRDA:237,5,0,- +DA:247,0 +BRDA:247,6,0,- +DA:264,0 +FNF:7 +FNH:0 +LF:15 +LH:0 +BRF:7 +BRH:0 +end_of_record +TN: +SF:node_modules/solady/src/utils/SignatureCheckerLib.sol +FN:32,SignatureCheckerLib.isValidSignatureNow +FNDA:16,SignatureCheckerLib.isValidSignatureNow +FN:120,SignatureCheckerLib.isValidSignatureNowCalldata +FNDA:0,SignatureCheckerLib.isValidSignatureNowCalldata +FN:208,SignatureCheckerLib.isValidSignatureNow +FNDA:0,SignatureCheckerLib.isValidSignatureNow +FN:274,SignatureCheckerLib.isValidSignatureNow +FNDA:0,SignatureCheckerLib.isValidSignatureNow +FN:342,SignatureCheckerLib.isValidERC1271SignatureNow +FNDA:0,SignatureCheckerLib.isValidERC1271SignatureNow +DA:359,0 +FN:378,SignatureCheckerLib.isValidERC1271SignatureNowCalldata +FNDA:0,SignatureCheckerLib.isValidERC1271SignatureNowCalldata +DA:395,0 +FN:415,SignatureCheckerLib.isValidERC1271SignatureNow +FNDA:0,SignatureCheckerLib.isValidERC1271SignatureNow +DA:433,0 +FN:453,SignatureCheckerLib.isValidERC1271SignatureNow +FNDA:0,SignatureCheckerLib.isValidERC1271SignatureNow +DA:471,0 +FN:497,SignatureCheckerLib.toEthSignedMessageHash +FNDA:0,SignatureCheckerLib.toEthSignedMessageHash +DA:502,0 +FN:511,SignatureCheckerLib.toEthSignedMessageHash +FNDA:0,SignatureCheckerLib.toEthSignedMessageHash +DA:529,0 +FN:539,SignatureCheckerLib.emptySignature +FNDA:0,SignatureCheckerLib.emptySignature +DA:542,0 +FNF:11 +FNH:1 +LF:7 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/core/Attestation.sol +FN:11,Attestation.attest +FNDA:1538,Attestation.attest +DA:12,1538 +FN:15,Attestation.attest +FNDA:0,Attestation.attest +DA:16,0 +FN:19,Attestation.revoke +FNDA:512,Attestation.revoke +DA:20,512 +FN:23,Attestation.revoke +FNDA:0,Attestation.revoke +DA:24,0 +FN:27,Attestation.readAttestation +FNDA:520,Attestation.readAttestation +DA:35,520 +FN:38,Attestation.readAttestations +FNDA:0,Attestation.readAttestations +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +FNF:6 +FNH:3 +LF:9 +LH:3 +BRF:0 +BRH:0 end_of_record TN: -SF:src/base/RSQuery.sol -FN:21,RSQuery.verifyWithRevert -FNDA:0,RSQuery.verifyWithRevert -DA:29,0 -DA:30,0 -DA:31,0 -FN:37,RSQuery.verifyWithRevert -FNDA:0,RSQuery.verifyWithRevert +SF:src/core/AttestationManager.sol +FN:32,AttestationManager._revoke +FNDA:512,AttestationManager._revoke +DA:33,512 +DA:34,512 +DA:35,0 +FN:38,AttestationManager._revoke +FNDA:0,AttestationManager._revoke +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +BRDA:45,0,0,- +BRDA:45,0,1,- DA:46,0 -DA:47,0 -BRDA:47,0,0,- -BRDA:47,0,1,- +BRDA:46,1,0,- +BRDA:46,1,1,- +DA:52,0 +FN:55,AttestationManager._attest +FNDA:1542,AttestationManager._attest +DA:62,1542 +DA:63,1542 +DA:65,518 +DA:66,518 +FN:69,AttestationManager._attest +FNDA:4,AttestationManager._attest +DA:76,4 +DA:77,4 +DA:80,4 +DA:81,4 +DA:82,8 +DA:83,8 +DA:89,8 +BRDA:89,2,0,4 +BRDA:89,2,1,4 +DA:90,4 +BRDA:90,3,0,- +BRDA:90,3,1,4 +DA:93,4 +DA:94,4 +FN:97,AttestationManager._storeAttestation +FNDA:1550,AttestationManager._storeAttestation +DA:105,1550 +DA:107,1550 +BRDA:107,4,0,608 +BRDA:107,4,1,942 +DA:108,608 +DA:111,942 +DA:112,942 +DA:114,942 +BRDA:114,5,0,- +BRDA:114,5,1,942 +DA:115,0 +DA:117,942 +DA:120,942 +DA:121,942 +DA:123,942 +DA:134,526 +DA:136,526 +FN:139,AttestationManager._storeRevocation +FNDA:512,AttestationManager._storeRevocation +DA:146,512 +DA:149,512 +DA:152,512 +BRDA:152,6,0,512 +BRDA:152,6,1,- +DA:153,512 +DA:157,0 +BRDA:157,7,0,- +BRDA:157,7,1,- +DA:158,0 +DA:162,0 +BRDA:162,8,0,- +BRDA:162,8,1,- +DA:163,0 +DA:166,0 +DA:167,0 +DA:169,0 +FN:176,AttestationManager._getAttestation +FNDA:524,AttestationManager._getAttestation +DA:185,524 +FNF:7 +FNH:6 +LF:51 +LH:33 +BRF:18 +BRH:7 +end_of_record +TN: +SF:src/core/ModuleManager.sol +FN:37,ModuleManager.deployModule +FNDA:0,ModuleManager.deployModule +DA:48,0 DA:49,0 -DA:50,0 -BRDA:50,1,0,- -BRDA:50,1,1,- +BRDA:49,0,0,- +BRDA:49,0,1,- +DA:53,0 +DA:56,0 +DA:62,0 +FN:65,ModuleManager.registerModule +FNDA:47,ModuleManager.registerModule +DA:72,47 +DA:73,47 +BRDA:73,1,0,2 +BRDA:73,1,1,45 +DA:75,45 +DA:81,44 +FN:84,ModuleManager._storeModuleRecord +FNDA:45,ModuleManager._storeModuleRecord +DA:94,45 +BRDA:94,2,0,- +BRDA:94,2,1,45 +DA:96,45 +BRDA:96,3,0,1 +BRDA:96,3,1,44 +DA:97,1 +DA:100,44 +BRDA:100,4,0,- +BRDA:100,4,1,44 +DA:103,44 +DA:107,44 +DA:110,44 +FNF:3 +FNH:2 +LF:16 +LH:11 +BRF:10 +BRH:6 +end_of_record +TN: +SF:src/core/ResolverManager.sol +FN:37,ResolverManager.registerResolver +FNDA:42,ResolverManager.registerResolver +DA:43,42 +DA:44,42 +DA:47,42 +DA:50,42 +BRDA:50,0,0,- +BRDA:50,0,1,42 DA:51,0 -DA:52,0 -DA:54,0 -FN:60,RSQuery.verifyWithRevert -FNDA:0,RSQuery.verifyWithRevert +DA:55,42 +DA:57,42 +FN:60,ResolverManager.setResolver +FNDA:0,ResolverManager.setResolver +DA:68,0 +DA:69,0 +DA:70,0 +FNF:2 +FNH:1 +LF:10 +LH:6 +BRF:2 +BRH:1 +end_of_record +TN: +SF:src/core/SchemaManager.sol +FN:17,SchemaManager.registerSchema +FNDA:42,SchemaManager.registerSchema +DA:27,42 +DA:28,42 +DA:31,42 +DA:33,42 +BRDA:33,0,0,- +BRDA:33,0,1,42 +DA:36,42 +DA:38,42 +FNF:1 +FNH:1 +LF:6 +LH:6 +BRF:2 +BRH:1 +end_of_record +TN: +SF:src/core/SignedAttestation.sol +FN:19,SignedAttestation.attest +FNDA:8,SignedAttestation.attest +DA:28,8 +DA:29,8 +DA:30,8 +DA:31,8 +BRDA:31,0,0,4 +BRDA:31,0,1,4 +DA:33,4 +FN:36,SignedAttestation.attest +FNDA:8,SignedAttestation.attest +DA:44,8 +DA:45,8 +DA:46,8 +DA:47,8 +BRDA:47,1,0,4 +BRDA:47,1,1,4 +DA:49,4 +FN:52,SignedAttestation.revoke +FNDA:0,SignedAttestation.revoke +DA:59,0 +DA:60,0 DA:61,0 DA:62,0 -FN:68,RSQuery.verifyWithRevert -FNDA:0,RSQuery.verifyWithRevert +BRDA:62,2,0,- +BRDA:62,2,1,- +DA:64,0 +FN:67,SignedAttestation.revoke +FNDA:0,SignedAttestation.revoke +DA:74,0 +DA:75,0 DA:76,0 DA:77,0 -BRDA:77,2,0,- -BRDA:77,2,1,- +BRDA:77,3,0,- +BRDA:77,3,1,- DA:79,0 -DA:80,0 -BRDA:80,3,0,- -BRDA:80,3,1,- -DA:81,0 -DA:82,0 -DA:84,0 -FN:90,RSQuery.findAttestation -FNDA:9,RSQuery.findAttestation -DA:98,9 -DA:99,9 -FN:105,RSQuery.findAttestation -FNDA:0,RSQuery.findAttestation -DA:113,0 -DA:114,0 -DA:115,0 -DA:116,0 -FN:120,RSQuery._requireValidAttestSignatureation -FNDA:0,RSQuery._requireValidAttestSignatureation -DA:121,0 -DA:122,0 -DA:123,0 -BRDA:123,4,0,- -BRDA:123,4,1,- -DA:124,0 -BRDA:124,5,0,- -BRDA:124,5,1,- +FN:82,SignedAttestation._domainNameAndVersion +FNDA:0,SignedAttestation._domainNameAndVersion +DA:89,0 +DA:90,0 +FN:93,SignedAttestation.getDigest +FNDA:4,SignedAttestation.getDigest +DA:101,4 +FN:104,SignedAttestation.getDigest +FNDA:4,SignedAttestation.getDigest +DA:112,4 FNF:7 -FNH:1 -LF:29 -LH:2 -BRF:12 -BRH:0 +FNH:4 +LF:24 +LH:12 +BRF:8 +BRH:4 end_of_record TN: -SF:src/base/RSSchema.sol -FN:45,RSSchema.registerSchema -FNDA:14,RSSchema.registerSchema -DA:53,14 -DA:63,14 -DA:66,14 -BRDA:66,0,0,1 -BRDA:66,0,1,13 -DA:67,1 -DA:71,13 -DA:72,13 -DA:74,13 -DA:76,13 -FN:82,RSSchema.setBridges -FNDA:1,RSSchema.setBridges -DA:83,1 +SF:src/core/TrustManager.sol +FN:33,TrustManager.trustAttesters +FNDA:258,TrustManager.trustAttesters +DA:34,258 +DA:35,258 +DA:36,258 +DA:37,258 +BRDA:37,0,0,- +BRDA:37,0,1,258 +DA:38,258 +BRDA:38,1,0,1 +BRDA:38,1,1,257 +DA:41,257 +DA:43,257 +BRDA:43,2,0,- +BRDA:43,2,1,257 +DA:44,0 +DA:47,257 +DA:48,257 +DA:49,257 +DA:51,257 +DA:52,257 +DA:53,12627 +DA:54,12627 +BRDA:54,3,0,- +BRDA:54,3,1,12627 +DA:55,12627 +FN:59,TrustManager.getTrustedAttesters +FNDA:257,TrustManager.getTrustedAttesters +DA:60,257 +FN:63,TrustManager.getTrustedAttesters +FNDA:0,TrustManager.getTrustedAttesters +DA:68,257 +DA:69,257 +DA:70,257 +DA:71,257 +DA:73,257 +DA:75,12627 +FN:79,TrustManager.check +FNDA:1,TrustManager.check +DA:80,1 +FN:83,TrustManager.checkForAccount +FNDA:1,TrustManager.checkForAccount DA:84,1 -FN:90,RSSchema.setResolver -FNDA:0,RSSchema.setResolver -DA:91,0 -DA:92,0 -DA:93,0 -FN:99,RSSchema.getSchema -FNDA:123,RSSchema.getSchema -DA:100,123 -FN:106,RSSchema.getBridges -FNDA:0,RSSchema.getBridges -DA:107,0 -FN:117,RSSchema._getUID -FNDA:14,RSSchema._getUID -DA:118,14 -FN:138,RSSchema._onlySchemaOwner -FNDA:1,RSSchema._onlySchemaOwner -DA:139,1 -BRDA:139,1,0,- -BRDA:139,1,1,1 -DA:140,0 -FNF:7 -FNH:5 -LF:18 -LH:13 -BRF:4 -BRH:3 -end_of_record -TN: -SF:src/eip712/EIP712Verifier.sol -FN:58,EIP712Verifier.getDomainSeparator -FNDA:0,EIP712Verifier.getDomainSeparator -DA:59,0 -FN:69,EIP712Verifier.getNonce -FNDA:52,EIP712Verifier.getNonce -DA:70,60 -FN:76,EIP712Verifier.getAttestTypeHash -FNDA:0,EIP712Verifier.getAttestTypeHash -DA:77,0 -FN:83,EIP712Verifier.getRevokeTypeHash -FNDA:0,EIP712Verifier.getRevokeTypeHash -DA:84,0 -FN:90,EIP712Verifier.getName -FNDA:0,EIP712Verifier.getName -DA:91,0 -FN:94,EIP712Verifier.getAttestationDigest -FNDA:55,EIP712Verifier.getAttestationDigest -DA:103,55 -FN:106,EIP712Verifier.getAttestationDigest -FNDA:0,EIP712Verifier.getAttestationDigest -DA:115,0 +FN:87,TrustManager.check +FNDA:1,TrustManager.check +DA:88,1 +FN:91,TrustManager.checkForAccount +FNDA:1,TrustManager.checkForAccount +DA:99,1 +FN:102,TrustManager._check +FNDA:2,TrustManager._check +DA:103,2 +DA:105,2 +DA:106,2 +DA:107,2 +DA:110,2 +BRDA:110,4,0,- +BRDA:110,4,1,2 +DA:111,0 +DA:115,2 +BRDA:115,5,0,- +BRDA:115,5,1,2 DA:116,0 -FN:119,EIP712Verifier._attestationDigest -FNDA:113,EIP712Verifier._attestationDigest -DA:128,113 -FN:149,EIP712Verifier._requireValidAttestSignature -FNDA:58,EIP712Verifier._requireValidAttestSignature -DA:150,58 -DA:151,58 -DA:153,58 -DA:154,58 -DA:155,58 -FN:158,EIP712Verifier._newNonce -FNDA:66,EIP712Verifier._newNonce -DA:160,66 -FN:164,EIP712Verifier.getRevocationDigest -FNDA:8,EIP712Verifier.getRevocationDigest -DA:173,8 -DA:174,8 -FN:177,EIP712Verifier._revocationDigest -FNDA:16,EIP712Verifier._revocationDigest -DA:186,16 -FN:195,EIP712Verifier._verifyRevoke -FNDA:8,EIP712Verifier._verifyRevoke -DA:196,8 -DA:197,8 -DA:199,8 -DA:200,8 -DA:201,8 -FN:204,EIP712Verifier._verifySignature -FNDA:66,EIP712Verifier._verifySignature -DA:213,66 -BRDA:213,0,0,- -BRDA:213,0,1,3 -DA:215,3 -BRDA:214,1,0,- -BRDA:214,1,1,3 +DA:117,0 +DA:118,0 +DA:123,2 +DA:124,2 +DA:125,2 +DA:126,0 +DA:127,0 +DA:129,0 +DA:130,0 +DA:131,0 +DA:133,0 +BRDA:133,6,0,- +BRDA:133,6,1,- +FN:138,TrustManager._check +FNDA:2,TrustManager._check +DA:139,2 +DA:141,2 +DA:142,2 +DA:143,2 +DA:146,2 +BRDA:146,7,0,- +BRDA:146,7,1,2 +DA:147,0 +DA:151,2 +BRDA:151,8,0,- +BRDA:151,8,1,2 +DA:152,0 +DA:153,0 +DA:154,0 +DA:159,2 +DA:160,2 +DA:161,2 +DA:162,0 +DA:163,0 +DA:165,0 +DA:166,0 +DA:167,0 +DA:169,0 +BRDA:169,9,0,- +BRDA:169,9,1,- +FN:174,TrustManager._requireValidAttestation +FNDA:2,TrustManager._requireValidAttestation +DA:182,2 +DA:183,2 +DA:184,2 +DA:185,2 +DA:187,2 +BRDA:187,10,0,2 +BRDA:187,10,1,- +DA:188,2 +DA:191,0 +BRDA:191,11,0,- +BRDA:191,11,1,- +DA:192,0 +DA:195,0 +BRDA:195,12,0,- +BRDA:195,12,1,- +DA:196,0 +DA:198,0 +BRDA:198,13,0,- +BRDA:198,13,1,- +DA:199,0 +FN:203,TrustManager._requireValidAttestation +FNDA:2,TrustManager._requireValidAttestation +DA:205,2 +DA:206,2 +DA:207,2 +DA:209,2 +BRDA:209,14,0,2 +BRDA:209,14,1,- +DA:210,2 +DA:213,0 +BRDA:213,15,0,- +BRDA:213,15,1,- +DA:214,0 +DA:217,0 +BRDA:217,16,0,- +BRDA:217,16,1,- DA:218,0 -DA:221,63 -BRDA:221,2,0,- -BRDA:221,2,1,63 -DA:222,0 -FN:227,EIP712Verifier._isContract -FNDA:66,EIP712Verifier._isContract -DA:228,66 -DA:230,66 -DA:232,66 -FNF:15 +FNF:11 FNH:10 -LF:31 -LH:23 -BRF:6 -BRH:3 +LF:86 +LH:55 +BRF:34 +BRH:11 end_of_record TN: -SF:src/lib/RSModuleDeploymentLib.sol -FN:9,RSModuleDeploymentLib.codeHash -FNDA:0,RSModuleDeploymentLib.codeHash -DA:11,0 -BRDA:11,0,0,- -DA:12,0 -FN:24,RSModuleDeploymentLib.deploy -FNDA:0,RSModuleDeploymentLib.deploy -DA:32,0 -DA:38,0 -DA:41,0 -DA:42,0 -DA:44,0 -BRDA:44,1,0,- -FN:54,RSModuleDeploymentLib.calcAddress -FNDA:0,RSModuleDeploymentLib.calcAddress -DA:55,0 +SF:src/lib/AttestationLib.sol +FN:15,AttestationLib.sload2 +FNDA:0,AttestationLib.sload2 +DA:16,0 +FN:19,AttestationLib.sstore2 +FNDA:0,AttestationLib.sstore2 +DA:30,0 +FN:33,AttestationLib.sstore2Salt +FNDA:0,AttestationLib.sstore2Salt +DA:34,0 +FN:37,AttestationLib.hash +FNDA:0,AttestationLib.hash +DA:45,0 +FN:48,AttestationLib.hash +FNDA:0,AttestationLib.hash DA:56,0 -DA:58,0 -FNF:3 +FN:59,AttestationLib.hash +FNDA:0,AttestationLib.hash +DA:67,0 +FN:70,AttestationLib.hash +FNDA:0,AttestationLib.hash +DA:78,0 +FNF:7 FNH:0 -LF:10 +LF:7 LH:0 -BRF:2 +BRF:0 BRH:0 end_of_record TN: -SF:src/resolver/SchemaResolver.sol -FN:48,SchemaResolver.isPayable -FNDA:0,SchemaResolver.isPayable -DA:49,0 -FN:64,SchemaResolver.attest -FNDA:0,SchemaResolver.attest -DA:65,0 -FN:71,SchemaResolver.multiAttest -FNDA:0,SchemaResolver.multiAttest -DA:80,0 -DA:86,0 -DA:88,0 -DA:90,0 -DA:91,0 -BRDA:91,0,0,- -BRDA:91,0,1,- -DA:92,0 -DA:96,0 -BRDA:96,1,0,- -BRDA:96,1,1,- -DA:97,0 -DA:102,0 -DA:106,0 -FN:112,SchemaResolver.revoke -FNDA:0,SchemaResolver.revoke -DA:113,0 -FN:119,SchemaResolver.multiRevoke -FNDA:0,SchemaResolver.multiRevoke -DA:128,0 -DA:134,0 -DA:136,0 -DA:138,0 -DA:139,0 -BRDA:139,2,0,- -BRDA:139,2,1,- -DA:140,0 -DA:144,0 -BRDA:144,3,0,- -BRDA:144,3,1,- -DA:145,0 -DA:150,0 -DA:154,0 -FN:198,SchemaResolver._onlyRSRegistry -FNDA:0,SchemaResolver._onlyRSRegistry -DA:199,0 -BRDA:199,4,0,- -BRDA:199,4,1,- -DA:200,0 -FNF:6 +SF:src/lib/Helpers.sol +FN:14,UIDLib.getUID +FNDA:0,UIDLib.getUID +DA:15,0 +FN:27,UIDLib.getUID +FNDA:0,UIDLib.getUID +DA:28,0 +FNF:2 FNH:0 -LF:25 +LF:2 LH:0 -BRF:10 +BRF:0 BRH:0 end_of_record TN: -SF:src/resolver/examples/DebugResolver.sol -FN:15,DebugResolver.onAttest -FNDA:0,DebugResolver.onAttest -DA:24,0 -DA:26,0 -FN:29,DebugResolver.onRevoke -FNDA:0,DebugResolver.onRevoke -DA:38,0 -FNF:2 +SF:src/lib/ModuleDeploymentLib.sol +FN:17,ModuleDeploymentLib.codeHash +FNDA:0,ModuleDeploymentLib.codeHash +DA:20,0 +BRDA:20,0,0,- +DA:21,0 +FN:38,ModuleDeploymentLib.deploy +FNDA:0,ModuleDeploymentLib.deploy +DA:47,0 +DA:50,0 +DA:56,0 +DA:58,0 +BRDA:58,1,0,- +DA:59,0 +FN:75,ModuleDeploymentLib.calcAddress +FNDA:0,ModuleDeploymentLib.calcAddress +DA:76,0 +DA:77,0 +DA:79,0 +FNF:3 FNH:0 -LF:3 +LF:10 LH:0 -BRF:0 +BRF:2 BRH:0 end_of_record TN: -SF:src/resolver/examples/TokenizedResolver.sol -FN:25,TokenizedResolver.onAttest -FNDA:0,TokenizedResolver.onAttest -DA:34,0 -DA:35,0 -FN:38,TokenizedResolver.onRevoke -FNDA:0,TokenizedResolver.onRevoke -DA:47,0 -FNF:2 +SF:src/lib/ModuleTypeLib.sol +FN:8,ModuleTypeLib.isType +FNDA:0,ModuleTypeLib.isType +DA:9,0 +FN:12,ModuleTypeLib.pack +FNDA:0,ModuleTypeLib.pack +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:18,0 +FN:21,ModuleTypeLib.packCalldata +FNDA:0,ModuleTypeLib.packCalldata +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +BRDA:29,0,0,- +BRDA:29,0,1,- +DA:30,0 +DA:32,0 +FNF:3 FNH:0 -LF:3 +LF:12 LH:0 -BRF:0 +BRF:2 BRH:0 end_of_record TN: -SF:src/resolver/examples/ValueResolver.sol -FN:16,ValueResolver.onAttest -FNDA:0,ValueResolver.onAttest +SF:src/lib/StubLib.sol +FN:12,StubLib.requireExternalSchemaValidation +FNDA:0,StubLib.requireExternalSchemaValidation +DA:20,0 +BRDA:20,0,0,- +BRDA:20,0,1,- +DA:22,0 DA:25,0 -FN:28,ValueResolver.onRevoke -FNDA:0,ValueResolver.onRevoke -DA:37,0 -FN:40,ValueResolver.isPayable -FNDA:0,ValueResolver.isPayable -DA:41,0 -FNF:3 +DA:26,0 +BRDA:24,1,0,- +BRDA:24,1,1,- +DA:28,0 +FN:32,StubLib.requireExternalSchemaValidation +FNDA:0,StubLib.requireExternalSchemaValidation +DA:40,0 +BRDA:40,2,0,- +BRDA:40,2,1,- +DA:42,0 +DA:45,0 +DA:46,0 +BRDA:44,3,0,- +BRDA:44,3,1,- +DA:48,0 +FN:52,StubLib.requireExternalResolverOnAttestation +FNDA:0,StubLib.requireExternalResolverOnAttestation +DA:58,0 +DA:60,0 +BRDA:60,4,0,- +BRDA:60,4,1,- +DA:61,0 +BRDA:61,5,0,- +BRDA:61,5,1,- +DA:62,0 +FN:66,StubLib.requireExternalResolverOnAttestation +FNDA:0,StubLib.requireExternalResolverOnAttestation +DA:72,0 +DA:74,0 +BRDA:74,6,0,- +BRDA:74,6,1,- +DA:76,0 +BRDA:76,7,0,- +BRDA:76,7,1,- +DA:77,0 +FN:81,StubLib.requireExternalResolverOnRevocation +FNDA:0,StubLib.requireExternalResolverOnRevocation +DA:87,0 +DA:89,0 +BRDA:89,8,0,- +BRDA:89,8,1,- +DA:90,0 +BRDA:90,9,0,- +BRDA:90,9,1,- +DA:91,0 +FN:95,StubLib.requireExternalResolverOnRevocation +FNDA:0,StubLib.requireExternalResolverOnRevocation +DA:101,0 +DA:103,0 +BRDA:103,10,0,- +BRDA:103,10,1,- +DA:105,0 +BRDA:105,11,0,- +BRDA:105,11,1,- +DA:106,0 +FN:110,StubLib.requireExternalResolverOnModuleRegistration +FNDA:0,StubLib.requireExternalResolverOnModuleRegistration +DA:116,0 +DA:118,0 +BRDA:118,12,0,- +BRDA:118,12,1,- +DA:120,0 +BRDA:120,13,0,- +BRDA:120,13,1,- +DA:121,0 +FNF:7 FNH:0 -LF:3 +LF:30 LH:0 -BRF:0 +BRF:28 BRH:0 end_of_record TN: -SF:test/utils/BaseTest.t.sol -FN:23,MockModule.readValue -FNDA:0,MockModule.readValue -DA:24,0 +SF:test/Base.t.sol FN:44,BaseTest.setUp FNDA:0,BaseTest.setUp DA:45,0 DA:46,0 -DA:47,0 -DA:53,0 +DA:48,0 +DA:49,0 +DA:51,0 +DA:52,0 +DA:54,0 +DA:55,0 +DA:57,0 +DA:58,0 DA:60,0 DA:61,0 DA:63,0 DA:64,0 DA:66,0 DA:67,0 -DA:68,0 +DA:69,0 +DA:70,0 DA:71,0 -DA:75,0 -DA:80,0 -FN:15,MockModuleWithArgs.readValue -FNDA:0,MockModuleWithArgs.readValue -DA:16,0 -FNF:3 +DA:73,0 +FN:82,BaseTest.initDefaultEnv +FNDA:0,BaseTest.initDefaultEnv +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:90,0 +DA:91,0 +FNF:2 FNH:0 -LF:16 +LF:26 LH:0 BRF:0 BRH:0 end_of_record TN: -SF:test/utils/BaseUtils.sol -FN:202,RegistryTestTools._setupHashi -FNDA:0,RegistryTestTools._setupHashi -DA:203,0 -DA:204,0 -DA:205,0 -DA:206,0 -DA:207,0 -DA:208,0 -DA:209,0 -DA:210,0 -DA:212,0 -FN:223,RegistryTestTools._setupInstance -FNDA:0,RegistryTestTools._setupInstance -DA:232,0 -DA:234,0 -DA:242,0 -DA:243,0 -FN:53,RegistryTestLib.mockAttestation -FNDA:19,RegistryTestLib.mockAttestation -DA:62,19 -DA:71,19 -FN:74,RegistryTestLib.newAttestation -FNDA:30,RegistryTestLib.newAttestation -DA:83,49 -DA:84,49 -DA:91,49 -FN:94,RegistryTestLib.signAttestation -FNDA:49,RegistryTestLib.signAttestation -DA:104,49 -DA:105,49 -DA:111,49 -DA:112,49 -FN:115,RegistryTestLib.signAttestation -FNDA:0,RegistryTestLib.signAttestation -DA:125,0 -DA:127,0 -DA:129,0 -DA:130,0 -DA:135,0 -DA:137,0 -DA:138,0 -FN:142,RegistryTestLib.revokeAttestation -FNDA:8,RegistryTestLib.revokeAttestation -DA:150,8 -DA:151,8 -DA:153,8 -DA:154,8 -DA:156,8 -DA:157,8 -DA:159,8 -DA:165,8 -FN:168,RegistryTestLib.registerSchema -FNDA:0,RegistryTestLib.registerSchema -DA:177,0 -FN:180,RegistryTestLib.deployAndRegister -FNDA:0,RegistryTestLib.deployAndRegister -DA:189,0 -FNF:9 -FNH:4 -LF:39 -LH:17 -BRF:0 -BRH:0 +SF:test/mocks/MockERC1271Attester.sol +FN:8,MockERC1271Attester.isValidSignature +FNDA:8,MockERC1271Attester.isValidSignature +DA:9,8 +BRDA:9,0,0,4 +BRDA:9,0,1,4 +DA:10,4 +FNF:1 +FNH:1 +LF:2 +LH:2 +BRF:2 +BRH:2 end_of_record TN: -SF:test/utils/ERC1271Attester.sol -FN:10,ERC1271Attester.isValidSignature -FNDA:3,ERC1271Attester.isValidSignature -DA:18,3 +SF:test/mocks/MockModule.sol +FN:5,MockModule.foo +FNDA:0,MockModule.foo +DA:6,0 FNF:1 -FNH:1 +FNH:0 LF:1 -LH:1 +LH:0 BRF:0 BRH:0 end_of_record +TN: +SF:test/mocks/MockResolver.sol +FN:17,MockResolver.reset +FNDA:2,MockResolver.reset +DA:18,2 +DA:19,2 +DA:20,2 +FN:23,MockResolver.supportsInterface +FNDA:42,MockResolver.supportsInterface +DA:24,42 +BRDA:24,0,0,42 +BRDA:24,0,1,- +FN:27,MockResolver.resolveAttestation +FNDA:0,MockResolver.resolveAttestation +DA:33,0 +DA:34,0 +FN:37,MockResolver.resolveAttestation +FNDA:0,MockResolver.resolveAttestation +DA:43,0 +DA:44,0 +FN:47,MockResolver.resolveRevocation +FNDA:0,MockResolver.resolveRevocation +DA:53,0 +DA:54,0 +FN:57,MockResolver.resolveRevocation +FNDA:0,MockResolver.resolveRevocation +DA:63,0 +DA:64,0 +FN:67,MockResolver.resolveModuleRegistration +FNDA:0,MockResolver.resolveModuleRegistration +DA:73,0 +DA:74,0 +FNF:7 +FNH:2 +LF:14 +LH:4 +BRF:2 +BRH:1 +end_of_record +TN: +SF:test/mocks/MockSchemaValidator.sol +FN:13,MockSchemaValidator.supportsInterface +FNDA:42,MockSchemaValidator.supportsInterface +DA:14,42 +BRDA:14,0,0,42 +BRDA:14,0,1,- +FN:17,MockSchemaValidator.validateSchema +FNDA:518,MockSchemaValidator.validateSchema +DA:23,518 +FN:26,MockSchemaValidator.validateSchema +FNDA:4,MockSchemaValidator.validateSchema +DA:32,4 +FNF:3 +FNH:3 +LF:3 +LH:3 +BRF:2 +BRH:1 +end_of_record diff --git a/src/core/SignedAttestation.sol b/src/core/SignedAttestation.sol index e061a061..5502f7db 100644 --- a/src/core/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -111,4 +111,26 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { { return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); } + + function getDigest( + RevocationRequest calldata request, + address attester + ) + external + view + returns (bytes32) + { + return _hashTypedData(request.hash(attesterNonce[attester] + 1)); + } + + function getDigest( + RevocationRequest[] calldata requests, + address attester + ) + external + view + returns (bytes32) + { + return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); + } } diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 7dc8060e..4961dadf 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -155,7 +155,11 @@ contract AttestationTest is BaseTest { _; } - function test_WhenUsingValidECDSA() external whenAttestingWithSignature { + modifier whenRevokingWithSignature() { + _; + } + + function test_WhenUsingValidECDSA() public whenAttestingWithSignature { uint256 nonceBefore = registry.attesterNonce(attester1.addr); // It should recover. uint32[] memory types = new uint32[](1); @@ -177,7 +181,27 @@ contract AttestationTest is BaseTest { assertEq(nonceAfter, nonceBefore + 1); } - function test_WhenUsingValidECDSAMulti() external whenAttestingWithSignature { + function test_WhenRevokingWithValidECDSA() public { + test_WhenUsingValidECDSA(); + + RevocationRequest memory request = mockRevocation(makeAddr("module")); + bytes32 digest = registry.getDigest(request, attester1.addr); + bytes memory sig = ecdsaSign(attester1.key, digest); + registry.revoke(attester1.addr, request, sig); + } + + function test_WhenRevokingWithValidECDSAMulti() public { + test_WhenUsingValidECDSAMulti(); + + RevocationRequest[] memory requests = new RevocationRequest[](2); + requests[0] = mockRevocation(makeAddr("module")); + requests[1] = mockRevocation(makeAddr("module1")); + bytes32 digest = registry.getDigest(requests, attester1.addr); + bytes memory sig = ecdsaSign(attester1.key, digest); + registry.revoke(attester1.addr, requests, sig); + } + + function test_WhenUsingValidECDSAMulti() public whenAttestingWithSignature { uint256 nonceBefore = registry.attesterNonce(attester1.addr); // It should recover. uint32[] memory types = new uint32[](1); diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index 25958cb6..72a0147d 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -4,6 +4,12 @@ pragma solidity ^0.8.0; import "./Base.t.sol"; contract ModuleRegistrationTest is BaseTest { + function test_WhenDeployingViaRegistry() public prankWithAccount(moduleDev1) { + bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); + + registry.deployModule(salt, defaultResolverUID, address(module1).code, "", ""); + } + function test_WhenRegisteringAModuleOnAnInvalidResolverUID() external prankWithAccount(moduleDev1) From 54fe752ff03583ca52b838f571223e9cd8407975 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 15:50:54 +0700 Subject: [PATCH 21/84] foo --- test/ModuleRegistration.t.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index 72a0147d..ef4d0958 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -7,7 +7,9 @@ contract ModuleRegistrationTest is BaseTest { function test_WhenDeployingViaRegistry() public prankWithAccount(moduleDev1) { bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); - registry.deployModule(salt, defaultResolverUID, address(module1).code, "", ""); + bytes memory bytecode = type(MockModuleWithArgs).creationCode; + + address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, abi.encode(313_131), ""); } function test_WhenRegisteringAModuleOnAnInvalidResolverUID() From 22b5c38d61ec31d3c75dd67730162e018d153792 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 15:53:22 +0700 Subject: [PATCH 22/84] feat: add module deployment via registry test --- test/ModuleRegistration.t.sol | 12 +++++++++++- test/mocks/MockModule.sol | 12 ++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index ef4d0958..2e88a7c2 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -2,14 +2,24 @@ pragma solidity ^0.8.0; import "./Base.t.sol"; +import "./mocks/MockModule.sol"; contract ModuleRegistrationTest is BaseTest { function test_WhenDeployingViaRegistry() public prankWithAccount(moduleDev1) { bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); + bytes memory bytecode = type(MockModule).creationCode; + + address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, "", ""); + } + + function test_WhenDeployingViaRegistryWithArgs() public prankWithAccount(moduleDev1) { + bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); + bytes memory bytecode = type(MockModuleWithArgs).creationCode; - address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, abi.encode(313_131), ""); + address moduleAddr = + registry.deployModule(salt, defaultResolverUID, bytecode, abi.encode(313_131), ""); } function test_WhenRegisteringAModuleOnAnInvalidResolverUID() diff --git a/test/mocks/MockModule.sol b/test/mocks/MockModule.sol index d97be71f..65ee9348 100644 --- a/test/mocks/MockModule.sol +++ b/test/mocks/MockModule.sol @@ -1,6 +1,18 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; +contract MockModuleWithArgs { + uint256 value; + + constructor(uint256 _value) { + value = _value; + } + + function readValue() public view returns (uint256) { + return value; + } +} + contract MockModule { function foo() public pure returns (uint256) { return 1; From a72ac4affbf37c3eae61a2215784357c86524936 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 16:14:32 +0700 Subject: [PATCH 23/84] chore: adding resolver tests --- test/Resolver.t.sol | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/test/Resolver.t.sol b/test/Resolver.t.sol index 1a54f422..c69f0df1 100644 --- a/test/Resolver.t.sol +++ b/test/Resolver.t.sol @@ -1,17 +1,27 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -contract ResolverTest { +import "./Base.t.sol"; + +contract ResolverTest is BaseTest { modifier whenRegisteringResolver() { _; } - function test_WhenNewResolver() external whenRegisteringResolver { + function test_WhenNewResolver() external whenRegisteringResolver prankWithAccount(opsEntity1) { // It should work. + + MockResolver newResolver = new MockResolver(false); + registry.registerResolver(IExternalResolver(address(newResolver))); } function test_WhenResolverAlreadyRegistered() external whenRegisteringResolver { // It should revert. + + MockResolver newResolver = new MockResolver(false); + registry.registerResolver(IExternalResolver(address(newResolver))); + vm.expectRevert(); + registry.registerResolver(IExternalResolver(address(newResolver))); } modifier whenUpdatingResolver() { @@ -20,9 +30,24 @@ contract ResolverTest { function test_WhenUsingUnauthorizedAccount() external whenUpdatingResolver { // It should revert. + MockResolver newResolver = new MockResolver(false); + vm.prank(opsEntity1.addr); + ResolverUID resolverUID = registry.registerResolver(IExternalResolver(address(newResolver))); + + vm.expectRevert(); + registry.setResolver(resolverUID, IExternalResolver(address(newResolver))); } function test_WhenUsingAuthorizedAccount() external whenUpdatingResolver { - // It should update. + MockResolver newResolver = new MockResolver(false); + vm.prank(opsEntity1.addr); + ResolverUID resolverUID = registry.registerResolver(IExternalResolver(address(newResolver))); + + MockResolver newResolver2 = new MockResolver(false); + vm.prank(opsEntity1.addr); + registry.setResolver(resolverUID, IExternalResolver(address(newResolver2))); + + // ResolverRecord memory record = registry.resolvers(resolverUID); + // assertEq(address(record.resolver), address(newResolver2)); } } From b72bb38aa8b7c282491a70b8ad651837bcf04f51 Mon Sep 17 00:00:00 2001 From: kopy-kat Date: Wed, 7 Feb 2024 09:16:44 +0000 Subject: [PATCH 24/84] chore: add node_modules to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1e529775..cd56cb3b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,8 @@ out/ /broadcast/*/31337/ /broadcast/**/dry-run/ - # Dotenv file .env .t + +node_modules From e99abbfb2ec76fa645e8a0f8719a159dfa02d7c3 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 16:31:38 +0700 Subject: [PATCH 25/84] chore: make docs prettier --- src/DataTypes.sol | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 3534e81e..96c456d9 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.19; import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.sol"; import { IExternalResolver } from "./external/IExternalResolver.sol"; -/*////////////////////////////////////////////////////////////// - STORAGE -//////////////////////////////////////////////////////////////*/ +/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ +/* Storage Structs */ +/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Struct that represents an attestation. struct AttestationRecord { @@ -38,9 +38,9 @@ struct ResolverRecord { address resolverOwner; // The address of the account used to register the resolver. } -/*////////////////////////////////////////////////////////////// - Attestation Requests -//////////////////////////////////////////////////////////////*/ +/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ +/* Attestation / Revocation Requests */ +/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * @dev A struct representing the arguments of the attestation request. @@ -51,20 +51,17 @@ struct AttestationRequest { bytes data; // Custom attestation data. ModuleType[] moduleTypes; // optional: The type(s) of the module. } -/*////////////////////////////////////////////////////////////// - Revocation Requests -//////////////////////////////////////////////////////////////*/ - /** * @dev A struct representing the arguments of the revocation request. */ + struct RevocationRequest { address moduleAddr; // The module address. } -/*////////////////////////////////////////////////////////////// - CUSTOM TYPES -//////////////////////////////////////////////////////////////*/ +/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ +/* Custom Types */ +/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ //---------------------- SchemaUID ------------------------------| type SchemaUID is bytes32; From 01e47e3affd58fc2e2dca5f5614c38fa6bba5be2 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 17:01:26 +0700 Subject: [PATCH 26/84] chore: improving readability with custom types --- src/Common.sol | 4 +++- src/DataTypes.sol | 12 ++++++++++++ src/core/AttestationManager.sol | 30 +++++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/Common.sol b/src/Common.sol index 468e803a..ea583854 100644 --- a/src/Common.sol +++ b/src/Common.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ResolverUID, SchemaUID } from "./DataTypes.sol"; +import { ResolverUID, SchemaUID, AttestationDataRef } from "./DataTypes.sol"; // A representation of an empty/uninitialized UID. bytes32 constant EMPTY_UID = 0; @@ -13,6 +13,8 @@ uint256 constant ZERO_TIMESTAMP = 0; address constant ZERO_ADDRESS = address(0); +AttestationDataRef constant EMPTY_ATTESTATION_REF = AttestationDataRef.wrap(address(0)); + /** * @dev Returns the current's block timestamp. This method is overridden during tests and used to simulate the * current block time. diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 96c456d9..e67ed032 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -93,6 +93,18 @@ function resolverNotEq(ResolverUID uid1, ResolverUID uid2) pure returns (bool) { type AttestationDataRef is address; +using { attestationDataRefEq as == } for AttestationDataRef global; + +function attestationDataRefEq( + AttestationDataRef uid1, + AttestationDataRef uid2 +) + pure + returns (bool) +{ + return AttestationDataRef.unwrap(uid1) == AttestationDataRef.unwrap(uid2); +} + type PackedModuleTypes is uint32; type ModuleType is uint32; diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 1f5d18d4..3d853fa1 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -18,7 +18,13 @@ import { StubLib } from "../lib/StubLib.sol"; import { AttestationLib } from "../lib/AttestationLib.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; -import { EMPTY_RESOLVER_UID, ZERO_ADDRESS, ZERO_TIMESTAMP, _time } from "../Common.sol"; +import { + EMPTY_ATTESTATION_REF, + EMPTY_RESOLVER_UID, + ZERO_ADDRESS, + ZERO_TIMESTAMP, + _time +} from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, SchemaManager { @@ -52,6 +58,14 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, records.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); } + /** + * Processes an attestation request and stores the attestation in the registry. + * If the attestation was made for a module that was not registered, the function will revert. + * function will get the external Schema Validator for the supplied SchemaUID + * and call it, if an external IExternalSchemaValidator was set + * function will get the external IExternalResolver for the module - that the attestation is for + * and call it, if an external Resolver was set + */ function _attest( address attester, SchemaUID schemaUID, @@ -76,7 +90,6 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, uint256 length = requests.length; AttestationRecord[] memory records = new AttestationRecord[](length); // loop will check that the batched attestation is made ONLY for the same resolver - // @dev if you want to use different resolvers, make different attestation calls ResolverUID resolverUID; for (uint256 i; i < length; i++) { ResolverUID resolverUID_cache; @@ -86,6 +99,8 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, request: requests[i] }); // cache the first resolverUID and compare it to the rest + // If the resolverUID is different, revert + // @dev if you want to use different resolvers, make different attestation calls if (i == 0) resolverUID = resolverUID_cache; else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); } @@ -94,6 +109,9 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, records.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); } + /** + * Stores an attestation in the registry. + */ function _storeAttestation( SchemaUID schemaUID, address attester, @@ -145,11 +163,13 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, { AttestationRecord storage attestationStorage = _moduleToAttesterToAttestations[request.moduleAddr][revoker]; - // SSLOAD entire record. This will later be passed to the resolver + + // SLOAD entire record. This will later be passed to the resolver attestation = attestationStorage; + resolverUID = _modules[request.moduleAddr].resolverUID; // Ensure that we aren't attempting to revoke a non-existing attestation. - if (AttestationDataRef.unwrap(attestation.dataPointer) == ZERO_ADDRESS) { + if (attestation.dataPointer == EMPTY_ATTESTATION_REF) { revert AttestationNotFound(); } @@ -163,7 +183,7 @@ abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, revert AlreadyRevoked(); } - resolverUID = _modules[attestation.moduleAddr].resolverUID; + // SSTORE revocation time to registry storage attestationStorage.revocationTime = _time(); // set revocation time to NOW emit Revoked({ From 4fd6781828deb8e86b11f8081fbfb222ff5157eb Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 17:08:49 +0700 Subject: [PATCH 27/84] chore: sorting imports --- src/IRegistry.sol | 10 +++++----- src/core/AttestationManager.sol | 18 ++++++------------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index ead5ab9d..89b84de3 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.19; import { - SchemaUID, - ResolverUID, - AttestationRequest, - AttestationRecord, AttestationDataRef, + AttestationRecord, + AttestationRequest, + ModuleType, + ResolverUID, RevocationRequest, - ModuleType + SchemaUID } from "./DataTypes.sol"; import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.sol"; diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 3d853fa1..bfe927fd 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -2,14 +2,14 @@ pragma solidity ^0.8.19; import { - AttestationRequest, - AttestationRecord, AttestationDataRef, - RevocationRequest, + AttestationRecord, + AttestationRequest, ModuleRecord, - SchemaUID, + ModuleType, ResolverUID, - ModuleType + RevocationRequest, + SchemaUID } from "../DataTypes.sol"; import { SchemaManager } from "./SchemaManager.sol"; import { ModuleManager } from "./ModuleManager.sol"; @@ -18,13 +18,7 @@ import { StubLib } from "../lib/StubLib.sol"; import { AttestationLib } from "../lib/AttestationLib.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; -import { - EMPTY_ATTESTATION_REF, - EMPTY_RESOLVER_UID, - ZERO_ADDRESS, - ZERO_TIMESTAMP, - _time -} from "../Common.sol"; +import { EMPTY_ATTESTATION_REF, EMPTY_RESOLVER_UID, _time, ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, SchemaManager { From 9cba3b481f27fbcd6144c733cfa8db18793b046f Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 7 Feb 2024 17:11:28 +0700 Subject: [PATCH 28/84] chore: sort --- src/core/AttestationManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index bfe927fd..e5893087 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -21,7 +21,7 @@ import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; import { EMPTY_ATTESTATION_REF, EMPTY_RESOLVER_UID, _time, ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; -abstract contract AttestationManager is IRegistry, TrustManager, ModuleManager, SchemaManager { +abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManager { using StubLib for *; using AttestationLib for *; // TODO: specify what using ModuleTypeLib for ModuleType[]; From 0427f8a5a014f7c2cad08e29b215f7682aa850dd Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 8 Feb 2024 07:54:31 +0700 Subject: [PATCH 29/84] focs --- src/core/SchemaManager.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/SchemaManager.sol b/src/core/SchemaManager.sol index 1d3e2741..1faabc60 100644 --- a/src/core/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -22,8 +22,6 @@ abstract contract SchemaManager is IRegistry { onlySchemaValidator(validator) returns (SchemaUID uid) { - // TODO: ERC165 check that validator is actually a valivator - SchemaRecord memory schemaRecord = SchemaRecord({ validator: validator, registeredAt: _time(), schema: schema }); @@ -38,6 +36,9 @@ abstract contract SchemaManager is IRegistry { emit SchemaRegistered(uid, msg.sender); } + /** + * If a validator is not address(0), we check if it supports the IExternalSchemaValidator interface + */ modifier onlySchemaValidator(IExternalSchemaValidator validator) { if ( address(validator) != address(0) From 585ed9a964f6911dfbd40b226e85ebc3e981f033 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 8 Feb 2024 11:58:06 +0700 Subject: [PATCH 30/84] WIP --- src/Common.sol | 3 +- src/DataTypes.sol | 11 +++ src/IRegistry.sol | 2 + src/core/TrustManager.sol | 96 +++++------------- src/core/TrustManagerLegacy.sol | 166 ++++++++++++++++++++++++++++++++ 5 files changed, 207 insertions(+), 71 deletions(-) create mode 100644 src/core/TrustManagerLegacy.sol diff --git a/src/Common.sol b/src/Common.sol index ea583854..4b29869d 100644 --- a/src/Common.sol +++ b/src/Common.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { ResolverUID, SchemaUID, AttestationDataRef } from "./DataTypes.sol"; +import { ResolverUID, SchemaUID, AttestationDataRef, ModuleType } from "./DataTypes.sol"; // A representation of an empty/uninitialized UID. bytes32 constant EMPTY_UID = 0; @@ -12,6 +12,7 @@ SchemaUID constant EMPTY_SCHEMA_UID = SchemaUID.wrap(EMPTY_UID); uint256 constant ZERO_TIMESTAMP = 0; address constant ZERO_ADDRESS = address(0); +ModuleType constant ZERO_MODULE_TYPE = ModuleType.wrap(0); AttestationDataRef constant EMPTY_ATTESTATION_REF = AttestationDataRef.wrap(address(0)); diff --git a/src/DataTypes.sol b/src/DataTypes.sol index e67ed032..f7dc690f 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -108,3 +108,14 @@ function attestationDataRefEq( type PackedModuleTypes is uint32; type ModuleType is uint32; + +using { moduleTypeEq as == } for ModuleType global; +using { moduleTypeNeq as != } for ModuleType global; + +function moduleTypeEq(ModuleType uid1, ModuleType uid2) pure returns (bool) { + return ModuleType.unwrap(uid1) == ModuleType.unwrap(uid2); +} + +function moduleTypeNeq(ModuleType uid1, ModuleType uid2) pure returns (bool) { + return ModuleType.unwrap(uid1) != ModuleType.unwrap(uid2); +} diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 89b84de3..60d2dda5 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -44,6 +44,8 @@ interface IRegistry is IERC7484 { error InvalidModuleType(); error AttestationNotFound(); + error InsufficientAttestations(); + function trustAttesters(uint8 threshold, address[] calldata attesters) external; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 4e544b59..7c5936a2 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.19; import { AttestationRecord, PackedModuleTypes, ModuleType } from "../DataTypes.sol"; -import { ZERO_TIMESTAMP } from "../Common.sol"; +import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; +import { TrustManagerLegacy } from "./TrustManagerLegacy.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; @@ -13,7 +14,7 @@ import { LibSort } from "solady/utils/LibSort.sol"; * Implements EIP-7484 to query attestations stored in the registry. * @dev This contract is abstract and provides utility functions to query attestations. */ -abstract contract TrustManager is IRegistry { +abstract contract TrustManager is IRegistry, TrustManagerLegacy { using ModuleTypeLib for PackedModuleTypes; using LibSort for address[]; @@ -56,32 +57,12 @@ abstract contract TrustManager is IRegistry { } } - function getTrustedAttesters() public view returns (address[] memory attesters) { - return getTrustedAttesters(msg.sender); - } - - function getTrustedAttesters(address smartAccount) - public - view - returns (address[] memory attesters) - { - TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; - uint256 count = trustedAttesters.attesterCount; - attesters = new address[](count); - attesters[0] = trustedAttesters.attester; - - for (uint256 i = 1; i < count; i++) { - // get next attester from linked List - attesters[i] = trustedAttesters.linkedAttesters[attesters[i - 1]]; - } - } - function check(address module) external view { - _check(msg.sender, module); + _check(msg.sender, module, ZERO_MODULE_TYPE); } function checkForAccount(address smartAccount, address module) external view { - _check(smartAccount, module); + _check(smartAccount, module, ZERO_MODULE_TYPE); } function check(address module, ModuleType moduleType) external view { @@ -115,56 +96,20 @@ abstract contract TrustManager is IRegistry { else if (threshold == 1) { AttestationRecord storage record = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); + if (moduleType != ZERO_MODULE_TYPE) _requireValidAttestation(moduleType, record); } // smart account has more than one trusted attester else { // loop though list and check if the attestation is valid AttestationRecord storage record = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); + if (moduleType != ZERO_MODULE_TYPE) _requireValidAttestation(moduleType, record); threshold--; for (uint256 i = 1; i < attesterCount; i++) { // get next attester from linked List attester = trustedAttesters.linkedAttesters[attester]; record = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); - // if threshold reached, exit loop - if (threshold == 0) return; - } - } - } - - function _check(address smartAccount, address module) internal view { - TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; - // SLOAD from one slot - uint256 attesterCount = trustedAttesters.attesterCount; - uint256 threshold = trustedAttesters.threshold; - address attester = trustedAttesters.attester; - - // smart account has no trusted attesters set - if (attester == address(0) && threshold != 0) { - revert NoTrustedAttestersFound(); - } - // smart account only has ONE trusted attester - // use this condition to save gas - else if (threshold == 1) { - AttestationRecord storage record = - _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(record); - } - // smart account has more than one trusted attester - else { - // loop though list and check if the attestation is valid - AttestationRecord storage record = - _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(record); - threshold--; - for (uint256 i = 1; i < attesterCount; i++) { - // get next attester from linked List - attester = trustedAttesters.linkedAttesters[attester]; - record = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(record); + if (moduleType != ZERO_MODULE_TYPE) _requireValidAttestation(moduleType, record); // if threshold reached, exit loop if (threshold == 0) return; } @@ -219,12 +164,23 @@ abstract contract TrustManager is IRegistry { } } - function _getAttestation( - address module, - address attester - ) - internal + function getTrustedAttesters() public view returns (address[] memory attesters) { + return getTrustedAttesters(msg.sender); + } + + function getTrustedAttesters(address smartAccount) + public view - virtual - returns (AttestationRecord storage attestation); + returns (address[] memory attesters) + { + TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; + uint256 count = trustedAttesters.attesterCount; + attesters = new address[](count); + attesters[0] = trustedAttesters.attester; + + for (uint256 i = 1; i < count; i++) { + // get next attester from linked List + attesters[i] = trustedAttesters.linkedAttesters[attesters[i - 1]]; + } + } } diff --git a/src/core/TrustManagerLegacy.sol b/src/core/TrustManagerLegacy.sol new file mode 100644 index 00000000..b27c4e58 --- /dev/null +++ b/src/core/TrustManagerLegacy.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { AttestationRecord } from "../DataTypes.sol"; +import { ZERO_TIMESTAMP } from "../Common.sol"; +import { IRegistry } from "../IRegistry.sol"; + +abstract contract TrustManagerLegacy is IRegistry { + function check(address module, address attester) public view returns (uint256 attestedAt) { + AttestationRecord storage attestation = _getAttestation(module, attester); + + // attestedAt = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestedAt := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (attestedAt == ZERO_TIMESTAMP) { + revert AttestationNotFound(); + } + + if (expirationTime != ZERO_TIMESTAMP) { + if (block.timestamp > expirationTime) { + revert AttestationNotFound(); + } + } + + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(attestation.attester); + } + } + + function checkN( + address module, + address[] calldata attesters, + uint256 threshold + ) + external + view + returns (uint256[] memory attestedAtArray) + { + uint256 attestersLength = attesters.length; + if (attestersLength < threshold || threshold == 0) { + threshold = attestersLength; + } + + uint256 timeNow = block.timestamp; + attestedAtArray = new uint256[](attestersLength); + + for (uint256 i; i < attestersLength; ++i) { + AttestationRecord storage attestation = _getAttestation(module, attesters[i]); + + uint256 attestationTime; // = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestationTime := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(attestation.attester); + } + + if (expirationTime != ZERO_TIMESTAMP) { + if (timeNow > expirationTime) { + revert AttestationNotFound(); + } + } + + attestedAtArray[i] = attestationTime; + + if (attestationTime == ZERO_TIMESTAMP) continue; + if (threshold != 0) --threshold; + } + if (threshold == 0) return attestedAtArray; + revert InsufficientAttestations(); + } + + function checkNUnsafe( + address module, + address[] calldata attesters, + uint256 threshold + ) + external + view + returns (uint256[] memory attestedAtArray) + { + uint256 attestersLength = attesters.length; + if (attestersLength < threshold || threshold == 0) { + threshold = attestersLength; + } + + uint256 timeNow = block.timestamp; + attestedAtArray = new uint256[](attestersLength); + + for (uint256 i; i < attestersLength; ++i) { + AttestationRecord storage attestation = _getAttestation(module, attesters[i]); + + uint256 attestationTime; // = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestationTime := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (revocationTime != ZERO_TIMESTAMP) { + attestedAtArray[i] = 0; + continue; + } + + attestedAtArray[i] = attestationTime; + + if (expirationTime != ZERO_TIMESTAMP) { + if (timeNow > expirationTime) { + attestedAtArray[i] = 0; + continue; + } + } + + if (attestationTime == ZERO_TIMESTAMP) continue; + if (threshold != 0) --threshold; + } + if (threshold == 0) return attestedAtArray; + revert InsufficientAttestations(); + } + + function _getAttestation( + address module, + address attester + ) + internal + view + virtual + returns (AttestationRecord storage attestation); +} From 7a050a85fe7b322c5cfefdea9373611edff98d33 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 8 Feb 2024 11:58:11 +0700 Subject: [PATCH 31/84] chore: adding docs --- src/IRegistry.sol | 35 ++++- src/core/Attestation.sol | 4 +- src/core/AttestationManager.sol | 145 +++++++++++++----- src/core/ModuleManager.sol | 3 + src/core/ResolverManager.sol | 32 ++-- src/core/SignedAttestation.sol | 8 +- src/core/TrustManager.sol | 4 +- ...l => TrustManagerExternalAttesterList.sol} | 2 +- src/lib/StubLib.sol | 1 - 9 files changed, 170 insertions(+), 64 deletions(-) rename src/core/{TrustManagerLegacy.sol => TrustManagerExternalAttesterList.sol} (98%) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 60d2dda5..d0d9bced 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -38,6 +38,7 @@ interface IRegistry is IERC7484 { event NewTrustedAttesters(); error InvalidResolver(IExternalResolver resolver); + error InvalidResolverUID(ResolverUID uid); error InvalidTrustedAttesterInput(); error NoTrustedAttestersFound(); error RevokedAttestation(address attester); @@ -68,8 +69,30 @@ interface IRegistry is IERC7484 { error InvalidSignature(); error InvalidModuleTypes(); + /** + * Allows msg.sender to attest to multiple modules' security status. + * The AttestationRequest.Data provided should match the attestation + * schema defined by the Schema corresponding to the SchemaUID + * + * @dev This function will revert if the same module is attested twice by the same attester. + * If you want to re-attest, you have to revoke your attestation first, and then attest again. + * + * @param schemaUID The SchemaUID of the schema the attestation is based on. + * @param request a single AttestationRequest + */ function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; + /** + * Allows msg.sender to attest to multiple modules' security status. + * The AttestationRequest.Data provided should match the attestation + * schema defined by the Schema corresponding to the SchemaUID + * + * @dev This function will revert if the same module is attested twice by the same attester. + * If you want to re-attest, you have to revoke your attestation first, and then attest again. + * + * @param schemaUID The SchemaUID of the schema the attestation is based on. + * @param requests An array of AttestationRequest + */ function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; function attest( @@ -88,21 +111,21 @@ interface IRegistry is IERC7484 { ) external; - function readAttestations( + function findAttestation( address module, - address[] calldata attesters + address attester ) external view - returns (AttestationRecord[] memory attestations); + returns (AttestationRecord memory attestation); - function readAttestation( + function findAttestations( address module, - address attester + address[] calldata attesters ) external view - returns (AttestationRecord memory attestation); + returns (AttestationRecord[] memory attestations); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Revocations */ diff --git a/src/core/Attestation.sol b/src/core/Attestation.sol index 779ae3f0..d5b997d1 100644 --- a/src/core/Attestation.sol +++ b/src/core/Attestation.sol @@ -24,7 +24,7 @@ abstract contract Attestation is IRegistry, AttestationManager { _revoke(msg.sender, requests); } - function readAttestation( + function findAttestation( address module, address attester ) @@ -35,7 +35,7 @@ abstract contract Attestation is IRegistry, AttestationManager { attestation = _getAttestation(module, attester); } - function readAttestations( + function findAttestations( address module, address[] calldata attesters ) diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index e5893087..bdd7fcaa 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -17,40 +17,28 @@ import { TrustManager } from "./TrustManager.sol"; import { StubLib } from "../lib/StubLib.sol"; import { AttestationLib } from "../lib/AttestationLib.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; +import { IRegistry } from "../IRegistry.sol"; import { EMPTY_ATTESTATION_REF, EMPTY_RESOLVER_UID, _time, ZERO_TIMESTAMP } from "../Common.sol"; -import { IRegistry } from "../IRegistry.sol"; +/** + * AttestationManager handles the registry's internal storage of new attestations and revocation of attestation + * @dev This contract is abstract and provides utility functions to store attestations and revocations. + */ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManager { using StubLib for *; - using AttestationLib for *; // TODO: specify what + using AttestationLib for AttestationDataRef; + using AttestationLib for AttestationRequest; + using AttestationLib for AttestationRequest[]; + using AttestationLib for address; using ModuleTypeLib for ModuleType[]; mapping(address module => mapping(address attester => AttestationRecord attestation)) internal _moduleToAttesterToAttestations; - function _revoke(address attester, RevocationRequest calldata request) internal { - (AttestationRecord memory record, ResolverUID resolverUID) = - _storeRevocation(attester, request); - record.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); - } - - function _revoke(address attester, RevocationRequest[] calldata requests) internal { - uint256 length = requests.length; - AttestationRecord[] memory records = new AttestationRecord[](length); - ResolverUID resolverUID; - for (uint256 i; i < length; i++) { - ResolverUID resolverUID_cache; - (records[i], resolverUID_cache) = _storeRevocation(attester, requests[i]); - if (i == 0) resolverUID = resolverUID_cache; - else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); - } - - // No schema validation required during revocation. the attestation data was already checked against - - // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? - records.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); - } + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Attestation */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** * Processes an attestation request and stores the attestation in the registry. @@ -74,6 +62,14 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, record.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); } + /** + * Processes an array of attestation requests and stores the attestations in the registry. + * If the attestation was made for a module that was not registered, the function will revert. + * function will get the external Schema Validator for the supplied SchemaUID + * and call it, if an external IExternalSchemaValidator was set + * function will get the external IExternalResolver for the module - that the attestation is for + * and call it, if an external Resolver was set + */ function _attest( address attester, SchemaUID schemaUID, @@ -87,6 +83,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, ResolverUID resolverUID; for (uint256 i; i < length; i++) { ResolverUID resolverUID_cache; + // save the attestation record into records array. (records[i], resolverUID_cache) = _storeAttestation({ schemaUID: schemaUID, attester: attester, @@ -99,12 +96,26 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); } + // Use StubLib to call schema Validation and resolver if needed records.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); records.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); } /** - * Stores an attestation in the registry. + * Stores an attestation in the registry storage. + * The bytes encoded AttestationRequest.Data is not stored directly into the registry storage, + * but rather stored with SSTORE2. SSTORE2/SLOAD2 is writing and reading contract storage + * paying a fraction of the cost, it uses contract code as storage, writing data takes the + * form of contract creations and reading data uses EXTCODECOPY. + * since attestation data is supposed to be immutable, it is a good candidate for SSTORE2 + * + * @dev This function will revert if the same module is attested twice by the same attester. + * If you want to re-attest, you have to revoke your attestation first, and then attest again. + * + * @param attester The address of the attesting account. + * @param request The AttestationRequest that was supplied via calldata + * @return record The AttestationRecord of what was written into registry storage + * @return resolverUID The resolverUID in charge for the module */ function _storeAttestation( SchemaUID schemaUID, @@ -114,6 +125,8 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, internal returns (AttestationRecord memory record, ResolverUID resolverUID) { + // TODO: what if schema behind schemaUID doesnt exist? + // Frontrun on L2s? uint48 timeNow = _time(); // Ensure that either no expiration time was set or that it was set in the future. if (request.expirationTime != ZERO_TIMESTAMP && request.expirationTime <= timeNow) { @@ -121,17 +134,19 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, } // caching module address. address module = request.moduleAddr; - ModuleRecord storage moduleRecord = _modules[request.moduleAddr]; + // SLOAD the resolverUID from the moduleRecord + resolverUID = _modules[module].resolverUID; // Ensure that attestation is for module that was registered. - if (moduleRecord.resolverUID != EMPTY_RESOLVER_UID) { + if (resolverUID != EMPTY_RESOLVER_UID) { revert InvalidAttestation(); } - resolverUID = moduleRecord.resolverUID; - // get salt used for SSTORE2 to avoid collisions during CREATE2 - bytes32 attestationSalt = attester.sstore2Salt(module); - AttestationDataRef sstore2Pointer = request.sstore2(attestationSalt); + // use SSTORE2 to store the data in attestationRequest + // @dev this will revert, if in a batched attestation, + // the same data is used twice by the same attester for the same module since the salt will be the same + AttestationDataRef sstore2Pointer = request.sstore2({ salt: attester.sstore2Salt(module) }); + // write into memory allocated record, since that is the return value record = AttestationRecord({ time: timeNow, expirationTime: request.expirationTime, @@ -148,45 +163,93 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, emit Attested(module, attester, schemaUID, sstore2Pointer); } + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Revocation */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /** + * Revoke a single Revocation Request + * This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, + * and pass the RevocationRecord to the resolver to check if the revocation is valid + */ + function _revoke(address attester, RevocationRequest calldata request) internal { + (AttestationRecord memory record, ResolverUID resolverUID) = + _storeRevocation(attester, request); + // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? + record.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); + } + + /** + * Revoke an array Revocation Request + * This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, + * and pass the RevocationRecord to the resolver to check if the revocation is valid + */ + function _revoke(address attester, RevocationRequest[] calldata requests) internal { + uint256 length = requests.length; + AttestationRecord[] memory records = new AttestationRecord[](length); + ResolverUID resolverUID; + // loop over revocation requests. This function will revert if different resolverUIDs + // are responsible for the modules that are subject of the revocation. This is to reduce complexity + // @dev if you want to revoke attestations from different resolvers, make different revocation calls + for (uint256 i; i < length; i++) { + ResolverUID resolverUID_cache; + (records[i], resolverUID_cache) = _storeRevocation(attester, requests[i]); + if (i == 0) resolverUID = resolverUID_cache; + else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); + } + + // No schema validation required during revocation. the attestation data was already checked against + + // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? + records.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); + } + + /** + * Gets the AttestationRecord for the supplied RevocationRequest and stores the revocation time in the registry storage + * @param revoker The address of the attesting account. + * @param request The AttestationRequest that was supplied via calldata + * @return record The AttestationRecord of what was written into registry storage + * @return resolverUID The resolverUID in charge for the module + */ function _storeRevocation( address revoker, RevocationRequest calldata request ) internal - returns (AttestationRecord memory attestation, ResolverUID resolverUID) + returns (AttestationRecord memory record, ResolverUID resolverUID) { AttestationRecord storage attestationStorage = _moduleToAttesterToAttestations[request.moduleAddr][revoker]; // SLOAD entire record. This will later be passed to the resolver - attestation = attestationStorage; + record = attestationStorage; resolverUID = _modules[request.moduleAddr].resolverUID; // Ensure that we aren't attempting to revoke a non-existing attestation. - if (attestation.dataPointer == EMPTY_ATTESTATION_REF) { + if (record.dataPointer == EMPTY_ATTESTATION_REF) { revert AttestationNotFound(); } // Allow only original attesters to revoke their attestations. - if (attestation.attester != revoker) { + if (record.attester != revoker) { revert AccessDenied(); } // Ensure that we aren't trying to revoke the same attestation twice. - if (attestation.revocationTime != ZERO_TIMESTAMP) { + if (record.revocationTime != ZERO_TIMESTAMP) { revert AlreadyRevoked(); } // SSTORE revocation time to registry storage attestationStorage.revocationTime = _time(); // set revocation time to NOW - emit Revoked({ - moduleAddr: attestation.moduleAddr, - revoker: revoker, - schema: attestation.schemaUID - }); + emit Revoked({ moduleAddr: record.moduleAddr, revoker: revoker, schema: record.schemaUID }); } + /** + * Returns the attestation records for the given module and attesters. + * This function is expected to be used by TrustManager and TrustManagerExternalAttesterList + */ function _getAttestation( address module, address attester diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 8bd92ce1..ebd003d8 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -70,6 +70,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { external { ResolverRecord storage resolver = resolvers[resolverUID]; + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); ModuleRecord memory record = _storeModuleRecord({ @@ -78,6 +79,8 @@ abstract contract ModuleManager is IRegistry, ResolverManager { resolverUID: resolverUID, metadata: metadata }); + // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s + // is this a problemt? record.requireExternalResolverOnModuleRegistration(resolver); } diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index 42fffce4..87081b84 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; -import { ZERO_ADDRESS } from "../Common.sol"; +import { EMPTY_RESOLVER_UID, ZERO_ADDRESS } from "../Common.sol"; import { IExternalResolver } from "../external/IExternalResolver.sol"; import { UIDLib } from "../lib/Helpers.sol"; import { IRegistry } from "../IRegistry.sol"; @@ -24,6 +24,16 @@ abstract contract ResolverManager is IRegistry { _; } + modifier notZero(ResolverUID uid) { + if (uid == EMPTY_RESOLVER_UID) { + revert InvalidResolverUID(uid); + } + _; + } + + /** + * If a resolver is not address(0), we check if it supports the IExternalResolver interface + */ modifier onlyResolver(IExternalResolver resolver) { if ( address(resolver) == address(0) @@ -34,36 +44,38 @@ abstract contract ResolverManager is IRegistry { _; } - function registerResolver(IExternalResolver _resolver) + function registerResolver(IExternalResolver resolver) external - onlyResolver(_resolver) + onlyResolver(resolver) returns (ResolverUID uid) { // build a ResolverRecord from the input - ResolverRecord memory resolver = - ResolverRecord({ resolver: _resolver, resolverOwner: msg.sender }); + ResolverRecord memory resolverRecord = + ResolverRecord({ resolver: resolver, resolverOwner: msg.sender }); // Computing a unique ID for the schema using its properties - uid = resolver.getUID(); + uid = resolverRecord.getUID(); // Checking if a schema with this UID already exists -> resolver can never be ZERO_ADDRESS if (address(resolvers[uid].resolver) != ZERO_ADDRESS) { revert ResolverAlreadyExists(); } - // Storing schema in the _schemas mapping - resolvers[uid] = resolver; + // SSTORE schema in the resolvers mapping + resolvers[uid] = resolverRecord; - emit NewResolver(uid, address(_resolver)); + emit NewResolver(uid, address(resolver)); } + // TODO: VULN: + // Attacker could register the same resolver, thus be the owner of the resolverUID, then set the resolver to a malicious contract function setResolver( ResolverUID uid, IExternalResolver resolver ) external onlyResolver(resolver) - onlyResolverOwner(uid) + onlyResolverOwner(uid) // authorization control { ResolverRecord storage referrer = resolvers[uid]; referrer.resolver = resolver; diff --git a/src/core/SignedAttestation.sol b/src/core/SignedAttestation.sol index 5502f7db..16f5ee8f 100644 --- a/src/core/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -10,12 +10,18 @@ import { IRegistry } from "../IRegistry.sol"; contract SignedAttestation is IRegistry, Attestation, EIP712 { using AttestationLib for AttestationRequest; - using AttestationLib for AttestationRequest[]; using AttestationLib for RevocationRequest; + using AttestationLib for AttestationRequest[]; using AttestationLib for RevocationRequest[]; mapping(address attester => uint256 nonce) public attesterNonce; + /** + * Attestation can be made on any SchemaUID. + * @dev the registry, will forward your AttestationRequest.Data to the SchemaManager to + * check if the schema exists. + * @param schemaUID The SchemaUID of the schema to be attested. + */ function attest( SchemaUID schemaUID, address attester, diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 7c5936a2..3d9f1b74 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.19; import { AttestationRecord, PackedModuleTypes, ModuleType } from "../DataTypes.sol"; import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; -import { TrustManagerLegacy } from "./TrustManagerLegacy.sol"; +import { TrustManagerExternalAttesterList } from "./TrustManagerExternalAttesterList.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; @@ -14,7 +14,7 @@ import { LibSort } from "solady/utils/LibSort.sol"; * Implements EIP-7484 to query attestations stored in the registry. * @dev This contract is abstract and provides utility functions to query attestations. */ -abstract contract TrustManager is IRegistry, TrustManagerLegacy { +abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { using ModuleTypeLib for PackedModuleTypes; using LibSort for address[]; diff --git a/src/core/TrustManagerLegacy.sol b/src/core/TrustManagerExternalAttesterList.sol similarity index 98% rename from src/core/TrustManagerLegacy.sol rename to src/core/TrustManagerExternalAttesterList.sol index b27c4e58..935f8092 100644 --- a/src/core/TrustManagerLegacy.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -5,7 +5,7 @@ import { AttestationRecord } from "../DataTypes.sol"; import { ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; -abstract contract TrustManagerLegacy is IRegistry { +abstract contract TrustManagerExternalAttesterList is IRegistry { function check(address module, address attester) public view returns (uint256 attestedAt) { AttestationRecord storage attestation = _getAttestation(module, attester); diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index 45809738..de442f0b 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -7,7 +7,6 @@ import { IExternalResolver } from "../external/IExternalResolver.sol"; import { ZERO_ADDRESS, ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; -// TODO: fix errors library StubLib { function requireExternalSchemaValidation( AttestationRecord memory attestationRecord, From c064bccc71e0f8792f820f6e90b5e5753c2c6f51 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 8 Feb 2024 12:08:14 +0700 Subject: [PATCH 32/84] feat: adding legacy functions to check --- src/IRegistry.sol | 26 +++++++++++++++ src/core/ModuleManager.sol | 32 +++++++++++++++++++ src/core/TrustManagerExternalAttesterList.sol | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index d0d9bced..307a4127 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -15,6 +15,9 @@ import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.so import { IExternalResolver } from "./external/IExternalResolver.sol"; interface IERC7484 { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Check with Registry internal attesters */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function check(address module) external view; function checkForAccount(address smartAccount, address module) external view; @@ -28,6 +31,21 @@ interface IERC7484 { ) external view; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Check with external attester(s) */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + function check(address module, address attester) external view returns (uint256 attestedAt); + + function checkN( + address module, + address[] calldata attesters, + uint256 threshold + ) + external + view + returns (uint256[] memory attestedAtArray); } interface IRegistry is IERC7484 { @@ -47,6 +65,14 @@ interface IRegistry is IERC7484 { error InsufficientAttestations(); + /** + * Allows smartaccounts - the end users of the registry - to appoint + * one or many attesters as trusted. + * + * @param threshold The minimum number of attestations required for a module + * to be considered secure. + * @param attesters The addresses of the attesters to be trusted. + */ function trustAttesters(uint8 threshold, address[] calldata attesters) external; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index ebd003d8..aae0c47f 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -84,6 +84,38 @@ abstract contract ModuleManager is IRegistry, ResolverManager { record.requireExternalResolverOnModuleRegistration(resolver); } + function deployViaFactory( + address factory, + bytes calldata callOnFactory, + bytes calldata metadata, + ResolverUID resolverUID + ) + external + payable + returns (address moduleAddress) + { + ResolverRecord storage resolver = resolvers[resolverUID]; + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolverUID(resolverUID); + // call external factory to deploy module + (bool ok, bytes memory returnData) = factory.call{ value: msg.value }(callOnFactory); + if (!ok) revert InvalidDeployment(); + + moduleAddress = abi.decode(returnData, (address)); + if (moduleAddress == ZERO_ADDRESS) revert InvalidDeployment(); + if (_isContract(moduleAddress) != true) revert InvalidDeployment(); + + ModuleRecord memory record = _storeModuleRecord({ + moduleAddress: moduleAddress, + // TODO: should we use msg.sender or the factory address? + sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function + resolverUID: resolverUID, + metadata: metadata + }); + // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s + // is this a problemt? + record.requireExternalResolverOnModuleRegistration(resolver); + } + function _storeModuleRecord( address moduleAddress, address sender, diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index 935f8092..29e73506 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -6,7 +6,7 @@ import { ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; abstract contract TrustManagerExternalAttesterList is IRegistry { - function check(address module, address attester) public view returns (uint256 attestedAt) { + function check(address module, address attester) external view returns (uint256 attestedAt) { AttestationRecord storage attestation = _getAttestation(module, attester); // attestedAt = attestation.time; From f53ccdee2248a9aabea2bedbf1b95a471e747a99 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 8 Feb 2024 13:57:39 +0700 Subject: [PATCH 33/84] feat: fix all tests --- lcov.info | 8 ++--- src/IRegistry.sol | 16 ++++++++++ src/core/AttestationManager.sol | 10 +++--- src/core/ModuleManager.sol | 15 +++++++-- src/core/TrustManager.sol | 56 ++++++++++++++------------------- src/lib/AttestationLib.sol | 2 ++ src/lib/Helpers.sol | 2 ++ src/lib/ModuleDeploymentLib.sol | 2 -- test/Attestation.t.sol | 51 ++++++++++++++---------------- test/Base.t.sol | 2 ++ test/ModuleRegistration.t.sol | 30 +++++++++++++----- test/TrustDelegation.t.sol | 11 +++++-- 12 files changed, 123 insertions(+), 82 deletions(-) diff --git a/lcov.info b/lcov.info index bed8558f..0fba2696 100644 --- a/lcov.info +++ b/lcov.info @@ -10592,11 +10592,11 @@ DA:20,512 FN:23,Attestation.revoke FNDA:0,Attestation.revoke DA:24,0 -FN:27,Attestation.readAttestation -FNDA:520,Attestation.readAttestation +FN:27,Attestation.findAttestation +FNDA:520,Attestation.findAttestation DA:35,520 -FN:38,Attestation.readAttestations -FNDA:0,Attestation.readAttestations +FN:38,Attestation.findAttestations +FNDA:0,Attestation.findAttestations DA:46,0 DA:47,0 DA:48,0 diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 307a4127..317b74f2 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -6,6 +6,7 @@ import { AttestationRecord, AttestationRequest, ModuleType, + ModuleRecord, ResolverUID, RevocationRequest, SchemaUID @@ -75,6 +76,15 @@ interface IRegistry is IERC7484 { */ function trustAttesters(uint8 threshold, address[] calldata attesters) external; + /** + * Get trusted attester for a specific smartAccount + * @param smartAccount The address of the smartAccount + */ + function getTrustedAttesters(address smartAccount) + external + view + returns (address[] memory attesters); + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Attestations */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ @@ -88,6 +98,7 @@ interface IRegistry is IERC7484 { ); error AlreadyRevoked(); + error ModuleNotFoundInRegistry(address module); error AccessDenied(); error InvalidAttestation(); error InvalidExpirationTime(); @@ -208,6 +219,11 @@ interface IRegistry is IERC7484 { ) external; + function getRegisteredModules(address moduleAddress) + external + view + returns (ModuleRecord memory moduleRecord); + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Manage Schemas */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index bdd7fcaa..7584aa56 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -21,10 +21,12 @@ import { IRegistry } from "../IRegistry.sol"; import { EMPTY_ATTESTATION_REF, EMPTY_RESOLVER_UID, _time, ZERO_TIMESTAMP } from "../Common.sol"; +import "forge-std/console2.sol"; /** * AttestationManager handles the registry's internal storage of new attestations and revocation of attestation * @dev This contract is abstract and provides utility functions to store attestations and revocations. */ + abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManager { using StubLib for *; using AttestationLib for AttestationDataRef; @@ -135,10 +137,10 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, // caching module address. address module = request.moduleAddr; // SLOAD the resolverUID from the moduleRecord - resolverUID = _modules[module].resolverUID; + resolverUID = _moduleAddrToRecords[module].resolverUID; // Ensure that attestation is for module that was registered. - if (resolverUID != EMPTY_RESOLVER_UID) { - revert InvalidAttestation(); + if (resolverUID == EMPTY_RESOLVER_UID) { + revert ModuleNotFoundInRegistry(module); } // use SSTORE2 to store the data in attestationRequest @@ -223,7 +225,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, // SLOAD entire record. This will later be passed to the resolver record = attestationStorage; - resolverUID = _modules[request.moduleAddr].resolverUID; + resolverUID = _moduleAddrToRecords[request.moduleAddr].resolverUID; // Ensure that we aren't attempting to revoke a non-existing attestation. if (record.dataPointer == EMPTY_ATTESTATION_REF) { diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index aae0c47f..50bd5174 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -32,7 +32,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { using ModuleDeploymentLib for address; using StubLib for *; - mapping(address moduleAddress => ModuleRecord moduleRecord) internal _modules; + mapping(address moduleAddress => ModuleRecord moduleRecord) internal _moduleAddrToRecords; function deployModule( bytes32 salt, @@ -50,6 +50,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { // address predictedModuleAddress = code.calculateAddress(deployParams, salt); + // TODO: should we use the initCode hash return value? (moduleAddr,,) = code.deploy(deployParams, salt, msg.value); // _storeModuleRecord() will check if module is already registered, // which should prevent reentry to any deploy function @@ -128,7 +129,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { // ensure that non-zero resolverUID was provided if (resolverUID == EMPTY_RESOLVER_UID) revert InvalidDeployment(); // ensure moduleAddress is not already registered - if (_modules[moduleAddress].resolverUID != EMPTY_RESOLVER_UID) { + if (_moduleAddrToRecords[moduleAddress].resolverUID != EMPTY_RESOLVER_UID) { revert AlreadyRegistered(moduleAddress); } // revert if moduleAddress is NOT a contract @@ -139,9 +140,17 @@ abstract contract ModuleManager is IRegistry, ResolverManager { ModuleRecord({ resolverUID: resolverUID, sender: sender, metadata: metadata }); // Store module record in _modules mapping - _modules[moduleAddress] = moduleRegistration; + _moduleAddrToRecords[moduleAddress] = moduleRegistration; // Emit ModuleRegistration event emit ModuleRegistration(moduleAddress, sender, ResolverUID.unwrap(resolverUID)); } + + function getRegisteredModules(address moduleAddress) + external + view + returns (ModuleRecord memory moduleRecord) + { + return _moduleAddrToRecords[moduleAddress]; + } } diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 3d9f1b74..c6689fb5 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -2,12 +2,14 @@ pragma solidity ^0.8.19; import { AttestationRecord, PackedModuleTypes, ModuleType } from "../DataTypes.sol"; -import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE } from "../Common.sol"; +import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE, ZERO_ADDRESS } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; import { TrustManagerExternalAttesterList } from "./TrustManagerExternalAttesterList.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; +import "forge-std/console2.sol"; + /** * @title TrustManager * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) @@ -52,9 +54,11 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { attestersLength--; for (uint256 i; i < attestersLength; i++) { address _attester = attesters[i]; - if (_attester == address(0)) revert InvalidTrustedAttesterInput(); + // user could have set attester to address(0) + if (_attester == ZERO_ADDRESS) revert InvalidTrustedAttesterInput(); _att.linkedAttesters[_attester] = attesters[i + 1]; } + emit NewTrustedAttesters(); } function check(address module) external view { @@ -88,7 +92,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { address attester = trustedAttesters.attester; // smart account has no trusted attesters set - if (attester == address(0) && threshold != 0) { + if (attester == ZERO_ADDRESS && threshold != 0) { revert NoTrustedAttestersFound(); } // smart account only has ONE trusted attester @@ -96,26 +100,32 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { else if (threshold == 1) { AttestationRecord storage record = _getAttestation({ module: module, attester: attester }); - if (moduleType != ZERO_MODULE_TYPE) _requireValidAttestation(moduleType, record); + _requireValidAttestation(moduleType, record); } // smart account has more than one trusted attester else { // loop though list and check if the attestation is valid AttestationRecord storage record = _getAttestation({ module: module, attester: attester }); - if (moduleType != ZERO_MODULE_TYPE) _requireValidAttestation(moduleType, record); - threshold--; + _requireValidAttestation(moduleType, record); for (uint256 i = 1; i < attesterCount; i++) { + threshold--; // get next attester from linked List attester = trustedAttesters.linkedAttesters[attester]; record = _getAttestation({ module: module, attester: attester }); - if (moduleType != ZERO_MODULE_TYPE) _requireValidAttestation(moduleType, record); + _requireValidAttestation(moduleType, record); // if threshold reached, exit loop if (threshold == 0) return; } } } + /** + * Check that attestationRecord is valid: + * - not revoked + * - not expired + * - correct module type (if not ZERO_MODULE_TYPE) + */ function _requireValidAttestation( ModuleType expectedType, AttestationRecord storage record @@ -123,51 +133,33 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { internal view { - // cache values + // cache values TODO:: assembly to ensure single SLOAD uint256 attestedAt = record.time; uint256 expirationTime = record.expirationTime; uint256 revocationTime = record.revocationTime; PackedModuleTypes packedModuleType = record.moduleTypes; + // check if any attestation was made if (attestedAt == ZERO_TIMESTAMP) { revert AttestationNotFound(); } + // check if attestation has expired if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { revert AttestationNotFound(); } + // check if attestation has been revoked if (revocationTime != ZERO_TIMESTAMP) { revert RevokedAttestation(record.attester); } - if (!packedModuleType.isType(expectedType)) { + // if a expectedType is set, check if the attestation is for the correct module type + // if no expectedType is set, module type is not checked + if (expectedType != ZERO_MODULE_TYPE && !packedModuleType.isType(expectedType)) { revert InvalidModuleType(); } } - function _requireValidAttestation(AttestationRecord storage record) internal view { - // cache values - uint256 attestedAt = record.time; - uint256 expirationTime = record.expirationTime; - uint256 revocationTime = record.revocationTime; - - if (attestedAt == ZERO_TIMESTAMP) { - revert AttestationNotFound(); - } - - if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { - revert AttestationNotFound(); - } - - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(record.attester); - } - } - - function getTrustedAttesters() public view returns (address[] memory attesters) { - return getTrustedAttesters(msg.sender); - } - function getTrustedAttesters(address smartAccount) public view diff --git a/src/lib/AttestationLib.sol b/src/lib/AttestationLib.sol index 1e99e21d..8d1f68fc 100644 --- a/src/lib/AttestationLib.sol +++ b/src/lib/AttestationLib.sol @@ -64,6 +64,7 @@ library AttestationLib { pure returns (bytes32 _hash) { + // TODO: check if this is correct _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); } @@ -75,6 +76,7 @@ library AttestationLib { pure returns (bytes32 _hash) { + // TODO: check if this is correct _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); } } diff --git a/src/lib/Helpers.sol b/src/lib/Helpers.sol index f5fca240..e6c4c490 100644 --- a/src/lib/Helpers.sol +++ b/src/lib/Helpers.sol @@ -12,6 +12,7 @@ library UIDLib { * @return schema UID. */ function getUID(SchemaRecord memory schemaRecord) internal pure returns (SchemaUID) { + // TODO: this is a frontrunning vuln return SchemaUID.wrap( keccak256(abi.encodePacked(schemaRecord.schema, address(schemaRecord.validator))) ); @@ -25,6 +26,7 @@ library UIDLib { * @return ResolverUID. */ function getUID(ResolverRecord memory resolver) internal pure returns (ResolverUID) { + // TODO: this is a frontrunning vuln return ResolverUID.wrap(keccak256(abi.encodePacked(resolver.resolver))); } } diff --git a/src/lib/ModuleDeploymentLib.sol b/src/lib/ModuleDeploymentLib.sol index 162fdb9c..ccdb1cd6 100644 --- a/src/lib/ModuleDeploymentLib.sol +++ b/src/lib/ModuleDeploymentLib.sol @@ -49,8 +49,6 @@ library ModuleDeploymentLib { // if params were abi.encodePacked in createCode, this will revert initCodeHash = keccak256(initCode); - // TODO: can we use a lib for this? - // solhint-disable-next-line no-inline-assembly assembly { moduleAddress := create2(value, add(initCode, 0x20), mload(initCode), salt) diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 4961dadf..ef5e1fa9 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -39,16 +39,15 @@ contract AttestationTest is BaseTest { request = RevocationRequest({ moduleAddr: module }); } - function test_WhenAttestingWithNoAttestationData(address module) - public - prankWithAccount(attester1) - { + function test_WhenAttestingWithNoAttestationData() public prankWithAccount(attester1) { + address module = address(new MockModule()); + registry.registerModule(defaultResolverUID, module, ""); uint32[] memory types = new uint32[](1); AttestationRequest memory request = mockAttestation(module, uint48(block.timestamp + 1), "", types); // It should store. registry.attest(defaultSchemaUID, request); - AttestationRecord memory record = registry.readAttestation(module, attester1.addr); + AttestationRecord memory record = registry.findAttestation(module, attester1.addr); assertEq(record.time, block.timestamp); assertEq(record.expirationTime, request.expirationTime); @@ -131,7 +130,7 @@ contract AttestationTest is BaseTest { uint32[] memory types = new uint32[](1); AttestationRequest memory request = - mockAttestation(makeAddr("module"), uint48(block.timestamp + 1), "", types); + mockAttestation(address(module1), uint48(block.timestamp + 1), "", types); // It should store. // TODO: it seems that the resolver is not being called registry.attest(defaultSchemaUID, request); @@ -164,14 +163,13 @@ contract AttestationTest is BaseTest { // It should recover. uint32[] memory types = new uint32[](1); AttestationRequest memory request = - mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); bytes32 digest = registry.getDigest(request, attester1.addr); bytes memory sig = ecdsaSign(attester1.key, digest); registry.attest(defaultSchemaUID, attester1.addr, request, sig); - AttestationRecord memory record = - registry.readAttestation(makeAddr("module"), attester1.addr); + AttestationRecord memory record = registry.findAttestation(address(module1), attester1.addr); uint256 nonceAfter = registry.attesterNonce(attester1.addr); assertEq(record.time, block.timestamp); @@ -184,7 +182,7 @@ contract AttestationTest is BaseTest { function test_WhenRevokingWithValidECDSA() public { test_WhenUsingValidECDSA(); - RevocationRequest memory request = mockRevocation(makeAddr("module")); + RevocationRequest memory request = mockRevocation(address(module1)); bytes32 digest = registry.getDigest(request, attester1.addr); bytes memory sig = ecdsaSign(attester1.key, digest); registry.revoke(attester1.addr, request, sig); @@ -194,8 +192,8 @@ contract AttestationTest is BaseTest { test_WhenUsingValidECDSAMulti(); RevocationRequest[] memory requests = new RevocationRequest[](2); - requests[0] = mockRevocation(makeAddr("module")); - requests[1] = mockRevocation(makeAddr("module1")); + requests[0] = mockRevocation(address(module1)); + requests[1] = mockRevocation(address(module2)); bytes32 digest = registry.getDigest(requests, attester1.addr); bytes memory sig = ecdsaSign(attester1.key, digest); registry.revoke(attester1.addr, requests, sig); @@ -207,15 +205,14 @@ contract AttestationTest is BaseTest { uint32[] memory types = new uint32[](1); AttestationRequest[] memory requests = new AttestationRequest[](2); - requests[0] = mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); - requests[1] = mockAttestation(makeAddr("module1"), uint48(block.timestamp + 100), "", types); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module2), uint48(block.timestamp + 100), "", types); bytes32 digest = registry.getDigest(requests, attester1.addr); bytes memory sig = ecdsaSign(attester1.key, digest); registry.attest(defaultSchemaUID, attester1.addr, requests, sig); - AttestationRecord memory record = - registry.readAttestation(makeAddr("module"), attester1.addr); + AttestationRecord memory record = registry.findAttestation(address(module1), attester1.addr); uint256 nonceAfter = registry.attesterNonce(attester1.addr); assertEq(record.time, block.timestamp); @@ -228,7 +225,7 @@ contract AttestationTest is BaseTest { function test_WhenUsingInvalidECDSA() external whenAttestingWithSignature { uint32[] memory types = new uint32[](1); AttestationRequest memory request = - mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); bytes32 digest = registry.getDigest(request, attester1.addr); bytes memory sig = ecdsaSign(attester1.key, digest); @@ -241,8 +238,8 @@ contract AttestationTest is BaseTest { function test_WhenUsingInvalidECDSAMulti() external whenAttestingWithSignature { uint32[] memory types = new uint32[](1); AttestationRequest[] memory requests = new AttestationRequest[](2); - requests[0] = mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); - requests[1] = mockAttestation(makeAddr("module1"), uint48(block.timestamp + 100), "", types); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module2), uint48(block.timestamp + 100), "", types); bytes32 digest = registry.getDigest(requests, attester1.addr); bytes memory sig = ecdsaSign(attester1.key, digest); @@ -255,13 +252,13 @@ contract AttestationTest is BaseTest { function test_WhenUsingValidERC1271() external whenAttestingWithSignature { uint32[] memory types = new uint32[](1); AttestationRequest memory request = - mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); bytes memory sig = "signature"; registry.attest(defaultSchemaUID, address(erc1271AttesterTrue), request, sig); AttestationRecord memory record = - registry.readAttestation(makeAddr("module"), address(erc1271AttesterTrue)); + registry.findAttestation(address(module1), address(erc1271AttesterTrue)); assertEq(record.time, block.timestamp); assertEq(record.expirationTime, request.expirationTime); @@ -272,14 +269,14 @@ contract AttestationTest is BaseTest { function test_WhenUsingValidERC1271Multi() external whenAttestingWithSignature { uint32[] memory types = new uint32[](1); AttestationRequest[] memory requests = new AttestationRequest[](2); - requests[0] = mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); - requests[1] = mockAttestation(makeAddr("module1"), uint48(block.timestamp + 100), "", types); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module2), uint48(block.timestamp + 100), "", types); bytes memory sig = "signature"; registry.attest(defaultSchemaUID, address(erc1271AttesterTrue), requests, sig); AttestationRecord memory record = - registry.readAttestation(makeAddr("module"), address(erc1271AttesterTrue)); + registry.findAttestation(address(module1), address(erc1271AttesterTrue)); assertEq(record.time, block.timestamp); assertEq(record.expirationTime, requests[0].expirationTime); @@ -292,8 +289,8 @@ contract AttestationTest is BaseTest { uint32[] memory types = new uint32[](1); AttestationRequest[] memory requests = new AttestationRequest[](2); - requests[0] = mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); - requests[1] = mockAttestation(makeAddr("module1"), uint48(block.timestamp + 100), "", types); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module2), uint48(block.timestamp + 100), "", types); bytes memory sig = "signature"; vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidSignature.selector)); @@ -304,7 +301,7 @@ contract AttestationTest is BaseTest { // It should revert. uint32[] memory types = new uint32[](1); AttestationRequest memory request = - mockAttestation(makeAddr("module"), uint48(block.timestamp + 100), "", types); + mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); bytes memory sig = "signature"; vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidSignature.selector)); diff --git a/test/Base.t.sol b/test/Base.t.sol index 39a81c81..596079c6 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -89,5 +89,7 @@ contract BaseTest is Test { vm.prank(moduleDev1.addr); registry.registerModule(defaultResolverUID, address(module1), ""); + vm.prank(moduleDev2.addr); + registry.registerModule(defaultResolverUID, address(module2), ""); } } diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index 2e88a7c2..bdef1e19 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -10,7 +10,9 @@ contract ModuleRegistrationTest is BaseTest { bytes memory bytecode = type(MockModule).creationCode; - address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, "", ""); + address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, "", "data"); + ModuleRecord memory record = registry.getRegisteredModules(moduleAddr); + assertTrue(record.resolverUID == defaultResolverUID); } function test_WhenDeployingViaRegistryWithArgs() public prankWithAccount(moduleDev1) { @@ -26,14 +28,15 @@ contract ModuleRegistrationTest is BaseTest { external prankWithAccount(moduleDev1) { + MockModule newModule = new MockModule(); // It should revert. ResolverUID invalidUID = ResolverUID.wrap(hex"00"); vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidResolver.selector, address(0))); - registry.registerModule(invalidUID, address(module2), ""); + registry.registerModule(invalidUID, address(newModule), ""); invalidUID = ResolverUID.wrap("1"); vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidResolver.selector, address(0))); - registry.registerModule(invalidUID, address(module2), ""); + registry.registerModule(invalidUID, address(newModule), ""); } function test_WhenRegisteringAModuleOnAValidResolverUID() @@ -42,19 +45,32 @@ contract ModuleRegistrationTest is BaseTest { { // It should register. - registry.registerModule(defaultResolverUID, address(module2), ""); + MockModule newModule = new MockModule(); + registry.registerModule(defaultResolverUID, address(newModule), ""); + } + + function test_WhenRegisteringAModuleOnAInValidResolverUID() + external + prankWithAccount(moduleDev1) + { + // It should revert + + MockModule newModule = new MockModule(); + vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidResolver.selector, address(0))); + registry.registerModule(ResolverUID.wrap(bytes32("foobar")), address(newModule), ""); } function test_WhenRegisteringTwoModulesWithTheSameBytecode() external prankWithAccount(moduleDev1) { + MockModule newModule = new MockModule(); // It should revert. - registry.registerModule(defaultResolverUID, address(module2), ""); + registry.registerModule(defaultResolverUID, address(newModule), ""); vm.expectRevert( - abi.encodeWithSelector(IRegistry.AlreadyRegistered.selector, address(module2)) + abi.encodeWithSelector(IRegistry.AlreadyRegistered.selector, address(newModule)) ); - registry.registerModule(defaultResolverUID, address(module2), ""); + registry.registerModule(defaultResolverUID, address(newModule), ""); } } diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol index 889822a6..ab78045d 100644 --- a/test/TrustDelegation.t.sol +++ b/test/TrustDelegation.t.sol @@ -17,12 +17,16 @@ contract TrustTest is AttestationTest { _; } - function test_WhenSupplyingOneAttester() external whenSettingAttester { + function test_WhenSupplyingOneAttester() + external + whenSettingAttester + prankWithAccount(smartAccount1) + { // It should set. address[] memory trustedAttesters = new address[](1); trustedAttesters[0] = address(attester1.addr); registry.trustAttesters(1, trustedAttesters); - address[] memory result = registry.getTrustedAttesters(); + address[] memory result = registry.getTrustedAttesters(smartAccount1.addr); assertEq(result.length, 1); assertEq(result[0], address(attester1.addr)); } @@ -30,6 +34,7 @@ contract TrustTest is AttestationTest { function test_WhenSupplyingManyAttesters(address[] memory attesters) external whenSettingAttester + prankWithAccount(smartAccount1) { vm.assume(attesters.length < 100); vm.assume(attesters.length > 0); @@ -41,7 +46,7 @@ contract TrustTest is AttestationTest { registry.trustAttesters(uint8(attesters.length), attesters); // It should set. // It should emit event. - address[] memory result = registry.getTrustedAttesters(); + address[] memory result = registry.getTrustedAttesters(smartAccount1.addr); assertEq(result.length, attesters.length); for (uint256 i; i < attesters.length; i++) { From b77ba0a3c66857428c6d19c3f33692be22c49890 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 8 Feb 2024 14:25:29 +0700 Subject: [PATCH 34/84] chore: more test cases --- src/IRegistry.sol | 2 ++ src/core/ModuleManager.sol | 7 ++++-- test/ModuleRegistration.t.sol | 47 +++++++++++++++++++++++++++++++++++ test/SchemaValidation.t.sol | 17 ++++++++++--- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 317b74f2..25ba6ab4 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -200,6 +200,8 @@ interface IRegistry is IERC7484 { error AlreadyRegistered(address module); error InvalidDeployment(); + error ModuleAddressIsNotContract(address moduleAddress); + error FactoryCallFailed(address factory); function deployModule( bytes32 salt, diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 50bd5174..9cd975da 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -97,13 +97,15 @@ abstract contract ModuleManager is IRegistry, ResolverManager { { ResolverRecord storage resolver = resolvers[resolverUID]; if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolverUID(resolverUID); + // prevent someone from calling a registry function pretending its a factory + if (factory == address(this)) revert FactoryCallFailed(factory); // call external factory to deploy module (bool ok, bytes memory returnData) = factory.call{ value: msg.value }(callOnFactory); - if (!ok) revert InvalidDeployment(); + if (!ok) revert FactoryCallFailed(factory); moduleAddress = abi.decode(returnData, (address)); if (moduleAddress == ZERO_ADDRESS) revert InvalidDeployment(); - if (_isContract(moduleAddress) != true) revert InvalidDeployment(); + if (_isContract(moduleAddress) == false) revert ModuleAddressIsNotContract(moduleAddress); ModuleRecord memory record = _storeModuleRecord({ moduleAddress: moduleAddress, @@ -133,6 +135,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { revert AlreadyRegistered(moduleAddress); } // revert if moduleAddress is NOT a contract + // this should catch address(0) if (!_isContract(moduleAddress)) revert InvalidDeployment(); // Store module metadata in _modules mapping diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index bdef1e19..78c69795 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -4,6 +4,18 @@ pragma solidity ^0.8.0; import "./Base.t.sol"; import "./mocks/MockModule.sol"; +contract Factory { + address returnAddress; + + function setReturnAddress(address _addr) public { + returnAddress = _addr; + } + + function deployFn() external returns (address) { + return returnAddress; + } +} + contract ModuleRegistrationTest is BaseTest { function test_WhenDeployingViaRegistry() public prankWithAccount(moduleDev1) { bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); @@ -73,4 +85,39 @@ contract ModuleRegistrationTest is BaseTest { ); registry.registerModule(defaultResolverUID, address(newModule), ""); } + + function test_WhenRegisteringViaFactory() public { + Factory factory = new Factory(); + + bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); + + factory.setReturnAddress(address(0)); + vm.expectRevert(); + registry.deployViaFactory( + address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID + ); + + factory.setReturnAddress(address(1)); + vm.expectRevert(); + + registry.deployViaFactory( + address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID + ); + + MockModule newModule = new MockModule(); + factory.setReturnAddress(address(newModule)); + registry.deployViaFactory( + address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID + ); + } + + function test_WhenUsingInvalidFactory() public { + vm.expectRevert(); + registry.deployViaFactory(address(0), "", "", defaultResolverUID); + } + + function test_WhenUsingRegistryASFactory() public { + vm.expectRevert(); + registry.deployViaFactory(address(registry), "", "", defaultResolverUID); + } } diff --git a/test/SchemaValidation.t.sol b/test/SchemaValidation.t.sol index 4a935bf0..63b649d9 100644 --- a/test/SchemaValidation.t.sol +++ b/test/SchemaValidation.t.sol @@ -1,17 +1,28 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -contract SchemaValidationTest { +import "./Base.t.sol"; + +contract SchemaValidationTest is BaseTest { modifier whenRegisteringNewSchema() { _; } function test_WhenSchemaAlreadyRegistered() external whenRegisteringNewSchema { - // It should revert. + string memory schema = "schema"; + SchemaUID uid = registry.registerSchema(schema, IExternalSchemaValidator(address(0))); + SchemaUID uid1 = + registry.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorFalse))); + + assertTrue(uid != uid1); } function test_WhenSchemaNew() external whenRegisteringNewSchema { // It should register schema. - // It should emit event. + + string memory schema = "schema"; + SchemaUID uid = registry.registerSchema(schema, IExternalSchemaValidator(address(0))); + + assertTrue(uid != SchemaUID.wrap(bytes32(0))); } } From 3f8e9d905b6f7dca47496cb1dfd6651038304bd6 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 8 Feb 2024 14:52:43 +0700 Subject: [PATCH 35/84] feat: adding slither --- .github/workflows/slither.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/workflows/slither.yml diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml new file mode 100644 index 00000000..47e01e81 --- /dev/null +++ b/.github/workflows/slither.yml @@ -0,0 +1,8 @@ +name: Slither Analysis +on: [push] +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: crytic/slither-action@v0.3.1 From 754fe626cbd05668b20b33f8dcdec62899ca7574 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Fri, 9 Feb 2024 09:06:08 +0700 Subject: [PATCH 36/84] chore fixing events --- Flatten.sol | 3571 +++++++++++++++++++++++++++++++ src/IRegistry.sol | 6 +- src/core/AttestationManager.sol | 3 - src/core/ModuleManager.sol | 3 +- src/core/ResolverManager.sol | 3 +- src/core/TrustManager.sol | 2 - 6 files changed, 3578 insertions(+), 10 deletions(-) create mode 100644 Flatten.sol diff --git a/Flatten.sol b/Flatten.sol new file mode 100644 index 00000000..3b15fd95 --- /dev/null +++ b/Flatten.sol @@ -0,0 +1,3571 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +interface IERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +/** + * @title The interface of an optional schema resolver. + */ +interface IExternalSchemaValidator is IERC165 { + /** + * @notice Validates an attestation request. + */ + function validateSchema(AttestationRecord calldata attestation) external view returns (bool); + + /** + * @notice Validates an array of attestation requests. + */ + function validateSchema(AttestationRecord[] calldata attestations) + external + view + returns (bool); +} + +/** + * @title The interface of an optional schema resolver. + * @dev The resolver is responsible for validating the schema and attestation data. + * @dev The resolver is also responsible for processing the attestation and revocation requests. + * + */ +interface IExternalResolver is IERC165 { + /** + * @dev Processes an attestation and verifies whether it's valid. + * + * @param attestation The new attestation. + * + * @return Whether the attestation is valid. + */ + function resolveAttestation(AttestationRecord calldata attestation) + external + payable + returns (bool); + + function resolveAttestation(AttestationRecord[] calldata attestation) + external + payable + returns (bool); + + /** + * @dev Processes an attestation revocation and verifies if it can be revoked. + * + * @param attestation The existing attestation to be revoked. + * + * @return Whether the attestation can be revoked. + */ + function resolveRevocation(AttestationRecord calldata attestation) + external + payable + returns (bool); + function resolveRevocation(AttestationRecord[] calldata attestation) + external + payable + returns (bool); + + /** + * @dev Processes a Module Registration + * + * @param module Module registration artefact + * + * @return Whether the registration is valid + */ + function resolveModuleRegistration(ModuleRecord calldata module) + external + payable + returns (bool); +} + +/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ +/* Storage Structs */ +/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + +// Struct that represents an attestation. +struct AttestationRecord { + uint48 time; // The time when the attestation was created (Unix timestamp). + uint48 expirationTime; // The time when the attestation expires (Unix timestamp). + uint48 revocationTime; // The time when the attestation was revoked (Unix timestamp). + PackedModuleTypes moduleTypes; // bit-wise encoded module types. See ModuleTypeLib + SchemaUID schemaUID; // The unique identifier of the schema. + address moduleAddr; // The implementation address of the module that is being attested. + address attester; // The attesting account. + AttestationDataRef dataPointer; // SSTORE2 pointer to the attestation data. +} + +// Struct that represents Module artefact. +struct ModuleRecord { + ResolverUID resolverUID; // The unique identifier of the resolver. + address sender; // The address of the sender who deployed the contract + bytes metadata; // Additional data related to the contract deployment +} + +struct SchemaRecord { + uint48 registeredAt; // The time when the schema was registered (Unix timestamp). + IExternalSchemaValidator validator; // Optional external schema validator. + string schema; // Custom specification of the schema (e.g., an ABI). +} + +struct ResolverRecord { + IExternalResolver resolver; // Optional resolver. + address resolverOwner; // The address of the account used to register the resolver. +} + +/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ +/* Attestation / Revocation Requests */ +/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + +/** + * @dev A struct representing the arguments of the attestation request. + */ +struct AttestationRequest { + address moduleAddr; // The moduleAddr of the attestation. + uint48 expirationTime; // The time when the attestation expires (Unix timestamp). + bytes data; // Custom attestation data. + ModuleType[] moduleTypes; // optional: The type(s) of the module. +} +/** + * @dev A struct representing the arguments of the revocation request. + */ + +struct RevocationRequest { + address moduleAddr; // The module address. +} + +/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ +/* Custom Types */ +/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + +//---------------------- SchemaUID ------------------------------| +type SchemaUID is bytes32; + +using { schemaEq as == } for SchemaUID global; +using { schemaNotEq as != } for SchemaUID global; + +function schemaEq(SchemaUID uid1, SchemaUID uid) pure returns (bool) { + return SchemaUID.unwrap(uid1) == SchemaUID.unwrap(uid); +} + +function schemaNotEq(SchemaUID uid1, SchemaUID uid) pure returns (bool) { + return SchemaUID.unwrap(uid1) != SchemaUID.unwrap(uid); +} + +//--------------------- ResolverUID -----------------------------| +type ResolverUID is bytes32; + +using { resolverEq as == } for ResolverUID global; +using { resolverNotEq as != } for ResolverUID global; + +function resolverEq(ResolverUID uid1, ResolverUID uid2) pure returns (bool) { + return ResolverUID.unwrap(uid1) == ResolverUID.unwrap(uid2); +} + +function resolverNotEq(ResolverUID uid1, ResolverUID uid2) pure returns (bool) { + return ResolverUID.unwrap(uid1) != ResolverUID.unwrap(uid2); +} + +type AttestationDataRef is address; + +using { attestationDataRefEq as == } for AttestationDataRef global; + +function attestationDataRefEq( + AttestationDataRef uid1, + AttestationDataRef uid2 +) + pure + returns (bool) +{ + return AttestationDataRef.unwrap(uid1) == AttestationDataRef.unwrap(uid2); +} + +type PackedModuleTypes is uint32; + +type ModuleType is uint32; + +using { moduleTypeEq as == } for ModuleType global; +using { moduleTypeNeq as != } for ModuleType global; + +function moduleTypeEq(ModuleType uid1, ModuleType uid2) pure returns (bool) { + return ModuleType.unwrap(uid1) == ModuleType.unwrap(uid2); +} + +function moduleTypeNeq(ModuleType uid1, ModuleType uid2) pure returns (bool) { + return ModuleType.unwrap(uid1) != ModuleType.unwrap(uid2); +} + +library UIDLib { + /** + * @dev Calculates a UID for a given schema. + * + * @param schemaRecord The input schema. + * + * @return schema UID. + */ + function getUID(SchemaRecord memory schemaRecord) internal pure returns (SchemaUID) { + // TODO: this is a frontrunning vuln + return SchemaUID.wrap( + keccak256(abi.encodePacked(schemaRecord.schema, address(schemaRecord.validator))) + ); + } + + /** + * @dev Calculates a UID for a given resolver. + * + * @param resolver The input schema. + * + * @return ResolverUID. + */ + function getUID(ResolverRecord memory resolver) internal pure returns (ResolverUID) { + // TODO: this is a frontrunning vuln + return ResolverUID.wrap(keccak256(abi.encodePacked(resolver.resolver))); + } +} + +// A representation of an empty/uninitialized UID. +bytes32 constant EMPTY_UID = 0; +ResolverUID constant EMPTY_RESOLVER_UID = ResolverUID.wrap(EMPTY_UID); +SchemaUID constant EMPTY_SCHEMA_UID = SchemaUID.wrap(EMPTY_UID); + +// A zero expiration represents an non-expiring attestation. +uint256 constant ZERO_TIMESTAMP = 0; + +address constant ZERO_ADDRESS = address(0); +ModuleType constant ZERO_MODULE_TYPE = ModuleType.wrap(0); + +AttestationDataRef constant EMPTY_ATTESTATION_REF = AttestationDataRef.wrap(address(0)); + +/** + * @dev Returns the current's block timestamp. This method is overridden during tests and used to simulate the + * current block time. + */ +function _time() view returns (uint48) { + return uint48(block.timestamp); +} + +/** + * @dev Returns whether an address is a contract. + * @param addr The address to check. + * + * @return true if `account` is a contract, false otherwise. + */ +function _isContract(address addr) view returns (bool) { + return addr.code.length > 0; +} + +interface IERC7484 { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Check with Registry internal attesters */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + function check(address module) external view; + + function checkForAccount(address smartAccount, address module) external view; + + function check(address module, ModuleType moduleType) external view; + + function checkForAccount( + address smartAccount, + address module, + ModuleType moduleType + ) + external + view; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Check with external attester(s) */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + function check(address module, address attester) external view returns (uint256 attestedAt); + + function checkN( + address module, + address[] calldata attesters, + uint256 threshold + ) + external + view + returns (uint256[] memory attestedAtArray); +} + +interface IRegistry is IERC7484 { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Smart Account - Trust Management */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + event NewTrustedAttesters(); + + error InvalidResolver(IExternalResolver resolver); + error InvalidResolverUID(ResolverUID uid); + error InvalidTrustedAttesterInput(); + error NoTrustedAttestersFound(); + error RevokedAttestation(address attester); + error InvalidModuleType(); + error AttestationNotFound(); + + error InsufficientAttestations(); + + /** + * Allows smartaccounts - the end users of the registry - to appoint + * one or many attesters as trusted. + * + * @param threshold The minimum number of attestations required for a module + * to be considered secure. + * @param attesters The addresses of the attesters to be trusted. + */ + function trustAttesters(uint8 threshold, address[] calldata attesters) external; + + /** + * Get trusted attester for a specific smartAccount + * @param smartAccount The address of the smartAccount + */ + function getTrustedAttesters(address smartAccount) + external + view + returns (address[] memory attesters); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Attestations */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + event Revoked(address indexed moduleAddr, address indexed revoker, SchemaUID schema); + event Attested( + address indexed moduleAddr, + address indexed attester, + SchemaUID schemaUID, + AttestationDataRef sstore2Pointer + ); + + error AlreadyRevoked(); + error ModuleNotFoundInRegistry(address module); + error AccessDenied(); + error InvalidAttestation(); + error InvalidExpirationTime(); + error DifferentResolvers(); + error InvalidSignature(); + error InvalidModuleTypes(); + + /** + * Allows msg.sender to attest to multiple modules' security status. + * The AttestationRequest.Data provided should match the attestation + * schema defined by the Schema corresponding to the SchemaUID + * + * @dev This function will revert if the same module is attested twice by the same attester. + * If you want to re-attest, you have to revoke your attestation first, and then attest again. + * + * @param schemaUID The SchemaUID of the schema the attestation is based on. + * @param request a single AttestationRequest + */ + function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; + + /** + * Allows msg.sender to attest to multiple modules' security status. + * The AttestationRequest.Data provided should match the attestation + * schema defined by the Schema corresponding to the SchemaUID + * + * @dev This function will revert if the same module is attested twice by the same attester. + * If you want to re-attest, you have to revoke your attestation first, and then attest again. + * + * @param schemaUID The SchemaUID of the schema the attestation is based on. + * @param requests An array of AttestationRequest + */ + function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; + + function attest( + SchemaUID schemaUID, + address attester, + AttestationRequest calldata request, + bytes calldata signature + ) + external; + + function attest( + SchemaUID schemaUID, + address attester, + AttestationRequest[] calldata requests, + bytes calldata signature + ) + external; + + function findAttestation( + address module, + address attester + ) + external + view + returns (AttestationRecord memory attestation); + + function findAttestations( + address module, + address[] calldata attesters + ) + external + view + returns (AttestationRecord[] memory attestations); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Revocations */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + function revoke(RevocationRequest calldata request) external; + + function revoke(RevocationRequest[] calldata requests) external; + + function revoke( + address attester, + RevocationRequest calldata request, + bytes calldata signature + ) + external; + + function revoke( + address attester, + RevocationRequest[] calldata requests, + bytes calldata signature + ) + external; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Module Registration */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + // Event triggered when a module is deployed. + event ModuleRegistration( + address indexed implementation, address indexed sender, bytes32 resolver + ); + event ModuleDeployed(address indexed implementation, bytes32 indexed salt, bytes32 resolver); + event ModuleDeployedExternalFactory( + address indexed implementation, address indexed factory, bytes32 resolver + ); + + error AlreadyRegistered(address module); + error InvalidDeployment(); + error ModuleAddressIsNotContract(address moduleAddress); + error FactoryCallFailed(address factory); + + function deployModule( + bytes32 salt, + ResolverUID resolverUID, + bytes calldata code, + bytes calldata deployParams, + bytes calldata metadata + ) + external + payable + returns (address moduleAddr); + + function registerModule( + ResolverUID resolverUID, + address moduleAddress, + bytes calldata metadata + ) + external; + + function getRegisteredModules(address moduleAddress) + external + view + returns (ModuleRecord memory moduleRecord); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Manage Schemas */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + event SchemaRegistered(SchemaUID indexed uid, address registerer); + + error SchemaAlreadyExists(SchemaUID uid); + + error InvalidSchema(); + error InvalidSchemaValidator(IExternalSchemaValidator validator); + + function registerSchema( + string calldata schema, + IExternalSchemaValidator validator // OPTIONAL + ) + external + returns (SchemaUID uid); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Manage Resolvers */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + event NewResolver(ResolverUID indexed uid, address resolver); + + error ResolverAlreadyExists(); + + function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid); + + function setResolver(ResolverUID uid, IExternalResolver resolver) external; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Stub Errors */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + error ExternalError_SchemaValidation(); + error ExternalError_ResolveAtteststation(); + error ExternalError_ResolveRevocation(); + error ExternalError_ModuleRegistration(); +} + +abstract contract SchemaManager is IRegistry { + using UIDLib for SchemaRecord; + // The global mapping between schema records and their IDs. + + mapping(SchemaUID uid => SchemaRecord schemaRecord) internal schemas; + + function registerSchema( + string calldata schema, + IExternalSchemaValidator validator // OPTIONAL + ) + external + onlySchemaValidator(validator) + returns (SchemaUID uid) + { + SchemaRecord memory schemaRecord = + SchemaRecord({ validator: validator, registeredAt: _time(), schema: schema }); + + // Computing a unique ID for the schema using its properties + uid = schemaRecord.getUID(); + + if (schemas[uid].registeredAt != ZERO_TIMESTAMP) revert SchemaAlreadyExists(uid); + + // Storing schema in the _schemas mapping + schemas[uid] = schemaRecord; + + emit SchemaRegistered(uid, msg.sender); + } + + /** + * If a validator is not address(0), we check if it supports the IExternalSchemaValidator interface + */ + modifier onlySchemaValidator(IExternalSchemaValidator validator) { + if ( + address(validator) != address(0) + && !validator.supportsInterface(type(IExternalSchemaValidator).interfaceId) + ) { + revert InvalidSchemaValidator(validator); + } + _; + } +} + +/** + * @title ModuleDeploymentLib + * @dev A library that can be used to deploy the Registry + * @author zeroknots + */ +library ModuleDeploymentLib { + /** + * @dev Gets the code hash of a contract at a given address. + * + * @param contractAddr The address of the contract. + * + * @return hash The hash of the contract code. + */ + function codeHash(address contractAddr) internal view returns (bytes32 hash) { + // solhint-disable-next-line no-inline-assembly + assembly { + if iszero(extcodesize(contractAddr)) { revert(0, 0) } + hash := extcodehash(contractAddr) + } + } + + /** + * @notice Creates a new contract using CREATE2 opcode. + * @dev This method uses the CREATE2 opcode to deploy a new contract with a deterministic address. + * + * @param createCode The creationCode for the contract. + * @param params The parameters for creating the contract. If the contract has a constructor, + * this MUST be provided. Function will fail if params are abi.encodePacked in createCode. + * @param salt The salt for creating the contract. + * + * @return moduleAddress The address of the deployed contract. + * @return initCodeHash packed (creationCode, constructor params) + * @return contractCodeHash hash of deployed bytecode + */ + function deploy( + bytes memory createCode, + bytes memory params, + bytes32 salt, + uint256 value + ) + internal + returns (address moduleAddress, bytes32 initCodeHash, bytes32 contractCodeHash) + { + bytes memory initCode = abi.encodePacked(createCode, params); + // this enforces, that constructor params were supplied via params argument + // if params were abi.encodePacked in createCode, this will revert + initCodeHash = keccak256(initCode); + + // solhint-disable-next-line no-inline-assembly + assembly { + moduleAddress := create2(value, add(initCode, 0x20), mload(initCode), salt) + // If the contract was not created successfully, the transaction is reverted. + if iszero(extcodesize(moduleAddress)) { revert(0, 0) } + contractCodeHash := extcodehash(moduleAddress) + } + } + + /** + * @notice Calculates the deterministic address of a contract that would be deployed using the CREATE2 opcode. + * @dev The calculated address is based on the contract's code, a salt, and the address of the current contract. + * @dev This function uses the formula specified in EIP-1014 (https://eips.ethereum.org/EIPS/eip-1014). + * + * @param _code The contract code that would be deployed. + * @param _salt A salt used for the address calculation. + * This must be the same salt that would be passed to the CREATE2 opcode. + * + * @return The address that the contract would be deployed + * at if the CREATE2 opcode was called with the specified _code and _salt. + */ + function calcAddress(bytes memory _code, bytes32 _salt) internal view returns (address) { + bytes32 hash = + keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(_code))); + // NOTE: cast last 20 bytes of hash to address + return address(uint160(uint256(hash))); + } + + error InvalidDeployment(); +} + +library StubLib { + function requireExternalSchemaValidation( + AttestationRecord memory attestationRecord, + SchemaRecord storage schema + ) + internal + view + { + // only run this function if the selected schemaUID exists + if (schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); + // validate Schema + IExternalSchemaValidator validator = schema.validator; + // if validator is set, call the validator + if ( + address(validator) != ZERO_ADDRESS + && validator.validateSchema(attestationRecord) == false + ) { + revert IRegistry.ExternalError_SchemaValidation(); + } + } + + function requireExternalSchemaValidation( + AttestationRecord[] memory attestationRecords, + SchemaRecord storage schema + ) + internal + view + { + // only run this function if the selected schemaUID exists + if (schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); + // validate Schema + IExternalSchemaValidator validator = schema.validator; + // if validator is set, call the validator + if ( + address(validator) != ZERO_ADDRESS + && validator.validateSchema(attestationRecords) == false + ) { + revert IRegistry.ExternalError_SchemaValidation(); + } + } + + function requireExternalResolverOnAttestation( + AttestationRecord memory attestationRecord, + ResolverRecord storage resolver + ) + internal + { + IExternalResolver resolverContract = resolver.resolver; + + if (address(resolverContract) == ZERO_ADDRESS) return; + if (resolverContract.resolveAttestation(attestationRecord) == false) { + revert IRegistry.ExternalError_ResolveAtteststation(); + } + } + + function requireExternalResolverOnAttestation( + AttestationRecord[] memory attestationRecords, + ResolverRecord storage resolver + ) + internal + { + IExternalResolver resolverContract = resolver.resolver; + + if (address(resolverContract) == ZERO_ADDRESS) return; + + if (resolverContract.resolveAttestation(attestationRecords) == false) { + revert IRegistry.ExternalError_ResolveAtteststation(); + } + } + + function requireExternalResolverOnRevocation( + AttestationRecord memory attestationRecord, + ResolverRecord storage resolver + ) + internal + { + IExternalResolver resolverContract = resolver.resolver; + + if (address(resolverContract) == ZERO_ADDRESS) return; + if (resolverContract.resolveRevocation(attestationRecord) == false) { + revert IRegistry.ExternalError_ResolveAtteststation(); + } + } + + function requireExternalResolverOnRevocation( + AttestationRecord[] memory attestationRecords, + ResolverRecord storage resolver + ) + internal + { + IExternalResolver resolverContract = resolver.resolver; + + if (address(resolverContract) == ZERO_ADDRESS) return; + + if (resolverContract.resolveAttestation(attestationRecords) == false) { + revert IRegistry.ExternalError_ResolveAtteststation(); + } + } + + function requireExternalResolverOnModuleRegistration( + ModuleRecord memory moduleRecord, + ResolverRecord storage resolver + ) + internal + { + IExternalResolver resolverContract = resolver.resolver; + + if (address(resolverContract) != ZERO_ADDRESS) return; + + if (resolverContract.resolveModuleRegistration(moduleRecord) == false) { + revert IRegistry.ExternalError_ModuleRegistration(); + } + } +} + +abstract contract ResolverManager is IRegistry { + using UIDLib for ResolverRecord; + + mapping(ResolverUID uid => ResolverRecord resolver) public resolvers; + + /** + * @dev Modifier to require that the caller is the owner of a resolver + * + * @param uid The UID of the resolver. + */ + modifier onlyResolverOwner(ResolverUID uid) { + if (resolvers[uid].resolverOwner != msg.sender) { + revert AccessDenied(); + } + _; + } + + modifier notZero(ResolverUID uid) { + if (uid == EMPTY_RESOLVER_UID) { + revert InvalidResolverUID(uid); + } + _; + } + + /** + * If a resolver is not address(0), we check if it supports the IExternalResolver interface + */ + modifier onlyResolver(IExternalResolver resolver) { + if ( + address(resolver) == address(0) + || !resolver.supportsInterface(type(IExternalResolver).interfaceId) + ) { + revert InvalidResolver(resolver); + } + _; + } + + function registerResolver(IExternalResolver resolver) + external + onlyResolver(resolver) + returns (ResolverUID uid) + { + // build a ResolverRecord from the input + ResolverRecord memory resolverRecord = + ResolverRecord({ resolver: resolver, resolverOwner: msg.sender }); + + // Computing a unique ID for the schema using its properties + uid = resolverRecord.getUID(); + + // Checking if a schema with this UID already exists -> resolver can never be ZERO_ADDRESS + if (address(resolvers[uid].resolver) != ZERO_ADDRESS) { + revert ResolverAlreadyExists(); + } + + // SSTORE schema in the resolvers mapping + resolvers[uid] = resolverRecord; + + emit NewResolver(uid, address(resolver)); + } + + // TODO: VULN: + // Attacker could register the same resolver, thus be the owner of the resolverUID, + // then set the resolver to a malicious contract + function setResolver( + ResolverUID uid, + IExternalResolver resolver + ) + external + onlyResolver(resolver) + onlyResolverOwner(uid) // authorization control + { + ResolverRecord storage referrer = resolvers[uid]; + referrer.resolver = resolver; + emit NewResolver(uid, address(resolver)); + } +} + +/** + * @title Module + * + * @dev The Module contract serves as a component in a larger system for handling smart contracts or "modules" + * within a blockchain ecosystem. This contract inherits from the IModule interface + * + * @dev The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract + * that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, + * sender address, deploy parameters hash, and additional metadata are stored in + * a struct and mapped to the module's address in + * the `_modules` mapping for easy access and management. + * + * @dev In conclusion, the Module is a central part of a system to manage, + * deploy, and interact with a set of smart contracts + * in a structured and controlled manner. + * + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + */ +abstract contract ModuleManager is IRegistry, ResolverManager { + using ModuleDeploymentLib for bytes; + using ModuleDeploymentLib for address; + using StubLib for *; + + mapping(address moduleAddress => ModuleRecord moduleRecord) internal _moduleAddrToRecords; + + function deployModule( + bytes32 salt, + ResolverUID resolverUID, + bytes calldata code, + bytes calldata deployParams, + bytes calldata metadata + ) + external + payable + returns (address moduleAddr) + { + ResolverRecord storage resolver = resolvers[resolverUID]; + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); + + // address predictedModuleAddress = code.calculateAddress(deployParams, salt); + + // TODO: should we use the initCode hash return value? + (moduleAddr,,) = code.deploy(deployParams, salt, msg.value); + // _storeModuleRecord() will check if module is already registered, + // which should prevent reentry to any deploy function + ModuleRecord memory record = _storeModuleRecord({ + moduleAddress: moduleAddr, // TODO: is this reentrancy? + sender: msg.sender, + resolverUID: resolverUID, + metadata: metadata + }); + record.requireExternalResolverOnModuleRegistration(resolver); + } + + function registerModule( + ResolverUID resolverUID, + address moduleAddress, + bytes calldata metadata + ) + external + { + ResolverRecord storage resolver = resolvers[resolverUID]; + + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); + + ModuleRecord memory record = _storeModuleRecord({ + moduleAddress: moduleAddress, + sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function + resolverUID: resolverUID, + metadata: metadata + }); + // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s + // is this a problemt? + record.requireExternalResolverOnModuleRegistration(resolver); + } + + function deployViaFactory( + address factory, + bytes calldata callOnFactory, + bytes calldata metadata, + ResolverUID resolverUID + ) + external + payable + returns (address moduleAddress) + { + ResolverRecord storage resolver = resolvers[resolverUID]; + if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolverUID(resolverUID); + // prevent someone from calling a registry function pretending its a factory + if (factory == address(this)) revert FactoryCallFailed(factory); + // call external factory to deploy module + (bool ok, bytes memory returnData) = factory.call{ value: msg.value }(callOnFactory); + if (!ok) revert FactoryCallFailed(factory); + + moduleAddress = abi.decode(returnData, (address)); + if (moduleAddress == ZERO_ADDRESS) revert InvalidDeployment(); + if (_isContract(moduleAddress) == false) revert ModuleAddressIsNotContract(moduleAddress); + + ModuleRecord memory record = _storeModuleRecord({ + moduleAddress: moduleAddress, + // TODO: should we use msg.sender or the factory address? + sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function + resolverUID: resolverUID, + metadata: metadata + }); + // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s + // is this a problemt? + record.requireExternalResolverOnModuleRegistration(resolver); + } + + function _storeModuleRecord( + address moduleAddress, + address sender, + ResolverUID resolverUID, + bytes calldata metadata + ) + internal + returns (ModuleRecord memory moduleRegistration) + { + // ensure that non-zero resolverUID was provided + if (resolverUID == EMPTY_RESOLVER_UID) revert InvalidDeployment(); + // ensure moduleAddress is not already registered + if (_moduleAddrToRecords[moduleAddress].resolverUID != EMPTY_RESOLVER_UID) { + revert AlreadyRegistered(moduleAddress); + } + // revert if moduleAddress is NOT a contract + // this should catch address(0) + if (!_isContract(moduleAddress)) revert InvalidDeployment(); + + // Store module metadata in _modules mapping + moduleRegistration = + ModuleRecord({ resolverUID: resolverUID, sender: sender, metadata: metadata }); + + // Store module record in _modules mapping + _moduleAddrToRecords[moduleAddress] = moduleRegistration; + + // Emit ModuleRegistration event + emit ModuleRegistration(moduleAddress, sender, ResolverUID.unwrap(resolverUID)); + } + + function getRegisteredModules(address moduleAddress) + external + view + returns (ModuleRecord memory moduleRecord) + { + return _moduleAddrToRecords[moduleAddress]; + } +} + +abstract contract TrustManagerExternalAttesterList is IRegistry { + function check(address module, address attester) external view returns (uint256 attestedAt) { + AttestationRecord storage attestation = _getAttestation(module, attester); + + // attestedAt = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestedAt := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (attestedAt == ZERO_TIMESTAMP) { + revert AttestationNotFound(); + } + + if (expirationTime != ZERO_TIMESTAMP) { + if (block.timestamp > expirationTime) { + revert AttestationNotFound(); + } + } + + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(attestation.attester); + } + } + + function checkN( + address module, + address[] calldata attesters, + uint256 threshold + ) + external + view + returns (uint256[] memory attestedAtArray) + { + uint256 attestersLength = attesters.length; + if (attestersLength < threshold || threshold == 0) { + threshold = attestersLength; + } + + uint256 timeNow = block.timestamp; + attestedAtArray = new uint256[](attestersLength); + + for (uint256 i; i < attestersLength; ++i) { + AttestationRecord storage attestation = _getAttestation(module, attesters[i]); + + uint256 attestationTime; // = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestationTime := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(attestation.attester); + } + + if (expirationTime != ZERO_TIMESTAMP) { + if (timeNow > expirationTime) { + revert AttestationNotFound(); + } + } + + attestedAtArray[i] = attestationTime; + + if (attestationTime == ZERO_TIMESTAMP) continue; + if (threshold != 0) --threshold; + } + if (threshold == 0) return attestedAtArray; + revert InsufficientAttestations(); + } + + function checkNUnsafe( + address module, + address[] calldata attesters, + uint256 threshold + ) + external + view + returns (uint256[] memory attestedAtArray) + { + uint256 attestersLength = attesters.length; + if (attestersLength < threshold || threshold == 0) { + threshold = attestersLength; + } + + uint256 timeNow = block.timestamp; + attestedAtArray = new uint256[](attestersLength); + + for (uint256 i; i < attestersLength; ++i) { + AttestationRecord storage attestation = _getAttestation(module, attesters[i]); + + uint256 attestationTime; // = attestation.time; + uint256 expirationTime; // = attestation.expirationTime; + uint256 revocationTime; // = attestation.revocationTime; + + // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables + // @dev the solidity version of the assembly code is above + // solhint-disable-next-line no-inline-assembly + assembly { + let mask := 0xffffffffffff + let times := sload(attestation.slot) + attestationTime := and(mask, times) + times := shr(48, times) + expirationTime := and(mask, times) + times := shr(48, times) + revocationTime := and(mask, times) + } + + if (revocationTime != ZERO_TIMESTAMP) { + attestedAtArray[i] = 0; + continue; + } + + attestedAtArray[i] = attestationTime; + + if (expirationTime != ZERO_TIMESTAMP) { + if (timeNow > expirationTime) { + attestedAtArray[i] = 0; + continue; + } + } + + if (attestationTime == ZERO_TIMESTAMP) continue; + if (threshold != 0) --threshold; + } + if (threshold == 0) return attestedAtArray; + revert InsufficientAttestations(); + } + + function _getAttestation( + address module, + address attester + ) + internal + view + virtual + returns (AttestationRecord storage attestation); +} + +library ModuleTypeLib { + function isType(PackedModuleTypes self, ModuleType moduleType) internal pure returns (bool) { + return (PackedModuleTypes.unwrap(self) & 2 ** ModuleType.unwrap(moduleType)) != 0; + } + + function pack(ModuleType[] memory moduleTypes) internal pure returns (PackedModuleTypes) { + uint32 result; + uint256 length = moduleTypes.length; + for (uint256 i; i < length; i++) { + result = result + uint32(2 ** ModuleType.unwrap(moduleTypes[i])); + } + return PackedModuleTypes.wrap(result); + } + + function packCalldata(ModuleType[] calldata moduleTypes) + internal + pure + returns (PackedModuleTypes) + { + uint32 result; + for (uint256 i; i < moduleTypes.length; i++) { + uint32 _type = ModuleType.unwrap(moduleTypes[i]); + if (_type > 31) revert IRegistry.InvalidModuleType(); + result = result + uint32(2 ** _type); + } + return PackedModuleTypes.wrap(result); + } +} + +/// @notice Optimized sorts and operations for sorted arrays. +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Sort.sol) +library LibSort { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INSERTION SORT */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + // - Faster on small arrays (32 or lesser elements). + // - Faster on almost sorted arrays. + // - Smaller bytecode. + // - May be suitable for view functions intended for off-chain querying. + + /// @dev Sorts the array in-place with insertion sort. + function insertionSort(uint256[] memory a) internal pure { + /// @solidity memory-safe-assembly + assembly { + let n := mload(a) // Length of `a`. + mstore(a, 0) // For insertion sort's inner loop to terminate. + let h := add(a, shl(5, n)) // High slot. + let s := 0x20 + let w := not(0x1f) + for { let i := add(a, s) } 1 {} { + i := add(i, s) + if gt(i, h) { break } + let k := mload(i) // Key. + let j := add(i, w) // The slot before the current slot. + let v := mload(j) // The value of `j`. + if iszero(gt(v, k)) { continue } + for {} 1 {} { + mstore(add(j, s), v) + j := add(j, w) // `sub(j, 0x20)`. + v := mload(j) + if iszero(gt(v, k)) { break } + } + mstore(add(j, s), k) + } + mstore(a, n) // Restore the length of `a`. + } + } + + /// @dev Sorts the array in-place with insertion sort. + function insertionSort(int256[] memory a) internal pure { + _flipSign(a); + insertionSort(_toUints(a)); + _flipSign(a); + } + + /// @dev Sorts the array in-place with insertion sort. + function insertionSort(address[] memory a) internal pure { + insertionSort(_toUints(a)); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INTRO-QUICKSORT */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + // - Faster on larger arrays (more than 32 elements). + // - Robust performance. + // - Larger bytecode. + + /// @dev Sorts the array in-place with intro-quicksort. + function sort(uint256[] memory a) internal pure { + /// @solidity memory-safe-assembly + assembly { + let w := not(0x1f) + let s := 0x20 + let n := mload(a) // Length of `a`. + mstore(a, 0) // For insertion sort's inner loop to terminate. + + // Let the stack be the start of the free memory. + let stack := mload(0x40) + + for {} iszero(lt(n, 2)) {} { + // Push `l` and `h` to the stack. + // The `shl` by 5 is equivalent to multiplying by `0x20`. + let l := add(a, s) + let h := add(a, shl(5, n)) + + let j := l + // forgefmt: disable-next-item + for {} iszero(or(eq(j, h), gt(mload(j), mload(add(j, s))))) {} { + j := add(j, s) + } + // If the array is already sorted. + if eq(j, h) { break } + + j := h + // forgefmt: disable-next-item + for {} iszero(gt(mload(j), mload(add(j, w)))) {} { + j := add(j, w) // `sub(j, 0x20)`. + } + // If the array is reversed sorted. + if eq(j, l) { + for {} 1 {} { + let t := mload(l) + mstore(l, mload(h)) + mstore(h, t) + h := add(h, w) // `sub(h, 0x20)`. + l := add(l, s) + if iszero(lt(l, h)) { break } + } + break + } + + // Push `l` and `h` onto the stack. + mstore(stack, l) + mstore(add(stack, s), h) + stack := add(stack, 0x40) + break + } + + for { let stackBottom := mload(0x40) } iszero(eq(stack, stackBottom)) {} { + // Pop `l` and `h` from the stack. + stack := sub(stack, 0x40) + let l := mload(stack) + let h := mload(add(stack, s)) + + // Do insertion sort if `h - l <= 0x20 * 12`. + // Threshold is fine-tuned via trial and error. + if iszero(gt(sub(h, l), 0x180)) { + // Hardcode sort the first 2 elements. + let i := add(l, s) + if iszero(lt(mload(l), mload(i))) { + let t := mload(i) + mstore(i, mload(l)) + mstore(l, t) + } + for {} 1 {} { + i := add(i, s) + if gt(i, h) { break } + let k := mload(i) // Key. + let j := add(i, w) // The slot before the current slot. + let v := mload(j) // The value of `j`. + if iszero(gt(v, k)) { continue } + for {} 1 {} { + mstore(add(j, s), v) + j := add(j, w) + v := mload(j) + if iszero(gt(v, k)) { break } + } + mstore(add(j, s), k) + } + continue + } + // Pivot slot is the average of `l` and `h`. + let p := add(shl(5, shr(6, add(l, h))), and(31, l)) + // Median of 3 with sorting. + { + function swap(a_, b_) -> _b, _a { + _b := a_ + _a := b_ + } + let e0 := mload(l) + let e1 := mload(h) + if iszero(lt(e0, e1)) { e1, e0 := swap(e0, e1) } + let e2 := mload(p) + if iszero(lt(e2, e1)) { e2, e1 := swap(e1, e2) } + if iszero(lt(e0, e2)) { e2, e0 := swap(e0, e2) } + mstore(p, e2) + mstore(h, e1) + mstore(l, e0) + } + // Hoare's partition. + { + // The value of the pivot slot. + let x := mload(p) + p := h + for { let i := l } 1 {} { + for {} 1 {} { + i := add(i, s) + if iszero(gt(x, mload(i))) { break } + } + let j := p + for {} 1 {} { + j := add(j, w) + if iszero(lt(x, mload(j))) { break } + } + p := j + if iszero(lt(i, p)) { break } + // Swap slots `i` and `p`. + let t := mload(i) + mstore(i, mload(p)) + mstore(p, t) + } + } + // If slice on right of pivot is non-empty, push onto stack. + { + mstore(stack, add(p, s)) + // Skip `mstore(add(stack, 0x20), h)`, as it is already on the stack. + stack := add(stack, shl(6, lt(add(p, s), h))) + } + // If slice on left of pivot is non-empty, push onto stack. + { + mstore(stack, l) + mstore(add(stack, s), p) + stack := add(stack, shl(6, gt(p, l))) + } + } + mstore(a, n) // Restore the length of `a`. + } + } + + /// @dev Sorts the array in-place with intro-quicksort. + function sort(int256[] memory a) internal pure { + _flipSign(a); + sort(_toUints(a)); + _flipSign(a); + } + + /// @dev Sorts the array in-place with intro-quicksort. + function sort(address[] memory a) internal pure { + sort(_toUints(a)); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* OTHER USEFUL OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + // For performance, the `uniquifySorted` methods will not revert if the + // array is not sorted -- it will simply remove consecutive duplicate elements. + + /// @dev Removes duplicate elements from a ascendingly sorted memory array. + function uniquifySorted(uint256[] memory a) internal pure { + /// @solidity memory-safe-assembly + assembly { + // If the length of `a` is greater than 1. + if iszero(lt(mload(a), 2)) { + let x := add(a, 0x20) + let y := add(a, 0x40) + let end := add(a, shl(5, add(mload(a), 1))) + for {} 1 {} { + if iszero(eq(mload(x), mload(y))) { + x := add(x, 0x20) + mstore(x, mload(y)) + } + y := add(y, 0x20) + if eq(y, end) { break } + } + mstore(a, shr(5, sub(x, a))) + } + } + } + + /// @dev Removes duplicate elements from a ascendingly sorted memory array. + function uniquifySorted(int256[] memory a) internal pure { + uniquifySorted(_toUints(a)); + } + + /// @dev Removes duplicate elements from a ascendingly sorted memory array. + function uniquifySorted(address[] memory a) internal pure { + uniquifySorted(_toUints(a)); + } + + /// @dev Returns whether `a` contains `needle`, and the index of `needle`. + /// `index` precedence: equal to > nearest before > nearest after. + function searchSorted(uint256[] memory a, uint256 needle) + internal + pure + returns (bool found, uint256 index) + { + (found, index) = _searchSorted(a, needle, 0); + } + + /// @dev Returns whether `a` contains `needle`, and the index of `needle`. + /// `index` precedence: equal to > nearest before > nearest after. + function searchSorted(int256[] memory a, int256 needle) + internal + pure + returns (bool found, uint256 index) + { + (found, index) = _searchSorted(_toUints(a), uint256(needle), 1 << 255); + } + + /// @dev Returns whether `a` contains `needle`, and the index of `needle`. + /// `index` precedence: equal to > nearest before > nearest after. + function searchSorted(address[] memory a, address needle) + internal + pure + returns (bool found, uint256 index) + { + (found, index) = _searchSorted(_toUints(a), uint256(uint160(needle)), 0); + } + + /// @dev Reverses the array in-place. + function reverse(uint256[] memory a) internal pure { + /// @solidity memory-safe-assembly + assembly { + if iszero(lt(mload(a), 2)) { + let s := 0x20 + let w := not(0x1f) + let h := add(a, shl(5, mload(a))) + for { a := add(a, s) } 1 {} { + let t := mload(a) + mstore(a, mload(h)) + mstore(h, t) + h := add(h, w) + a := add(a, s) + if iszero(lt(a, h)) { break } + } + } + } + } + + /// @dev Reverses the array in-place. + function reverse(int256[] memory a) internal pure { + reverse(_toUints(a)); + } + + /// @dev Reverses the array in-place. + function reverse(address[] memory a) internal pure { + reverse(_toUints(a)); + } + + /// @dev Returns a copy of the array. + function copy(uint256[] memory a) internal pure returns (uint256[] memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let end := add(add(result, 0x20), shl(5, mload(a))) + let o := result + for { let d := sub(a, result) } 1 {} { + mstore(o, mload(add(o, d))) + o := add(0x20, o) + if eq(o, end) { break } + } + mstore(0x40, o) + } + } + + /// @dev Returns a copy of the array. + function copy(int256[] memory a) internal pure returns (int256[] memory result) { + result = _toInts(copy(_toUints(a))); + } + + /// @dev Returns a copy of the array. + function copy(address[] memory a) internal pure returns (address[] memory result) { + result = _toAddresses(copy(_toUints(a))); + } + + /// @dev Returns whether the array is sorted in ascending order. + function isSorted(uint256[] memory a) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + result := 1 + if iszero(lt(mload(a), 2)) { + let end := add(a, shl(5, mload(a))) + for { a := add(a, 0x20) } 1 {} { + let p := mload(a) + a := add(a, 0x20) + result := iszero(gt(p, mload(a))) + if iszero(mul(result, xor(a, end))) { break } + } + } + } + } + + /// @dev Returns whether the array is sorted in ascending order. + function isSorted(int256[] memory a) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + result := 1 + if iszero(lt(mload(a), 2)) { + let end := add(a, shl(5, mload(a))) + for { a := add(a, 0x20) } 1 {} { + let p := mload(a) + a := add(a, 0x20) + result := iszero(sgt(p, mload(a))) + if iszero(mul(result, xor(a, end))) { break } + } + } + } + } + + /// @dev Returns whether the array is sorted in ascending order. + function isSorted(address[] memory a) internal pure returns (bool result) { + result = isSorted(_toUints(a)); + } + + /// @dev Returns whether the array is strictly ascending (sorted and uniquified). + function isSortedAndUniquified(uint256[] memory a) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + result := 1 + if iszero(lt(mload(a), 2)) { + let end := add(a, shl(5, mload(a))) + for { a := add(a, 0x20) } 1 {} { + let p := mload(a) + a := add(a, 0x20) + result := lt(p, mload(a)) + if iszero(mul(result, xor(a, end))) { break } + } + } + } + } + + /// @dev Returns whether the array is strictly ascending (sorted and uniquified). + function isSortedAndUniquified(int256[] memory a) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + result := 1 + if iszero(lt(mload(a), 2)) { + let end := add(a, shl(5, mload(a))) + for { a := add(a, 0x20) } 1 {} { + let p := mload(a) + a := add(a, 0x20) + result := slt(p, mload(a)) + if iszero(mul(result, xor(a, end))) { break } + } + } + } + } + + /// @dev Returns whether the array is strictly ascending (sorted and uniquified). + function isSortedAndUniquified(address[] memory a) internal pure returns (bool result) { + result = isSortedAndUniquified(_toUints(a)); + } + + /// @dev Returns the sorted set difference of `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function difference(uint256[] memory a, uint256[] memory b) + internal + pure + returns (uint256[] memory c) + { + c = _difference(a, b, 0); + } + + /// @dev Returns the sorted set difference between `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function difference(int256[] memory a, int256[] memory b) + internal + pure + returns (int256[] memory c) + { + c = _toInts(_difference(_toUints(a), _toUints(b), 1 << 255)); + } + + /// @dev Returns the sorted set difference between `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function difference(address[] memory a, address[] memory b) + internal + pure + returns (address[] memory c) + { + c = _toAddresses(_difference(_toUints(a), _toUints(b), 0)); + } + + /// @dev Returns the sorted set intersection between `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function intersection(uint256[] memory a, uint256[] memory b) + internal + pure + returns (uint256[] memory c) + { + c = _intersection(a, b, 0); + } + + /// @dev Returns the sorted set intersection between `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function intersection(int256[] memory a, int256[] memory b) + internal + pure + returns (int256[] memory c) + { + c = _toInts(_intersection(_toUints(a), _toUints(b), 1 << 255)); + } + + /// @dev Returns the sorted set intersection between `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function intersection(address[] memory a, address[] memory b) + internal + pure + returns (address[] memory c) + { + c = _toAddresses(_intersection(_toUints(a), _toUints(b), 0)); + } + + /// @dev Returns the sorted set union of `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function union(uint256[] memory a, uint256[] memory b) + internal + pure + returns (uint256[] memory c) + { + c = _union(a, b, 0); + } + + /// @dev Returns the sorted set union of `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function union(int256[] memory a, int256[] memory b) + internal + pure + returns (int256[] memory c) + { + c = _toInts(_union(_toUints(a), _toUints(b), 1 << 255)); + } + + /// @dev Returns the sorted set union between `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function union(address[] memory a, address[] memory b) + internal + pure + returns (address[] memory c) + { + c = _toAddresses(_union(_toUints(a), _toUints(b), 0)); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* PRIVATE HELPERS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Reinterpret cast to an uint256 array. + function _toUints(int256[] memory a) private pure returns (uint256[] memory casted) { + /// @solidity memory-safe-assembly + assembly { + casted := a + } + } + + /// @dev Reinterpret cast to an uint256 array. + function _toUints(address[] memory a) private pure returns (uint256[] memory casted) { + /// @solidity memory-safe-assembly + assembly { + // As any address written to memory will have the upper 96 bits + // of the word zeroized (as per Solidity spec), we can directly + // compare these addresses as if they are whole uint256 words. + casted := a + } + } + + /// @dev Reinterpret cast to an int array. + function _toInts(uint256[] memory a) private pure returns (int256[] memory casted) { + /// @solidity memory-safe-assembly + assembly { + casted := a + } + } + + /// @dev Reinterpret cast to an address array. + function _toAddresses(uint256[] memory a) private pure returns (address[] memory casted) { + /// @solidity memory-safe-assembly + assembly { + casted := a + } + } + + /// @dev Converts an array of signed integers to unsigned + /// integers suitable for sorting or vice versa. + function _flipSign(int256[] memory a) private pure { + /// @solidity memory-safe-assembly + assembly { + let w := shl(255, 1) + for { let end := add(a, shl(5, mload(a))) } iszero(eq(a, end)) {} { + a := add(a, 0x20) + mstore(a, add(mload(a), w)) + } + } + } + + /// @dev Returns whether `a` contains `needle`, and the index of `needle`. + /// `index` precedence: equal to > nearest before > nearest after. + function _searchSorted(uint256[] memory a, uint256 needle, uint256 signed) + private + pure + returns (bool found, uint256 index) + { + /// @solidity memory-safe-assembly + assembly { + let w := not(0) + let l := 1 + let h := mload(a) + let t := 0 + for { needle := add(signed, needle) } 1 {} { + index := shr(1, add(l, h)) + t := add(signed, mload(add(a, shl(5, index)))) + if or(gt(l, h), eq(t, needle)) { break } + // Decide whether to search the left or right half. + if iszero(gt(needle, t)) { + h := add(index, w) + continue + } + l := add(index, 1) + } + // `index` will be zero in the case of an empty array, + // or when the value is less than the smallest value in the array. + found := eq(t, needle) + t := iszero(iszero(index)) + index := mul(add(index, w), t) + found := and(found, t) + } + } + + /// @dev Returns the sorted set difference of `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function _difference(uint256[] memory a, uint256[] memory b, uint256 signed) + private + pure + returns (uint256[] memory c) + { + /// @solidity memory-safe-assembly + assembly { + let s := 0x20 + let aEnd := add(a, shl(5, mload(a))) + let bEnd := add(b, shl(5, mload(b))) + c := mload(0x40) // Set `c` to the free memory pointer. + a := add(a, s) + b := add(b, s) + let k := c + for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { + let u := mload(a) + let v := mload(b) + if iszero(xor(u, v)) { + a := add(a, s) + b := add(b, s) + continue + } + if iszero(lt(add(u, signed), add(v, signed))) { + b := add(b, s) + continue + } + k := add(k, s) + mstore(k, u) + a := add(a, s) + } + for {} iszero(gt(a, aEnd)) {} { + k := add(k, s) + mstore(k, mload(a)) + a := add(a, s) + } + mstore(c, shr(5, sub(k, c))) // Store the length of `c`. + mstore(0x40, add(k, s)) // Allocate the memory for `c`. + } + } + + /// @dev Returns the sorted set intersection between `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function _intersection(uint256[] memory a, uint256[] memory b, uint256 signed) + private + pure + returns (uint256[] memory c) + { + /// @solidity memory-safe-assembly + assembly { + let s := 0x20 + let aEnd := add(a, shl(5, mload(a))) + let bEnd := add(b, shl(5, mload(b))) + c := mload(0x40) // Set `c` to the free memory pointer. + a := add(a, s) + b := add(b, s) + let k := c + for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { + let u := mload(a) + let v := mload(b) + if iszero(xor(u, v)) { + k := add(k, s) + mstore(k, u) + a := add(a, s) + b := add(b, s) + continue + } + if iszero(lt(add(u, signed), add(v, signed))) { + b := add(b, s) + continue + } + a := add(a, s) + } + mstore(c, shr(5, sub(k, c))) // Store the length of `c`. + mstore(0x40, add(k, s)) // Allocate the memory for `c`. + } + } + + /// @dev Returns the sorted set union of `a` and `b`. + /// Note: Behaviour is undefined if inputs are not sorted and uniquified. + function _union(uint256[] memory a, uint256[] memory b, uint256 signed) + private + pure + returns (uint256[] memory c) + { + /// @solidity memory-safe-assembly + assembly { + let s := 0x20 + let aEnd := add(a, shl(5, mload(a))) + let bEnd := add(b, shl(5, mload(b))) + c := mload(0x40) // Set `c` to the free memory pointer. + a := add(a, s) + b := add(b, s) + let k := c + for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { + let u := mload(a) + let v := mload(b) + if iszero(xor(u, v)) { + k := add(k, s) + mstore(k, u) + a := add(a, s) + b := add(b, s) + continue + } + if iszero(lt(add(u, signed), add(v, signed))) { + k := add(k, s) + mstore(k, v) + b := add(b, s) + continue + } + k := add(k, s) + mstore(k, u) + a := add(a, s) + } + for {} iszero(gt(a, aEnd)) {} { + k := add(k, s) + mstore(k, mload(a)) + a := add(a, s) + } + for {} iszero(gt(b, bEnd)) {} { + k := add(k, s) + mstore(k, mload(b)) + b := add(b, s) + } + mstore(c, shr(5, sub(k, c))) // Store the length of `c`. + mstore(0x40, add(k, s)) // Allocate the memory for `c`. + } + } +} + +/** + * @title TrustManager + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + * Implements EIP-7484 to query attestations stored in the registry. + * @dev This contract is abstract and provides utility functions to query attestations. + */ +abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { + using ModuleTypeLib for PackedModuleTypes; + using LibSort for address[]; + + // packed struct to allow for efficient storage. + // if only one attester is trusted, it only requires 1 SLOAD + + struct TrustedAttesters { + uint8 attesterCount; + uint8 threshold; + address attester; + mapping(address attester => address linkedAttester) linkedAttesters; + } + + mapping(address account => TrustedAttesters attesters) internal _accountToAttester; + + // Deliberately using memory here, so we can sort the array + function trustAttesters(uint8 threshold, address[] memory attesters) external { + uint256 attestersLength = attesters.length; + attesters.sort(); + attesters.uniquifySorted(); + if (attestersLength == 0) revert InvalidTrustedAttesterInput(); + if (attesters.length != attestersLength) revert InvalidTrustedAttesterInput(); + // sort attesters + + TrustedAttesters storage _att = _accountToAttester[msg.sender]; + // threshold cannot be greater than the number of attesters + if (threshold > attestersLength) { + threshold = uint8(attestersLength); + } + // + _att.attesterCount = uint8(attestersLength); + _att.threshold = threshold; + _att.attester = attesters[0]; + + attestersLength--; + for (uint256 i; i < attestersLength; i++) { + address _attester = attesters[i]; + // user could have set attester to address(0) + if (_attester == ZERO_ADDRESS) revert InvalidTrustedAttesterInput(); + _att.linkedAttesters[_attester] = attesters[i + 1]; + } + emit NewTrustedAttesters(); + } + + function check(address module) external view { + _check(msg.sender, module, ZERO_MODULE_TYPE); + } + + function checkForAccount(address smartAccount, address module) external view { + _check(smartAccount, module, ZERO_MODULE_TYPE); + } + + function check(address module, ModuleType moduleType) external view { + _check(msg.sender, module, moduleType); + } + + function checkForAccount( + address smartAccount, + address module, + ModuleType moduleType + ) + external + view + { + _check(smartAccount, module, moduleType); + } + + function _check(address smartAccount, address module, ModuleType moduleType) internal view { + TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; + // SLOAD from one slot + uint256 attesterCount = trustedAttesters.attesterCount; + uint256 threshold = trustedAttesters.threshold; + address attester = trustedAttesters.attester; + + // smart account has no trusted attesters set + if (attester == ZERO_ADDRESS && threshold != 0) { + revert NoTrustedAttestersFound(); + } + // smart account only has ONE trusted attester + // use this condition to save gas + else if (threshold == 1) { + AttestationRecord storage record = + _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(moduleType, record); + } + // smart account has more than one trusted attester + else { + // loop though list and check if the attestation is valid + AttestationRecord storage record = + _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(moduleType, record); + for (uint256 i = 1; i < attesterCount; i++) { + threshold--; + // get next attester from linked List + attester = trustedAttesters.linkedAttesters[attester]; + record = _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(moduleType, record); + // if threshold reached, exit loop + if (threshold == 0) return; + } + } + } + + /** + * Check that attestationRecord is valid: + * - not revoked + * - not expired + * - correct module type (if not ZERO_MODULE_TYPE) + */ + function _requireValidAttestation( + ModuleType expectedType, + AttestationRecord storage record + ) + internal + view + { + // cache values TODO:: assembly to ensure single SLOAD + uint256 attestedAt = record.time; + uint256 expirationTime = record.expirationTime; + uint256 revocationTime = record.revocationTime; + PackedModuleTypes packedModuleType = record.moduleTypes; + + // check if any attestation was made + if (attestedAt == ZERO_TIMESTAMP) { + revert AttestationNotFound(); + } + + // check if attestation has expired + if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { + revert AttestationNotFound(); + } + + // check if attestation has been revoked + if (revocationTime != ZERO_TIMESTAMP) { + revert RevokedAttestation(record.attester); + } + // if a expectedType is set, check if the attestation is for the correct module type + // if no expectedType is set, module type is not checked + if (expectedType != ZERO_MODULE_TYPE && !packedModuleType.isType(expectedType)) { + revert InvalidModuleType(); + } + } + + function getTrustedAttesters(address smartAccount) + public + view + returns (address[] memory attesters) + { + TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; + uint256 count = trustedAttesters.attesterCount; + attesters = new address[](count); + attesters[0] = trustedAttesters.attester; + + for (uint256 i = 1; i < count; i++) { + // get next attester from linked List + attesters[i] = trustedAttesters.linkedAttesters[attesters[i - 1]]; + } + } +} + +/// @notice Read and write to persistent storage at a fraction of the cost. +/// @author Solady (https://github.com/vectorized/solmady/blob/main/src/utils/SSTORE2.sol) +/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie) +/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol) +/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol) +library SSTORE2 { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTANTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev We skip the first byte as it's a STOP opcode, + /// which ensures the contract can't be called. + uint256 internal constant DATA_OFFSET = 1; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CUSTOM ERRORS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Unable to deploy the storage contract. + error DeploymentFailed(); + + /// @dev The storage contract address is invalid. + error InvalidPointer(); + + /// @dev Attempt to read outside of the storage contract's bytecode bounds. + error ReadOutOfBounds(); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* WRITE LOGIC */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Writes `data` into the bytecode of a storage contract and returns its address. + function write(bytes memory data) internal returns (address pointer) { + /// @solidity memory-safe-assembly + assembly { + let originalDataLength := mload(data) + + // Add 1 to data size since we are prefixing it with a STOP opcode. + let dataSize := add(originalDataLength, DATA_OFFSET) + + /** + * ------------------------------------------------------------------------------+ + * Opcode | Mnemonic | Stack | Memory | + * ------------------------------------------------------------------------------| + * 61 dataSize | PUSH2 dataSize | dataSize | | + * 80 | DUP1 | dataSize dataSize | | + * 60 0xa | PUSH1 0xa | 0xa dataSize dataSize | | + * 3D | RETURNDATASIZE | 0 0xa dataSize dataSize | | + * 39 | CODECOPY | dataSize | [0..dataSize): code | + * 3D | RETURNDATASIZE | 0 dataSize | [0..dataSize): code | + * F3 | RETURN | | [0..dataSize): code | + * 00 | STOP | | | + * ------------------------------------------------------------------------------+ + * @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called. + * Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16. + */ + mstore( + // Do a out-of-gas revert if `dataSize` is more than 2 bytes. + // The actual EVM limit may be smaller and may change over time. + add(data, gt(dataSize, 0xffff)), + // Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2. + or(0xfd61000080600a3d393df300, shl(0x40, dataSize)) + ) + + // Deploy a new contract with the generated creation code. + pointer := create(0, add(data, 0x15), add(dataSize, 0xa)) + + // If `pointer` is zero, revert. + if iszero(pointer) { + // Store the function selector of `DeploymentFailed()`. + mstore(0x00, 0x30116425) + // Revert with (offset, size). + revert(0x1c, 0x04) + } + + // Restore original length of the variable size `data`. + mstore(data, originalDataLength) + } + } + + /// @dev Writes `data` into the bytecode of a storage contract with `salt` + /// and returns its deterministic address. + function writeDeterministic(bytes memory data, bytes32 salt) + internal + returns (address pointer) + { + /// @solidity memory-safe-assembly + assembly { + let originalDataLength := mload(data) + let dataSize := add(originalDataLength, DATA_OFFSET) + + mstore( + // Do a out-of-gas revert if `dataSize` is more than 2 bytes. + // The actual EVM limit may be smaller and may change over time. + add(data, gt(dataSize, 0xffff)), + // Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2. + or(0xfd61000080600a3d393df300, shl(0x40, dataSize)) + ) + + // Deploy a new contract with the generated creation code. + pointer := create2(0, add(data, 0x15), add(dataSize, 0xa), salt) + + // If `pointer` is zero, revert. + if iszero(pointer) { + // Store the function selector of `DeploymentFailed()`. + mstore(0x00, 0x30116425) + // Revert with (offset, size). + revert(0x1c, 0x04) + } + + // Restore original length of the variable size `data`. + mstore(data, originalDataLength) + } + } + + /// @dev Returns the initialization code hash of the storage contract for `data`. + /// Used for mining vanity addresses with create2crunch. + function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) { + /// @solidity memory-safe-assembly + assembly { + let originalDataLength := mload(data) + let dataSize := add(originalDataLength, DATA_OFFSET) + + // Do a out-of-gas revert if `dataSize` is more than 2 bytes. + // The actual EVM limit may be smaller and may change over time. + returndatacopy(returndatasize(), returndatasize(), shr(16, dataSize)) + + mstore(data, or(0x61000080600a3d393df300, shl(0x40, dataSize))) + + hash := keccak256(add(data, 0x15), add(dataSize, 0xa)) + + // Restore original length of the variable size `data`. + mstore(data, originalDataLength) + } + } + + /// @dev Returns the address of the storage contract for `data` + /// deployed with `salt` by `deployer`. + /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. + function predictDeterministicAddress(bytes memory data, bytes32 salt, address deployer) + internal + pure + returns (address predicted) + { + bytes32 hash = initCodeHash(data); + /// @solidity memory-safe-assembly + assembly { + // Compute and store the bytecode hash. + mstore8(0x00, 0xff) // Write the prefix. + mstore(0x35, hash) + mstore(0x01, shl(96, deployer)) + mstore(0x15, salt) + predicted := keccak256(0x00, 0x55) + // Restore the part of the free memory pointer that has been overwritten. + mstore(0x35, 0) + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* READ LOGIC */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns all the `data` from the bytecode of the storage contract at `pointer`. + function read(address pointer) internal view returns (bytes memory data) { + /// @solidity memory-safe-assembly + assembly { + let pointerCodesize := extcodesize(pointer) + if iszero(pointerCodesize) { + // Store the function selector of `InvalidPointer()`. + mstore(0x00, 0x11052bb4) + // Revert with (offset, size). + revert(0x1c, 0x04) + } + // Offset all indices by 1 to skip the STOP opcode. + let size := sub(pointerCodesize, DATA_OFFSET) + + // Get the pointer to the free memory and allocate + // enough 32-byte words for the data and the length of the data, + // then copy the code to the allocated memory. + // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. + data := mload(0x40) + mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) + mstore(data, size) + mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. + extcodecopy(pointer, add(data, 0x20), DATA_OFFSET, size) + } + } + + /// @dev Returns the `data` from the bytecode of the storage contract at `pointer`, + /// from the byte at `start`, to the end of the data stored. + function read(address pointer, uint256 start) internal view returns (bytes memory data) { + /// @solidity memory-safe-assembly + assembly { + let pointerCodesize := extcodesize(pointer) + if iszero(pointerCodesize) { + // Store the function selector of `InvalidPointer()`. + mstore(0x00, 0x11052bb4) + // Revert with (offset, size). + revert(0x1c, 0x04) + } + + // If `!(pointer.code.size > start)`, reverts. + // This also handles the case where `start + DATA_OFFSET` overflows. + if iszero(gt(pointerCodesize, start)) { + // Store the function selector of `ReadOutOfBounds()`. + mstore(0x00, 0x84eb0dd1) + // Revert with (offset, size). + revert(0x1c, 0x04) + } + let size := sub(pointerCodesize, add(start, DATA_OFFSET)) + + // Get the pointer to the free memory and allocate + // enough 32-byte words for the data and the length of the data, + // then copy the code to the allocated memory. + // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. + data := mload(0x40) + mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) + mstore(data, size) + mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. + extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size) + } + } + + /// @dev Returns the `data` from the bytecode of the storage contract at `pointer`, + /// from the byte at `start`, to the byte at `end` (exclusive) of the data stored. + function read(address pointer, uint256 start, uint256 end) + internal + view + returns (bytes memory data) + { + /// @solidity memory-safe-assembly + assembly { + let pointerCodesize := extcodesize(pointer) + if iszero(pointerCodesize) { + // Store the function selector of `InvalidPointer()`. + mstore(0x00, 0x11052bb4) + // Revert with (offset, size). + revert(0x1c, 0x04) + } + + // If `!(pointer.code.size > end) || (start > end)`, revert. + // This also handles the cases where + // `end + DATA_OFFSET` or `start + DATA_OFFSET` overflows. + if iszero( + and( + gt(pointerCodesize, end), // Within bounds. + iszero(gt(start, end)) // Valid range. + ) + ) { + // Store the function selector of `ReadOutOfBounds()`. + mstore(0x00, 0x84eb0dd1) + // Revert with (offset, size). + revert(0x1c, 0x04) + } + let size := sub(end, start) + + // Get the pointer to the free memory and allocate + // enough 32-byte words for the data and the length of the data, + // then copy the code to the allocated memory. + // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. + data := mload(0x40) + mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) + mstore(data, size) + mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. + extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size) + } + } +} + +library AttestationLib { + // The hash of the data type used to relay calls to the attest function. It's the value of + bytes32 internal constant ATTEST_TYPEHASH = + keccak256("AttestationRequest(address,uint48,bytes,uint32[])"); + + // The hash of the data type used to relay calls to the revoke function. It's the value of + bytes32 internal constant REVOKE_TYPEHASH = keccak256("RevocationRequest(address)"); + + function sload2(AttestationDataRef dataPointer) internal view returns (bytes memory data) { + data = SSTORE2.read(AttestationDataRef.unwrap(dataPointer)); + } + + function sstore2( + AttestationRequest calldata request, + bytes32 salt + ) + internal + returns (AttestationDataRef dataPointer) + { + /** + * @dev We are using CREATE2 to deterministically generate the address of the attestation data. + * Checking if an attestation pointer already exists, would cost more GAS in the average case. + */ + dataPointer = AttestationDataRef.wrap(SSTORE2.writeDeterministic(request.data, salt)); + } + + function sstore2Salt(address attester, address module) internal view returns (bytes32 salt) { + salt = keccak256(abi.encodePacked(attester, module, block.timestamp, block.chainid)); + } + + function hash( + AttestationRequest calldata data, + uint256 nonce + ) + internal + pure + returns (bytes32 _hash) + { + _hash = keccak256(abi.encode(ATTEST_TYPEHASH, keccak256(abi.encode(data)), nonce)); + } + + function hash( + AttestationRequest[] calldata data, + uint256 nonce + ) + internal + pure + returns (bytes32 _hash) + { + _hash = keccak256(abi.encode(ATTEST_TYPEHASH, keccak256(abi.encode(data)), nonce)); + } + + function hash( + RevocationRequest calldata data, + uint256 nonce + ) + internal + pure + returns (bytes32 _hash) + { + // TODO: check if this is correct + _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); + } + + function hash( + RevocationRequest[] calldata data, + uint256 nonce + ) + internal + pure + returns (bytes32 _hash) + { + // TODO: check if this is correct + _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); + } +} + +/** + * AttestationManager handles the registry's internal storage of new attestations and revocation of attestation + * @dev This contract is abstract and provides utility functions to store attestations and revocations. + */ +abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManager { + using StubLib for *; + using AttestationLib for AttestationDataRef; + using AttestationLib for AttestationRequest; + using AttestationLib for AttestationRequest[]; + using AttestationLib for address; + using ModuleTypeLib for ModuleType[]; + + mapping(address module => mapping(address attester => AttestationRecord attestation)) internal + _moduleToAttesterToAttestations; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Attestation */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /** + * Processes an attestation request and stores the attestation in the registry. + * If the attestation was made for a module that was not registered, the function will revert. + * function will get the external Schema Validator for the supplied SchemaUID + * and call it, if an external IExternalSchemaValidator was set + * function will get the external IExternalResolver for the module - that the attestation is for + * and call it, if an external Resolver was set + */ + function _attest( + address attester, + SchemaUID schemaUID, + AttestationRequest calldata request + ) + internal + { + (AttestationRecord memory record, ResolverUID resolverUID) = + _storeAttestation({ schemaUID: schemaUID, attester: attester, request: request }); + + record.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); + record.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); + } + + /** + * Processes an array of attestation requests and stores the attestations in the registry. + * If the attestation was made for a module that was not registered, the function will revert. + * function will get the external Schema Validator for the supplied SchemaUID + * and call it, if an external IExternalSchemaValidator was set + * function will get the external IExternalResolver for the module - that the attestation is for + * and call it, if an external Resolver was set + */ + function _attest( + address attester, + SchemaUID schemaUID, + AttestationRequest[] calldata requests + ) + internal + { + uint256 length = requests.length; + AttestationRecord[] memory records = new AttestationRecord[](length); + // loop will check that the batched attestation is made ONLY for the same resolver + ResolverUID resolverUID; + for (uint256 i; i < length; i++) { + ResolverUID resolverUID_cache; + // save the attestation record into records array. + (records[i], resolverUID_cache) = _storeAttestation({ + schemaUID: schemaUID, + attester: attester, + request: requests[i] + }); + // cache the first resolverUID and compare it to the rest + // If the resolverUID is different, revert + // @dev if you want to use different resolvers, make different attestation calls + if (i == 0) resolverUID = resolverUID_cache; + else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); + } + + // Use StubLib to call schema Validation and resolver if needed + records.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); + records.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); + } + + /** + * Stores an attestation in the registry storage. + * The bytes encoded AttestationRequest.Data is not stored directly into the registry storage, + * but rather stored with SSTORE2. SSTORE2/SLOAD2 is writing and reading contract storage + * paying a fraction of the cost, it uses contract code as storage, writing data takes the + * form of contract creations and reading data uses EXTCODECOPY. + * since attestation data is supposed to be immutable, it is a good candidate for SSTORE2 + * + * @dev This function will revert if the same module is attested twice by the same attester. + * If you want to re-attest, you have to revoke your attestation first, and then attest again. + * + * @param attester The address of the attesting account. + * @param request The AttestationRequest that was supplied via calldata + * @return record The AttestationRecord of what was written into registry storage + * @return resolverUID The resolverUID in charge for the module + */ + function _storeAttestation( + SchemaUID schemaUID, + address attester, + AttestationRequest calldata request + ) + internal + returns (AttestationRecord memory record, ResolverUID resolverUID) + { + // TODO: what if schema behind schemaUID doesnt exist? + // Frontrun on L2s? + uint48 timeNow = _time(); + // Ensure that either no expiration time was set or that it was set in the future. + if (request.expirationTime != ZERO_TIMESTAMP && request.expirationTime <= timeNow) { + revert InvalidExpirationTime(); + } + // caching module address. + address module = request.moduleAddr; + // SLOAD the resolverUID from the moduleRecord + resolverUID = _moduleAddrToRecords[module].resolverUID; + // Ensure that attestation is for module that was registered. + if (resolverUID == EMPTY_RESOLVER_UID) { + revert ModuleNotFoundInRegistry(module); + } + + // use SSTORE2 to store the data in attestationRequest + // @dev this will revert, if in a batched attestation, + // the same data is used twice by the same attester for the same module since the salt will be the same + AttestationDataRef sstore2Pointer = request.sstore2({ salt: attester.sstore2Salt(module) }); + + // write into memory allocated record, since that is the return value + record = AttestationRecord({ + time: timeNow, + expirationTime: request.expirationTime, + revocationTime: uint48(ZERO_TIMESTAMP), + moduleTypes: request.moduleTypes.packCalldata(), + schemaUID: schemaUID, + moduleAddr: module, + attester: attester, + dataPointer: sstore2Pointer + }); + // SSTORE attestation to registry storage + _moduleToAttesterToAttestations[request.moduleAddr][attester] = record; + + emit Attested(module, attester, schemaUID, sstore2Pointer); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* Revocation */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /** + * Revoke a single Revocation Request + * This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, + * and pass the RevocationRecord to the resolver to check if the revocation is valid + */ + function _revoke(address attester, RevocationRequest calldata request) internal { + (AttestationRecord memory record, ResolverUID resolverUID) = + _storeRevocation(attester, request); + // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? + record.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); + } + + /** + * Revoke an array Revocation Request + * This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, + * and pass the RevocationRecord to the resolver to check if the revocation is valid + */ + function _revoke(address attester, RevocationRequest[] calldata requests) internal { + uint256 length = requests.length; + AttestationRecord[] memory records = new AttestationRecord[](length); + ResolverUID resolverUID; + // loop over revocation requests. This function will revert if different resolverUIDs + // are responsible for the modules that are subject of the revocation. This is to reduce complexity + // @dev if you want to revoke attestations from different resolvers, make different revocation calls + for (uint256 i; i < length; i++) { + ResolverUID resolverUID_cache; + (records[i], resolverUID_cache) = _storeRevocation(attester, requests[i]); + if (i == 0) resolverUID = resolverUID_cache; + else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); + } + + // No schema validation required during revocation. the attestation data was already checked against + + // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? + records.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); + } + + /** + * Gets the AttestationRecord for the supplied RevocationRequest and stores the revocation time in the registry storage + * @param revoker The address of the attesting account. + * @param request The AttestationRequest that was supplied via calldata + * @return record The AttestationRecord of what was written into registry storage + * @return resolverUID The resolverUID in charge for the module + */ + function _storeRevocation( + address revoker, + RevocationRequest calldata request + ) + internal + returns (AttestationRecord memory record, ResolverUID resolverUID) + { + AttestationRecord storage attestationStorage = + _moduleToAttesterToAttestations[request.moduleAddr][revoker]; + + // SLOAD entire record. This will later be passed to the resolver + record = attestationStorage; + resolverUID = _moduleAddrToRecords[request.moduleAddr].resolverUID; + + // Ensure that we aren't attempting to revoke a non-existing attestation. + if (record.dataPointer == EMPTY_ATTESTATION_REF) { + revert AttestationNotFound(); + } + + // Allow only original attesters to revoke their attestations. + if (record.attester != revoker) { + revert AccessDenied(); + } + + // Ensure that we aren't trying to revoke the same attestation twice. + if (record.revocationTime != ZERO_TIMESTAMP) { + revert AlreadyRevoked(); + } + + // SSTORE revocation time to registry storage + attestationStorage.revocationTime = _time(); + // set revocation time to NOW + emit Revoked({ moduleAddr: record.moduleAddr, revoker: revoker, schema: record.schemaUID }); + } + + /** + * Returns the attestation records for the given module and attesters. + * This function is expected to be used by TrustManager and TrustManagerExternalAttesterList + */ + function _getAttestation( + address module, + address attester + ) + internal + view + override + returns (AttestationRecord storage) + { + return _moduleToAttesterToAttestations[module][attester]; + } +} + +abstract contract Attestation is IRegistry, AttestationManager { + function attest(SchemaUID schemaUID, AttestationRequest calldata request) external { + _attest(msg.sender, schemaUID, request); + } + + function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external { + _attest(msg.sender, schemaUID, requests); + } + + function revoke(RevocationRequest calldata request) external { + _revoke(msg.sender, request); + } + + function revoke(RevocationRequest[] calldata requests) external { + _revoke(msg.sender, requests); + } + + function findAttestation( + address module, + address attester + ) + external + view + returns (AttestationRecord memory attestation) + { + attestation = _getAttestation(module, attester); + } + + function findAttestations( + address module, + address[] calldata attesters + ) + external + view + returns (AttestationRecord[] memory attestations) + { + uint256 length = attesters.length; + attestations = new AttestationRecord[](length); + for (uint256 i; i < length; i++) { + attestations[i] = _getAttestation(module, attesters[i]); + } + } +} + +/// @notice Contract for EIP-712 typed structured data hashing and signing. +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol) +/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol) +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol) +/// +/// @dev Note, this implementation: +/// - Uses `address(this)` for the `verifyingContract` field. +/// - Does NOT use the optional EIP-712 salt. +/// - Does NOT use any EIP-712 extensions. +/// This is for simplicity and to save gas. +/// If you need to customize, please fork / modify accordingly. +abstract contract EIP712 { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTANTS AND IMMUTABLES */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. + bytes32 internal constant _DOMAIN_TYPEHASH = + 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; + + uint256 private immutable _cachedThis; + uint256 private immutable _cachedChainId; + bytes32 private immutable _cachedNameHash; + bytes32 private immutable _cachedVersionHash; + bytes32 private immutable _cachedDomainSeparator; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTRUCTOR */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Cache the hashes for cheaper runtime gas costs. + /// In the case of upgradeable contracts (i.e. proxies), + /// or if the chain id changes due to a hard fork, + /// the domain separator will be seamlessly calculated on-the-fly. + constructor() { + _cachedThis = uint256(uint160(address(this))); + _cachedChainId = block.chainid; + + string memory name; + string memory version; + if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion(); + bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name)); + bytes32 versionHash = + _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version)); + _cachedNameHash = nameHash; + _cachedVersionHash = versionHash; + + bytes32 separator; + if (!_domainNameAndVersionMayChange()) { + /// @solidity memory-safe-assembly + assembly { + let m := mload(0x40) // Load the free memory pointer. + mstore(m, _DOMAIN_TYPEHASH) + mstore(add(m, 0x20), nameHash) + mstore(add(m, 0x40), versionHash) + mstore(add(m, 0x60), chainid()) + mstore(add(m, 0x80), address()) + separator := keccak256(m, 0xa0) + } + } + _cachedDomainSeparator = separator; + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* FUNCTIONS TO OVERRIDE */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Please override this function to return the domain name and version. + /// ``` + /// function _domainNameAndVersion() + /// internal + /// pure + /// virtual + /// returns (string memory name, string memory version) + /// { + /// name = "Solady"; + /// version = "1"; + /// } + /// ``` + /// + /// Note: If the returned result may change after the contract has been deployed, + /// you must override `_domainNameAndVersionMayChange()` to return true. + function _domainNameAndVersion() + internal + view + virtual + returns (string memory name, string memory version); + + /// @dev Returns if `_domainNameAndVersion()` may change + /// after the contract has been deployed (i.e. after the constructor). + /// Default: false. + function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {} + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* HASHING OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns the EIP-712 domain separator. + function _domainSeparator() internal view virtual returns (bytes32 separator) { + if (_domainNameAndVersionMayChange()) { + separator = _buildDomainSeparator(); + } else { + separator = _cachedDomainSeparator; + if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator(); + } + } + + /// @dev Returns the hash of the fully encoded EIP-712 message for this domain, + /// given `structHash`, as defined in + /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct. + /// + /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message: + /// ``` + /// bytes32 digest = _hashTypedData(keccak256(abi.encode( + /// keccak256("Mail(address to,string contents)"), + /// mailTo, + /// keccak256(bytes(mailContents)) + /// ))); + /// address signer = ECDSA.recover(digest, signature); + /// ``` + function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) { + // We will use `digest` to store the domain separator to save a bit of gas. + if (_domainNameAndVersionMayChange()) { + digest = _buildDomainSeparator(); + } else { + digest = _cachedDomainSeparator; + if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator(); + } + /// @solidity memory-safe-assembly + assembly { + // Compute the digest. + mstore(0x00, 0x1901000000000000) // Store "\x19\x01". + mstore(0x1a, digest) // Store the domain separator. + mstore(0x3a, structHash) // Store the struct hash. + digest := keccak256(0x18, 0x42) + // Restore the part of the free memory slot that was overwritten. + mstore(0x3a, 0) + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* EIP-5267 OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev See: https://eips.ethereum.org/EIPS/eip-5267 + function eip712Domain() + public + view + virtual + returns ( + bytes1 fields, + string memory name, + string memory version, + uint256 chainId, + address verifyingContract, + bytes32 salt, + uint256[] memory extensions + ) + { + fields = hex"0f"; // `0b01111`. + (name, version) = _domainNameAndVersion(); + chainId = block.chainid; + verifyingContract = address(this); + salt = salt; // `bytes32(0)`. + extensions = extensions; // `new uint256[](0)`. + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* PRIVATE HELPERS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns the EIP-712 domain separator. + function _buildDomainSeparator() private view returns (bytes32 separator) { + // We will use `separator` to store the name hash to save a bit of gas. + bytes32 versionHash; + if (_domainNameAndVersionMayChange()) { + (string memory name, string memory version) = _domainNameAndVersion(); + separator = keccak256(bytes(name)); + versionHash = keccak256(bytes(version)); + } else { + separator = _cachedNameHash; + versionHash = _cachedVersionHash; + } + /// @solidity memory-safe-assembly + assembly { + let m := mload(0x40) // Load the free memory pointer. + mstore(m, _DOMAIN_TYPEHASH) + mstore(add(m, 0x20), separator) // Name hash. + mstore(add(m, 0x40), versionHash) + mstore(add(m, 0x60), chainid()) + mstore(add(m, 0x80), address()) + separator := keccak256(m, 0xa0) + } + } + + /// @dev Returns if the cached domain separator has been invalidated. + function _cachedDomainSeparatorInvalidated() private view returns (bool result) { + uint256 cachedChainId = _cachedChainId; + uint256 cachedThis = _cachedThis; + /// @solidity memory-safe-assembly + assembly { + result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis))) + } + } +} + +/// @notice Signature verification helper that supports both ECDSA signatures from EOAs +/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe. +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol) +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol) +/// +/// @dev Note: +/// - The signature checking functions use the ecrecover precompile (0x1). +/// - The `bytes memory signature` variants use the identity precompile (0x4) +/// to copy memory internally. +/// - Unlike ECDSA signatures, contract signatures are revocable. +/// - As of Solady version 0.0.134, all `bytes signature` variants accept both +/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. +/// See: https://eips.ethereum.org/EIPS/eip-2098 +/// This is for calldata efficiency on smart accounts prevalent on L2s. +/// +/// WARNING! Do NOT use signatures as unique identifiers: +/// - Use a nonce in the digest to prevent replay attacks on the same contract. +/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. +/// EIP-712 also enables readable signing of typed data for better user safety. +/// This implementation does NOT check if a signature is non-malleable. +library SignatureCheckerLib { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* SIGNATURE CHECKING OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns whether `signature` is valid for `signer` and `hash`. + /// If `signer` is a smart contract, the signature is validated with ERC1271. + /// Otherwise, the signature is validated with `ECDSA.recover`. + function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) + internal + view + returns (bool isValid) + { + /// @solidity memory-safe-assembly + assembly { + // Clean the upper 96 bits of `signer` in case they are dirty. + for { signer := shr(96, shl(96, signer)) } signer {} { + let m := mload(0x40) + mstore(0x00, hash) + mstore(0x40, mload(add(signature, 0x20))) // `r`. + if eq(mload(signature), 64) { + let vs := mload(add(signature, 0x40)) + mstore(0x20, add(shr(255, vs), 27)) // `v`. + mstore(0x60, shr(1, shl(1, vs))) // `s`. + let t := + staticcall( + gas(), // Amount of gas left for the transaction. + 1, // Address of `ecrecover`. + 0x00, // Start of input. + 0x80, // Size of input. + 0x01, // Start of output. + 0x20 // Size of output. + ) + // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. + if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { + isValid := 1 + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + break + } + } + if eq(mload(signature), 65) { + mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. + mstore(0x60, mload(add(signature, 0x40))) // `s`. + let t := + staticcall( + gas(), // Amount of gas left for the transaction. + 1, // Address of `ecrecover`. + 0x00, // Start of input. + 0x80, // Size of input. + 0x01, // Start of output. + 0x20 // Size of output. + ) + // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. + if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { + isValid := 1 + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + break + } + } + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + + let f := shl(224, 0x1626ba7e) + mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. + mstore(add(m, 0x04), hash) + let d := add(m, 0x24) + mstore(d, 0x40) // The offset of the `signature` in the calldata. + // Copy the `signature` over. + let n := add(0x20, mload(signature)) + pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) + // forgefmt: disable-next-item + isValid := and( + // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). + eq(mload(d), f), + // Whether the staticcall does not revert. + // This must be placed at the end of the `and` clause, + // as the arguments are evaluated from right to left. + staticcall( + gas(), // Remaining gas. + signer, // The `signer` address. + m, // Offset of calldata in memory. + add(returndatasize(), 0x44), // Length of calldata in memory. + d, // Offset of returndata. + 0x20 // Length of returndata to write. + ) + ) + break + } + } + } + + /// @dev Returns whether `signature` is valid for `signer` and `hash`. + /// If `signer` is a smart contract, the signature is validated with ERC1271. + /// Otherwise, the signature is validated with `ECDSA.recover`. + function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature) + internal + view + returns (bool isValid) + { + /// @solidity memory-safe-assembly + assembly { + // Clean the upper 96 bits of `signer` in case they are dirty. + for { signer := shr(96, shl(96, signer)) } signer {} { + let m := mload(0x40) + mstore(0x00, hash) + if eq(signature.length, 64) { + let vs := calldataload(add(signature.offset, 0x20)) + mstore(0x20, add(shr(255, vs), 27)) // `v`. + mstore(0x40, calldataload(signature.offset)) // `r`. + mstore(0x60, shr(1, shl(1, vs))) // `s`. + let t := + staticcall( + gas(), // Amount of gas left for the transaction. + 1, // Address of `ecrecover`. + 0x00, // Start of input. + 0x80, // Size of input. + 0x01, // Start of output. + 0x20 // Size of output. + ) + // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. + if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { + isValid := 1 + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + break + } + } + if eq(signature.length, 65) { + mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. + calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`. + let t := + staticcall( + gas(), // Amount of gas left for the transaction. + 1, // Address of `ecrecover`. + 0x00, // Start of input. + 0x80, // Size of input. + 0x01, // Start of output. + 0x20 // Size of output. + ) + // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. + if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { + isValid := 1 + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + break + } + } + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + + let f := shl(224, 0x1626ba7e) + mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. + mstore(add(m, 0x04), hash) + let d := add(m, 0x24) + mstore(d, 0x40) // The offset of the `signature` in the calldata. + mstore(add(m, 0x44), signature.length) + // Copy the `signature` over. + calldatacopy(add(m, 0x64), signature.offset, signature.length) + // forgefmt: disable-next-item + isValid := and( + // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). + eq(mload(d), f), + // Whether the staticcall does not revert. + // This must be placed at the end of the `and` clause, + // as the arguments are evaluated from right to left. + staticcall( + gas(), // Remaining gas. + signer, // The `signer` address. + m, // Offset of calldata in memory. + add(signature.length, 0x64), // Length of calldata in memory. + d, // Offset of returndata. + 0x20 // Length of returndata to write. + ) + ) + break + } + } + } + + /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`. + /// If `signer` is a smart contract, the signature is validated with ERC1271. + /// Otherwise, the signature is validated with `ECDSA.recover`. + function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) + internal + view + returns (bool isValid) + { + /// @solidity memory-safe-assembly + assembly { + // Clean the upper 96 bits of `signer` in case they are dirty. + for { signer := shr(96, shl(96, signer)) } signer {} { + let m := mload(0x40) + mstore(0x00, hash) + mstore(0x20, add(shr(255, vs), 27)) // `v`. + mstore(0x40, r) // `r`. + mstore(0x60, shr(1, shl(1, vs))) // `s`. + let t := + staticcall( + gas(), // Amount of gas left for the transaction. + 1, // Address of `ecrecover`. + 0x00, // Start of input. + 0x80, // Size of input. + 0x01, // Start of output. + 0x20 // Size of output. + ) + // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. + if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { + isValid := 1 + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + break + } + + let f := shl(224, 0x1626ba7e) + mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. + mstore(add(m, 0x04), hash) + let d := add(m, 0x24) + mstore(d, 0x40) // The offset of the `signature` in the calldata. + mstore(add(m, 0x44), 65) // Length of the signature. + mstore(add(m, 0x64), r) // `r`. + mstore(add(m, 0x84), mload(0x60)) // `s`. + mstore8(add(m, 0xa4), mload(0x20)) // `v`. + // forgefmt: disable-next-item + isValid := and( + // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). + eq(mload(d), f), + // Whether the staticcall does not revert. + // This must be placed at the end of the `and` clause, + // as the arguments are evaluated from right to left. + staticcall( + gas(), // Remaining gas. + signer, // The `signer` address. + m, // Offset of calldata in memory. + 0xa5, // Length of calldata in memory. + d, // Offset of returndata. + 0x20 // Length of returndata to write. + ) + ) + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + break + } + } + } + + /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`. + /// If `signer` is a smart contract, the signature is validated with ERC1271. + /// Otherwise, the signature is validated with `ECDSA.recover`. + function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) + internal + view + returns (bool isValid) + { + /// @solidity memory-safe-assembly + assembly { + // Clean the upper 96 bits of `signer` in case they are dirty. + for { signer := shr(96, shl(96, signer)) } signer {} { + let m := mload(0x40) + mstore(0x00, hash) + mstore(0x20, and(v, 0xff)) // `v`. + mstore(0x40, r) // `r`. + mstore(0x60, s) // `s`. + let t := + staticcall( + gas(), // Amount of gas left for the transaction. + 1, // Address of `ecrecover`. + 0x00, // Start of input. + 0x80, // Size of input. + 0x01, // Start of output. + 0x20 // Size of output. + ) + // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. + if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { + isValid := 1 + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + break + } + + let f := shl(224, 0x1626ba7e) + mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. + mstore(add(m, 0x04), hash) + let d := add(m, 0x24) + mstore(d, 0x40) // The offset of the `signature` in the calldata. + mstore(add(m, 0x44), 65) // Length of the signature. + mstore(add(m, 0x64), r) // `r`. + mstore(add(m, 0x84), s) // `s`. + mstore8(add(m, 0xa4), v) // `v`. + // forgefmt: disable-next-item + isValid := and( + // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). + eq(mload(d), f), + // Whether the staticcall does not revert. + // This must be placed at the end of the `and` clause, + // as the arguments are evaluated from right to left. + staticcall( + gas(), // Remaining gas. + signer, // The `signer` address. + m, // Offset of calldata in memory. + 0xa5, // Length of calldata in memory. + d, // Offset of returndata. + 0x20 // Length of returndata to write. + ) + ) + mstore(0x60, 0) // Restore the zero slot. + mstore(0x40, m) // Restore the free memory pointer. + break + } + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* ERC1271 OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract. + function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature) + internal + view + returns (bool isValid) + { + /// @solidity memory-safe-assembly + assembly { + let m := mload(0x40) + let f := shl(224, 0x1626ba7e) + mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. + mstore(add(m, 0x04), hash) + let d := add(m, 0x24) + mstore(d, 0x40) // The offset of the `signature` in the calldata. + // Copy the `signature` over. + let n := add(0x20, mload(signature)) + pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) + // forgefmt: disable-next-item + isValid := and( + // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). + eq(mload(d), f), + // Whether the staticcall does not revert. + // This must be placed at the end of the `and` clause, + // as the arguments are evaluated from right to left. + staticcall( + gas(), // Remaining gas. + signer, // The `signer` address. + m, // Offset of calldata in memory. + add(returndatasize(), 0x44), // Length of calldata in memory. + d, // Offset of returndata. + 0x20 // Length of returndata to write. + ) + ) + } + } + + /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract. + function isValidERC1271SignatureNowCalldata( + address signer, + bytes32 hash, + bytes calldata signature + ) internal view returns (bool isValid) { + /// @solidity memory-safe-assembly + assembly { + let m := mload(0x40) + let f := shl(224, 0x1626ba7e) + mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. + mstore(add(m, 0x04), hash) + let d := add(m, 0x24) + mstore(d, 0x40) // The offset of the `signature` in the calldata. + mstore(add(m, 0x44), signature.length) + // Copy the `signature` over. + calldatacopy(add(m, 0x64), signature.offset, signature.length) + // forgefmt: disable-next-item + isValid := and( + // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). + eq(mload(d), f), + // Whether the staticcall does not revert. + // This must be placed at the end of the `and` clause, + // as the arguments are evaluated from right to left. + staticcall( + gas(), // Remaining gas. + signer, // The `signer` address. + m, // Offset of calldata in memory. + add(signature.length, 0x64), // Length of calldata in memory. + d, // Offset of returndata. + 0x20 // Length of returndata to write. + ) + ) + } + } + + /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash` + /// for an ERC1271 `signer` contract. + function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) + internal + view + returns (bool isValid) + { + /// @solidity memory-safe-assembly + assembly { + let m := mload(0x40) + let f := shl(224, 0x1626ba7e) + mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. + mstore(add(m, 0x04), hash) + let d := add(m, 0x24) + mstore(d, 0x40) // The offset of the `signature` in the calldata. + mstore(add(m, 0x44), 65) // Length of the signature. + mstore(add(m, 0x64), r) // `r`. + mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`. + mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`. + // forgefmt: disable-next-item + isValid := and( + // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). + eq(mload(d), f), + // Whether the staticcall does not revert. + // This must be placed at the end of the `and` clause, + // as the arguments are evaluated from right to left. + staticcall( + gas(), // Remaining gas. + signer, // The `signer` address. + m, // Offset of calldata in memory. + 0xa5, // Length of calldata in memory. + d, // Offset of returndata. + 0x20 // Length of returndata to write. + ) + ) + } + } + + /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash` + /// for an ERC1271 `signer` contract. + function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) + internal + view + returns (bool isValid) + { + /// @solidity memory-safe-assembly + assembly { + let m := mload(0x40) + let f := shl(224, 0x1626ba7e) + mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. + mstore(add(m, 0x04), hash) + let d := add(m, 0x24) + mstore(d, 0x40) // The offset of the `signature` in the calldata. + mstore(add(m, 0x44), 65) // Length of the signature. + mstore(add(m, 0x64), r) // `r`. + mstore(add(m, 0x84), s) // `s`. + mstore8(add(m, 0xa4), v) // `v`. + // forgefmt: disable-next-item + isValid := and( + // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). + eq(mload(d), f), + // Whether the staticcall does not revert. + // This must be placed at the end of the `and` clause, + // as the arguments are evaluated from right to left. + staticcall( + gas(), // Remaining gas. + signer, // The `signer` address. + m, // Offset of calldata in memory. + 0xa5, // Length of calldata in memory. + d, // Offset of returndata. + 0x20 // Length of returndata to write. + ) + ) + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* HASHING OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns an Ethereum Signed Message, created from a `hash`. + /// This produces a hash corresponding to the one signed with the + /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) + /// JSON-RPC method as part of EIP-191. + function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x20, hash) // Store into scratch space for keccak256. + mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. + result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. + } + } + + /// @dev Returns an Ethereum Signed Message, created from `s`. + /// This produces a hash corresponding to the one signed with the + /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) + /// JSON-RPC method as part of EIP-191. + /// Note: Supports lengths of `s` up to 999999 bytes. + function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + let sLength := mload(s) + let o := 0x20 + mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. + mstore(0x00, 0x00) + // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. + for { let temp := sLength } 1 {} { + o := sub(o, 1) + mstore8(o, add(48, mod(temp, 10))) + temp := div(temp, 10) + if iszero(temp) { break } + } + let n := sub(0x3a, o) // Header length: `26 + 32 - o`. + // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. + returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) + mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. + result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) + mstore(s, sLength) // Restore the length. + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* EMPTY CALLDATA HELPERS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns an empty calldata bytes. + function emptySignature() internal pure returns (bytes calldata signature) { + /// @solidity memory-safe-assembly + assembly { + signature.length := 0 + } + } +} + +contract SignedAttestation is IRegistry, Attestation, EIP712 { + using AttestationLib for AttestationRequest; + using AttestationLib for RevocationRequest; + using AttestationLib for AttestationRequest[]; + using AttestationLib for RevocationRequest[]; + + mapping(address attester => uint256 nonce) public attesterNonce; + + /** + * Attestation can be made on any SchemaUID. + * @dev the registry, will forward your AttestationRequest.Data to the SchemaManager to + * check if the schema exists. + * @param schemaUID The SchemaUID of the schema to be attested. + */ + function attest( + SchemaUID schemaUID, + address attester, + AttestationRequest calldata request, + bytes calldata signature + ) + external + { + // verify signature + uint256 nonce = ++attesterNonce[attester]; + bytes32 digest = _hashTypedData(request.hash(nonce)); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); + if (!valid) revert InvalidSignature(); + + _attest(attester, schemaUID, request); + } + + function attest( + SchemaUID schemaUID, + address attester, + AttestationRequest[] calldata requests, + bytes calldata signature + ) + external + { + uint256 nonce = ++attesterNonce[attester]; + bytes32 digest = _hashTypedData(requests.hash(nonce)); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); + if (!valid) revert InvalidSignature(); + + _attest(attester, schemaUID, requests); + } + + function revoke( + address attester, + RevocationRequest calldata request, + bytes calldata signature + ) + external + { + uint256 nonce = ++attesterNonce[attester]; + bytes32 digest = _hashTypedData(request.hash(nonce)); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); + if (!valid) revert InvalidSignature(); + + _revoke(attester, request); + } + + function revoke( + address attester, + RevocationRequest[] calldata requests, + bytes calldata signature + ) + external + { + uint256 nonce = ++attesterNonce[attester]; + bytes32 digest = _hashTypedData(requests.hash(nonce)); + bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); + if (!valid) revert InvalidSignature(); + + _revoke(attester, requests); + } + + function _domainNameAndVersion() + internal + view + virtual + override + returns (string memory name, string memory version) + { + name = "RhinestoneRegistry"; + version = "v1.0"; + } + + function getDigest( + AttestationRequest calldata request, + address attester + ) + external + view + returns (bytes32) + { + return _hashTypedData(request.hash(attesterNonce[attester] + 1)); + } + + function getDigest( + AttestationRequest[] calldata requests, + address attester + ) + external + view + returns (bytes32) + { + return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); + } + + function getDigest( + RevocationRequest calldata request, + address attester + ) + external + view + returns (bytes32) + { + return _hashTypedData(request.hash(attesterNonce[attester] + 1)); + } + + function getDigest( + RevocationRequest[] calldata requests, + address attester + ) + external + view + returns (bytes32) + { + return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); + } +} + +/** + * @author zeroknots + */ + +contract Registry is IRegistry, SignedAttestation { } + diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 25ba6ab4..fd847449 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -94,7 +94,7 @@ interface IRegistry is IERC7484 { address indexed moduleAddr, address indexed attester, SchemaUID schemaUID, - AttestationDataRef sstore2Pointer + AttestationDataRef indexed sstore2Pointer ); error AlreadyRevoked(); @@ -230,7 +230,7 @@ interface IRegistry is IERC7484 { /* Manage Schemas */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - event SchemaRegistered(SchemaUID indexed uid, address registerer); + event SchemaRegistered(SchemaUID indexed uid, address indexed registerer); error SchemaAlreadyExists(SchemaUID uid); @@ -248,7 +248,7 @@ interface IRegistry is IERC7484 { /* Manage Resolvers */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - event NewResolver(ResolverUID indexed uid, address resolver); + event NewResolver(ResolverUID indexed uid, address indexed resolver); error ResolverAlreadyExists(); diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 7584aa56..8dda24c9 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -5,7 +5,6 @@ import { AttestationDataRef, AttestationRecord, AttestationRequest, - ModuleRecord, ModuleType, ResolverUID, RevocationRequest, @@ -21,12 +20,10 @@ import { IRegistry } from "../IRegistry.sol"; import { EMPTY_ATTESTATION_REF, EMPTY_RESOLVER_UID, _time, ZERO_TIMESTAMP } from "../Common.sol"; -import "forge-std/console2.sol"; /** * AttestationManager handles the registry's internal storage of new attestations and revocation of attestation * @dev This contract is abstract and provides utility functions to store attestations and revocations. */ - abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManager { using StubLib for *; using AttestationLib for AttestationDataRef; diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 9cd975da..a4a26a4a 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -72,6 +72,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { { ResolverRecord storage resolver = resolvers[resolverUID]; + // ensure that non-zero resolverUID was provided if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); ModuleRecord memory record = _storeModuleRecord({ @@ -81,7 +82,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { metadata: metadata }); // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s - // is this a problemt? + // is this a problem? record.requireExternalResolverOnModuleRegistration(resolver); } diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index 87081b84..c11d5556 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -68,7 +68,8 @@ abstract contract ResolverManager is IRegistry { } // TODO: VULN: - // Attacker could register the same resolver, thus be the owner of the resolverUID, then set the resolver to a malicious contract + // Attacker could register the same resolver, thus be the owner of the resolverUID, + // then set the resolver to a malicious contract function setResolver( ResolverUID uid, IExternalResolver resolver diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index c6689fb5..043e5d1a 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -8,8 +8,6 @@ import { TrustManagerExternalAttesterList } from "./TrustManagerExternalAttester import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; -import "forge-std/console2.sol"; - /** * @title TrustManager * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) From 06dda1944135d7be20ad7afcd9990cac113adce1 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Fri, 9 Feb 2024 09:55:01 +0700 Subject: [PATCH 37/84] feat: clean up module deployment --- src/IRegistry.sol | 12 +++- src/core/ModuleManager.sol | 43 +++++++++----- src/external/IExternalResolver.sol | 10 +++- src/external/examples/ResolverBase.sol | 19 +++++++ src/external/examples/TokenizedResolver.sol | 62 +++++++++++++++++++++ src/lib/Helpers.sol | 12 ++-- src/lib/ModuleDeploymentLib.sol | 40 ++----------- src/lib/StubLib.sol | 11 +++- test/ModuleRegistration.t.sol | 9 ++- test/mocks/MockResolver.sol | 6 +- 10 files changed, 158 insertions(+), 66 deletions(-) create mode 100644 src/external/examples/ResolverBase.sol create mode 100644 src/external/examples/TokenizedResolver.sol diff --git a/src/IRegistry.sol b/src/IRegistry.sol index fd847449..845f54c1 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -206,8 +206,7 @@ interface IRegistry is IERC7484 { function deployModule( bytes32 salt, ResolverUID resolverUID, - bytes calldata code, - bytes calldata deployParams, + bytes calldata initCode, bytes calldata metadata ) external @@ -221,6 +220,15 @@ interface IRegistry is IERC7484 { ) external; + + function calcModuleAddress( + bytes32 salt, + bytes calldata initCode + ) + external + view + returns (address); + function getRegisteredModules(address moduleAddress) external view diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index a4a26a4a..5c477d7d 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -37,30 +37,42 @@ abstract contract ModuleManager is IRegistry, ResolverManager { function deployModule( bytes32 salt, ResolverUID resolverUID, - bytes calldata code, - bytes calldata deployParams, + bytes calldata initCode, bytes calldata metadata ) external payable - returns (address moduleAddr) + returns (address moduleAddress) { ResolverRecord storage resolver = resolvers[resolverUID]; if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); // address predictedModuleAddress = code.calculateAddress(deployParams, salt); - // TODO: should we use the initCode hash return value? - (moduleAddr,,) = code.deploy(deployParams, salt, msg.value); + moduleAddress = initCode.deploy(salt); // _storeModuleRecord() will check if module is already registered, // which should prevent reentry to any deploy function ModuleRecord memory record = _storeModuleRecord({ - moduleAddress: moduleAddr, // TODO: is this reentrancy? + moduleAddress: moduleAddress, // TODO: is this reentrancy? sender: msg.sender, resolverUID: resolverUID, metadata: metadata }); - record.requireExternalResolverOnModuleRegistration(resolver); + record.requireExternalResolverOnModuleRegistration({ + moduleAddress: moduleAddress, + resolver: resolver + }); + } + + function calcModuleAddress( + bytes32 salt, + bytes calldata initCode + ) + external + view + returns (address) + { + return initCode.calcAddress(salt); } function registerModule( @@ -81,9 +93,10 @@ abstract contract ModuleManager is IRegistry, ResolverManager { resolverUID: resolverUID, metadata: metadata }); - // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s - // is this a problem? - record.requireExternalResolverOnModuleRegistration(resolver); + record.requireExternalResolverOnModuleRegistration({ + moduleAddress: moduleAddress, + resolver: resolver + }); } function deployViaFactory( @@ -96,7 +109,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { payable returns (address moduleAddress) { - ResolverRecord storage resolver = resolvers[resolverUID]; + ResolverRecord memory resolver = resolvers[resolverUID]; if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolverUID(resolverUID); // prevent someone from calling a registry function pretending its a factory if (factory == address(this)) revert FactoryCallFailed(factory); @@ -115,9 +128,11 @@ abstract contract ModuleManager is IRegistry, ResolverManager { resolverUID: resolverUID, metadata: metadata }); - // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s - // is this a problemt? - record.requireExternalResolverOnModuleRegistration(resolver); + + record.requireExternalResolverOnModuleRegistration({ + moduleAddress: moduleAddress, + resolver: resolver + }); } function _storeModuleRecord( diff --git a/src/external/IExternalResolver.sol b/src/external/IExternalResolver.sol index 1ac39eb1..88876a36 100644 --- a/src/external/IExternalResolver.sol +++ b/src/external/IExternalResolver.sol @@ -47,11 +47,17 @@ interface IExternalResolver is IERC165 { /** * @dev Processes a Module Registration * - * @param module Module registration artefact + * @param sender The msg.sender of the module registration + * @param moduleAddress address of the module + * @param record Module registration artefact * * @return Whether the registration is valid */ - function resolveModuleRegistration(ModuleRecord calldata module) + function resolveModuleRegistration( + address sender, + address moduleAddress, + ModuleRecord calldata record + ) external payable returns (bool); diff --git a/src/external/examples/ResolverBase.sol b/src/external/examples/ResolverBase.sol new file mode 100644 index 00000000..cd04a0f7 --- /dev/null +++ b/src/external/examples/ResolverBase.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { IExternalResolver } from "../IExternalResolver.sol"; +import { IRegistry } from "../../IRegistry.sol"; +import "../../DataTypes.sol"; + +abstract contract ResolverBase is IExternalResolver { + IRegistry internal immutable REGISTRY; + + constructor(IRegistry _registry) { + REGISTRY = _registry; + } + + modifier onlyRegistry() { + require(msg.sender == address(REGISTRY), "ONLY_REGISTRY"); + _; + } +} diff --git a/src/external/examples/TokenizedResolver.sol b/src/external/examples/TokenizedResolver.sol new file mode 100644 index 00000000..e11b72cd --- /dev/null +++ b/src/external/examples/TokenizedResolver.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "./ResolverBase.sol"; +import "forge-std/interfaces/IERC20.sol"; + +contract TokenizedResolver is ResolverBase { + IERC20 public immutable TOKEN; + uint256 internal immutable fee = 1e18; + + constructor(IERC20 _token, IRegistry _registry) ResolverBase(_registry) { + TOKEN = _token; + } + + function supportsInterface(bytes4 interfaceID) external view override returns (bool) { } + + function resolveAttestation(AttestationRecord calldata attestation) + external + payable + override + onlyRegistry + returns (bool) + { } + + function resolveAttestation(AttestationRecord[] calldata attestation) + external + payable + override + onlyRegistry + returns (bool) + { } + + function resolveRevocation(AttestationRecord calldata attestation) + external + payable + override + onlyRegistry + returns (bool) + { } + + function resolveRevocation(AttestationRecord[] calldata attestation) + external + payable + override + onlyRegistry + returns (bool) + { } + + function resolveModuleRegistration( + address sender, + address moduleAddress, + ModuleRecord calldata record + ) + external + payable + override + onlyRegistry + returns (bool) + { + TOKEN.transferFrom(sender, address(this), fee); + } +} diff --git a/src/lib/Helpers.sol b/src/lib/Helpers.sol index e6c4c490..82ce56e1 100644 --- a/src/lib/Helpers.sol +++ b/src/lib/Helpers.sol @@ -11,10 +11,11 @@ library UIDLib { * * @return schema UID. */ - function getUID(SchemaRecord memory schemaRecord) internal pure returns (SchemaUID) { - // TODO: this is a frontrunning vuln + function getUID(SchemaRecord memory schemaRecord) internal view returns (SchemaUID) { return SchemaUID.wrap( - keccak256(abi.encodePacked(schemaRecord.schema, address(schemaRecord.validator))) + keccak256( + abi.encodePacked(msg.sender, schemaRecord.schema, address(schemaRecord.validator)) + ) ); } @@ -25,8 +26,7 @@ library UIDLib { * * @return ResolverUID. */ - function getUID(ResolverRecord memory resolver) internal pure returns (ResolverUID) { - // TODO: this is a frontrunning vuln - return ResolverUID.wrap(keccak256(abi.encodePacked(resolver.resolver))); + function getUID(ResolverRecord memory resolver) internal view returns (ResolverUID) { + return ResolverUID.wrap(keccak256(abi.encodePacked(msg.sender, resolver.resolver))); } } diff --git a/src/lib/ModuleDeploymentLib.sol b/src/lib/ModuleDeploymentLib.sol index ccdb1cd6..ee46a4a5 100644 --- a/src/lib/ModuleDeploymentLib.sol +++ b/src/lib/ModuleDeploymentLib.sol @@ -7,54 +7,22 @@ pragma solidity ^0.8.19; * @author zeroknots */ library ModuleDeploymentLib { - /** - * @dev Gets the code hash of a contract at a given address. - * - * @param contractAddr The address of the contract. - * - * @return hash The hash of the contract code. - */ - function codeHash(address contractAddr) internal view returns (bytes32 hash) { - // solhint-disable-next-line no-inline-assembly - assembly { - if iszero(extcodesize(contractAddr)) { revert(0, 0) } - hash := extcodehash(contractAddr) - } - } - /** * @notice Creates a new contract using CREATE2 opcode. * @dev This method uses the CREATE2 opcode to deploy a new contract with a deterministic address. * - * @param createCode The creationCode for the contract. - * @param params The parameters for creating the contract. If the contract has a constructor, + * @param initCode The creationCode for the contract. * this MUST be provided. Function will fail if params are abi.encodePacked in createCode. - * @param salt The salt for creating the contract. * * @return moduleAddress The address of the deployed contract. - * @return initCodeHash packed (creationCode, constructor params) - * @return contractCodeHash hash of deployed bytecode */ - function deploy( - bytes memory createCode, - bytes memory params, - bytes32 salt, - uint256 value - ) - internal - returns (address moduleAddress, bytes32 initCodeHash, bytes32 contractCodeHash) - { - bytes memory initCode = abi.encodePacked(createCode, params); - // this enforces, that constructor params were supplied via params argument - // if params were abi.encodePacked in createCode, this will revert - initCodeHash = keccak256(initCode); - + function deploy(bytes memory initCode, bytes32 salt) internal returns (address moduleAddress) { + uint256 value = msg.value; // solhint-disable-next-line no-inline-assembly assembly { moduleAddress := create2(value, add(initCode, 0x20), mload(initCode), salt) // If the contract was not created successfully, the transaction is reverted. if iszero(extcodesize(moduleAddress)) { revert(0, 0) } - contractCodeHash := extcodehash(moduleAddress) } } @@ -70,7 +38,7 @@ library ModuleDeploymentLib { * @return The address that the contract would be deployed * at if the CREATE2 opcode was called with the specified _code and _salt. */ - function calcAddress(bytes memory _code, bytes32 _salt) internal view returns (address) { + function calcAddress(bytes calldata _code, bytes32 _salt) internal view returns (address) { bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(_code))); // NOTE: cast last 20 bytes of hash to address diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index de442f0b..526021db 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -108,7 +108,8 @@ library StubLib { function requireExternalResolverOnModuleRegistration( ModuleRecord memory moduleRecord, - ResolverRecord storage resolver + address moduleAddress, + ResolverRecord memory resolver ) internal { @@ -116,7 +117,13 @@ library StubLib { if (address(resolverContract) != ZERO_ADDRESS) return; - if (resolverContract.resolveModuleRegistration(moduleRecord) == false) { + if ( + resolverContract.resolveModuleRegistration({ + sender: msg.sender, + moduleAddress: moduleAddress, + record: moduleRecord + }) == false + ) { revert IRegistry.ExternalError_ModuleRegistration(); } } diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index 78c69795..11685cdd 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -22,7 +22,7 @@ contract ModuleRegistrationTest is BaseTest { bytes memory bytecode = type(MockModule).creationCode; - address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, "", "data"); + address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, ""); ModuleRecord memory record = registry.getRegisteredModules(moduleAddr); assertTrue(record.resolverUID == defaultResolverUID); } @@ -31,9 +31,12 @@ contract ModuleRegistrationTest is BaseTest { bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); bytes memory bytecode = type(MockModuleWithArgs).creationCode; + bytes memory initCode = abi.encodePacked(bytecode, abi.encode(313_131)); - address moduleAddr = - registry.deployModule(salt, defaultResolverUID, bytecode, abi.encode(313_131), ""); + address moduleAddr = registry.deployModule(salt, defaultResolverUID, initCode, ""); + + address moduleAddrCalc = registry.calcModuleAddress(salt, initCode); + assertTrue(moduleAddr == moduleAddrCalc); } function test_WhenRegisteringAModuleOnAnInvalidResolverUID() diff --git a/test/mocks/MockResolver.sol b/test/mocks/MockResolver.sol index 7a7d2ddc..25d7d4d3 100644 --- a/test/mocks/MockResolver.sol +++ b/test/mocks/MockResolver.sol @@ -64,7 +64,11 @@ contract MockResolver is IExternalResolver { return returnVal; } - function resolveModuleRegistration(ModuleRecord calldata module) + function resolveModuleRegistration( + address sender, + address moduleRecord, + ModuleRecord calldata record + ) external payable override From 09976c7709fc6a775a63846efd601dd2657487d4 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Fri, 9 Feb 2024 10:02:03 +0700 Subject: [PATCH 38/84] cleaning up --- Flatten.sol | 3571 --------------------------------------------- src/IRegistry.sol | 1 - 2 files changed, 3572 deletions(-) delete mode 100644 Flatten.sol diff --git a/Flatten.sol b/Flatten.sol deleted file mode 100644 index 3b15fd95..00000000 --- a/Flatten.sol +++ /dev/null @@ -1,3571 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; - -interface IERC165 { - /// @notice Query if a contract implements an interface - /// @param interfaceID The interface identifier, as specified in ERC-165 - /// @dev Interface identification is specified in ERC-165. This function - /// uses less than 30,000 gas. - /// @return `true` if the contract implements `interfaceID` and - /// `interfaceID` is not 0xffffffff, `false` otherwise - function supportsInterface(bytes4 interfaceID) external view returns (bool); -} - -/** - * @title The interface of an optional schema resolver. - */ -interface IExternalSchemaValidator is IERC165 { - /** - * @notice Validates an attestation request. - */ - function validateSchema(AttestationRecord calldata attestation) external view returns (bool); - - /** - * @notice Validates an array of attestation requests. - */ - function validateSchema(AttestationRecord[] calldata attestations) - external - view - returns (bool); -} - -/** - * @title The interface of an optional schema resolver. - * @dev The resolver is responsible for validating the schema and attestation data. - * @dev The resolver is also responsible for processing the attestation and revocation requests. - * - */ -interface IExternalResolver is IERC165 { - /** - * @dev Processes an attestation and verifies whether it's valid. - * - * @param attestation The new attestation. - * - * @return Whether the attestation is valid. - */ - function resolveAttestation(AttestationRecord calldata attestation) - external - payable - returns (bool); - - function resolveAttestation(AttestationRecord[] calldata attestation) - external - payable - returns (bool); - - /** - * @dev Processes an attestation revocation and verifies if it can be revoked. - * - * @param attestation The existing attestation to be revoked. - * - * @return Whether the attestation can be revoked. - */ - function resolveRevocation(AttestationRecord calldata attestation) - external - payable - returns (bool); - function resolveRevocation(AttestationRecord[] calldata attestation) - external - payable - returns (bool); - - /** - * @dev Processes a Module Registration - * - * @param module Module registration artefact - * - * @return Whether the registration is valid - */ - function resolveModuleRegistration(ModuleRecord calldata module) - external - payable - returns (bool); -} - -/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ -/* Storage Structs */ -/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - -// Struct that represents an attestation. -struct AttestationRecord { - uint48 time; // The time when the attestation was created (Unix timestamp). - uint48 expirationTime; // The time when the attestation expires (Unix timestamp). - uint48 revocationTime; // The time when the attestation was revoked (Unix timestamp). - PackedModuleTypes moduleTypes; // bit-wise encoded module types. See ModuleTypeLib - SchemaUID schemaUID; // The unique identifier of the schema. - address moduleAddr; // The implementation address of the module that is being attested. - address attester; // The attesting account. - AttestationDataRef dataPointer; // SSTORE2 pointer to the attestation data. -} - -// Struct that represents Module artefact. -struct ModuleRecord { - ResolverUID resolverUID; // The unique identifier of the resolver. - address sender; // The address of the sender who deployed the contract - bytes metadata; // Additional data related to the contract deployment -} - -struct SchemaRecord { - uint48 registeredAt; // The time when the schema was registered (Unix timestamp). - IExternalSchemaValidator validator; // Optional external schema validator. - string schema; // Custom specification of the schema (e.g., an ABI). -} - -struct ResolverRecord { - IExternalResolver resolver; // Optional resolver. - address resolverOwner; // The address of the account used to register the resolver. -} - -/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ -/* Attestation / Revocation Requests */ -/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - -/** - * @dev A struct representing the arguments of the attestation request. - */ -struct AttestationRequest { - address moduleAddr; // The moduleAddr of the attestation. - uint48 expirationTime; // The time when the attestation expires (Unix timestamp). - bytes data; // Custom attestation data. - ModuleType[] moduleTypes; // optional: The type(s) of the module. -} -/** - * @dev A struct representing the arguments of the revocation request. - */ - -struct RevocationRequest { - address moduleAddr; // The module address. -} - -/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ -/* Custom Types */ -/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - -//---------------------- SchemaUID ------------------------------| -type SchemaUID is bytes32; - -using { schemaEq as == } for SchemaUID global; -using { schemaNotEq as != } for SchemaUID global; - -function schemaEq(SchemaUID uid1, SchemaUID uid) pure returns (bool) { - return SchemaUID.unwrap(uid1) == SchemaUID.unwrap(uid); -} - -function schemaNotEq(SchemaUID uid1, SchemaUID uid) pure returns (bool) { - return SchemaUID.unwrap(uid1) != SchemaUID.unwrap(uid); -} - -//--------------------- ResolverUID -----------------------------| -type ResolverUID is bytes32; - -using { resolverEq as == } for ResolverUID global; -using { resolverNotEq as != } for ResolverUID global; - -function resolverEq(ResolverUID uid1, ResolverUID uid2) pure returns (bool) { - return ResolverUID.unwrap(uid1) == ResolverUID.unwrap(uid2); -} - -function resolverNotEq(ResolverUID uid1, ResolverUID uid2) pure returns (bool) { - return ResolverUID.unwrap(uid1) != ResolverUID.unwrap(uid2); -} - -type AttestationDataRef is address; - -using { attestationDataRefEq as == } for AttestationDataRef global; - -function attestationDataRefEq( - AttestationDataRef uid1, - AttestationDataRef uid2 -) - pure - returns (bool) -{ - return AttestationDataRef.unwrap(uid1) == AttestationDataRef.unwrap(uid2); -} - -type PackedModuleTypes is uint32; - -type ModuleType is uint32; - -using { moduleTypeEq as == } for ModuleType global; -using { moduleTypeNeq as != } for ModuleType global; - -function moduleTypeEq(ModuleType uid1, ModuleType uid2) pure returns (bool) { - return ModuleType.unwrap(uid1) == ModuleType.unwrap(uid2); -} - -function moduleTypeNeq(ModuleType uid1, ModuleType uid2) pure returns (bool) { - return ModuleType.unwrap(uid1) != ModuleType.unwrap(uid2); -} - -library UIDLib { - /** - * @dev Calculates a UID for a given schema. - * - * @param schemaRecord The input schema. - * - * @return schema UID. - */ - function getUID(SchemaRecord memory schemaRecord) internal pure returns (SchemaUID) { - // TODO: this is a frontrunning vuln - return SchemaUID.wrap( - keccak256(abi.encodePacked(schemaRecord.schema, address(schemaRecord.validator))) - ); - } - - /** - * @dev Calculates a UID for a given resolver. - * - * @param resolver The input schema. - * - * @return ResolverUID. - */ - function getUID(ResolverRecord memory resolver) internal pure returns (ResolverUID) { - // TODO: this is a frontrunning vuln - return ResolverUID.wrap(keccak256(abi.encodePacked(resolver.resolver))); - } -} - -// A representation of an empty/uninitialized UID. -bytes32 constant EMPTY_UID = 0; -ResolverUID constant EMPTY_RESOLVER_UID = ResolverUID.wrap(EMPTY_UID); -SchemaUID constant EMPTY_SCHEMA_UID = SchemaUID.wrap(EMPTY_UID); - -// A zero expiration represents an non-expiring attestation. -uint256 constant ZERO_TIMESTAMP = 0; - -address constant ZERO_ADDRESS = address(0); -ModuleType constant ZERO_MODULE_TYPE = ModuleType.wrap(0); - -AttestationDataRef constant EMPTY_ATTESTATION_REF = AttestationDataRef.wrap(address(0)); - -/** - * @dev Returns the current's block timestamp. This method is overridden during tests and used to simulate the - * current block time. - */ -function _time() view returns (uint48) { - return uint48(block.timestamp); -} - -/** - * @dev Returns whether an address is a contract. - * @param addr The address to check. - * - * @return true if `account` is a contract, false otherwise. - */ -function _isContract(address addr) view returns (bool) { - return addr.code.length > 0; -} - -interface IERC7484 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Check with Registry internal attesters */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - function check(address module) external view; - - function checkForAccount(address smartAccount, address module) external view; - - function check(address module, ModuleType moduleType) external view; - - function checkForAccount( - address smartAccount, - address module, - ModuleType moduleType - ) - external - view; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Check with external attester(s) */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - function check(address module, address attester) external view returns (uint256 attestedAt); - - function checkN( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray); -} - -interface IRegistry is IERC7484 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Smart Account - Trust Management */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - event NewTrustedAttesters(); - - error InvalidResolver(IExternalResolver resolver); - error InvalidResolverUID(ResolverUID uid); - error InvalidTrustedAttesterInput(); - error NoTrustedAttestersFound(); - error RevokedAttestation(address attester); - error InvalidModuleType(); - error AttestationNotFound(); - - error InsufficientAttestations(); - - /** - * Allows smartaccounts - the end users of the registry - to appoint - * one or many attesters as trusted. - * - * @param threshold The minimum number of attestations required for a module - * to be considered secure. - * @param attesters The addresses of the attesters to be trusted. - */ - function trustAttesters(uint8 threshold, address[] calldata attesters) external; - - /** - * Get trusted attester for a specific smartAccount - * @param smartAccount The address of the smartAccount - */ - function getTrustedAttesters(address smartAccount) - external - view - returns (address[] memory attesters); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Attestations */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - event Revoked(address indexed moduleAddr, address indexed revoker, SchemaUID schema); - event Attested( - address indexed moduleAddr, - address indexed attester, - SchemaUID schemaUID, - AttestationDataRef sstore2Pointer - ); - - error AlreadyRevoked(); - error ModuleNotFoundInRegistry(address module); - error AccessDenied(); - error InvalidAttestation(); - error InvalidExpirationTime(); - error DifferentResolvers(); - error InvalidSignature(); - error InvalidModuleTypes(); - - /** - * Allows msg.sender to attest to multiple modules' security status. - * The AttestationRequest.Data provided should match the attestation - * schema defined by the Schema corresponding to the SchemaUID - * - * @dev This function will revert if the same module is attested twice by the same attester. - * If you want to re-attest, you have to revoke your attestation first, and then attest again. - * - * @param schemaUID The SchemaUID of the schema the attestation is based on. - * @param request a single AttestationRequest - */ - function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; - - /** - * Allows msg.sender to attest to multiple modules' security status. - * The AttestationRequest.Data provided should match the attestation - * schema defined by the Schema corresponding to the SchemaUID - * - * @dev This function will revert if the same module is attested twice by the same attester. - * If you want to re-attest, you have to revoke your attestation first, and then attest again. - * - * @param schemaUID The SchemaUID of the schema the attestation is based on. - * @param requests An array of AttestationRequest - */ - function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; - - function attest( - SchemaUID schemaUID, - address attester, - AttestationRequest calldata request, - bytes calldata signature - ) - external; - - function attest( - SchemaUID schemaUID, - address attester, - AttestationRequest[] calldata requests, - bytes calldata signature - ) - external; - - function findAttestation( - address module, - address attester - ) - external - view - returns (AttestationRecord memory attestation); - - function findAttestations( - address module, - address[] calldata attesters - ) - external - view - returns (AttestationRecord[] memory attestations); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Revocations */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - function revoke(RevocationRequest calldata request) external; - - function revoke(RevocationRequest[] calldata requests) external; - - function revoke( - address attester, - RevocationRequest calldata request, - bytes calldata signature - ) - external; - - function revoke( - address attester, - RevocationRequest[] calldata requests, - bytes calldata signature - ) - external; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Module Registration */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - // Event triggered when a module is deployed. - event ModuleRegistration( - address indexed implementation, address indexed sender, bytes32 resolver - ); - event ModuleDeployed(address indexed implementation, bytes32 indexed salt, bytes32 resolver); - event ModuleDeployedExternalFactory( - address indexed implementation, address indexed factory, bytes32 resolver - ); - - error AlreadyRegistered(address module); - error InvalidDeployment(); - error ModuleAddressIsNotContract(address moduleAddress); - error FactoryCallFailed(address factory); - - function deployModule( - bytes32 salt, - ResolverUID resolverUID, - bytes calldata code, - bytes calldata deployParams, - bytes calldata metadata - ) - external - payable - returns (address moduleAddr); - - function registerModule( - ResolverUID resolverUID, - address moduleAddress, - bytes calldata metadata - ) - external; - - function getRegisteredModules(address moduleAddress) - external - view - returns (ModuleRecord memory moduleRecord); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Manage Schemas */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - event SchemaRegistered(SchemaUID indexed uid, address registerer); - - error SchemaAlreadyExists(SchemaUID uid); - - error InvalidSchema(); - error InvalidSchemaValidator(IExternalSchemaValidator validator); - - function registerSchema( - string calldata schema, - IExternalSchemaValidator validator // OPTIONAL - ) - external - returns (SchemaUID uid); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Manage Resolvers */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - event NewResolver(ResolverUID indexed uid, address resolver); - - error ResolverAlreadyExists(); - - function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid); - - function setResolver(ResolverUID uid, IExternalResolver resolver) external; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Stub Errors */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - error ExternalError_SchemaValidation(); - error ExternalError_ResolveAtteststation(); - error ExternalError_ResolveRevocation(); - error ExternalError_ModuleRegistration(); -} - -abstract contract SchemaManager is IRegistry { - using UIDLib for SchemaRecord; - // The global mapping between schema records and their IDs. - - mapping(SchemaUID uid => SchemaRecord schemaRecord) internal schemas; - - function registerSchema( - string calldata schema, - IExternalSchemaValidator validator // OPTIONAL - ) - external - onlySchemaValidator(validator) - returns (SchemaUID uid) - { - SchemaRecord memory schemaRecord = - SchemaRecord({ validator: validator, registeredAt: _time(), schema: schema }); - - // Computing a unique ID for the schema using its properties - uid = schemaRecord.getUID(); - - if (schemas[uid].registeredAt != ZERO_TIMESTAMP) revert SchemaAlreadyExists(uid); - - // Storing schema in the _schemas mapping - schemas[uid] = schemaRecord; - - emit SchemaRegistered(uid, msg.sender); - } - - /** - * If a validator is not address(0), we check if it supports the IExternalSchemaValidator interface - */ - modifier onlySchemaValidator(IExternalSchemaValidator validator) { - if ( - address(validator) != address(0) - && !validator.supportsInterface(type(IExternalSchemaValidator).interfaceId) - ) { - revert InvalidSchemaValidator(validator); - } - _; - } -} - -/** - * @title ModuleDeploymentLib - * @dev A library that can be used to deploy the Registry - * @author zeroknots - */ -library ModuleDeploymentLib { - /** - * @dev Gets the code hash of a contract at a given address. - * - * @param contractAddr The address of the contract. - * - * @return hash The hash of the contract code. - */ - function codeHash(address contractAddr) internal view returns (bytes32 hash) { - // solhint-disable-next-line no-inline-assembly - assembly { - if iszero(extcodesize(contractAddr)) { revert(0, 0) } - hash := extcodehash(contractAddr) - } - } - - /** - * @notice Creates a new contract using CREATE2 opcode. - * @dev This method uses the CREATE2 opcode to deploy a new contract with a deterministic address. - * - * @param createCode The creationCode for the contract. - * @param params The parameters for creating the contract. If the contract has a constructor, - * this MUST be provided. Function will fail if params are abi.encodePacked in createCode. - * @param salt The salt for creating the contract. - * - * @return moduleAddress The address of the deployed contract. - * @return initCodeHash packed (creationCode, constructor params) - * @return contractCodeHash hash of deployed bytecode - */ - function deploy( - bytes memory createCode, - bytes memory params, - bytes32 salt, - uint256 value - ) - internal - returns (address moduleAddress, bytes32 initCodeHash, bytes32 contractCodeHash) - { - bytes memory initCode = abi.encodePacked(createCode, params); - // this enforces, that constructor params were supplied via params argument - // if params were abi.encodePacked in createCode, this will revert - initCodeHash = keccak256(initCode); - - // solhint-disable-next-line no-inline-assembly - assembly { - moduleAddress := create2(value, add(initCode, 0x20), mload(initCode), salt) - // If the contract was not created successfully, the transaction is reverted. - if iszero(extcodesize(moduleAddress)) { revert(0, 0) } - contractCodeHash := extcodehash(moduleAddress) - } - } - - /** - * @notice Calculates the deterministic address of a contract that would be deployed using the CREATE2 opcode. - * @dev The calculated address is based on the contract's code, a salt, and the address of the current contract. - * @dev This function uses the formula specified in EIP-1014 (https://eips.ethereum.org/EIPS/eip-1014). - * - * @param _code The contract code that would be deployed. - * @param _salt A salt used for the address calculation. - * This must be the same salt that would be passed to the CREATE2 opcode. - * - * @return The address that the contract would be deployed - * at if the CREATE2 opcode was called with the specified _code and _salt. - */ - function calcAddress(bytes memory _code, bytes32 _salt) internal view returns (address) { - bytes32 hash = - keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(_code))); - // NOTE: cast last 20 bytes of hash to address - return address(uint160(uint256(hash))); - } - - error InvalidDeployment(); -} - -library StubLib { - function requireExternalSchemaValidation( - AttestationRecord memory attestationRecord, - SchemaRecord storage schema - ) - internal - view - { - // only run this function if the selected schemaUID exists - if (schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); - // validate Schema - IExternalSchemaValidator validator = schema.validator; - // if validator is set, call the validator - if ( - address(validator) != ZERO_ADDRESS - && validator.validateSchema(attestationRecord) == false - ) { - revert IRegistry.ExternalError_SchemaValidation(); - } - } - - function requireExternalSchemaValidation( - AttestationRecord[] memory attestationRecords, - SchemaRecord storage schema - ) - internal - view - { - // only run this function if the selected schemaUID exists - if (schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); - // validate Schema - IExternalSchemaValidator validator = schema.validator; - // if validator is set, call the validator - if ( - address(validator) != ZERO_ADDRESS - && validator.validateSchema(attestationRecords) == false - ) { - revert IRegistry.ExternalError_SchemaValidation(); - } - } - - function requireExternalResolverOnAttestation( - AttestationRecord memory attestationRecord, - ResolverRecord storage resolver - ) - internal - { - IExternalResolver resolverContract = resolver.resolver; - - if (address(resolverContract) == ZERO_ADDRESS) return; - if (resolverContract.resolveAttestation(attestationRecord) == false) { - revert IRegistry.ExternalError_ResolveAtteststation(); - } - } - - function requireExternalResolverOnAttestation( - AttestationRecord[] memory attestationRecords, - ResolverRecord storage resolver - ) - internal - { - IExternalResolver resolverContract = resolver.resolver; - - if (address(resolverContract) == ZERO_ADDRESS) return; - - if (resolverContract.resolveAttestation(attestationRecords) == false) { - revert IRegistry.ExternalError_ResolveAtteststation(); - } - } - - function requireExternalResolverOnRevocation( - AttestationRecord memory attestationRecord, - ResolverRecord storage resolver - ) - internal - { - IExternalResolver resolverContract = resolver.resolver; - - if (address(resolverContract) == ZERO_ADDRESS) return; - if (resolverContract.resolveRevocation(attestationRecord) == false) { - revert IRegistry.ExternalError_ResolveAtteststation(); - } - } - - function requireExternalResolverOnRevocation( - AttestationRecord[] memory attestationRecords, - ResolverRecord storage resolver - ) - internal - { - IExternalResolver resolverContract = resolver.resolver; - - if (address(resolverContract) == ZERO_ADDRESS) return; - - if (resolverContract.resolveAttestation(attestationRecords) == false) { - revert IRegistry.ExternalError_ResolveAtteststation(); - } - } - - function requireExternalResolverOnModuleRegistration( - ModuleRecord memory moduleRecord, - ResolverRecord storage resolver - ) - internal - { - IExternalResolver resolverContract = resolver.resolver; - - if (address(resolverContract) != ZERO_ADDRESS) return; - - if (resolverContract.resolveModuleRegistration(moduleRecord) == false) { - revert IRegistry.ExternalError_ModuleRegistration(); - } - } -} - -abstract contract ResolverManager is IRegistry { - using UIDLib for ResolverRecord; - - mapping(ResolverUID uid => ResolverRecord resolver) public resolvers; - - /** - * @dev Modifier to require that the caller is the owner of a resolver - * - * @param uid The UID of the resolver. - */ - modifier onlyResolverOwner(ResolverUID uid) { - if (resolvers[uid].resolverOwner != msg.sender) { - revert AccessDenied(); - } - _; - } - - modifier notZero(ResolverUID uid) { - if (uid == EMPTY_RESOLVER_UID) { - revert InvalidResolverUID(uid); - } - _; - } - - /** - * If a resolver is not address(0), we check if it supports the IExternalResolver interface - */ - modifier onlyResolver(IExternalResolver resolver) { - if ( - address(resolver) == address(0) - || !resolver.supportsInterface(type(IExternalResolver).interfaceId) - ) { - revert InvalidResolver(resolver); - } - _; - } - - function registerResolver(IExternalResolver resolver) - external - onlyResolver(resolver) - returns (ResolverUID uid) - { - // build a ResolverRecord from the input - ResolverRecord memory resolverRecord = - ResolverRecord({ resolver: resolver, resolverOwner: msg.sender }); - - // Computing a unique ID for the schema using its properties - uid = resolverRecord.getUID(); - - // Checking if a schema with this UID already exists -> resolver can never be ZERO_ADDRESS - if (address(resolvers[uid].resolver) != ZERO_ADDRESS) { - revert ResolverAlreadyExists(); - } - - // SSTORE schema in the resolvers mapping - resolvers[uid] = resolverRecord; - - emit NewResolver(uid, address(resolver)); - } - - // TODO: VULN: - // Attacker could register the same resolver, thus be the owner of the resolverUID, - // then set the resolver to a malicious contract - function setResolver( - ResolverUID uid, - IExternalResolver resolver - ) - external - onlyResolver(resolver) - onlyResolverOwner(uid) // authorization control - { - ResolverRecord storage referrer = resolvers[uid]; - referrer.resolver = resolver; - emit NewResolver(uid, address(resolver)); - } -} - -/** - * @title Module - * - * @dev The Module contract serves as a component in a larger system for handling smart contracts or "modules" - * within a blockchain ecosystem. This contract inherits from the IModule interface - * - * @dev The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract - * that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, - * sender address, deploy parameters hash, and additional metadata are stored in - * a struct and mapped to the module's address in - * the `_modules` mapping for easy access and management. - * - * @dev In conclusion, the Module is a central part of a system to manage, - * deploy, and interact with a set of smart contracts - * in a structured and controlled manner. - * - * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) - */ -abstract contract ModuleManager is IRegistry, ResolverManager { - using ModuleDeploymentLib for bytes; - using ModuleDeploymentLib for address; - using StubLib for *; - - mapping(address moduleAddress => ModuleRecord moduleRecord) internal _moduleAddrToRecords; - - function deployModule( - bytes32 salt, - ResolverUID resolverUID, - bytes calldata code, - bytes calldata deployParams, - bytes calldata metadata - ) - external - payable - returns (address moduleAddr) - { - ResolverRecord storage resolver = resolvers[resolverUID]; - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); - - // address predictedModuleAddress = code.calculateAddress(deployParams, salt); - - // TODO: should we use the initCode hash return value? - (moduleAddr,,) = code.deploy(deployParams, salt, msg.value); - // _storeModuleRecord() will check if module is already registered, - // which should prevent reentry to any deploy function - ModuleRecord memory record = _storeModuleRecord({ - moduleAddress: moduleAddr, // TODO: is this reentrancy? - sender: msg.sender, - resolverUID: resolverUID, - metadata: metadata - }); - record.requireExternalResolverOnModuleRegistration(resolver); - } - - function registerModule( - ResolverUID resolverUID, - address moduleAddress, - bytes calldata metadata - ) - external - { - ResolverRecord storage resolver = resolvers[resolverUID]; - - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); - - ModuleRecord memory record = _storeModuleRecord({ - moduleAddress: moduleAddress, - sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function - resolverUID: resolverUID, - metadata: metadata - }); - // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s - // is this a problemt? - record.requireExternalResolverOnModuleRegistration(resolver); - } - - function deployViaFactory( - address factory, - bytes calldata callOnFactory, - bytes calldata metadata, - ResolverUID resolverUID - ) - external - payable - returns (address moduleAddress) - { - ResolverRecord storage resolver = resolvers[resolverUID]; - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolverUID(resolverUID); - // prevent someone from calling a registry function pretending its a factory - if (factory == address(this)) revert FactoryCallFailed(factory); - // call external factory to deploy module - (bool ok, bytes memory returnData) = factory.call{ value: msg.value }(callOnFactory); - if (!ok) revert FactoryCallFailed(factory); - - moduleAddress = abi.decode(returnData, (address)); - if (moduleAddress == ZERO_ADDRESS) revert InvalidDeployment(); - if (_isContract(moduleAddress) == false) revert ModuleAddressIsNotContract(moduleAddress); - - ModuleRecord memory record = _storeModuleRecord({ - moduleAddress: moduleAddress, - // TODO: should we use msg.sender or the factory address? - sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function - resolverUID: resolverUID, - metadata: metadata - }); - // TODO: in case of registerModule() the resolver doesnt know the msg.sender since record.sender == address(0)s - // is this a problemt? - record.requireExternalResolverOnModuleRegistration(resolver); - } - - function _storeModuleRecord( - address moduleAddress, - address sender, - ResolverUID resolverUID, - bytes calldata metadata - ) - internal - returns (ModuleRecord memory moduleRegistration) - { - // ensure that non-zero resolverUID was provided - if (resolverUID == EMPTY_RESOLVER_UID) revert InvalidDeployment(); - // ensure moduleAddress is not already registered - if (_moduleAddrToRecords[moduleAddress].resolverUID != EMPTY_RESOLVER_UID) { - revert AlreadyRegistered(moduleAddress); - } - // revert if moduleAddress is NOT a contract - // this should catch address(0) - if (!_isContract(moduleAddress)) revert InvalidDeployment(); - - // Store module metadata in _modules mapping - moduleRegistration = - ModuleRecord({ resolverUID: resolverUID, sender: sender, metadata: metadata }); - - // Store module record in _modules mapping - _moduleAddrToRecords[moduleAddress] = moduleRegistration; - - // Emit ModuleRegistration event - emit ModuleRegistration(moduleAddress, sender, ResolverUID.unwrap(resolverUID)); - } - - function getRegisteredModules(address moduleAddress) - external - view - returns (ModuleRecord memory moduleRecord) - { - return _moduleAddrToRecords[moduleAddress]; - } -} - -abstract contract TrustManagerExternalAttesterList is IRegistry { - function check(address module, address attester) external view returns (uint256 attestedAt) { - AttestationRecord storage attestation = _getAttestation(module, attester); - - // attestedAt = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestedAt := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (attestedAt == ZERO_TIMESTAMP) { - revert AttestationNotFound(); - } - - if (expirationTime != ZERO_TIMESTAMP) { - if (block.timestamp > expirationTime) { - revert AttestationNotFound(); - } - } - - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(attestation.attester); - } - } - - function checkN( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray) - { - uint256 attestersLength = attesters.length; - if (attestersLength < threshold || threshold == 0) { - threshold = attestersLength; - } - - uint256 timeNow = block.timestamp; - attestedAtArray = new uint256[](attestersLength); - - for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage attestation = _getAttestation(module, attesters[i]); - - uint256 attestationTime; // = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestationTime := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(attestation.attester); - } - - if (expirationTime != ZERO_TIMESTAMP) { - if (timeNow > expirationTime) { - revert AttestationNotFound(); - } - } - - attestedAtArray[i] = attestationTime; - - if (attestationTime == ZERO_TIMESTAMP) continue; - if (threshold != 0) --threshold; - } - if (threshold == 0) return attestedAtArray; - revert InsufficientAttestations(); - } - - function checkNUnsafe( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray) - { - uint256 attestersLength = attesters.length; - if (attestersLength < threshold || threshold == 0) { - threshold = attestersLength; - } - - uint256 timeNow = block.timestamp; - attestedAtArray = new uint256[](attestersLength); - - for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage attestation = _getAttestation(module, attesters[i]); - - uint256 attestationTime; // = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly - assembly { - let mask := 0xffffffffffff - let times := sload(attestation.slot) - attestationTime := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (revocationTime != ZERO_TIMESTAMP) { - attestedAtArray[i] = 0; - continue; - } - - attestedAtArray[i] = attestationTime; - - if (expirationTime != ZERO_TIMESTAMP) { - if (timeNow > expirationTime) { - attestedAtArray[i] = 0; - continue; - } - } - - if (attestationTime == ZERO_TIMESTAMP) continue; - if (threshold != 0) --threshold; - } - if (threshold == 0) return attestedAtArray; - revert InsufficientAttestations(); - } - - function _getAttestation( - address module, - address attester - ) - internal - view - virtual - returns (AttestationRecord storage attestation); -} - -library ModuleTypeLib { - function isType(PackedModuleTypes self, ModuleType moduleType) internal pure returns (bool) { - return (PackedModuleTypes.unwrap(self) & 2 ** ModuleType.unwrap(moduleType)) != 0; - } - - function pack(ModuleType[] memory moduleTypes) internal pure returns (PackedModuleTypes) { - uint32 result; - uint256 length = moduleTypes.length; - for (uint256 i; i < length; i++) { - result = result + uint32(2 ** ModuleType.unwrap(moduleTypes[i])); - } - return PackedModuleTypes.wrap(result); - } - - function packCalldata(ModuleType[] calldata moduleTypes) - internal - pure - returns (PackedModuleTypes) - { - uint32 result; - for (uint256 i; i < moduleTypes.length; i++) { - uint32 _type = ModuleType.unwrap(moduleTypes[i]); - if (_type > 31) revert IRegistry.InvalidModuleType(); - result = result + uint32(2 ** _type); - } - return PackedModuleTypes.wrap(result); - } -} - -/// @notice Optimized sorts and operations for sorted arrays. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Sort.sol) -library LibSort { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INSERTION SORT */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // - Faster on small arrays (32 or lesser elements). - // - Faster on almost sorted arrays. - // - Smaller bytecode. - // - May be suitable for view functions intended for off-chain querying. - - /// @dev Sorts the array in-place with insertion sort. - function insertionSort(uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - let n := mload(a) // Length of `a`. - mstore(a, 0) // For insertion sort's inner loop to terminate. - let h := add(a, shl(5, n)) // High slot. - let s := 0x20 - let w := not(0x1f) - for { let i := add(a, s) } 1 {} { - i := add(i, s) - if gt(i, h) { break } - let k := mload(i) // Key. - let j := add(i, w) // The slot before the current slot. - let v := mload(j) // The value of `j`. - if iszero(gt(v, k)) { continue } - for {} 1 {} { - mstore(add(j, s), v) - j := add(j, w) // `sub(j, 0x20)`. - v := mload(j) - if iszero(gt(v, k)) { break } - } - mstore(add(j, s), k) - } - mstore(a, n) // Restore the length of `a`. - } - } - - /// @dev Sorts the array in-place with insertion sort. - function insertionSort(int256[] memory a) internal pure { - _flipSign(a); - insertionSort(_toUints(a)); - _flipSign(a); - } - - /// @dev Sorts the array in-place with insertion sort. - function insertionSort(address[] memory a) internal pure { - insertionSort(_toUints(a)); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTRO-QUICKSORT */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // - Faster on larger arrays (more than 32 elements). - // - Robust performance. - // - Larger bytecode. - - /// @dev Sorts the array in-place with intro-quicksort. - function sort(uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - let w := not(0x1f) - let s := 0x20 - let n := mload(a) // Length of `a`. - mstore(a, 0) // For insertion sort's inner loop to terminate. - - // Let the stack be the start of the free memory. - let stack := mload(0x40) - - for {} iszero(lt(n, 2)) {} { - // Push `l` and `h` to the stack. - // The `shl` by 5 is equivalent to multiplying by `0x20`. - let l := add(a, s) - let h := add(a, shl(5, n)) - - let j := l - // forgefmt: disable-next-item - for {} iszero(or(eq(j, h), gt(mload(j), mload(add(j, s))))) {} { - j := add(j, s) - } - // If the array is already sorted. - if eq(j, h) { break } - - j := h - // forgefmt: disable-next-item - for {} iszero(gt(mload(j), mload(add(j, w)))) {} { - j := add(j, w) // `sub(j, 0x20)`. - } - // If the array is reversed sorted. - if eq(j, l) { - for {} 1 {} { - let t := mload(l) - mstore(l, mload(h)) - mstore(h, t) - h := add(h, w) // `sub(h, 0x20)`. - l := add(l, s) - if iszero(lt(l, h)) { break } - } - break - } - - // Push `l` and `h` onto the stack. - mstore(stack, l) - mstore(add(stack, s), h) - stack := add(stack, 0x40) - break - } - - for { let stackBottom := mload(0x40) } iszero(eq(stack, stackBottom)) {} { - // Pop `l` and `h` from the stack. - stack := sub(stack, 0x40) - let l := mload(stack) - let h := mload(add(stack, s)) - - // Do insertion sort if `h - l <= 0x20 * 12`. - // Threshold is fine-tuned via trial and error. - if iszero(gt(sub(h, l), 0x180)) { - // Hardcode sort the first 2 elements. - let i := add(l, s) - if iszero(lt(mload(l), mload(i))) { - let t := mload(i) - mstore(i, mload(l)) - mstore(l, t) - } - for {} 1 {} { - i := add(i, s) - if gt(i, h) { break } - let k := mload(i) // Key. - let j := add(i, w) // The slot before the current slot. - let v := mload(j) // The value of `j`. - if iszero(gt(v, k)) { continue } - for {} 1 {} { - mstore(add(j, s), v) - j := add(j, w) - v := mload(j) - if iszero(gt(v, k)) { break } - } - mstore(add(j, s), k) - } - continue - } - // Pivot slot is the average of `l` and `h`. - let p := add(shl(5, shr(6, add(l, h))), and(31, l)) - // Median of 3 with sorting. - { - function swap(a_, b_) -> _b, _a { - _b := a_ - _a := b_ - } - let e0 := mload(l) - let e1 := mload(h) - if iszero(lt(e0, e1)) { e1, e0 := swap(e0, e1) } - let e2 := mload(p) - if iszero(lt(e2, e1)) { e2, e1 := swap(e1, e2) } - if iszero(lt(e0, e2)) { e2, e0 := swap(e0, e2) } - mstore(p, e2) - mstore(h, e1) - mstore(l, e0) - } - // Hoare's partition. - { - // The value of the pivot slot. - let x := mload(p) - p := h - for { let i := l } 1 {} { - for {} 1 {} { - i := add(i, s) - if iszero(gt(x, mload(i))) { break } - } - let j := p - for {} 1 {} { - j := add(j, w) - if iszero(lt(x, mload(j))) { break } - } - p := j - if iszero(lt(i, p)) { break } - // Swap slots `i` and `p`. - let t := mload(i) - mstore(i, mload(p)) - mstore(p, t) - } - } - // If slice on right of pivot is non-empty, push onto stack. - { - mstore(stack, add(p, s)) - // Skip `mstore(add(stack, 0x20), h)`, as it is already on the stack. - stack := add(stack, shl(6, lt(add(p, s), h))) - } - // If slice on left of pivot is non-empty, push onto stack. - { - mstore(stack, l) - mstore(add(stack, s), p) - stack := add(stack, shl(6, gt(p, l))) - } - } - mstore(a, n) // Restore the length of `a`. - } - } - - /// @dev Sorts the array in-place with intro-quicksort. - function sort(int256[] memory a) internal pure { - _flipSign(a); - sort(_toUints(a)); - _flipSign(a); - } - - /// @dev Sorts the array in-place with intro-quicksort. - function sort(address[] memory a) internal pure { - sort(_toUints(a)); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OTHER USEFUL OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // For performance, the `uniquifySorted` methods will not revert if the - // array is not sorted -- it will simply remove consecutive duplicate elements. - - /// @dev Removes duplicate elements from a ascendingly sorted memory array. - function uniquifySorted(uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - // If the length of `a` is greater than 1. - if iszero(lt(mload(a), 2)) { - let x := add(a, 0x20) - let y := add(a, 0x40) - let end := add(a, shl(5, add(mload(a), 1))) - for {} 1 {} { - if iszero(eq(mload(x), mload(y))) { - x := add(x, 0x20) - mstore(x, mload(y)) - } - y := add(y, 0x20) - if eq(y, end) { break } - } - mstore(a, shr(5, sub(x, a))) - } - } - } - - /// @dev Removes duplicate elements from a ascendingly sorted memory array. - function uniquifySorted(int256[] memory a) internal pure { - uniquifySorted(_toUints(a)); - } - - /// @dev Removes duplicate elements from a ascendingly sorted memory array. - function uniquifySorted(address[] memory a) internal pure { - uniquifySorted(_toUints(a)); - } - - /// @dev Returns whether `a` contains `needle`, and the index of `needle`. - /// `index` precedence: equal to > nearest before > nearest after. - function searchSorted(uint256[] memory a, uint256 needle) - internal - pure - returns (bool found, uint256 index) - { - (found, index) = _searchSorted(a, needle, 0); - } - - /// @dev Returns whether `a` contains `needle`, and the index of `needle`. - /// `index` precedence: equal to > nearest before > nearest after. - function searchSorted(int256[] memory a, int256 needle) - internal - pure - returns (bool found, uint256 index) - { - (found, index) = _searchSorted(_toUints(a), uint256(needle), 1 << 255); - } - - /// @dev Returns whether `a` contains `needle`, and the index of `needle`. - /// `index` precedence: equal to > nearest before > nearest after. - function searchSorted(address[] memory a, address needle) - internal - pure - returns (bool found, uint256 index) - { - (found, index) = _searchSorted(_toUints(a), uint256(uint160(needle)), 0); - } - - /// @dev Reverses the array in-place. - function reverse(uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - if iszero(lt(mload(a), 2)) { - let s := 0x20 - let w := not(0x1f) - let h := add(a, shl(5, mload(a))) - for { a := add(a, s) } 1 {} { - let t := mload(a) - mstore(a, mload(h)) - mstore(h, t) - h := add(h, w) - a := add(a, s) - if iszero(lt(a, h)) { break } - } - } - } - } - - /// @dev Reverses the array in-place. - function reverse(int256[] memory a) internal pure { - reverse(_toUints(a)); - } - - /// @dev Reverses the array in-place. - function reverse(address[] memory a) internal pure { - reverse(_toUints(a)); - } - - /// @dev Returns a copy of the array. - function copy(uint256[] memory a) internal pure returns (uint256[] memory result) { - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - let end := add(add(result, 0x20), shl(5, mload(a))) - let o := result - for { let d := sub(a, result) } 1 {} { - mstore(o, mload(add(o, d))) - o := add(0x20, o) - if eq(o, end) { break } - } - mstore(0x40, o) - } - } - - /// @dev Returns a copy of the array. - function copy(int256[] memory a) internal pure returns (int256[] memory result) { - result = _toInts(copy(_toUints(a))); - } - - /// @dev Returns a copy of the array. - function copy(address[] memory a) internal pure returns (address[] memory result) { - result = _toAddresses(copy(_toUints(a))); - } - - /// @dev Returns whether the array is sorted in ascending order. - function isSorted(uint256[] memory a) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - if iszero(lt(mload(a), 2)) { - let end := add(a, shl(5, mload(a))) - for { a := add(a, 0x20) } 1 {} { - let p := mload(a) - a := add(a, 0x20) - result := iszero(gt(p, mload(a))) - if iszero(mul(result, xor(a, end))) { break } - } - } - } - } - - /// @dev Returns whether the array is sorted in ascending order. - function isSorted(int256[] memory a) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - if iszero(lt(mload(a), 2)) { - let end := add(a, shl(5, mload(a))) - for { a := add(a, 0x20) } 1 {} { - let p := mload(a) - a := add(a, 0x20) - result := iszero(sgt(p, mload(a))) - if iszero(mul(result, xor(a, end))) { break } - } - } - } - } - - /// @dev Returns whether the array is sorted in ascending order. - function isSorted(address[] memory a) internal pure returns (bool result) { - result = isSorted(_toUints(a)); - } - - /// @dev Returns whether the array is strictly ascending (sorted and uniquified). - function isSortedAndUniquified(uint256[] memory a) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - if iszero(lt(mload(a), 2)) { - let end := add(a, shl(5, mload(a))) - for { a := add(a, 0x20) } 1 {} { - let p := mload(a) - a := add(a, 0x20) - result := lt(p, mload(a)) - if iszero(mul(result, xor(a, end))) { break } - } - } - } - } - - /// @dev Returns whether the array is strictly ascending (sorted and uniquified). - function isSortedAndUniquified(int256[] memory a) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - if iszero(lt(mload(a), 2)) { - let end := add(a, shl(5, mload(a))) - for { a := add(a, 0x20) } 1 {} { - let p := mload(a) - a := add(a, 0x20) - result := slt(p, mload(a)) - if iszero(mul(result, xor(a, end))) { break } - } - } - } - } - - /// @dev Returns whether the array is strictly ascending (sorted and uniquified). - function isSortedAndUniquified(address[] memory a) internal pure returns (bool result) { - result = isSortedAndUniquified(_toUints(a)); - } - - /// @dev Returns the sorted set difference of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function difference(uint256[] memory a, uint256[] memory b) - internal - pure - returns (uint256[] memory c) - { - c = _difference(a, b, 0); - } - - /// @dev Returns the sorted set difference between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function difference(int256[] memory a, int256[] memory b) - internal - pure - returns (int256[] memory c) - { - c = _toInts(_difference(_toUints(a), _toUints(b), 1 << 255)); - } - - /// @dev Returns the sorted set difference between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function difference(address[] memory a, address[] memory b) - internal - pure - returns (address[] memory c) - { - c = _toAddresses(_difference(_toUints(a), _toUints(b), 0)); - } - - /// @dev Returns the sorted set intersection between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function intersection(uint256[] memory a, uint256[] memory b) - internal - pure - returns (uint256[] memory c) - { - c = _intersection(a, b, 0); - } - - /// @dev Returns the sorted set intersection between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function intersection(int256[] memory a, int256[] memory b) - internal - pure - returns (int256[] memory c) - { - c = _toInts(_intersection(_toUints(a), _toUints(b), 1 << 255)); - } - - /// @dev Returns the sorted set intersection between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function intersection(address[] memory a, address[] memory b) - internal - pure - returns (address[] memory c) - { - c = _toAddresses(_intersection(_toUints(a), _toUints(b), 0)); - } - - /// @dev Returns the sorted set union of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function union(uint256[] memory a, uint256[] memory b) - internal - pure - returns (uint256[] memory c) - { - c = _union(a, b, 0); - } - - /// @dev Returns the sorted set union of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function union(int256[] memory a, int256[] memory b) - internal - pure - returns (int256[] memory c) - { - c = _toInts(_union(_toUints(a), _toUints(b), 1 << 255)); - } - - /// @dev Returns the sorted set union between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function union(address[] memory a, address[] memory b) - internal - pure - returns (address[] memory c) - { - c = _toAddresses(_union(_toUints(a), _toUints(b), 0)); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Reinterpret cast to an uint256 array. - function _toUints(int256[] memory a) private pure returns (uint256[] memory casted) { - /// @solidity memory-safe-assembly - assembly { - casted := a - } - } - - /// @dev Reinterpret cast to an uint256 array. - function _toUints(address[] memory a) private pure returns (uint256[] memory casted) { - /// @solidity memory-safe-assembly - assembly { - // As any address written to memory will have the upper 96 bits - // of the word zeroized (as per Solidity spec), we can directly - // compare these addresses as if they are whole uint256 words. - casted := a - } - } - - /// @dev Reinterpret cast to an int array. - function _toInts(uint256[] memory a) private pure returns (int256[] memory casted) { - /// @solidity memory-safe-assembly - assembly { - casted := a - } - } - - /// @dev Reinterpret cast to an address array. - function _toAddresses(uint256[] memory a) private pure returns (address[] memory casted) { - /// @solidity memory-safe-assembly - assembly { - casted := a - } - } - - /// @dev Converts an array of signed integers to unsigned - /// integers suitable for sorting or vice versa. - function _flipSign(int256[] memory a) private pure { - /// @solidity memory-safe-assembly - assembly { - let w := shl(255, 1) - for { let end := add(a, shl(5, mload(a))) } iszero(eq(a, end)) {} { - a := add(a, 0x20) - mstore(a, add(mload(a), w)) - } - } - } - - /// @dev Returns whether `a` contains `needle`, and the index of `needle`. - /// `index` precedence: equal to > nearest before > nearest after. - function _searchSorted(uint256[] memory a, uint256 needle, uint256 signed) - private - pure - returns (bool found, uint256 index) - { - /// @solidity memory-safe-assembly - assembly { - let w := not(0) - let l := 1 - let h := mload(a) - let t := 0 - for { needle := add(signed, needle) } 1 {} { - index := shr(1, add(l, h)) - t := add(signed, mload(add(a, shl(5, index)))) - if or(gt(l, h), eq(t, needle)) { break } - // Decide whether to search the left or right half. - if iszero(gt(needle, t)) { - h := add(index, w) - continue - } - l := add(index, 1) - } - // `index` will be zero in the case of an empty array, - // or when the value is less than the smallest value in the array. - found := eq(t, needle) - t := iszero(iszero(index)) - index := mul(add(index, w), t) - found := and(found, t) - } - } - - /// @dev Returns the sorted set difference of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function _difference(uint256[] memory a, uint256[] memory b, uint256 signed) - private - pure - returns (uint256[] memory c) - { - /// @solidity memory-safe-assembly - assembly { - let s := 0x20 - let aEnd := add(a, shl(5, mload(a))) - let bEnd := add(b, shl(5, mload(b))) - c := mload(0x40) // Set `c` to the free memory pointer. - a := add(a, s) - b := add(b, s) - let k := c - for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { - let u := mload(a) - let v := mload(b) - if iszero(xor(u, v)) { - a := add(a, s) - b := add(b, s) - continue - } - if iszero(lt(add(u, signed), add(v, signed))) { - b := add(b, s) - continue - } - k := add(k, s) - mstore(k, u) - a := add(a, s) - } - for {} iszero(gt(a, aEnd)) {} { - k := add(k, s) - mstore(k, mload(a)) - a := add(a, s) - } - mstore(c, shr(5, sub(k, c))) // Store the length of `c`. - mstore(0x40, add(k, s)) // Allocate the memory for `c`. - } - } - - /// @dev Returns the sorted set intersection between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function _intersection(uint256[] memory a, uint256[] memory b, uint256 signed) - private - pure - returns (uint256[] memory c) - { - /// @solidity memory-safe-assembly - assembly { - let s := 0x20 - let aEnd := add(a, shl(5, mload(a))) - let bEnd := add(b, shl(5, mload(b))) - c := mload(0x40) // Set `c` to the free memory pointer. - a := add(a, s) - b := add(b, s) - let k := c - for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { - let u := mload(a) - let v := mload(b) - if iszero(xor(u, v)) { - k := add(k, s) - mstore(k, u) - a := add(a, s) - b := add(b, s) - continue - } - if iszero(lt(add(u, signed), add(v, signed))) { - b := add(b, s) - continue - } - a := add(a, s) - } - mstore(c, shr(5, sub(k, c))) // Store the length of `c`. - mstore(0x40, add(k, s)) // Allocate the memory for `c`. - } - } - - /// @dev Returns the sorted set union of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function _union(uint256[] memory a, uint256[] memory b, uint256 signed) - private - pure - returns (uint256[] memory c) - { - /// @solidity memory-safe-assembly - assembly { - let s := 0x20 - let aEnd := add(a, shl(5, mload(a))) - let bEnd := add(b, shl(5, mload(b))) - c := mload(0x40) // Set `c` to the free memory pointer. - a := add(a, s) - b := add(b, s) - let k := c - for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { - let u := mload(a) - let v := mload(b) - if iszero(xor(u, v)) { - k := add(k, s) - mstore(k, u) - a := add(a, s) - b := add(b, s) - continue - } - if iszero(lt(add(u, signed), add(v, signed))) { - k := add(k, s) - mstore(k, v) - b := add(b, s) - continue - } - k := add(k, s) - mstore(k, u) - a := add(a, s) - } - for {} iszero(gt(a, aEnd)) {} { - k := add(k, s) - mstore(k, mload(a)) - a := add(a, s) - } - for {} iszero(gt(b, bEnd)) {} { - k := add(k, s) - mstore(k, mload(b)) - b := add(b, s) - } - mstore(c, shr(5, sub(k, c))) // Store the length of `c`. - mstore(0x40, add(k, s)) // Allocate the memory for `c`. - } - } -} - -/** - * @title TrustManager - * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) - * Implements EIP-7484 to query attestations stored in the registry. - * @dev This contract is abstract and provides utility functions to query attestations. - */ -abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { - using ModuleTypeLib for PackedModuleTypes; - using LibSort for address[]; - - // packed struct to allow for efficient storage. - // if only one attester is trusted, it only requires 1 SLOAD - - struct TrustedAttesters { - uint8 attesterCount; - uint8 threshold; - address attester; - mapping(address attester => address linkedAttester) linkedAttesters; - } - - mapping(address account => TrustedAttesters attesters) internal _accountToAttester; - - // Deliberately using memory here, so we can sort the array - function trustAttesters(uint8 threshold, address[] memory attesters) external { - uint256 attestersLength = attesters.length; - attesters.sort(); - attesters.uniquifySorted(); - if (attestersLength == 0) revert InvalidTrustedAttesterInput(); - if (attesters.length != attestersLength) revert InvalidTrustedAttesterInput(); - // sort attesters - - TrustedAttesters storage _att = _accountToAttester[msg.sender]; - // threshold cannot be greater than the number of attesters - if (threshold > attestersLength) { - threshold = uint8(attestersLength); - } - // - _att.attesterCount = uint8(attestersLength); - _att.threshold = threshold; - _att.attester = attesters[0]; - - attestersLength--; - for (uint256 i; i < attestersLength; i++) { - address _attester = attesters[i]; - // user could have set attester to address(0) - if (_attester == ZERO_ADDRESS) revert InvalidTrustedAttesterInput(); - _att.linkedAttesters[_attester] = attesters[i + 1]; - } - emit NewTrustedAttesters(); - } - - function check(address module) external view { - _check(msg.sender, module, ZERO_MODULE_TYPE); - } - - function checkForAccount(address smartAccount, address module) external view { - _check(smartAccount, module, ZERO_MODULE_TYPE); - } - - function check(address module, ModuleType moduleType) external view { - _check(msg.sender, module, moduleType); - } - - function checkForAccount( - address smartAccount, - address module, - ModuleType moduleType - ) - external - view - { - _check(smartAccount, module, moduleType); - } - - function _check(address smartAccount, address module, ModuleType moduleType) internal view { - TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; - // SLOAD from one slot - uint256 attesterCount = trustedAttesters.attesterCount; - uint256 threshold = trustedAttesters.threshold; - address attester = trustedAttesters.attester; - - // smart account has no trusted attesters set - if (attester == ZERO_ADDRESS && threshold != 0) { - revert NoTrustedAttestersFound(); - } - // smart account only has ONE trusted attester - // use this condition to save gas - else if (threshold == 1) { - AttestationRecord storage record = - _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); - } - // smart account has more than one trusted attester - else { - // loop though list and check if the attestation is valid - AttestationRecord storage record = - _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); - for (uint256 i = 1; i < attesterCount; i++) { - threshold--; - // get next attester from linked List - attester = trustedAttesters.linkedAttesters[attester]; - record = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); - // if threshold reached, exit loop - if (threshold == 0) return; - } - } - } - - /** - * Check that attestationRecord is valid: - * - not revoked - * - not expired - * - correct module type (if not ZERO_MODULE_TYPE) - */ - function _requireValidAttestation( - ModuleType expectedType, - AttestationRecord storage record - ) - internal - view - { - // cache values TODO:: assembly to ensure single SLOAD - uint256 attestedAt = record.time; - uint256 expirationTime = record.expirationTime; - uint256 revocationTime = record.revocationTime; - PackedModuleTypes packedModuleType = record.moduleTypes; - - // check if any attestation was made - if (attestedAt == ZERO_TIMESTAMP) { - revert AttestationNotFound(); - } - - // check if attestation has expired - if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { - revert AttestationNotFound(); - } - - // check if attestation has been revoked - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(record.attester); - } - // if a expectedType is set, check if the attestation is for the correct module type - // if no expectedType is set, module type is not checked - if (expectedType != ZERO_MODULE_TYPE && !packedModuleType.isType(expectedType)) { - revert InvalidModuleType(); - } - } - - function getTrustedAttesters(address smartAccount) - public - view - returns (address[] memory attesters) - { - TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; - uint256 count = trustedAttesters.attesterCount; - attesters = new address[](count); - attesters[0] = trustedAttesters.attester; - - for (uint256 i = 1; i < count; i++) { - // get next attester from linked List - attesters[i] = trustedAttesters.linkedAttesters[attesters[i - 1]]; - } - } -} - -/// @notice Read and write to persistent storage at a fraction of the cost. -/// @author Solady (https://github.com/vectorized/solmady/blob/main/src/utils/SSTORE2.sol) -/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol) -/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol) -library SSTORE2 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev We skip the first byte as it's a STOP opcode, - /// which ensures the contract can't be called. - uint256 internal constant DATA_OFFSET = 1; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Unable to deploy the storage contract. - error DeploymentFailed(); - - /// @dev The storage contract address is invalid. - error InvalidPointer(); - - /// @dev Attempt to read outside of the storage contract's bytecode bounds. - error ReadOutOfBounds(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* WRITE LOGIC */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Writes `data` into the bytecode of a storage contract and returns its address. - function write(bytes memory data) internal returns (address pointer) { - /// @solidity memory-safe-assembly - assembly { - let originalDataLength := mload(data) - - // Add 1 to data size since we are prefixing it with a STOP opcode. - let dataSize := add(originalDataLength, DATA_OFFSET) - - /** - * ------------------------------------------------------------------------------+ - * Opcode | Mnemonic | Stack | Memory | - * ------------------------------------------------------------------------------| - * 61 dataSize | PUSH2 dataSize | dataSize | | - * 80 | DUP1 | dataSize dataSize | | - * 60 0xa | PUSH1 0xa | 0xa dataSize dataSize | | - * 3D | RETURNDATASIZE | 0 0xa dataSize dataSize | | - * 39 | CODECOPY | dataSize | [0..dataSize): code | - * 3D | RETURNDATASIZE | 0 dataSize | [0..dataSize): code | - * F3 | RETURN | | [0..dataSize): code | - * 00 | STOP | | | - * ------------------------------------------------------------------------------+ - * @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called. - * Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16. - */ - mstore( - // Do a out-of-gas revert if `dataSize` is more than 2 bytes. - // The actual EVM limit may be smaller and may change over time. - add(data, gt(dataSize, 0xffff)), - // Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2. - or(0xfd61000080600a3d393df300, shl(0x40, dataSize)) - ) - - // Deploy a new contract with the generated creation code. - pointer := create(0, add(data, 0x15), add(dataSize, 0xa)) - - // If `pointer` is zero, revert. - if iszero(pointer) { - // Store the function selector of `DeploymentFailed()`. - mstore(0x00, 0x30116425) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // Restore original length of the variable size `data`. - mstore(data, originalDataLength) - } - } - - /// @dev Writes `data` into the bytecode of a storage contract with `salt` - /// and returns its deterministic address. - function writeDeterministic(bytes memory data, bytes32 salt) - internal - returns (address pointer) - { - /// @solidity memory-safe-assembly - assembly { - let originalDataLength := mload(data) - let dataSize := add(originalDataLength, DATA_OFFSET) - - mstore( - // Do a out-of-gas revert if `dataSize` is more than 2 bytes. - // The actual EVM limit may be smaller and may change over time. - add(data, gt(dataSize, 0xffff)), - // Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2. - or(0xfd61000080600a3d393df300, shl(0x40, dataSize)) - ) - - // Deploy a new contract with the generated creation code. - pointer := create2(0, add(data, 0x15), add(dataSize, 0xa), salt) - - // If `pointer` is zero, revert. - if iszero(pointer) { - // Store the function selector of `DeploymentFailed()`. - mstore(0x00, 0x30116425) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // Restore original length of the variable size `data`. - mstore(data, originalDataLength) - } - } - - /// @dev Returns the initialization code hash of the storage contract for `data`. - /// Used for mining vanity addresses with create2crunch. - function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) { - /// @solidity memory-safe-assembly - assembly { - let originalDataLength := mload(data) - let dataSize := add(originalDataLength, DATA_OFFSET) - - // Do a out-of-gas revert if `dataSize` is more than 2 bytes. - // The actual EVM limit may be smaller and may change over time. - returndatacopy(returndatasize(), returndatasize(), shr(16, dataSize)) - - mstore(data, or(0x61000080600a3d393df300, shl(0x40, dataSize))) - - hash := keccak256(add(data, 0x15), add(dataSize, 0xa)) - - // Restore original length of the variable size `data`. - mstore(data, originalDataLength) - } - } - - /// @dev Returns the address of the storage contract for `data` - /// deployed with `salt` by `deployer`. - /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. - function predictDeterministicAddress(bytes memory data, bytes32 salt, address deployer) - internal - pure - returns (address predicted) - { - bytes32 hash = initCodeHash(data); - /// @solidity memory-safe-assembly - assembly { - // Compute and store the bytecode hash. - mstore8(0x00, 0xff) // Write the prefix. - mstore(0x35, hash) - mstore(0x01, shl(96, deployer)) - mstore(0x15, salt) - predicted := keccak256(0x00, 0x55) - // Restore the part of the free memory pointer that has been overwritten. - mstore(0x35, 0) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* READ LOGIC */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns all the `data` from the bytecode of the storage contract at `pointer`. - function read(address pointer) internal view returns (bytes memory data) { - /// @solidity memory-safe-assembly - assembly { - let pointerCodesize := extcodesize(pointer) - if iszero(pointerCodesize) { - // Store the function selector of `InvalidPointer()`. - mstore(0x00, 0x11052bb4) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - // Offset all indices by 1 to skip the STOP opcode. - let size := sub(pointerCodesize, DATA_OFFSET) - - // Get the pointer to the free memory and allocate - // enough 32-byte words for the data and the length of the data, - // then copy the code to the allocated memory. - // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. - data := mload(0x40) - mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) - mstore(data, size) - mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. - extcodecopy(pointer, add(data, 0x20), DATA_OFFSET, size) - } - } - - /// @dev Returns the `data` from the bytecode of the storage contract at `pointer`, - /// from the byte at `start`, to the end of the data stored. - function read(address pointer, uint256 start) internal view returns (bytes memory data) { - /// @solidity memory-safe-assembly - assembly { - let pointerCodesize := extcodesize(pointer) - if iszero(pointerCodesize) { - // Store the function selector of `InvalidPointer()`. - mstore(0x00, 0x11052bb4) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // If `!(pointer.code.size > start)`, reverts. - // This also handles the case where `start + DATA_OFFSET` overflows. - if iszero(gt(pointerCodesize, start)) { - // Store the function selector of `ReadOutOfBounds()`. - mstore(0x00, 0x84eb0dd1) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - let size := sub(pointerCodesize, add(start, DATA_OFFSET)) - - // Get the pointer to the free memory and allocate - // enough 32-byte words for the data and the length of the data, - // then copy the code to the allocated memory. - // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. - data := mload(0x40) - mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) - mstore(data, size) - mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. - extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size) - } - } - - /// @dev Returns the `data` from the bytecode of the storage contract at `pointer`, - /// from the byte at `start`, to the byte at `end` (exclusive) of the data stored. - function read(address pointer, uint256 start, uint256 end) - internal - view - returns (bytes memory data) - { - /// @solidity memory-safe-assembly - assembly { - let pointerCodesize := extcodesize(pointer) - if iszero(pointerCodesize) { - // Store the function selector of `InvalidPointer()`. - mstore(0x00, 0x11052bb4) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // If `!(pointer.code.size > end) || (start > end)`, revert. - // This also handles the cases where - // `end + DATA_OFFSET` or `start + DATA_OFFSET` overflows. - if iszero( - and( - gt(pointerCodesize, end), // Within bounds. - iszero(gt(start, end)) // Valid range. - ) - ) { - // Store the function selector of `ReadOutOfBounds()`. - mstore(0x00, 0x84eb0dd1) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - let size := sub(end, start) - - // Get the pointer to the free memory and allocate - // enough 32-byte words for the data and the length of the data, - // then copy the code to the allocated memory. - // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. - data := mload(0x40) - mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) - mstore(data, size) - mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. - extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size) - } - } -} - -library AttestationLib { - // The hash of the data type used to relay calls to the attest function. It's the value of - bytes32 internal constant ATTEST_TYPEHASH = - keccak256("AttestationRequest(address,uint48,bytes,uint32[])"); - - // The hash of the data type used to relay calls to the revoke function. It's the value of - bytes32 internal constant REVOKE_TYPEHASH = keccak256("RevocationRequest(address)"); - - function sload2(AttestationDataRef dataPointer) internal view returns (bytes memory data) { - data = SSTORE2.read(AttestationDataRef.unwrap(dataPointer)); - } - - function sstore2( - AttestationRequest calldata request, - bytes32 salt - ) - internal - returns (AttestationDataRef dataPointer) - { - /** - * @dev We are using CREATE2 to deterministically generate the address of the attestation data. - * Checking if an attestation pointer already exists, would cost more GAS in the average case. - */ - dataPointer = AttestationDataRef.wrap(SSTORE2.writeDeterministic(request.data, salt)); - } - - function sstore2Salt(address attester, address module) internal view returns (bytes32 salt) { - salt = keccak256(abi.encodePacked(attester, module, block.timestamp, block.chainid)); - } - - function hash( - AttestationRequest calldata data, - uint256 nonce - ) - internal - pure - returns (bytes32 _hash) - { - _hash = keccak256(abi.encode(ATTEST_TYPEHASH, keccak256(abi.encode(data)), nonce)); - } - - function hash( - AttestationRequest[] calldata data, - uint256 nonce - ) - internal - pure - returns (bytes32 _hash) - { - _hash = keccak256(abi.encode(ATTEST_TYPEHASH, keccak256(abi.encode(data)), nonce)); - } - - function hash( - RevocationRequest calldata data, - uint256 nonce - ) - internal - pure - returns (bytes32 _hash) - { - // TODO: check if this is correct - _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); - } - - function hash( - RevocationRequest[] calldata data, - uint256 nonce - ) - internal - pure - returns (bytes32 _hash) - { - // TODO: check if this is correct - _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); - } -} - -/** - * AttestationManager handles the registry's internal storage of new attestations and revocation of attestation - * @dev This contract is abstract and provides utility functions to store attestations and revocations. - */ -abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManager { - using StubLib for *; - using AttestationLib for AttestationDataRef; - using AttestationLib for AttestationRequest; - using AttestationLib for AttestationRequest[]; - using AttestationLib for address; - using ModuleTypeLib for ModuleType[]; - - mapping(address module => mapping(address attester => AttestationRecord attestation)) internal - _moduleToAttesterToAttestations; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Attestation */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /** - * Processes an attestation request and stores the attestation in the registry. - * If the attestation was made for a module that was not registered, the function will revert. - * function will get the external Schema Validator for the supplied SchemaUID - * and call it, if an external IExternalSchemaValidator was set - * function will get the external IExternalResolver for the module - that the attestation is for - * and call it, if an external Resolver was set - */ - function _attest( - address attester, - SchemaUID schemaUID, - AttestationRequest calldata request - ) - internal - { - (AttestationRecord memory record, ResolverUID resolverUID) = - _storeAttestation({ schemaUID: schemaUID, attester: attester, request: request }); - - record.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); - record.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); - } - - /** - * Processes an array of attestation requests and stores the attestations in the registry. - * If the attestation was made for a module that was not registered, the function will revert. - * function will get the external Schema Validator for the supplied SchemaUID - * and call it, if an external IExternalSchemaValidator was set - * function will get the external IExternalResolver for the module - that the attestation is for - * and call it, if an external Resolver was set - */ - function _attest( - address attester, - SchemaUID schemaUID, - AttestationRequest[] calldata requests - ) - internal - { - uint256 length = requests.length; - AttestationRecord[] memory records = new AttestationRecord[](length); - // loop will check that the batched attestation is made ONLY for the same resolver - ResolverUID resolverUID; - for (uint256 i; i < length; i++) { - ResolverUID resolverUID_cache; - // save the attestation record into records array. - (records[i], resolverUID_cache) = _storeAttestation({ - schemaUID: schemaUID, - attester: attester, - request: requests[i] - }); - // cache the first resolverUID and compare it to the rest - // If the resolverUID is different, revert - // @dev if you want to use different resolvers, make different attestation calls - if (i == 0) resolverUID = resolverUID_cache; - else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); - } - - // Use StubLib to call schema Validation and resolver if needed - records.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); - records.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); - } - - /** - * Stores an attestation in the registry storage. - * The bytes encoded AttestationRequest.Data is not stored directly into the registry storage, - * but rather stored with SSTORE2. SSTORE2/SLOAD2 is writing and reading contract storage - * paying a fraction of the cost, it uses contract code as storage, writing data takes the - * form of contract creations and reading data uses EXTCODECOPY. - * since attestation data is supposed to be immutable, it is a good candidate for SSTORE2 - * - * @dev This function will revert if the same module is attested twice by the same attester. - * If you want to re-attest, you have to revoke your attestation first, and then attest again. - * - * @param attester The address of the attesting account. - * @param request The AttestationRequest that was supplied via calldata - * @return record The AttestationRecord of what was written into registry storage - * @return resolverUID The resolverUID in charge for the module - */ - function _storeAttestation( - SchemaUID schemaUID, - address attester, - AttestationRequest calldata request - ) - internal - returns (AttestationRecord memory record, ResolverUID resolverUID) - { - // TODO: what if schema behind schemaUID doesnt exist? - // Frontrun on L2s? - uint48 timeNow = _time(); - // Ensure that either no expiration time was set or that it was set in the future. - if (request.expirationTime != ZERO_TIMESTAMP && request.expirationTime <= timeNow) { - revert InvalidExpirationTime(); - } - // caching module address. - address module = request.moduleAddr; - // SLOAD the resolverUID from the moduleRecord - resolverUID = _moduleAddrToRecords[module].resolverUID; - // Ensure that attestation is for module that was registered. - if (resolverUID == EMPTY_RESOLVER_UID) { - revert ModuleNotFoundInRegistry(module); - } - - // use SSTORE2 to store the data in attestationRequest - // @dev this will revert, if in a batched attestation, - // the same data is used twice by the same attester for the same module since the salt will be the same - AttestationDataRef sstore2Pointer = request.sstore2({ salt: attester.sstore2Salt(module) }); - - // write into memory allocated record, since that is the return value - record = AttestationRecord({ - time: timeNow, - expirationTime: request.expirationTime, - revocationTime: uint48(ZERO_TIMESTAMP), - moduleTypes: request.moduleTypes.packCalldata(), - schemaUID: schemaUID, - moduleAddr: module, - attester: attester, - dataPointer: sstore2Pointer - }); - // SSTORE attestation to registry storage - _moduleToAttesterToAttestations[request.moduleAddr][attester] = record; - - emit Attested(module, attester, schemaUID, sstore2Pointer); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* Revocation */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /** - * Revoke a single Revocation Request - * This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, - * and pass the RevocationRecord to the resolver to check if the revocation is valid - */ - function _revoke(address attester, RevocationRequest calldata request) internal { - (AttestationRecord memory record, ResolverUID resolverUID) = - _storeRevocation(attester, request); - // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? - record.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); - } - - /** - * Revoke an array Revocation Request - * This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, - * and pass the RevocationRecord to the resolver to check if the revocation is valid - */ - function _revoke(address attester, RevocationRequest[] calldata requests) internal { - uint256 length = requests.length; - AttestationRecord[] memory records = new AttestationRecord[](length); - ResolverUID resolverUID; - // loop over revocation requests. This function will revert if different resolverUIDs - // are responsible for the modules that are subject of the revocation. This is to reduce complexity - // @dev if you want to revoke attestations from different resolvers, make different revocation calls - for (uint256 i; i < length; i++) { - ResolverUID resolverUID_cache; - (records[i], resolverUID_cache) = _storeRevocation(attester, requests[i]); - if (i == 0) resolverUID = resolverUID_cache; - else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); - } - - // No schema validation required during revocation. the attestation data was already checked against - - // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? - records.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); - } - - /** - * Gets the AttestationRecord for the supplied RevocationRequest and stores the revocation time in the registry storage - * @param revoker The address of the attesting account. - * @param request The AttestationRequest that was supplied via calldata - * @return record The AttestationRecord of what was written into registry storage - * @return resolverUID The resolverUID in charge for the module - */ - function _storeRevocation( - address revoker, - RevocationRequest calldata request - ) - internal - returns (AttestationRecord memory record, ResolverUID resolverUID) - { - AttestationRecord storage attestationStorage = - _moduleToAttesterToAttestations[request.moduleAddr][revoker]; - - // SLOAD entire record. This will later be passed to the resolver - record = attestationStorage; - resolverUID = _moduleAddrToRecords[request.moduleAddr].resolverUID; - - // Ensure that we aren't attempting to revoke a non-existing attestation. - if (record.dataPointer == EMPTY_ATTESTATION_REF) { - revert AttestationNotFound(); - } - - // Allow only original attesters to revoke their attestations. - if (record.attester != revoker) { - revert AccessDenied(); - } - - // Ensure that we aren't trying to revoke the same attestation twice. - if (record.revocationTime != ZERO_TIMESTAMP) { - revert AlreadyRevoked(); - } - - // SSTORE revocation time to registry storage - attestationStorage.revocationTime = _time(); - // set revocation time to NOW - emit Revoked({ moduleAddr: record.moduleAddr, revoker: revoker, schema: record.schemaUID }); - } - - /** - * Returns the attestation records for the given module and attesters. - * This function is expected to be used by TrustManager and TrustManagerExternalAttesterList - */ - function _getAttestation( - address module, - address attester - ) - internal - view - override - returns (AttestationRecord storage) - { - return _moduleToAttesterToAttestations[module][attester]; - } -} - -abstract contract Attestation is IRegistry, AttestationManager { - function attest(SchemaUID schemaUID, AttestationRequest calldata request) external { - _attest(msg.sender, schemaUID, request); - } - - function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external { - _attest(msg.sender, schemaUID, requests); - } - - function revoke(RevocationRequest calldata request) external { - _revoke(msg.sender, request); - } - - function revoke(RevocationRequest[] calldata requests) external { - _revoke(msg.sender, requests); - } - - function findAttestation( - address module, - address attester - ) - external - view - returns (AttestationRecord memory attestation) - { - attestation = _getAttestation(module, attester); - } - - function findAttestations( - address module, - address[] calldata attesters - ) - external - view - returns (AttestationRecord[] memory attestations) - { - uint256 length = attesters.length; - attestations = new AttestationRecord[](length); - for (uint256 i; i < length; i++) { - attestations[i] = _getAttestation(module, attesters[i]); - } - } -} - -/// @notice Contract for EIP-712 typed structured data hashing and signing. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol) -/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol) -/// -/// @dev Note, this implementation: -/// - Uses `address(this)` for the `verifyingContract` field. -/// - Does NOT use the optional EIP-712 salt. -/// - Does NOT use any EIP-712 extensions. -/// This is for simplicity and to save gas. -/// If you need to customize, please fork / modify accordingly. -abstract contract EIP712 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS AND IMMUTABLES */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. - bytes32 internal constant _DOMAIN_TYPEHASH = - 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; - - uint256 private immutable _cachedThis; - uint256 private immutable _cachedChainId; - bytes32 private immutable _cachedNameHash; - bytes32 private immutable _cachedVersionHash; - bytes32 private immutable _cachedDomainSeparator; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTRUCTOR */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Cache the hashes for cheaper runtime gas costs. - /// In the case of upgradeable contracts (i.e. proxies), - /// or if the chain id changes due to a hard fork, - /// the domain separator will be seamlessly calculated on-the-fly. - constructor() { - _cachedThis = uint256(uint160(address(this))); - _cachedChainId = block.chainid; - - string memory name; - string memory version; - if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion(); - bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name)); - bytes32 versionHash = - _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version)); - _cachedNameHash = nameHash; - _cachedVersionHash = versionHash; - - bytes32 separator; - if (!_domainNameAndVersionMayChange()) { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Load the free memory pointer. - mstore(m, _DOMAIN_TYPEHASH) - mstore(add(m, 0x20), nameHash) - mstore(add(m, 0x40), versionHash) - mstore(add(m, 0x60), chainid()) - mstore(add(m, 0x80), address()) - separator := keccak256(m, 0xa0) - } - } - _cachedDomainSeparator = separator; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* FUNCTIONS TO OVERRIDE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Please override this function to return the domain name and version. - /// ``` - /// function _domainNameAndVersion() - /// internal - /// pure - /// virtual - /// returns (string memory name, string memory version) - /// { - /// name = "Solady"; - /// version = "1"; - /// } - /// ``` - /// - /// Note: If the returned result may change after the contract has been deployed, - /// you must override `_domainNameAndVersionMayChange()` to return true. - function _domainNameAndVersion() - internal - view - virtual - returns (string memory name, string memory version); - - /// @dev Returns if `_domainNameAndVersion()` may change - /// after the contract has been deployed (i.e. after the constructor). - /// Default: false. - function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {} - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HASHING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the EIP-712 domain separator. - function _domainSeparator() internal view virtual returns (bytes32 separator) { - if (_domainNameAndVersionMayChange()) { - separator = _buildDomainSeparator(); - } else { - separator = _cachedDomainSeparator; - if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator(); - } - } - - /// @dev Returns the hash of the fully encoded EIP-712 message for this domain, - /// given `structHash`, as defined in - /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct. - /// - /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message: - /// ``` - /// bytes32 digest = _hashTypedData(keccak256(abi.encode( - /// keccak256("Mail(address to,string contents)"), - /// mailTo, - /// keccak256(bytes(mailContents)) - /// ))); - /// address signer = ECDSA.recover(digest, signature); - /// ``` - function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) { - // We will use `digest` to store the domain separator to save a bit of gas. - if (_domainNameAndVersionMayChange()) { - digest = _buildDomainSeparator(); - } else { - digest = _cachedDomainSeparator; - if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator(); - } - /// @solidity memory-safe-assembly - assembly { - // Compute the digest. - mstore(0x00, 0x1901000000000000) // Store "\x19\x01". - mstore(0x1a, digest) // Store the domain separator. - mstore(0x3a, structHash) // Store the struct hash. - digest := keccak256(0x18, 0x42) - // Restore the part of the free memory slot that was overwritten. - mstore(0x3a, 0) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EIP-5267 OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev See: https://eips.ethereum.org/EIPS/eip-5267 - function eip712Domain() - public - view - virtual - returns ( - bytes1 fields, - string memory name, - string memory version, - uint256 chainId, - address verifyingContract, - bytes32 salt, - uint256[] memory extensions - ) - { - fields = hex"0f"; // `0b01111`. - (name, version) = _domainNameAndVersion(); - chainId = block.chainid; - verifyingContract = address(this); - salt = salt; // `bytes32(0)`. - extensions = extensions; // `new uint256[](0)`. - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the EIP-712 domain separator. - function _buildDomainSeparator() private view returns (bytes32 separator) { - // We will use `separator` to store the name hash to save a bit of gas. - bytes32 versionHash; - if (_domainNameAndVersionMayChange()) { - (string memory name, string memory version) = _domainNameAndVersion(); - separator = keccak256(bytes(name)); - versionHash = keccak256(bytes(version)); - } else { - separator = _cachedNameHash; - versionHash = _cachedVersionHash; - } - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Load the free memory pointer. - mstore(m, _DOMAIN_TYPEHASH) - mstore(add(m, 0x20), separator) // Name hash. - mstore(add(m, 0x40), versionHash) - mstore(add(m, 0x60), chainid()) - mstore(add(m, 0x80), address()) - separator := keccak256(m, 0xa0) - } - } - - /// @dev Returns if the cached domain separator has been invalidated. - function _cachedDomainSeparatorInvalidated() private view returns (bool result) { - uint256 cachedChainId = _cachedChainId; - uint256 cachedThis = _cachedThis; - /// @solidity memory-safe-assembly - assembly { - result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis))) - } - } -} - -/// @notice Signature verification helper that supports both ECDSA signatures from EOAs -/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol) -/// -/// @dev Note: -/// - The signature checking functions use the ecrecover precompile (0x1). -/// - The `bytes memory signature` variants use the identity precompile (0x4) -/// to copy memory internally. -/// - Unlike ECDSA signatures, contract signatures are revocable. -/// - As of Solady version 0.0.134, all `bytes signature` variants accept both -/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. -/// See: https://eips.ethereum.org/EIPS/eip-2098 -/// This is for calldata efficiency on smart accounts prevalent on L2s. -/// -/// WARNING! Do NOT use signatures as unique identifiers: -/// - Use a nonce in the digest to prevent replay attacks on the same contract. -/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. -/// EIP-712 also enables readable signing of typed data for better user safety. -/// This implementation does NOT check if a signature is non-malleable. -library SignatureCheckerLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* SIGNATURE CHECKING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns whether `signature` is valid for `signer` and `hash`. - /// If `signer` is a smart contract, the signature is validated with ERC1271. - /// Otherwise, the signature is validated with `ECDSA.recover`. - function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits of `signer` in case they are dirty. - for { signer := shr(96, shl(96, signer)) } signer {} { - let m := mload(0x40) - mstore(0x00, hash) - mstore(0x40, mload(add(signature, 0x20))) // `r`. - if eq(mload(signature), 64) { - let vs := mload(add(signature, 0x40)) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - if eq(mload(signature), 65) { - mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. - mstore(0x60, mload(add(signature, 0x40))) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - // Copy the `signature` over. - let n := add(0x20, mload(signature)) - pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - add(returndatasize(), 0x44), // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - break - } - } - } - - /// @dev Returns whether `signature` is valid for `signer` and `hash`. - /// If `signer` is a smart contract, the signature is validated with ERC1271. - /// Otherwise, the signature is validated with `ECDSA.recover`. - function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits of `signer` in case they are dirty. - for { signer := shr(96, shl(96, signer)) } signer {} { - let m := mload(0x40) - mstore(0x00, hash) - if eq(signature.length, 64) { - let vs := calldataload(add(signature.offset, 0x20)) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x40, calldataload(signature.offset)) // `r`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - if eq(signature.length, 65) { - mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. - calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), signature.length) - // Copy the `signature` over. - calldatacopy(add(m, 0x64), signature.offset, signature.length) - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - add(signature.length, 0x64), // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - break - } - } - } - - /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`. - /// If `signer` is a smart contract, the signature is validated with ERC1271. - /// Otherwise, the signature is validated with `ECDSA.recover`. - function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits of `signer` in case they are dirty. - for { signer := shr(96, shl(96, signer)) } signer {} { - let m := mload(0x40) - mstore(0x00, hash) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x40, r) // `r`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), 65) // Length of the signature. - mstore(add(m, 0x64), r) // `r`. - mstore(add(m, 0x84), mload(0x60)) // `s`. - mstore8(add(m, 0xa4), mload(0x20)) // `v`. - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - 0xa5, // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - } - - /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`. - /// If `signer` is a smart contract, the signature is validated with ERC1271. - /// Otherwise, the signature is validated with `ECDSA.recover`. - function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits of `signer` in case they are dirty. - for { signer := shr(96, shl(96, signer)) } signer {} { - let m := mload(0x40) - mstore(0x00, hash) - mstore(0x20, and(v, 0xff)) // `v`. - mstore(0x40, r) // `r`. - mstore(0x60, s) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), 65) // Length of the signature. - mstore(add(m, 0x64), r) // `r`. - mstore(add(m, 0x84), s) // `s`. - mstore8(add(m, 0xa4), v) // `v`. - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - 0xa5, // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC1271 OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract. - function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - // Copy the `signature` over. - let n := add(0x20, mload(signature)) - pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - add(returndatasize(), 0x44), // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - } - - /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract. - function isValidERC1271SignatureNowCalldata( - address signer, - bytes32 hash, - bytes calldata signature - ) internal view returns (bool isValid) { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), signature.length) - // Copy the `signature` over. - calldatacopy(add(m, 0x64), signature.offset, signature.length) - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - add(signature.length, 0x64), // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - } - - /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash` - /// for an ERC1271 `signer` contract. - function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), 65) // Length of the signature. - mstore(add(m, 0x64), r) // `r`. - mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`. - mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`. - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - 0xa5, // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - } - - /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash` - /// for an ERC1271 `signer` contract. - function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), 65) // Length of the signature. - mstore(add(m, 0x64), r) // `r`. - mstore(add(m, 0x84), s) // `s`. - mstore8(add(m, 0xa4), v) // `v`. - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - 0xa5, // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HASHING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns an Ethereum Signed Message, created from a `hash`. - /// This produces a hash corresponding to the one signed with the - /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) - /// JSON-RPC method as part of EIP-191. - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, hash) // Store into scratch space for keccak256. - mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. - result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. - } - } - - /// @dev Returns an Ethereum Signed Message, created from `s`. - /// This produces a hash corresponding to the one signed with the - /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) - /// JSON-RPC method as part of EIP-191. - /// Note: Supports lengths of `s` up to 999999 bytes. - function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - let sLength := mload(s) - let o := 0x20 - mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. - mstore(0x00, 0x00) - // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. - for { let temp := sLength } 1 {} { - o := sub(o, 1) - mstore8(o, add(48, mod(temp, 10))) - temp := div(temp, 10) - if iszero(temp) { break } - } - let n := sub(0x3a, o) // Header length: `26 + 32 - o`. - // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. - returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) - mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. - result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) - mstore(s, sLength) // Restore the length. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EMPTY CALLDATA HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns an empty calldata bytes. - function emptySignature() internal pure returns (bytes calldata signature) { - /// @solidity memory-safe-assembly - assembly { - signature.length := 0 - } - } -} - -contract SignedAttestation is IRegistry, Attestation, EIP712 { - using AttestationLib for AttestationRequest; - using AttestationLib for RevocationRequest; - using AttestationLib for AttestationRequest[]; - using AttestationLib for RevocationRequest[]; - - mapping(address attester => uint256 nonce) public attesterNonce; - - /** - * Attestation can be made on any SchemaUID. - * @dev the registry, will forward your AttestationRequest.Data to the SchemaManager to - * check if the schema exists. - * @param schemaUID The SchemaUID of the schema to be attested. - */ - function attest( - SchemaUID schemaUID, - address attester, - AttestationRequest calldata request, - bytes calldata signature - ) - external - { - // verify signature - uint256 nonce = ++attesterNonce[attester]; - bytes32 digest = _hashTypedData(request.hash(nonce)); - bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); - if (!valid) revert InvalidSignature(); - - _attest(attester, schemaUID, request); - } - - function attest( - SchemaUID schemaUID, - address attester, - AttestationRequest[] calldata requests, - bytes calldata signature - ) - external - { - uint256 nonce = ++attesterNonce[attester]; - bytes32 digest = _hashTypedData(requests.hash(nonce)); - bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); - if (!valid) revert InvalidSignature(); - - _attest(attester, schemaUID, requests); - } - - function revoke( - address attester, - RevocationRequest calldata request, - bytes calldata signature - ) - external - { - uint256 nonce = ++attesterNonce[attester]; - bytes32 digest = _hashTypedData(request.hash(nonce)); - bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); - if (!valid) revert InvalidSignature(); - - _revoke(attester, request); - } - - function revoke( - address attester, - RevocationRequest[] calldata requests, - bytes calldata signature - ) - external - { - uint256 nonce = ++attesterNonce[attester]; - bytes32 digest = _hashTypedData(requests.hash(nonce)); - bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); - if (!valid) revert InvalidSignature(); - - _revoke(attester, requests); - } - - function _domainNameAndVersion() - internal - view - virtual - override - returns (string memory name, string memory version) - { - name = "RhinestoneRegistry"; - version = "v1.0"; - } - - function getDigest( - AttestationRequest calldata request, - address attester - ) - external - view - returns (bytes32) - { - return _hashTypedData(request.hash(attesterNonce[attester] + 1)); - } - - function getDigest( - AttestationRequest[] calldata requests, - address attester - ) - external - view - returns (bytes32) - { - return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); - } - - function getDigest( - RevocationRequest calldata request, - address attester - ) - external - view - returns (bytes32) - { - return _hashTypedData(request.hash(attesterNonce[attester] + 1)); - } - - function getDigest( - RevocationRequest[] calldata requests, - address attester - ) - external - view - returns (bytes32) - { - return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); - } -} - -/** - * @author zeroknots - */ - -contract Registry is IRegistry, SignedAttestation { } - diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 845f54c1..4b92c3d3 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -220,7 +220,6 @@ interface IRegistry is IERC7484 { ) external; - function calcModuleAddress( bytes32 salt, bytes calldata initCode From 0a27e91f4f6305e6f19629333285dd31d630c5c3 Mon Sep 17 00:00:00 2001 From: zeroknots <102132718+zeroknots@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:45:05 +0700 Subject: [PATCH 39/84] Update src/core/ModuleManager.sol Co-authored-by: Konrad --- src/core/ModuleManager.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 5c477d7d..1beb6e18 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -12,8 +12,7 @@ import { IRegistry } from "../IRegistry.sol"; /** * @title Module * - * @dev The Module contract serves as a component in a larger system for handling smart contracts or "modules" - * within a blockchain ecosystem. This contract inherits from the IModule interface + * @dev The Module contract is responsible for handling the registration, storage and retrieval of modules on the Registry. This contract inherits from the IModule interface * * @dev The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract * that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, From 0b60962f07b5c141bffa1e455a040c0074b95a6f Mon Sep 17 00:00:00 2001 From: zeroknots <102132718+zeroknots@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:47:09 +0700 Subject: [PATCH 40/84] Feature/v0.3 fixes (#54) * adding invariance testing and docs --- src/IRegistry.sol | 32 ++++++++++++++++++++++++++++++ src/core/AttestationManager.sol | 7 ++++--- src/core/ResolverManager.sol | 3 --- src/core/TrustManager.sol | 31 ++++++++++++++++++++++++----- src/external/IExternalResolver.sol | 2 +- src/lib/ModuleTypeLib.sol | 15 +------------- test/Attestation.t.sol | 8 +++++--- test/TrustDelegation.t.sol | 12 +++++++++++ 8 files changed, 81 insertions(+), 29 deletions(-) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 4b92c3d3..4d1974b4 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -132,6 +132,19 @@ interface IRegistry is IERC7484 { */ function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; + /** + * Allows attester to attest by signing an AttestationRequest (ECDSA or ERC1271) + * The AttestationRequest.Data provided should match the attestation + * schema defined by the Schema corresponding to the SchemaUID + * + * @dev This function will revert if the same module is attested twice by the same attester. + * If you want to re-attest, you have to revoke your attestation first, and then attest again. + * + * @param schemaUID The SchemaUID of the schema the attestation is based on. + * @param attester The address of the attester + * @param request An AttestationRequest + * @param signature The signature of the attester. ECDSA or ERC1271 + */ function attest( SchemaUID schemaUID, address attester, @@ -140,6 +153,19 @@ interface IRegistry is IERC7484 { ) external; + /** + * Allows attester to attest by signing an AttestationRequest (ECDSA or ERC1271) + * The AttestationRequest.Data provided should match the attestation + * schema defined by the Schema corresponding to the SchemaUID + * + * @dev This function will revert if the same module is attested twice by the same attester. + * If you want to re-attest, you have to revoke your attestation first, and then attest again. + * + * @param schemaUID The SchemaUID of the schema the attestation is based on. + * @param attester The address of the attester + * @param requests An array of AttestationRequest + * @param signature The signature of the attester. ECDSA or ERC1271 + */ function attest( SchemaUID schemaUID, address attester, @@ -148,6 +174,9 @@ interface IRegistry is IERC7484 { ) external; + /** + * Getter function to get AttestationRequest made by one attester + */ function findAttestation( address module, address attester @@ -156,6 +185,9 @@ interface IRegistry is IERC7484 { view returns (AttestationRecord memory attestation); + /** + * Getter function to get AttestationRequest made by multiple attesters + */ function findAttestations( address module, address[] calldata attesters diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 8dda24c9..2f3db58e 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -124,8 +124,9 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, internal returns (AttestationRecord memory record, ResolverUID resolverUID) { - // TODO: what if schema behind schemaUID doesnt exist? - // Frontrun on L2s? + // if schema behind schemaUID is not registered, revert. + if (schemas[schemaUID].registeredAt == ZERO_TIMESTAMP) revert InvalidSchema(); + uint48 timeNow = _time(); // Ensure that either no expiration time was set or that it was set in the future. if (request.expirationTime != ZERO_TIMESTAMP && request.expirationTime <= timeNow) { @@ -150,7 +151,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, time: timeNow, expirationTime: request.expirationTime, revocationTime: uint48(ZERO_TIMESTAMP), - moduleTypes: request.moduleTypes.packCalldata(), + moduleTypes: request.moduleTypes.pack(), schemaUID: schemaUID, moduleAddr: module, attester: attester, diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index c11d5556..36fd9be3 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -67,9 +67,6 @@ abstract contract ResolverManager is IRegistry { emit NewResolver(uid, address(resolver)); } - // TODO: VULN: - // Attacker could register the same resolver, thus be the owner of the resolverUID, - // then set the resolver to a malicious contract function setResolver( ResolverUID uid, IExternalResolver resolver diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 043e5d1a..4e48d89a 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -131,11 +131,32 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { internal view { - // cache values TODO:: assembly to ensure single SLOAD - uint256 attestedAt = record.time; - uint256 expirationTime = record.expirationTime; - uint256 revocationTime = record.revocationTime; - PackedModuleTypes packedModuleType = record.moduleTypes; + uint256 attestedAt; + uint256 expirationTime; + uint256 revocationTime; + PackedModuleTypes packedModuleType; + /* + * Ensure only one SLOAD + * Assembly equiv to: + * + * uint256 attestedAt = record.time; + * uint256 expirationTime = record.expirationTime; + * uint256 revocationTime = record.revocationTime; + * PackedModuleTypes packedModuleType = record.moduleTypes; + */ + + // solhint-disable-next-line no-inline-assembly + assembly { + let mask := 0xffffffffffff + let slot := sload(record.slot) + attestedAt := and(mask, slot) + slot := shr(48, slot) + expirationTime := and(mask, slot) + slot := shr(48, slot) + revocationTime := and(mask, slot) + slot := shr(48, slot) + packedModuleType := and(mask, slot) + } // check if any attestation was made if (attestedAt == ZERO_TIMESTAMP) { diff --git a/src/external/IExternalResolver.sol b/src/external/IExternalResolver.sol index 88876a36..0f5beb33 100644 --- a/src/external/IExternalResolver.sol +++ b/src/external/IExternalResolver.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationRecord, ModuleRecord } from "../DataTypes.sol"; import { IERC165 } from "forge-std/interfaces/IERC165.sol"; diff --git a/src/lib/ModuleTypeLib.sol b/src/lib/ModuleTypeLib.sol index 43475fe4..c0660cc6 100644 --- a/src/lib/ModuleTypeLib.sol +++ b/src/lib/ModuleTypeLib.sol @@ -9,20 +9,7 @@ library ModuleTypeLib { return (PackedModuleTypes.unwrap(self) & 2 ** ModuleType.unwrap(moduleType)) != 0; } - function pack(ModuleType[] memory moduleTypes) internal pure returns (PackedModuleTypes) { - uint32 result; - uint256 length = moduleTypes.length; - for (uint256 i; i < length; i++) { - result = result + uint32(2 ** ModuleType.unwrap(moduleTypes[i])); - } - return PackedModuleTypes.wrap(result); - } - - function packCalldata(ModuleType[] calldata moduleTypes) - internal - pure - returns (PackedModuleTypes) - { + function pack(ModuleType[] calldata moduleTypes) internal pure returns (PackedModuleTypes) { uint32 result; for (uint256 i; i < moduleTypes.length; i++) { uint32 _type = ModuleType.unwrap(moduleTypes[i]); diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index ef5e1fa9..19776176 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; +pragma solidity ^0.8.24; import "./Base.t.sol"; import "src/DataTypes.sol"; @@ -132,7 +132,6 @@ contract AttestationTest is BaseTest { AttestationRequest memory request = mockAttestation(address(module1), uint48(block.timestamp + 1), "", types); // It should store. - // TODO: it seems that the resolver is not being called registry.attest(defaultSchemaUID, request); assertTrue(resolverTrue.onAttestCalled()); @@ -161,7 +160,9 @@ contract AttestationTest is BaseTest { function test_WhenUsingValidECDSA() public whenAttestingWithSignature { uint256 nonceBefore = registry.attesterNonce(attester1.addr); // It should recover. - uint32[] memory types = new uint32[](1); + uint32[] memory types = new uint32[](2); + types[0] = 1; + types[1] = 2; AttestationRequest memory request = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); @@ -177,6 +178,7 @@ contract AttestationTest is BaseTest { assertEq(record.moduleAddr, request.moduleAddr); assertEq(record.attester, attester1.addr); assertEq(nonceAfter, nonceBefore + 1); + assertEq(PackedModuleTypes.unwrap(record.moduleTypes), 2 ** 1 + 2 ** 2); } function test_WhenRevokingWithValidECDSA() public { diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol index ab78045d..3a85959e 100644 --- a/test/TrustDelegation.t.sol +++ b/test/TrustDelegation.t.sol @@ -88,6 +88,18 @@ contract TrustTest is AttestationTest { } function test_WhenAttestersSetAndAllOk() external whenQueryingRegisty { + test_WhenUsingValidECDSA(); + + vm.startPrank(smartAccount1.addr); // It should not revert. + address[] memory attesters = new address[](2); + attesters[0] = address(attester1.addr); + attesters[1] = address(attester2.addr); + registry.trustAttesters(1, attesters); + + registry.check(address(module1), ModuleType.wrap(1)); + registry.check(address(module1), ModuleType.wrap(2)); + vm.expectRevert(); + registry.check(address(module1), ModuleType.wrap(3)); } } From 0382e33983b899dbad9466d62307c9c415ac36fe Mon Sep 17 00:00:00 2001 From: zeroknots <102132718+zeroknots@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:48:41 +0700 Subject: [PATCH 41/84] Feature/v0.3 fixes (#55) * moduleTypes now checked to not be duplicates on attestation --- .github/workflows/foundryTest.yml | 8 +- src/IRegistry.sol | 47 +++++++- src/core/AttestationManager.sol | 9 +- src/core/ModuleManager.sol | 2 +- src/core/SchemaManager.sol | 4 + src/external/examples/DebugResolver.sol.bak | 52 --------- src/external/examples/SimpleValidator.sol.bak | 32 ------ .../examples/TokenizedResolver.sol.bak | 60 ----------- src/external/examples/ValueResolver.sol.bak | 54 ---------- src/lib/ModuleTypeLib.sol | 23 ++-- test/Attestation.t.sol | 36 +++++++ test/ModuleRegistration.t.sol | 2 +- test/invariance/Handler.t.sol | 100 ++++++++++++++++++ test/invariance/invariant_ImmutableData.t.sol | 41 +++++++ 14 files changed, 252 insertions(+), 218 deletions(-) delete mode 100644 src/external/examples/DebugResolver.sol.bak delete mode 100644 src/external/examples/SimpleValidator.sol.bak delete mode 100644 src/external/examples/TokenizedResolver.sol.bak delete mode 100644 src/external/examples/ValueResolver.sol.bak create mode 100644 test/invariance/Handler.t.sol create mode 100644 test/invariance/invariant_ImmutableData.t.sol diff --git a/.github/workflows/foundryTest.yml b/.github/workflows/foundryTest.yml index 39745bdd..78dc9449 100644 --- a/.github/workflows/foundryTest.yml +++ b/.github/workflows/foundryTest.yml @@ -1,11 +1,5 @@ -on: - push: - branches: - - main - - 'releases/**' - name: Foundry Tests - +on: [push] jobs: check: name: Foundry project diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 4d1974b4..afab6aa5 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -9,7 +9,8 @@ import { ModuleRecord, ResolverUID, RevocationRequest, - SchemaUID + SchemaUID, + SchemaRecord } from "./DataTypes.sol"; import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.sol"; @@ -200,10 +201,33 @@ interface IRegistry is IERC7484 { /* Revocations */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + /** + * Allows msg.sender to revoke an attstation made by the same msg.sender + * + * @dev this function will revert if the attestation is not found + * @dev this function will revert if the attestation is already revoked + * + * @param request the RevocationRequest + */ function revoke(RevocationRequest calldata request) external; + /** + * Allows msg.sender to revoke multiple attstations made by the same msg.sender + * + * @dev this function will revert if the attestation is not found + * @dev this function will revert if the attestation is already revoked + * + * @param requests the RevocationRequests + */ function revoke(RevocationRequest[] calldata requests) external; + /** + * Allows attester to revoke an attestation by signing an RevocationRequest (ECDSA or ERC1271) + * + * @param attester the signer / revoker + * @param request the RevocationRequest + * @param signature ECDSA or ERC1271 signature + */ function revoke( address attester, RevocationRequest calldata request, @@ -211,6 +235,14 @@ interface IRegistry is IERC7484 { ) external; + /** + * Allows attester to revoke an attestation by signing an RevocationRequest (ECDSA or ERC1271) + * @dev if you want to revoke multiple attestations, but from different attesters, call this function multiple times + * + * @param attester the signer / revoker + * @param requests array of RevocationRequests + * @param signature ECDSA or ERC1271 signature + */ function revoke( address attester, RevocationRequest[] calldata requests, @@ -235,6 +267,15 @@ interface IRegistry is IERC7484 { error ModuleAddressIsNotContract(address moduleAddress); error FactoryCallFailed(address factory); + /** + * This registry implements a CREATE2 factory, that allows module developers to register and deploy module bytecode + * @param salt The salt to be used in the CREATE2 factory + * @param resolverUID The resolverUID to be used in the CREATE2 factory + * @param initCode The initCode to be used in the CREATE2 factory + * @param metadata The metadata to be stored on the registry. + * This field is optional, and might be used by the module developer to store additional + * information about the module or facilitate business logic with the Resolver stub + */ function deployModule( bytes32 salt, ResolverUID resolverUID, @@ -260,7 +301,7 @@ interface IRegistry is IERC7484 { view returns (address); - function getRegisteredModules(address moduleAddress) + function getRegisteredModule(address moduleAddress) external view returns (ModuleRecord memory moduleRecord); @@ -283,6 +324,8 @@ interface IRegistry is IERC7484 { external returns (SchemaUID uid); + function findSchema(SchemaUID uid) external returns (SchemaRecord memory record); + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Manage Resolvers */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 2f3db58e..3b29f968 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -160,7 +160,12 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, // SSTORE attestation to registry storage _moduleToAttesterToAttestations[request.moduleAddr][attester] = record; - emit Attested(module, attester, schemaUID, sstore2Pointer); + emit Attested({ + moduleAddr: module, + attester: attester, + schemaUID: schemaUID, + sstore2Pointer: sstore2Pointer + }); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -243,7 +248,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, // SSTORE revocation time to registry storage attestationStorage.revocationTime = _time(); // set revocation time to NOW - emit Revoked({ moduleAddr: record.moduleAddr, revoker: revoker, schema: record.schemaUID }); + emit Revoked({ moduleAddr: request.moduleAddr, revoker: revoker, schema: record.schemaUID }); } /** diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 1beb6e18..26edb93e 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -164,7 +164,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { emit ModuleRegistration(moduleAddress, sender, ResolverUID.unwrap(resolverUID)); } - function getRegisteredModules(address moduleAddress) + function getRegisteredModule(address moduleAddress) external view returns (ModuleRecord memory moduleRecord) diff --git a/src/core/SchemaManager.sol b/src/core/SchemaManager.sol index 1faabc60..a38d922b 100644 --- a/src/core/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -48,4 +48,8 @@ abstract contract SchemaManager is IRegistry { } _; } + + function findSchema(SchemaUID uid) external view override returns (SchemaRecord memory) { + return schemas[uid]; + } } diff --git a/src/external/examples/DebugResolver.sol.bak b/src/external/examples/DebugResolver.sol.bak deleted file mode 100644 index 02deb69e..00000000 --- a/src/external/examples/DebugResolver.sol.bak +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { ResolverBase } from "../ResolverBase.sol"; -import { AttestationRecord, ModuleRecord } from "../../DataTypes.sol"; -import { console2 } from "forge-std/console2.sol"; - -/** - * @title DebugResolver - * @author zeroknots - * @notice A debug resolver for testing purposes. - */ -contract DebugResolver is ResolverBase { - constructor(address rs) ResolverBase(rs) { } - - function onAttest( - AttestationRecord calldata attestation, - uint256 /*value*/ - ) - internal - view - override - returns (bool) - { - console2.log(attestation.attester); - - return true; - } - - function onRevoke( - AttestationRecord calldata attestation, - uint256 value - ) - internal - pure - override - returns (bool) - { - return true; - } - - function onModuleRegistration( - ModuleRecord calldata module, - uint256 value - ) - internal - override - returns (bool) - { - return true; - } -} diff --git a/src/external/examples/SimpleValidator.sol.bak b/src/external/examples/SimpleValidator.sol.bak deleted file mode 100644 index 78caf7df..00000000 --- a/src/external/examples/SimpleValidator.sol.bak +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { IExternalSchemaValidator } from "../IExternalSchemaValidator.sol"; -import { AttestationRequest } from "../../DataTypes.sol"; - -/** - * @title SimpleValidator - * @author zeroknots - * @notice A simple validator that always returns true. - */ -contract SimpleValidator is IExternalSchemaValidator { - function validateSchema(AttestationRequest calldata attestation) - external - pure - override - returns (bool) - { - return true; - } - - function validateSchema(AttestationRequest[] calldata attestation) - external - pure - override - returns (bool) - { - return true; - } - - function supportsInterface(bytes4 interfaceID) external view override returns (bool) { } -} diff --git a/src/external/examples/TokenizedResolver.sol.bak b/src/external/examples/TokenizedResolver.sol.bak deleted file mode 100644 index 6478f748..00000000 --- a/src/external/examples/TokenizedResolver.sol.bak +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { ResolverBase, AttestationRecord, ModuleRecord } from "../ResolverBase.sol"; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -/** - * @title TokenizedResolver - * @author zeroknots - * @notice A resolver for tokenized attestations. - */ - -contract TokenizedResolver is ResolverBase { - using SafeERC20 for IERC20; - - IERC20 private immutable token; - - uint256 immutable fee = 10; - - constructor(address rs, address tokenAddr) ResolverBase(rs) { - token = IERC20(tokenAddr); - } - - function onAttest( - AttestationRecord calldata attestation, - uint256 value - ) - internal - virtual - override - returns (bool) - { - token.safeTransferFrom(attestation.attester, address(this), fee); - return true; - } - - function onRevoke( - AttestationRecord calldata attestation, - uint256 value - ) - internal - virtual - override - returns (bool) - { - return true; - } - - function onModuleRegistration( - ModuleRecord calldata module, - uint256 value - ) - internal - override - returns (bool) - { - return true; - } -} diff --git a/src/external/examples/ValueResolver.sol.bak b/src/external/examples/ValueResolver.sol.bak deleted file mode 100644 index 0d1191ab..00000000 --- a/src/external/examples/ValueResolver.sol.bak +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { ResolverBase, AttestationRecord, ModuleRecord } from "../ResolverBase.sol"; - -/** - * @title ValueResolver - * @author zeroknots - * @notice A resolver for value (ETH) attestations. - */ -contract ValueResolver is ResolverBase { - uint256 public immutable FEE = 10; - - constructor(address rs) ResolverBase(rs) { } - - function onAttest( - AttestationRecord calldata attestation, - uint256 value - ) - internal - virtual - override - returns (bool) - { - return msg.value > FEE; - } - - function onRevoke( - AttestationRecord calldata attestation, - uint256 value - ) - internal - virtual - override - returns (bool) - { - return true; - } - - function isPayable() public pure override returns (bool) { - return true; - } - - function onModuleRegistration( - ModuleRecord calldata module, - uint256 value - ) - internal - override - returns (bool) - { - return true; - } -} diff --git a/src/lib/ModuleTypeLib.sol b/src/lib/ModuleTypeLib.sol index c0660cc6..201f2335 100644 --- a/src/lib/ModuleTypeLib.sol +++ b/src/lib/ModuleTypeLib.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.21; import { PackedModuleTypes, ModuleType } from "../DataTypes.sol"; +import { LibSort } from "solady/utils/LibSort.sol"; import { IRegistry } from "../IRegistry.sol"; library ModuleTypeLib { @@ -9,13 +10,21 @@ library ModuleTypeLib { return (PackedModuleTypes.unwrap(self) & 2 ** ModuleType.unwrap(moduleType)) != 0; } - function pack(ModuleType[] calldata moduleTypes) internal pure returns (PackedModuleTypes) { - uint32 result; - for (uint256 i; i < moduleTypes.length; i++) { - uint32 _type = ModuleType.unwrap(moduleTypes[i]); - if (_type > 31) revert IRegistry.InvalidModuleType(); - result = result + uint32(2 ** _type); + + function isType(uint32 packed, uint32 check) internal pure returns (bool) { + return (packed & 2 ** check) != 0; + } + + function pack(ModuleType[] memory moduleTypes) internal pure returns (PackedModuleTypes) { + uint256 length = moduleTypes.length; + uint32 packed; + uint32 _type; + for (uint256 i; i < length; i++) { + _type = ModuleType.unwrap(moduleTypes[i]); + if (_type > 31 && isType(packed, _type)) revert IRegistry.InvalidModuleType(); + packed = packed + uint32(2 ** _type); + } - return PackedModuleTypes.wrap(result); + return PackedModuleTypes.wrap(packed); } } diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 19776176..62807e35 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -55,6 +55,42 @@ contract AttestationTest is BaseTest { assertEq(record.attester, attester1.addr); } + function test_WhenReAttestingToARevokedAttestation() public prankWithAccount(attester1) { + address module = address(new MockModule()); + registry.registerModule(defaultResolverUID, module, ""); + uint32[] memory types = new uint32[](1); + AttestationRequest memory request = + mockAttestation(module, uint48(block.timestamp + 1), "", types); + // It should store. + registry.attest(defaultSchemaUID, request); + AttestationRecord memory record = registry.findAttestation(module, attester1.addr); + + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, request.expirationTime); + assertEq(record.revocationTime, 0); + assertEq(record.moduleAddr, request.moduleAddr); + assertEq(record.attester, attester1.addr); + + RevocationRequest memory revocation = RevocationRequest({ moduleAddr: module }); + + vm.warp(block.timestamp + 100); + registry.revoke(revocation); + + record = registry.findAttestation({ module: module, attester: attester1.addr }); + assertEq(record.revocationTime, block.timestamp); + vm.warp(block.timestamp + 100); + + request.expirationTime = uint48(block.timestamp + 100); + registry.attest(defaultSchemaUID, request); + record = registry.findAttestation({ module: module, attester: attester1.addr }); + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, request.expirationTime); + // ensure revocation time is reset + assertEq(record.revocationTime, 0); + assertEq(record.moduleAddr, request.moduleAddr); + assertEq(record.attester, attester1.addr); + } + function test_WhenAttestingWithExpirationTimeInThePast( address module, bytes memory data, diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index 11685cdd..b22e4826 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -23,7 +23,7 @@ contract ModuleRegistrationTest is BaseTest { bytes memory bytecode = type(MockModule).creationCode; address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, ""); - ModuleRecord memory record = registry.getRegisteredModules(moduleAddr); + ModuleRecord memory record = registry.getRegisteredModule(moduleAddr); assertTrue(record.resolverUID == defaultResolverUID); } diff --git a/test/invariance/Handler.t.sol b/test/invariance/Handler.t.sol new file mode 100644 index 00000000..f0b3ed2f --- /dev/null +++ b/test/invariance/Handler.t.sol @@ -0,0 +1,100 @@ +import { CommonBase } from "forge-std/Base.sol"; +import { StdCheats } from "forge-std/StdCheats.sol"; +import { StdUtils } from "forge-std/StdUtils.sol"; + +import "src/Registry.sol"; +import "src/DataTypes.sol"; +import "src/external/IExternalSchemaValidator.sol"; +import "src/external/IExternalResolver.sol"; +import "../mocks/MockSchemaValidator.sol"; +import "../mocks/MockResolver.sol"; +import { LibSort } from "solady/utils/LibSort.sol"; + +contract Handler is CommonBase, StdCheats, StdUtils { + Registry immutable REGISTRY; + + using LibSort for uint256[]; + + constructor(Registry _registry) { + REGISTRY = _registry; + } + + function handle_registerResolver() public returns (ResolverUID) { + MockResolver resolverTrue = new MockResolver(true); + return REGISTRY.registerResolver(IExternalResolver(address(resolverTrue))); + } + + function _pickRandomSchemaUID(uint256 nr) internal returns (SchemaUID uid) { + SchemaUID[] memory uids = new SchemaUID[](5); + uids[0] = handle_registerSchema("schema1"); + uids[1] = handle_registerSchema("schema2"); + uids[2] = handle_registerSchema("schema3"); + uids[3] = handle_registerSchema("schema4"); + uids[4] = handle_registerSchema("schema5"); + + return uids[nr % 5]; + } + + function _pickRandomResolverUID(uint256 nr) internal returns (ResolverUID uid) { + ResolverUID[] memory uids = new ResolverUID[](5); + uids[0] = handle_registerResolver(); + uids[1] = handle_registerResolver(); + uids[2] = handle_registerResolver(); + uids[3] = handle_registerResolver(); + uids[4] = handle_registerResolver(); + + return uids[nr % 5]; + } + + function handle_registerSchema(string memory schema) public returns (SchemaUID) { + MockSchemaValidator schemaValidatorTrue = new MockSchemaValidator(true); + return + REGISTRY.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorTrue))); + } + + function handle_registerModule( + uint256 randomResolverNr, + address moduleAddr, + bytes calldata bytecode, + bytes calldata metadata + ) + public + { + vm.etch(moduleAddr, bytecode); + ResolverUID uid = _pickRandomResolverUID(randomResolverNr); + + REGISTRY.registerModule(uid, moduleAddr, metadata); + } + + function _pickTypes() private pure returns (ModuleType[] memory ret) { + ret = new ModuleType[](3); + ret[0] = ModuleType.wrap(1); + ret[1] = ModuleType.wrap(2); + ret[2] = ModuleType.wrap(3); + } + + function handle_attest( + uint256 randResolv, + bytes calldata bytecode, + bytes calldata metadata, + uint256 randomSchemaUID, + AttestationRequest memory request + ) + public + { + bound(request.expirationTime, block.timestamp, type(uint48).max); + request.moduleTypes = _pickTypes(); + + handle_registerModule(randResolv, request.moduleAddr, bytecode, metadata); + SchemaUID uid = _pickRandomSchemaUID(randomSchemaUID); + + REGISTRY.attest(uid, request); + } + + // function handle_revoke(uint256 randomSchemaUID, AttestationRequest calldata request) external { + // handle_attest(randomSchemaUID, request); + // + // RevocationRequest memory revoke = RevocationRequest(request.moduleAddr); + // REGISTRY.revoke(revoke); + // } +} diff --git a/test/invariance/invariant_ImmutableData.t.sol b/test/invariance/invariant_ImmutableData.t.sol new file mode 100644 index 00000000..e651211e --- /dev/null +++ b/test/invariance/invariant_ImmutableData.t.sol @@ -0,0 +1,41 @@ +import "src/IRegistry.sol"; +import "../Base.t.sol"; +import "./Handler.t.sol"; + +contract ImmutableData is BaseTest { + AttestationDataRef defaultDataRef; + Handler handler; + + function setUp() public override { + super.setUp(); + handler = new Handler(registry); + + AttestationRecord memory attRecord = + registry.findAttestation(address(module1), attester1.addr); + defaultDataRef = attRecord.dataPointer; + + bytes4[] memory targetSelectors = new bytes4[](4); + targetSelectors[0] = Handler.handle_registerSchema.selector; + targetSelectors[1] = Handler.handle_registerResolver.selector; + targetSelectors[2] = Handler.handle_registerModule.selector; + targetSelectors[3] = Handler.handle_attest.selector; + // targetSelectors[3] = Handler.handle_revoke.selector; + + targetContract(address(handler)); + targetSelector(FuzzSelector({ addr: address(handler), selectors: targetSelectors })); + } + + function invariant_resolver_immutable() public { } + + function invariant_schema_immutable() public { + SchemaRecord memory record = registry.findSchema(defaultSchemaUID); + assertEq(record.schema, defaultSchema); + } + + function invariant_attestation_immutable() public { + AttestationRecord memory record = registry.findAttestation(address(module1), attester1.addr); + assertTrue(record.dataPointer == defaultDataRef); + assertEq(record.moduleAddr, address(module1)); + assertEq(record.attester, attester1.addr); + } +} From ac66539dd40433014adeab73a381700823a17951 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Mon, 12 Feb 2024 10:54:54 +0700 Subject: [PATCH 42/84] chore: adding TODO inline comment --- src/core/ModuleManager.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 26edb93e..506ec83a 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -113,6 +113,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { // prevent someone from calling a registry function pretending its a factory if (factory == address(this)) revert FactoryCallFailed(factory); // call external factory to deploy module + // TODO: this could be reentrancy since its not using CEI (bool ok, bytes memory returnData) = factory.call{ value: msg.value }(callOnFactory); if (!ok) revert FactoryCallFailed(factory); From 4fd5a6d86a84329e6f75c0215ec8079ce80d9688 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Mon, 12 Feb 2024 11:16:22 +0700 Subject: [PATCH 43/84] chore: moving TrustedAttesterRecord to DataTypes.sol for consistency --- src/DataTypes.sol | 8 ++++++++ src/core/TrustManager.sol | 28 ++++++++++++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/DataTypes.sol b/src/DataTypes.sol index f7dc690f..4ee33979 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -38,6 +38,14 @@ struct ResolverRecord { address resolverOwner; // The address of the account used to register the resolver. } +// Struct that represents a trusted attester. +struct TrustedAttesterRecord { + uint8 attesterCount; // number of attesters in the linked list + uint8 threshold; // minimum number of attesters required + address attester; // first attester in linked list. (packed to save gas) + mapping(address attester => address linkedAttester) linkedAttesters; +} + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Attestation / Revocation Requests */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 4e48d89a..d8ec183b 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -1,7 +1,12 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; -import { AttestationRecord, PackedModuleTypes, ModuleType } from "../DataTypes.sol"; +import { + AttestationRecord, + PackedModuleTypes, + ModuleType, + TrustedAttesterRecord +} from "../DataTypes.sol"; import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE, ZERO_ADDRESS } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; import { TrustManagerExternalAttesterList } from "./TrustManagerExternalAttesterList.sol"; @@ -18,17 +23,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { using ModuleTypeLib for PackedModuleTypes; using LibSort for address[]; - // packed struct to allow for efficient storage. - // if only one attester is trusted, it only requires 1 SLOAD - - struct TrustedAttesters { - uint8 attesterCount; - uint8 threshold; - address attester; - mapping(address attester => address linkedAttester) linkedAttesters; - } - - mapping(address account => TrustedAttesters attesters) internal _accountToAttester; + mapping(address account => TrustedAttesterRecord attesters) internal _accountToAttester; // Deliberately using memory here, so we can sort the array function trustAttesters(uint8 threshold, address[] memory attesters) external { @@ -39,7 +34,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { if (attesters.length != attestersLength) revert InvalidTrustedAttesterInput(); // sort attesters - TrustedAttesters storage _att = _accountToAttester[msg.sender]; + TrustedAttesterRecord storage _att = _accountToAttester[msg.sender]; // threshold cannot be greater than the number of attesters if (threshold > attestersLength) { threshold = uint8(attestersLength); @@ -83,7 +78,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { } function _check(address smartAccount, address module, ModuleType moduleType) internal view { - TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; + TrustedAttesterRecord storage trustedAttesters = _accountToAttester[smartAccount]; // SLOAD from one slot uint256 attesterCount = trustedAttesters.attesterCount; uint256 threshold = trustedAttesters.threshold; @@ -184,10 +179,11 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { view returns (address[] memory attesters) { - TrustedAttesters storage trustedAttesters = _accountToAttester[smartAccount]; + TrustedAttesterRecord storage trustedAttesters = _accountToAttester[smartAccount]; uint256 count = trustedAttesters.attesterCount; + address attester0 = trustedAttesters.attester; attesters = new address[](count); - attesters[0] = trustedAttesters.attester; + attesters[0] = attester0; for (uint256 i = 1; i < count; i++) { // get next attester from linked List From d346104720bf564d93ff63a97ac372c4202ae16d Mon Sep 17 00:00:00 2001 From: zeroknots Date: Mon, 12 Feb 2024 12:56:12 +0700 Subject: [PATCH 44/84] feat: test for factory deploy --- src/core/SchemaManager.sol | 9 ++++++++ src/core/TrustManager.sol | 4 ++++ src/lib/ModuleTypeLib.sol | 2 -- test/Base.t.sol | 12 ++++++++++ test/invariance/Handler.t.sol | 22 +++++++++++++------ test/invariance/invariant_ImmutableData.t.sol | 8 ++++--- test/mocks/MockFactory.sol | 12 ++++++++++ 7 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 test/mocks/MockFactory.sol diff --git a/src/core/SchemaManager.sol b/src/core/SchemaManager.sol index a38d922b..b85f4fc8 100644 --- a/src/core/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -8,6 +8,15 @@ import { UIDLib } from "../lib/Helpers.sol"; import { ZERO_TIMESTAMP, _time } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; +/** + * @title SchemaManager + * Allows the creation registration and creation of schemas. + * Schemas are used to describe attestation data. It is recommended to use ABI encoded data as schema. + * An external schema validator contract may be provided, + * which will be called for every attestation made against the schema. + * arbitrary checks may be implemented, by SchemaValidators + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + */ abstract contract SchemaManager is IRegistry { using UIDLib for SchemaRecord; // The global mapping between schema records and their IDs. diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index d8ec183b..7f3df26c 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -15,6 +15,10 @@ import { LibSort } from "solady/utils/LibSort.sol"; /** * @title TrustManager + * Allows smart accounts to query the registry for the security status of modules. + * Smart accounts may trust a list of attesters to attest to the security status of + * modules and configure a minimum threshold of how many attestations have to be in place + * to consider a module as "trust worthy" * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) * Implements EIP-7484 to query attestations stored in the registry. * @dev This contract is abstract and provides utility functions to query attestations. diff --git a/src/lib/ModuleTypeLib.sol b/src/lib/ModuleTypeLib.sol index 201f2335..f625866f 100644 --- a/src/lib/ModuleTypeLib.sol +++ b/src/lib/ModuleTypeLib.sol @@ -10,7 +10,6 @@ library ModuleTypeLib { return (PackedModuleTypes.unwrap(self) & 2 ** ModuleType.unwrap(moduleType)) != 0; } - function isType(uint32 packed, uint32 check) internal pure returns (bool) { return (packed & 2 ** check) != 0; } @@ -23,7 +22,6 @@ library ModuleTypeLib { _type = ModuleType.unwrap(moduleTypes[i]); if (_type > 31 && isType(packed, _type)) revert IRegistry.InvalidModuleType(); packed = packed + uint32(2 ** _type); - } return PackedModuleTypes.wrap(packed); } diff --git a/test/Base.t.sol b/test/Base.t.sol index 596079c6..6d70dc08 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -17,6 +17,7 @@ contract BaseTest is Test { Account attester1; Account attester2; + Account invarAttester; Account moduleDev1; Account moduleDev2; @@ -50,6 +51,7 @@ contract BaseTest is Test { attester1 = makeAccount("attester1"); attester2 = makeAccount("attester2"); + invarAttester = makeAccount("invarAttester"); moduleDev1 = makeAccount("moduleDev1"); moduleDev2 = makeAccount("moduleDev2"); @@ -91,5 +93,15 @@ contract BaseTest is Test { registry.registerModule(defaultResolverUID, address(module1), ""); vm.prank(moduleDev2.addr); registry.registerModule(defaultResolverUID, address(module2), ""); + + AttestationRequest memory req = AttestationRequest({ + moduleAddr: address(module1), + expirationTime: uint48(block.timestamp + 100_000), + data: "0x", + moduleTypes: new ModuleType[](0) + }); + + vm.prank(invarAttester.addr); + registry.attest(defaultSchemaUID, req); } } diff --git a/test/invariance/Handler.t.sol b/test/invariance/Handler.t.sol index f0b3ed2f..21037eff 100644 --- a/test/invariance/Handler.t.sol +++ b/test/invariance/Handler.t.sol @@ -8,15 +8,18 @@ import "src/external/IExternalSchemaValidator.sol"; import "src/external/IExternalResolver.sol"; import "../mocks/MockSchemaValidator.sol"; import "../mocks/MockResolver.sol"; +import "../mocks/MockFactory.sol"; import { LibSort } from "solady/utils/LibSort.sol"; contract Handler is CommonBase, StdCheats, StdUtils { Registry immutable REGISTRY; + MockFactory immutable FACTORY; using LibSort for uint256[]; constructor(Registry _registry) { REGISTRY = _registry; + FACTORY = new MockFactory(); } function handle_registerResolver() public returns (ResolverUID) { @@ -82,7 +85,7 @@ contract Handler is CommonBase, StdCheats, StdUtils { ) public { - bound(request.expirationTime, block.timestamp, type(uint48).max); + bound(request.expirationTime, block.timestamp + 1, type(uint48).max); request.moduleTypes = _pickTypes(); handle_registerModule(randResolv, request.moduleAddr, bytecode, metadata); @@ -91,10 +94,15 @@ contract Handler is CommonBase, StdCheats, StdUtils { REGISTRY.attest(uid, request); } - // function handle_revoke(uint256 randomSchemaUID, AttestationRequest calldata request) external { - // handle_attest(randomSchemaUID, request); - // - // RevocationRequest memory revoke = RevocationRequest(request.moduleAddr); - // REGISTRY.revoke(revoke); - // } + function handle_registerModuleWithFactory( + uint256 randomResolverNr, + bytes calldata bytecode + ) + external + { + ResolverUID uid = _pickRandomResolverUID(randomResolverNr); + REGISTRY.deployViaFactory( + address(FACTORY), abi.encodeCall(MockFactory.deploy, (bytecode)), "", uid + ); + } } diff --git a/test/invariance/invariant_ImmutableData.t.sol b/test/invariance/invariant_ImmutableData.t.sol index e651211e..bb6077ae 100644 --- a/test/invariance/invariant_ImmutableData.t.sol +++ b/test/invariance/invariant_ImmutableData.t.sol @@ -11,7 +11,7 @@ contract ImmutableData is BaseTest { handler = new Handler(registry); AttestationRecord memory attRecord = - registry.findAttestation(address(module1), attester1.addr); + registry.findAttestation(address(module1), invarAttester.addr); defaultDataRef = attRecord.dataPointer; bytes4[] memory targetSelectors = new bytes4[](4); @@ -33,9 +33,11 @@ contract ImmutableData is BaseTest { } function invariant_attestation_immutable() public { - AttestationRecord memory record = registry.findAttestation(address(module1), attester1.addr); + AttestationRecord memory record = + registry.findAttestation(address(module1), invarAttester.addr); assertTrue(record.dataPointer == defaultDataRef); assertEq(record.moduleAddr, address(module1)); - assertEq(record.attester, attester1.addr); + assertEq(record.attester, invarAttester.addr); + assertTrue(record.attester != address(0)); } } diff --git a/test/mocks/MockFactory.sol b/test/mocks/MockFactory.sol new file mode 100644 index 00000000..1463c1c8 --- /dev/null +++ b/test/mocks/MockFactory.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +contract MockFactory { + function deploy(bytes memory bytecode) external returns (address addr) { + // solhint-disable-next-line no-inline-assembly + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + if iszero(extcodesize(addr)) { revert(0, 0) } + } + } +} From 75881e99265374a61dcde1c52a73ea66e8c302a9 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Mon, 12 Feb 2024 13:06:20 +0700 Subject: [PATCH 45/84] feat: fuzz set resolver --- test/invariance/Handler.t.sol | 18 ++++++++++++++---- test/invariance/invariant_ImmutableData.t.sol | 12 +++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/test/invariance/Handler.t.sol b/test/invariance/Handler.t.sol index 21037eff..5594ce24 100644 --- a/test/invariance/Handler.t.sol +++ b/test/invariance/Handler.t.sol @@ -27,6 +27,13 @@ contract Handler is CommonBase, StdCheats, StdUtils { return REGISTRY.registerResolver(IExternalResolver(address(resolverTrue))); } + function handle_setResolver() external { + ResolverUID uid = handle_registerResolver(); + MockResolver resolverTrue = new MockResolver(true); + + REGISTRY.setResolver(uid, resolverTrue); + } + function _pickRandomSchemaUID(uint256 nr) internal returns (SchemaUID uid) { SchemaUID[] memory uids = new SchemaUID[](5); uids[0] = handle_registerSchema("schema1"); @@ -49,10 +56,11 @@ contract Handler is CommonBase, StdCheats, StdUtils { return uids[nr % 5]; } - function handle_registerSchema(string memory schema) public returns (SchemaUID) { + function handle_registerSchema(string memory schema) public returns (SchemaUID uid) { MockSchemaValidator schemaValidatorTrue = new MockSchemaValidator(true); - return + uid = REGISTRY.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorTrue))); + SchemaRecord memory record = REGISTRY.findSchema(uid); } function handle_registerModule( @@ -96,12 +104,14 @@ contract Handler is CommonBase, StdCheats, StdUtils { function handle_registerModuleWithFactory( uint256 randomResolverNr, - bytes calldata bytecode + bytes calldata bytecode, + uint256 value ) external { + vm.deal(address(this), value); ResolverUID uid = _pickRandomResolverUID(randomResolverNr); - REGISTRY.deployViaFactory( + REGISTRY.deployViaFactory{ value: value }( address(FACTORY), abi.encodeCall(MockFactory.deploy, (bytecode)), "", uid ); } diff --git a/test/invariance/invariant_ImmutableData.t.sol b/test/invariance/invariant_ImmutableData.t.sol index bb6077ae..ba08feea 100644 --- a/test/invariance/invariant_ImmutableData.t.sol +++ b/test/invariance/invariant_ImmutableData.t.sol @@ -14,11 +14,13 @@ contract ImmutableData is BaseTest { registry.findAttestation(address(module1), invarAttester.addr); defaultDataRef = attRecord.dataPointer; - bytes4[] memory targetSelectors = new bytes4[](4); + bytes4[] memory targetSelectors = new bytes4[](6); targetSelectors[0] = Handler.handle_registerSchema.selector; targetSelectors[1] = Handler.handle_registerResolver.selector; - targetSelectors[2] = Handler.handle_registerModule.selector; - targetSelectors[3] = Handler.handle_attest.selector; + targetSelectors[2] = Handler.handle_setResolver.selector; + targetSelectors[3] = Handler.handle_registerModule.selector; + targetSelectors[4] = Handler.handle_attest.selector; + targetSelectors[5] = Handler.handle_registerModuleWithFactory.selector; // targetSelectors[3] = Handler.handle_revoke.selector; targetContract(address(handler)); @@ -40,4 +42,8 @@ contract ImmutableData is BaseTest { assertEq(record.attester, invarAttester.addr); assertTrue(record.attester != address(0)); } + + function invariant_balance() public { + assertEq(address(registry).balance, 0); + } } From 5add7c26740c75c1ea7c2fa1214b21e7070b3921 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Mon, 12 Feb 2024 13:36:28 +0700 Subject: [PATCH 46/84] feat: non packed moduleTypes now 256 bit to save on padding gas --- .gas-snapshot | 181 ++++++++++++--------------------- src/DataTypes.sol | 2 +- src/core/SignedAttestation.sol | 25 +++-- src/lib/ModuleTypeLib.sol | 4 +- 4 files changed, 79 insertions(+), 133 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 6f525ff0..22e92d2e 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,117 +1,64 @@ -AttestationDelegationTest:testAttest() (gas: 236573) -AttestationDelegationTest:testAttest__RevertWhen_InvalidSignature() (gas: 81176) -AttestationDelegationTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 73165) -AttestationDelegationTest:testAttest__RevertWhen__InvalidSchema() (gas: 70136) -AttestationDelegationTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 103950) -AttestationDelegationTest:testAttest__RevertWhen__ZeroImplementation() (gas: 73402) -AttestationDelegationTest:testAttest__With__LargeAttestation() (gas: 441727) -AttestationDelegationTest:testMultiAttest() (gas: 578905) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 265016) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidLength__DataLength() (gas: 20031) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidLength__SignatureLength() (gas: 206425) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 260976) -AttestationDelegationTest:testMultiAttest__RevertWhen__InvalidSignature() (gas: 235201) -AttestationDelegationTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 296050) -AttestationDelegationTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 263189) -AttestationDelegationTest:testMultiRevoke() (gas: 633071) -AttestationDelegationTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558481) -AttestationDelegationTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 208998) -AttestationDelegationTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539188) -AttestationDelegationTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541281) -AttestationDelegationTest:testRevoke() (gas: 273143) -AttestationDelegationTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 291032) -AttestationDelegationTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 75399) -AttestationDelegationTest:testRevoke__RevertWhen__InvalidSchema() (gas: 265512) -AttestationDelegationTest:testRevoke__RevertWhen__InvalidSignature() (gas: 247924) -AttestationResolveTest:testResolveAttestation() (gas: 17618) -AttestationResolveTest:testResolveAttestation__RevertWhen__InsufficientValue() (gas: 20250) -AttestationResolveTest:testResolveAttestation__RevertWhen__InvalidAttestation() (gas: 20692) -AttestationResolveTest:testResolveAttestation__RevertWhen__InvalidRevocation() (gas: 20801) -AttestationResolveTest:testResolveAttestation__RevertWhen__ResolverNotPayableAndValue() (gas: 20180) -AttestationResolveTest:testResolveAttestation__RevertWhen__ZeroResolverAndValue() (gas: 16986) -AttestationResolveTest:testResolveAttestation__WithValue() (gas: 59663) -AttestationTest:testAttest() (gas: 193605) -AttestationTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 32738) -AttestationTest:testAttest__RevertWhen__InvalidSchema() (gas: 29891) -AttestationTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 63679) -AttestationTest:testAttest__RevertWhen__ZeroImplementation() (gas: 32975) -AttestationTest:testAttest__With__LargeAttestation() (gas: 395769) -AttestationTest:testMultiAttest() (gas: 513511) -AttestationTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 200140) -AttestationTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 196255) -AttestationTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 231271) -AttestationTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 229167) -AttestationTest:testMultiRevoke() (gas: 551445) -AttestationTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558315) -AttestationTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 208926) -AttestationTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539116) -AttestationTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541194) -AttestationTest:testRevoke() (gas: 213722) -AttestationTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 227327) -AttestationTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 40476) -AttestationTest:testRevoke__RevertWhen__InvalidSchema() (gas: 211792) -AttestationTest:testRevoke__RevertWhen__NotOriginalAttester() (gas: 210546) -DeployRegistryTest:testRun() (gas: 4643338) -EIP712VerifierTest:testGetAttestationDigest() (gas: 20134) -EIP712VerifierTest:testGetNonce() (gas: 48075) -EIP712VerifierTest:testGetRevocationDigest() (gas: 18477) -EIP712VerifierTest:testVerifyAttest() (gas: 45223) -EIP712VerifierTest:testVerifyAttest__RevertWhen__InvalidSignature() (gas: 39132) -EIP712VerifierTest:testVerifyRevoke() (gas: 43469) -MockRegistryTest:testCheck() (gas: 5721) -MockRegistryTest:testCheckN() (gas: 7736) -MockRegistryTest:testCheckNUnsafe() (gas: 7779) -ModuleTest:testCreate3() (gas: 186050) -ModuleTest:testDeployNoArgs() (gas: 205553) -ModuleTest:testDeployWithArgs() (gas: 229068) -ModuleTest:testExternalFactory() (gas: 345786) -ModuleTest:testNonexistingModule__ShouldRevert() (gas: 78699) -ModuleTest:testReRegisterModule__ShouldRevert() (gas: 214229) -QueryTest:testAttest() (gas: 193605) -QueryTest:testAttest__RevertWhen__InvalidExpirationTime() (gas: 32872) -QueryTest:testAttest__RevertWhen__InvalidSchema() (gas: 29936) -QueryTest:testAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 63813) -QueryTest:testAttest__RevertWhen__ZeroImplementation() (gas: 33042) -QueryTest:testAttest__With__LargeAttestation() (gas: 395836) -QueryTest:testCheckAttestation() (gas: 195284) -QueryTest:testCheckAttestationInternal() (gas: 220632) -QueryTest:testCheckAttestation__RevertWhen__AttestationNotExistent() (gas: 13252) -QueryTest:testCheckAttestation__RevertWhen__Expired() (gas: 241272) -QueryTest:testCheckAttestation__RevertWhen__Revoked() (gas: 215460) -QueryTest:testCheckNAttestation() (gas: 405649) -QueryTest:testCheckNAttestationUnsafe() (gas: 410051) -QueryTest:testCheckNAttestationUnsafe__Expired() (gas: 410778) -QueryTest:testCheckNAttestationUnsafe__RevertWhen__ThresholdNotMet() (gas: 211697) -QueryTest:testCheckNAttestationUnsafe__Revoked() (gas: 426892) -QueryTest:testCheckNAttestation__RevertWhen__Expired() (gas: 479196) -QueryTest:testCheckNAttestation__RevertWhen__Revoked() (gas: 445326) -QueryTest:testCheckNAttestation__RevertWhen__ThresholdNotMet() (gas: 204685) -QueryTest:testFindAttestation() (gas: 197129) -QueryTest:testFindAttestations() (gas: 409961) -QueryTest:testMultiAttest() (gas: 513555) -QueryTest:testMultiAttest__RevertWhen__InvalidExpirationTime() (gas: 200162) -QueryTest:testMultiAttest__RevertWhen__InvalidSchema() (gas: 196277) -QueryTest:testMultiAttest__RevertWhen__ValidatorSaysInvalidAttestation() (gas: 231337) -QueryTest:testMultiAttest__RevertWhen__ZeroImplementation() (gas: 229189) -QueryTest:testMultiRevoke() (gas: 551645) -QueryTest:testMultiRevoke__RevertWhen__AlreadyRevoked() (gas: 558359) -QueryTest:testMultiRevoke__RevertWhen__AttestationNotFound() (gas: 209036) -QueryTest:testMultiRevoke__RevertWhen__InvalidSchema() (gas: 539116) -QueryTest:testMultiRevoke__RevertWhen__NotOriginalAttester() (gas: 541216) -QueryTest:testRevoke() (gas: 213920) -QueryTest:testRevoke__RevertWhen__AlreadyRevoked() (gas: 227504) -QueryTest:testRevoke__RevertWhen__AttestationNotFound() (gas: 40564) -QueryTest:testRevoke__RevertWhen__InvalidSchema() (gas: 211814) -QueryTest:testRevoke__RevertWhen__NotOriginalAttester() (gas: 210613) -RegistryGasComparisonTest:testGasCheck() (gas: 24495) -RegistryGasComparisonTest:testGasCheckN__Given__ThreeAttesters() (gas: 52969) -RegistryGasComparisonTest:testGasCheckN__Given__TwoAttesters() (gas: 39930) -SchemaTest:testRegisterResolver() (gas: 57051) -SchemaTest:testRegisterResolver__RevertWhen__AlreadyExists() (gas: 62725) -SchemaTest:testRegisterResolver__RevertWhen__InvalidResolver() (gas: 11729) -SchemaTest:testRegisterSchema() (gas: 60492) -SchemaTest:testRegisterSchema__RevertWhen__AlreadyExists() (gas: 63043) -SchemaTest:testSetResolver() (gas: 63614) -SimpleRegistryIntegrationTest:testGasRegistryCheck() (gas: 200467) -TokenizedResolverTest:testTokenizedResolver() (gas: 510624) -ValueResolverTest:testValueResolver() (gas: 490772) \ No newline at end of file +AttestationTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23722, ~: 23351) +AttestationTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16632, ~: 16589) +AttestationTest:test_WhenAttestingWithNoAttestationData() (gas: 275754) +AttestationTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18340, ~: 18746) +AttestationTest:test_WhenAttesting_ShouldCallResolver() (gas: 180377) +AttestationTest:test_WhenReAttestingToARevokedAttestation() (gas: 338657) +AttestationTest:test_WhenRevokingAttestationThatDoesntExist(address) (runs: 256, μ: 28076, ~: 28076) +AttestationTest:test_WhenRevokingWithValidECDSA() (gas: 242527) +AttestationTest:test_WhenRevokingWithValidECDSAMulti() (gas: 409188) +AttestationTest:test_WhenTokensAreNotPaid() (gas: 210) +AttestationTest:test_WhenTokensArePaid() (gas: 232) +AttestationTest:test_WhenUsingInvalidECDSA() (gas: 51781) +AttestationTest:test_WhenUsingInvalidECDSAMulti() (gas: 60278) +AttestationTest:test_WhenUsingInvalidERC1271() (gas: 45423) +AttestationTest:test_WhenUsingInvalidERC1271Multi() (gas: 51256) +AttestationTest:test_WhenUsingValidECDSA() (gas: 224301) +AttestationTest:test_WhenUsingValidECDSAMulti() (gas: 385255) +AttestationTest:test_WhenUsingValidERC1271() (gas: 211515) +AttestationTest:test_WhenUsingValidERC1271Multi() (gas: 371452) +ImmutableData:invariant_attestation_immutable() (runs: 1200, calls: 24000, reverts: 1442) +ImmutableData:invariant_balance() (runs: 1200, calls: 24000, reverts: 1442) +ImmutableData:invariant_resolver_immutable() (runs: 1200, calls: 24000, reverts: 1442) +ImmutableData:invariant_schema_immutable() (runs: 1200, calls: 24000, reverts: 1442) +ModuleRegistrationTest:test_WhenDeployingViaRegistry() (gas: 130550) +ModuleRegistrationTest:test_WhenDeployingViaRegistryWithArgs() (gas: 153624) +ModuleRegistrationTest:test_WhenRegisteringAModuleOnAInValidResolverUID() (gas: 74823) +ModuleRegistrationTest:test_WhenRegisteringAModuleOnAValidResolverUID() (gas: 105879) +ModuleRegistrationTest:test_WhenRegisteringAModuleOnAnInvalidResolverUID() (gas: 81381) +ModuleRegistrationTest:test_WhenRegisteringTwoModulesWithTheSameBytecode() (gas: 108658) +ModuleRegistrationTest:test_WhenRegisteringViaFactory() (gas: 223948) +ModuleRegistrationTest:test_WhenUsingInvalidFactory() (gas: 18581) +ModuleRegistrationTest:test_WhenUsingRegistryASFactory() (gas: 15736) +ResolverTest:test_WhenNewResolver() (gas: 306513) +ResolverTest:test_WhenResolverAlreadyRegistered() (gas: 304095) +ResolverTest:test_WhenUsingAuthorizedAccount() (gas: 554370) +ResolverTest:test_WhenUsingUnauthorizedAccount() (gas: 306456) +SchemaValidationTest:test_WhenSchemaAlreadyRegistered() (gas: 107982) +SchemaValidationTest:test_WhenSchemaNew() (gas: 53734) +TrustTest:test_WhenAttesterSetButNoAttestationMade() (gas: 253) +TrustTest:test_WhenAttestersSetAndAllOk() (gas: 285372) +TrustTest:test_WhenAttestersSetButThresholdTooLow() (gas: 188) +TrustTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23743, ~: 23379) +TrustTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16610, ~: 16567) +TrustTest:test_WhenAttestingWithNoAttestationData() (gas: 275732) +TrustTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18362, ~: 18768) +TrustTest:test_WhenAttesting_ShouldCallResolver() (gas: 180444) +TrustTest:test_WhenNoAttestersSet() (gas: 39247) +TrustTest:test_WhenReAttestingToARevokedAttestation() (gas: 338679) +TrustTest:test_WhenRevokingAttestationThatDoesntExist(address) (runs: 256, μ: 28098, ~: 28098) +TrustTest:test_WhenRevokingWithValidECDSA() (gas: 242505) +TrustTest:test_WhenRevokingWithValidECDSAMulti() (gas: 409254) +TrustTest:test_WhenSupplyingManyAttesters(address[]) (runs: 256, μ: 1326591, ~: 1326627) +TrustTest:test_WhenSupplyingOneAttester() (gas: 42672) +TrustTest:test_WhenSupplyingSameAttesterMultipleTimes() (gas: 13159) +TrustTest:test_WhenTokensAreNotPaid() (gas: 210) +TrustTest:test_WhenTokensArePaid() (gas: 232) +TrustTest:test_WhenUsingInvalidECDSA() (gas: 51803) +TrustTest:test_WhenUsingInvalidECDSAMulti() (gas: 60256) +TrustTest:test_WhenUsingInvalidERC1271() (gas: 45423) +TrustTest:test_WhenUsingInvalidERC1271Multi() (gas: 51345) +TrustTest:test_WhenUsingValidECDSA() (gas: 224301) +TrustTest:test_WhenUsingValidECDSAMulti() (gas: 385233) +TrustTest:test_WhenUsingValidERC1271() (gas: 211515) +TrustTest:test_WhenUsingValidERC1271Multi() (gas: 371430) \ No newline at end of file diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 4ee33979..478c3413 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -115,7 +115,7 @@ function attestationDataRefEq( type PackedModuleTypes is uint32; -type ModuleType is uint32; +type ModuleType is uint256; using { moduleTypeEq as == } for ModuleType global; using { moduleTypeNeq as != } for ModuleType global; diff --git a/src/core/SignedAttestation.sol b/src/core/SignedAttestation.sol index 16f5ee8f..5bba8c4b 100644 --- a/src/core/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -30,13 +30,12 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { ) external { - // verify signature uint256 nonce = ++attesterNonce[attester]; bytes32 digest = _hashTypedData(request.hash(nonce)); bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); if (!valid) revert InvalidSignature(); - _attest(attester, schemaUID, request); + _attest({ attester: attester, schemaUID: schemaUID, request: request }); } function attest( @@ -52,7 +51,7 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); if (!valid) revert InvalidSignature(); - _attest(attester, schemaUID, requests); + _attest({ attester: attester, schemaUID: schemaUID, requests: requests }); } function revoke( @@ -67,7 +66,7 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); if (!valid) revert InvalidSignature(); - _revoke(attester, request); + _revoke({ attester: attester, request: request }); } function revoke( @@ -82,7 +81,7 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); if (!valid) revert InvalidSignature(); - _revoke(attester, requests); + _revoke({ attester: attester, requests: requests }); } function _domainNameAndVersion() @@ -102,9 +101,9 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { ) external view - returns (bytes32) + returns (bytes32 digest) { - return _hashTypedData(request.hash(attesterNonce[attester] + 1)); + digest = _hashTypedData(request.hash(attesterNonce[attester] + 1)); } function getDigest( @@ -113,9 +112,9 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { ) external view - returns (bytes32) + returns (bytes32 digest) { - return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); + digest = _hashTypedData(requests.hash(attesterNonce[attester] + 1)); } function getDigest( @@ -124,9 +123,9 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { ) external view - returns (bytes32) + returns (bytes32 digest) { - return _hashTypedData(request.hash(attesterNonce[attester] + 1)); + digest = _hashTypedData(request.hash(attesterNonce[attester] + 1)); } function getDigest( @@ -135,8 +134,8 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { ) external view - returns (bytes32) + returns (bytes32 digest) { - return _hashTypedData(requests.hash(attesterNonce[attester] + 1)); + digest = _hashTypedData(requests.hash(attesterNonce[attester] + 1)); } } diff --git a/src/lib/ModuleTypeLib.sol b/src/lib/ModuleTypeLib.sol index f625866f..013d01cc 100644 --- a/src/lib/ModuleTypeLib.sol +++ b/src/lib/ModuleTypeLib.sol @@ -10,14 +10,14 @@ library ModuleTypeLib { return (PackedModuleTypes.unwrap(self) & 2 ** ModuleType.unwrap(moduleType)) != 0; } - function isType(uint32 packed, uint32 check) internal pure returns (bool) { + function isType(uint32 packed, uint256 check) internal pure returns (bool) { return (packed & 2 ** check) != 0; } function pack(ModuleType[] memory moduleTypes) internal pure returns (PackedModuleTypes) { uint256 length = moduleTypes.length; uint32 packed; - uint32 _type; + uint256 _type; for (uint256 i; i < length; i++) { _type = ModuleType.unwrap(moduleTypes[i]); if (_type > 31 && isType(packed, _type)) revert IRegistry.InvalidModuleType(); From a951298545293dad41f5a92998c087315f828932 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Mon, 12 Feb 2024 15:27:56 +0700 Subject: [PATCH 47/84] feat: ERC7512 prototype --- docs/Attestation.md | 12 ++-- src/external/examples/ERC7512Schema.sol | 78 +++++++++++++++++++++++++ src/lib/AttestationLib.sol | 2 +- 3 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 src/external/examples/ERC7512Schema.sol diff --git a/docs/Attestation.md b/docs/Attestation.md index 4be579ec..d3229093 100644 --- a/docs/Attestation.md +++ b/docs/Attestation.md @@ -12,11 +12,12 @@ AttestationRequest data is `abi.encode()` according to a defined schema. The data is not stored in the storage of thr Registry, but is rather stored with `SSTORE2` to save gas and a pointer to this data is stored on the Registry. ```solidity + struct AttestationRequest { address moduleAddr; // The moduleAddr of the attestation. uint48 expirationTime; // The time when the attestation expires (Unix timestamp). - uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors. bytes data; // Custom attestation data. + ModuleType[] moduleTypes; // optional: The type(s) of the module. } ``` @@ -26,19 +27,20 @@ AttestationRecord stored in the registry contract storage ```solidity struct AttestationRecord { - SchemaUID schemaUID; // The unique identifier of the schema. - address moduleAddr; // The recipient of the attestation i.e. module - address attester; // The attester/sender of the attestation. uint48 time; // The time when the attestation was created (Unix timestamp). uint48 expirationTime; // The time when the attestation expires (Unix timestamp). uint48 revocationTime; // The time when the attestation was revoked (Unix timestamp). + PackedModuleTypes moduleTypes; // bit-wise encoded module types. See ModuleTypeLib + SchemaUID schemaUID; // The unique identifier of the schema. + address moduleAddr; // The implementation address of the module that is being attested. + address attester; // The attesting account. AttestationDataRef dataPointer; // SSTORE2 pointer to the attestation data. } ``` ![Sequence Diagram](../public/docs/attestationOnly.svg) -### Interactions with the SchemaValidator +### Interactions with the IExternalSchemaValidator Attestation data can be validated with an external contract than may to `abi.decode()` and validate all or specific fields. diff --git a/src/external/examples/ERC7512Schema.sol b/src/external/examples/ERC7512Schema.sol new file mode 100644 index 00000000..32fa083d --- /dev/null +++ b/src/external/examples/ERC7512Schema.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import { IExternalSchemaValidator } from "../IExternalSchemaValidator.sol"; +import { AttestationRecord, AttestationDataRef } from "../../DataTypes.sol"; +import { AttestationLib } from "../../lib/AttestationLib.sol"; +import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; + +interface ERC7512 { + error ERC7512_InvalidModuleAddr(); + + struct Auditor { + string name; + string uri; + string[] authors; + } + + struct Contract { + bytes32 chainId; + address deployment; + } + + enum SignatureType { + SECP256K1, + ERC1271 + } + + struct Signature { + SignatureType sigType; + address signer; + bytes data; + } + + struct AuditSummary { + Auditor auditor; + uint256 issuedAt; + uint256[] ercs; + Contract auditedContract; + bytes32 auditHash; + string auditUri; + uint256 signedAt; + Signature auditorSignature; + } +} + +contract ERC7512SchemaValidator is IExternalSchemaValidator, ERC7512 { + using AttestationLib for AttestationDataRef; + + function supportsInterface(bytes4 interfaceID) external pure override returns (bool) { + return (interfaceID == type(IExternalSchemaValidator).interfaceId); + } + + function validateSchema(AttestationRecord calldata attestation) + external + view + override + returns (bool valid) + { + AuditSummary memory summary = abi.decode(attestation.dataPointer.sload2(), (AuditSummary)); + if (summary.auditedContract.deployment != attestation.moduleAddr) { + return false; + } + if (summary.issuedAt > attestation.time) { + return false; + } + + valid = SignatureCheckerLib.isValidSignatureNow( + summary.auditorSignature.signer, summary.auditHash, summary.auditorSignature.data + ); + } + + function validateSchema(AttestationRecord[] calldata attestations) + external + view + override + returns (bool) + { } +} diff --git a/src/lib/AttestationLib.sol b/src/lib/AttestationLib.sol index 8d1f68fc..4490d379 100644 --- a/src/lib/AttestationLib.sol +++ b/src/lib/AttestationLib.sol @@ -7,7 +7,7 @@ import { SSTORE2 } from "solady/utils/SSTORE2.sol"; library AttestationLib { // The hash of the data type used to relay calls to the attest function. It's the value of bytes32 internal constant ATTEST_TYPEHASH = - keccak256("AttestationRequest(address,uint48,bytes,uint32[])"); + keccak256("AttestationRequest(address,uint48,bytes,uint256[])"); // The hash of the data type used to relay calls to the revoke function. It's the value of bytes32 internal constant REVOKE_TYPEHASH = keccak256("RevocationRequest(address)"); From d5a2953ca116d5bbf4c1609d72944bc72164f0c0 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 13 Feb 2024 11:31:15 +0700 Subject: [PATCH 48/84] feat: try/catch on revocations now --- src/lib/StubLib.sol | 23 ++++++++++++++++------- test/mocks/MockResolver.sol | 2 ++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index 526021db..ff5d85a5 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -8,6 +8,8 @@ import { ZERO_ADDRESS, ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; library StubLib { + event ResolverRevocationError(IExternalResolver resolver); + function requireExternalSchemaValidation( AttestationRecord memory attestationRecord, SchemaRecord storage schema @@ -82,12 +84,16 @@ library StubLib { ResolverRecord storage resolver ) internal + returns (bool resolved) { IExternalResolver resolverContract = resolver.resolver; - if (address(resolverContract) == ZERO_ADDRESS) return; - if (resolverContract.resolveRevocation(attestationRecord) == false) { - revert IRegistry.ExternalError_ResolveAtteststation(); + if (address(resolverContract) == ZERO_ADDRESS) return true; + try resolverContract.resolveRevocation(attestationRecord) returns (bool _resolved) { + if (_resolved) return true; + } catch { + emit ResolverRevocationError(resolverContract); + return false; } } @@ -96,13 +102,16 @@ library StubLib { ResolverRecord storage resolver ) internal + returns (bool resolved) { IExternalResolver resolverContract = resolver.resolver; - if (address(resolverContract) == ZERO_ADDRESS) return; - - if (resolverContract.resolveAttestation(attestationRecords) == false) { - revert IRegistry.ExternalError_ResolveAtteststation(); + if (address(resolverContract) == ZERO_ADDRESS) return true; + try resolverContract.resolveRevocation(attestationRecords) returns (bool _resolved) { + if (_resolved) return true; + } catch { + emit ResolverRevocationError(resolverContract); + return false; } } diff --git a/test/mocks/MockResolver.sol b/test/mocks/MockResolver.sol index 25d7d4d3..b6943c3a 100644 --- a/test/mocks/MockResolver.sol +++ b/test/mocks/MockResolver.sol @@ -50,6 +50,7 @@ contract MockResolver is IExternalResolver { override returns (bool) { + revert(); onRevokeCalled = true; return returnVal; } @@ -60,6 +61,7 @@ contract MockResolver is IExternalResolver { override returns (bool) { + revert(); onRevokeCalled = true; return returnVal; } From 7774a8c81c68b7f9282d28558e8420cf56a38faf Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 13 Feb 2024 12:39:18 +0700 Subject: [PATCH 49/84] chore: docs --- lcov.info | 1686 ++++++++++------- src/IRegistry.sol | 54 +- src/core/Attestation.sol | 18 + src/core/AttestationManager.sol | 7 +- src/core/ModuleManager.sol | 21 +- src/core/ResolverManager.sol | 15 +- src/core/SchemaManager.sol | 6 + src/core/SignedAttestation.sol | 21 +- src/core/TrustManager.sol | 32 +- src/lib/ModuleTypeLib.sol | 1 - src/lib/StubLib.sol | 11 +- test/ModuleRegistration.t.sol | 2 +- test/TrustDelegation.t.sol | 4 +- test/invariance/Handler.t.sol | 20 + test/invariance/invariant_ImmutableData.t.sol | 1 + 15 files changed, 1184 insertions(+), 715 deletions(-) diff --git a/lcov.info b/lcov.info index 0fba2696..71b48971 100644 --- a/lcov.info +++ b/lcov.info @@ -1234,10 +1234,10 @@ DA:528,0 BRDA:528,13,0,- BRDA:528,13,1,- FN:532,StdCheatsSafe.makeAddrAndKey -FNDA:0,StdCheatsSafe.makeAddrAndKey -DA:533,0 -DA:534,0 -DA:535,0 +FNDA:36,StdCheatsSafe.makeAddrAndKey +DA:533,36 +DA:534,36 +DA:535,36 FN:539,StdCheatsSafe.makeAddr FNDA:0,StdCheatsSafe.makeAddr DA:540,0 @@ -1250,8 +1250,8 @@ DA:551,0 DA:553,0 DA:554,0 FN:558,StdCheatsSafe.makeAccount -FNDA:0,StdCheatsSafe.makeAccount -DA:559,0 +FNDA:36,StdCheatsSafe.makeAccount +DA:559,36 FN:562,StdCheatsSafe.deriveRememberKey FNDA:0,StdCheatsSafe.deriveRememberKey DA:567,0 @@ -1399,9 +1399,9 @@ FN:813,StdCheats.console2_log_StdCheats FNDA:0,StdCheats.console2_log_StdCheats DA:814,0 FNF:59 -FNH:0 +FNH:2 LF:233 -LH:0 +LH:4 BRF:44 BRH:0 end_of_record @@ -1423,11 +1423,11 @@ FN:49,StdInvariant.targetArtifactSelector FNDA:0,StdInvariant.targetArtifactSelector DA:50,0 FN:53,StdInvariant.targetContract -FNDA:0,StdInvariant.targetContract -DA:54,0 +FNDA:4,StdInvariant.targetContract +DA:54,4 FN:57,StdInvariant.targetSelector -FNDA:0,StdInvariant.targetSelector -DA:58,0 +FNDA:4,StdInvariant.targetSelector +DA:58,4 FN:61,StdInvariant.targetSender FNDA:0,StdInvariant.targetSender DA:62,0 @@ -1462,9 +1462,9 @@ FN:104,StdInvariant.targetInterfaces FNDA:0,StdInvariant.targetInterfaces DA:105,0 FNF:18 -FNH:0 +FNH:2 LF:18 -LH:0 +LH:2 BRF:0 BRH:0 end_of_record @@ -1610,6 +1610,108 @@ BRH:0 end_of_record TN: SF:node_modules/forge-std/src/StdStorage.sol +FN:237,stdStorage.sigs +FNDA:0,stdStorage.sigs +DA:238,0 +FN:241,stdStorage.find +FNDA:0,stdStorage.find +DA:242,0 +FN:245,stdStorage.target +FNDA:0,stdStorage.target +DA:246,0 +FN:249,stdStorage.sig +FNDA:0,stdStorage.sig +DA:250,0 +FN:253,stdStorage.sig +FNDA:0,stdStorage.sig +DA:254,0 +FN:257,stdStorage.with_key +FNDA:0,stdStorage.with_key +DA:258,0 +FN:261,stdStorage.with_key +FNDA:0,stdStorage.with_key +DA:262,0 +FN:265,stdStorage.with_key +FNDA:0,stdStorage.with_key +DA:266,0 +FN:269,stdStorage.depth +FNDA:0,stdStorage.depth +DA:270,0 +FN:273,stdStorage.checked_write +FNDA:0,stdStorage.checked_write +DA:274,0 +FN:277,stdStorage.checked_write +FNDA:0,stdStorage.checked_write +DA:278,0 +FN:281,stdStorage.checked_write_int +FNDA:0,stdStorage.checked_write_int +DA:282,0 +FN:285,stdStorage.checked_write +FNDA:0,stdStorage.checked_write +DA:286,0 +DA:289,0 +DA:291,0 +FN:294,stdStorage.checked_write +FNDA:0,stdStorage.checked_write +DA:295,0 +DA:296,0 +DA:297,0 +DA:298,0 +DA:300,0 +DA:301,0 +BRDA:301,0,0,- +BRDA:301,0,1,- +DA:302,0 +DA:304,0 +DA:306,0 +DA:308,0 +DA:309,0 +DA:311,0 +DA:313,0 +BRDA:313,1,0,- +BRDA:313,1,1,- +DA:314,0 +BRDA:314,2,0,- +BRDA:314,2,1,- +DA:319,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:323,0 +FN:326,stdStorage.read_bytes32 +FNDA:0,stdStorage.read_bytes32 +DA:327,0 +FN:330,stdStorage.read_bool +FNDA:0,stdStorage.read_bool +DA:331,0 +FN:334,stdStorage.read_address +FNDA:0,stdStorage.read_address +DA:335,0 +FN:338,stdStorage.read_uint +FNDA:0,stdStorage.read_uint +DA:339,0 +FN:342,stdStorage.read_int +FNDA:0,stdStorage.read_int +DA:343,0 +FN:346,stdStorage.parent +FNDA:0,stdStorage.parent +DA:347,0 +FN:350,stdStorage.root +FNDA:0,stdStorage.root +DA:351,0 +FN:355,stdStorage.bytesToBytes32 +FNDA:0,stdStorage.bytesToBytes32 +DA:356,0 +DA:358,0 +DA:359,0 +DA:360,0 +DA:362,0 +FN:366,stdStorage.flatten +FNDA:0,stdStorage.flatten +DA:367,0 +DA:368,0 +DA:369,0 +DA:376,0 FN:22,stdStorageSafe.sigs FNDA:0,stdStorageSafe.sigs DA:23,0 @@ -1780,108 +1882,6 @@ DA:221,0 DA:222,0 DA:223,0 DA:230,0 -FN:237,stdStorage.sigs -FNDA:0,stdStorage.sigs -DA:238,0 -FN:241,stdStorage.find -FNDA:0,stdStorage.find -DA:242,0 -FN:245,stdStorage.target -FNDA:0,stdStorage.target -DA:246,0 -FN:249,stdStorage.sig -FNDA:0,stdStorage.sig -DA:250,0 -FN:253,stdStorage.sig -FNDA:0,stdStorage.sig -DA:254,0 -FN:257,stdStorage.with_key -FNDA:0,stdStorage.with_key -DA:258,0 -FN:261,stdStorage.with_key -FNDA:0,stdStorage.with_key -DA:262,0 -FN:265,stdStorage.with_key -FNDA:0,stdStorage.with_key -DA:266,0 -FN:269,stdStorage.depth -FNDA:0,stdStorage.depth -DA:270,0 -FN:273,stdStorage.checked_write -FNDA:0,stdStorage.checked_write -DA:274,0 -FN:277,stdStorage.checked_write -FNDA:0,stdStorage.checked_write -DA:278,0 -FN:281,stdStorage.checked_write_int -FNDA:0,stdStorage.checked_write_int -DA:282,0 -FN:285,stdStorage.checked_write -FNDA:0,stdStorage.checked_write -DA:286,0 -DA:289,0 -DA:291,0 -FN:294,stdStorage.checked_write -FNDA:0,stdStorage.checked_write -DA:295,0 -DA:296,0 -DA:297,0 -DA:298,0 -DA:300,0 -DA:301,0 -BRDA:301,0,0,- -BRDA:301,0,1,- -DA:302,0 -DA:304,0 -DA:306,0 -DA:308,0 -DA:309,0 -DA:311,0 -DA:313,0 -BRDA:313,1,0,- -BRDA:313,1,1,- -DA:314,0 -BRDA:314,2,0,- -BRDA:314,2,1,- -DA:319,0 -DA:320,0 -DA:321,0 -DA:322,0 -DA:323,0 -FN:326,stdStorage.read_bytes32 -FNDA:0,stdStorage.read_bytes32 -DA:327,0 -FN:330,stdStorage.read_bool -FNDA:0,stdStorage.read_bool -DA:331,0 -FN:334,stdStorage.read_address -FNDA:0,stdStorage.read_address -DA:335,0 -FN:338,stdStorage.read_uint -FNDA:0,stdStorage.read_uint -DA:339,0 -FN:342,stdStorage.read_int -FNDA:0,stdStorage.read_int -DA:343,0 -FN:346,stdStorage.parent -FNDA:0,stdStorage.parent -DA:347,0 -FN:350,stdStorage.root -FNDA:0,stdStorage.root -DA:351,0 -FN:355,stdStorage.bytesToBytes32 -FNDA:0,stdStorage.bytesToBytes32 -DA:356,0 -DA:358,0 -DA:359,0 -DA:360,0 -DA:362,0 -FN:366,stdStorage.flatten -FNDA:0,stdStorage.flatten -DA:367,0 -DA:368,0 -DA:369,0 -DA:376,0 FNF:42 FNH:0 LF:154 @@ -2135,12 +2135,12 @@ end_of_record TN: SF:node_modules/forge-std/src/StdUtils.sol FN:33,StdUtils._bound -FNDA:0,StdUtils._bound -DA:34,0 +FNDA:4,StdUtils._bound +DA:34,4 BRDA:34,0,0,- -BRDA:34,0,1,- -DA:37,0 -BRDA:37,1,0,- +BRDA:34,0,1,4 +DA:37,4 +BRDA:37,1,0,4 BRDA:37,1,1,- DA:39,0 DA:43,0 @@ -2168,9 +2168,9 @@ BRDA:55,7,0,- BRDA:55,7,1,- DA:56,0 FN:60,StdUtils.bound -FNDA:0,StdUtils.bound -DA:61,0 -DA:62,0 +FNDA:4,StdUtils.bound +DA:61,4 +DA:62,4 FN:65,StdUtils._bound FNDA:0,StdUtils._bound DA:66,0 @@ -2239,30 +2239,30 @@ FN:184,StdUtils.addressFromLast20Bytes FNDA:0,StdUtils.addressFromLast20Bytes DA:185,0 FN:191,StdUtils._castLogPayloadViewToPure -FNDA:0,StdUtils._castLogPayloadViewToPure -DA:197,0 +FNDA:4,StdUtils._castLogPayloadViewToPure +DA:197,4 FN:201,StdUtils._sendLogPayload -FNDA:0,StdUtils._sendLogPayload -DA:202,0 +FNDA:4,StdUtils._sendLogPayload +DA:202,4 FN:205,StdUtils._sendLogPayloadView -FNDA:0,StdUtils._sendLogPayloadView -DA:206,0 -DA:207,0 +FNDA:4,StdUtils._sendLogPayloadView +DA:206,4 +DA:207,4 FN:215,StdUtils.console2_log_StdUtils FNDA:0,StdUtils.console2_log_StdUtils DA:216,0 FN:219,StdUtils.console2_log_StdUtils -FNDA:0,StdUtils.console2_log_StdUtils -DA:220,0 +FNDA:4,StdUtils.console2_log_StdUtils +DA:220,4 FN:223,StdUtils.console2_log_StdUtils FNDA:0,StdUtils.console2_log_StdUtils DA:224,0 FNF:21 -FNH:0 +FNH:6 LF:59 -LH:0 +LH:9 BRF:22 -BRH:0 +BRH:2 end_of_record TN: SF:node_modules/forge-std/src/console.sol @@ -10286,7 +10286,7 @@ end_of_record TN: SF:node_modules/solady/src/utils/EIP712.sol FN:95,EIP712._domainNameAndVersionMayChange -FNDA:24,EIP712._domainNameAndVersionMayChange +FNDA:42,EIP712._domainNameAndVersionMayChange FN:102,EIP712._domainSeparator FNDA:0,EIP712._domainSeparator DA:103,0 @@ -10298,16 +10298,16 @@ DA:107,0 BRDA:107,1,0,- BRDA:107,1,1,- FN:124,EIP712._hashTypedData -FNDA:24,EIP712._hashTypedData -DA:126,24 +FNDA:42,EIP712._hashTypedData +DA:126,42 BRDA:126,2,0,- -BRDA:126,2,1,24 +BRDA:126,2,1,42 DA:127,0 -DA:129,24 -DA:130,24 +DA:129,42 +DA:130,42 BRDA:130,3,0,- -BRDA:130,3,1,24 -DA:138,24 +BRDA:130,3,1,42 +DA:138,42 FN:149,EIP712.eip712Domain FNDA:0,EIP712.eip712Domain DA:163,0 @@ -10329,10 +10329,10 @@ DA:184,0 DA:185,0 DA:195,0 FN:200,EIP712._cachedDomainSeparatorInvalidated -FNDA:24,EIP712._cachedDomainSeparatorInvalidated -DA:201,24 -DA:202,24 -DA:205,24 +FNDA:42,EIP712._cachedDomainSeparatorInvalidated +DA:201,42 +DA:202,42 +DA:205,42 FNF:6 FNH:3 LF:26 @@ -10543,7 +10543,7 @@ end_of_record TN: SF:node_modules/solady/src/utils/SignatureCheckerLib.sol FN:32,SignatureCheckerLib.isValidSignatureNow -FNDA:16,SignatureCheckerLib.isValidSignatureNow +FNDA:25,SignatureCheckerLib.isValidSignatureNow FN:120,SignatureCheckerLib.isValidSignatureNowCalldata FNDA:0,SignatureCheckerLib.isValidSignatureNowCalldata FN:208,SignatureCheckerLib.isValidSignatureNow @@ -10580,27 +10580,27 @@ BRH:0 end_of_record TN: SF:src/core/Attestation.sol -FN:11,Attestation.attest -FNDA:1538,Attestation.attest -DA:12,1538 -FN:15,Attestation.attest +FN:14,Attestation.attest +FNDA:1612,Attestation.attest +DA:15,1612 +FN:21,Attestation.attest FNDA:0,Attestation.attest -DA:16,0 -FN:19,Attestation.revoke -FNDA:512,Attestation.revoke -DA:20,512 -FN:23,Attestation.revoke +DA:22,0 +FN:28,Attestation.revoke +FNDA:514,Attestation.revoke +DA:29,514 +FN:35,Attestation.revoke FNDA:0,Attestation.revoke -DA:24,0 -FN:27,Attestation.findAttestation -FNDA:520,Attestation.findAttestation -DA:35,520 -FN:38,Attestation.findAttestations +DA:36,0 +FN:42,Attestation.findAttestation +FNDA:25,Attestation.findAttestation +DA:50,25 +FN:56,Attestation.findAttestations FNDA:0,Attestation.findAttestations -DA:46,0 -DA:47,0 -DA:48,0 -DA:49,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 FNF:6 FNH:3 LF:9 @@ -10610,386 +10610,555 @@ BRH:0 end_of_record TN: SF:src/core/AttestationManager.sol -FN:32,AttestationManager._revoke -FNDA:512,AttestationManager._revoke -DA:33,512 -DA:34,512 -DA:35,0 -FN:38,AttestationManager._revoke -FNDA:0,AttestationManager._revoke -DA:39,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:44,0 -DA:45,0 -BRDA:45,0,0,- -BRDA:45,0,1,- -DA:46,0 -BRDA:46,1,0,- -BRDA:46,1,1,- -DA:52,0 -FN:55,AttestationManager._attest -FNDA:1542,AttestationManager._attest -DA:62,1542 -DA:63,1542 -DA:65,518 -DA:66,518 -FN:69,AttestationManager._attest -FNDA:4,AttestationManager._attest -DA:76,4 -DA:77,4 -DA:80,4 -DA:81,4 -DA:82,8 -DA:83,8 -DA:89,8 -BRDA:89,2,0,4 -BRDA:89,2,1,4 -DA:90,4 -BRDA:90,3,0,- -BRDA:90,3,1,4 -DA:93,4 -DA:94,4 -FN:97,AttestationManager._storeAttestation -FNDA:1550,AttestationManager._storeAttestation -DA:105,1550 -DA:107,1550 -BRDA:107,4,0,608 -BRDA:107,4,1,942 -DA:108,608 -DA:111,942 -DA:112,942 -DA:114,942 -BRDA:114,5,0,- -BRDA:114,5,1,942 -DA:115,0 -DA:117,942 -DA:120,942 -DA:121,942 -DA:123,942 -DA:134,526 -DA:136,526 -FN:139,AttestationManager._storeRevocation -FNDA:512,AttestationManager._storeRevocation -DA:146,512 -DA:149,512 -DA:152,512 -BRDA:152,6,0,512 -BRDA:152,6,1,- -DA:153,512 -DA:157,0 -BRDA:157,7,0,- -BRDA:157,7,1,- -DA:158,0 -DA:162,0 -BRDA:162,8,0,- -BRDA:162,8,1,- -DA:163,0 -DA:166,0 -DA:167,0 -DA:169,0 -FN:176,AttestationManager._getAttestation -FNDA:524,AttestationManager._getAttestation -DA:185,524 +FN:50,AttestationManager._attest +FNDA:1619,AttestationManager._attest +DA:57,1619 +DA:58,1619 +DA:60,83 +DA:61,83 +FN:72,AttestationManager._attest +FNDA:6,AttestationManager._attest +DA:79,6 +DA:80,6 +DA:82,6 +DA:83,6 +DA:84,12 +DA:86,12 +DA:94,12 +BRDA:94,0,0,6 +BRDA:94,0,1,6 +DA:95,6 +BRDA:95,1,0,- +BRDA:95,1,1,6 +DA:99,6 +DA:100,6 +FN:119,AttestationManager._storeAttestation +FNDA:1631,AttestationManager._storeAttestation +DA:128,1631 +BRDA:128,2,0,- +BRDA:128,2,1,1631 +DA:130,1631 +DA:132,1631 +BRDA:132,3,0,614 +BRDA:132,3,1,1017 +DA:133,614 +DA:136,1017 +DA:138,1017 +DA:140,1017 +BRDA:140,4,0,922 +BRDA:140,4,1,95 +DA:141,922 +DA:147,95 +DA:150,95 +DA:161,95 +DA:163,95 +FN:180,AttestationManager._revoke +FNDA:516,AttestationManager._revoke +DA:181,516 +DA:182,516 +DA:183,4 +FN:191,AttestationManager._revoke +FNDA:2,AttestationManager._revoke +DA:192,2 +DA:193,2 +DA:194,2 +DA:198,2 +DA:199,4 +DA:200,4 +DA:201,4 +BRDA:201,5,0,2 +BRDA:201,5,1,2 +DA:202,2 +BRDA:202,6,0,- +BRDA:202,6,1,2 +DA:206,2 +FN:216,AttestationManager._storeRevocation +FNDA:520,AttestationManager._storeRevocation +DA:223,520 +DA:227,520 +DA:228,520 +DA:231,520 +BRDA:231,7,0,512 +BRDA:231,7,1,8 +DA:232,512 +DA:236,8 +BRDA:236,8,0,- +BRDA:236,8,1,8 +DA:237,0 +DA:241,8 +BRDA:241,9,0,- +BRDA:241,9,1,8 +DA:242,0 +DA:246,8 +DA:248,8 +FN:255,AttestationManager._getAttestation +FNDA:32,AttestationManager._getAttestation +DA:264,32 FNF:7 -FNH:6 -LF:51 -LH:33 -BRF:18 -BRH:7 +FNH:7 +LF:50 +LH:48 +BRF:20 +BRH:15 end_of_record TN: SF:src/core/ModuleManager.sol -FN:37,ModuleManager.deployModule -FNDA:0,ModuleManager.deployModule -DA:48,0 -DA:49,0 -BRDA:49,0,0,- -BRDA:49,0,1,- -DA:53,0 -DA:56,0 -DA:62,0 -FN:65,ModuleManager.registerModule -FNDA:47,ModuleManager.registerModule -DA:72,47 -DA:73,47 -BRDA:73,1,0,2 -BRDA:73,1,1,45 -DA:75,45 -DA:81,44 -FN:84,ModuleManager._storeModuleRecord -FNDA:45,ModuleManager._storeModuleRecord -DA:94,45 -BRDA:94,2,0,- -BRDA:94,2,1,45 -DA:96,45 -BRDA:96,3,0,1 -BRDA:96,3,1,44 -DA:97,1 -DA:100,44 -BRDA:100,4,0,- -BRDA:100,4,1,44 -DA:103,44 -DA:107,44 -DA:110,44 -FNF:3 -FNH:2 -LF:16 -LH:11 -BRF:10 -BRH:6 +FN:41,ModuleManager.deployModule +FNDA:2,ModuleManager.deployModule +DA:51,2 +DA:52,2 +BRDA:52,0,0,- +BRDA:52,0,1,2 +DA:56,2 +DA:59,2 +DA:65,2 +FN:74,ModuleManager.calcModuleAddress +FNDA:1,ModuleManager.calcModuleAddress +DA:82,1 +FN:88,ModuleManager.registerModule +FNDA:146,ModuleManager.registerModule +DA:95,146 +DA:98,146 +BRDA:98,1,0,3 +BRDA:98,1,1,143 +DA:100,143 +DA:106,142 +FN:115,ModuleManager.deployViaFactory +FNDA:13,ModuleManager.deployViaFactory +DA:125,13 +DA:126,13 +BRDA:126,2,0,- +BRDA:126,2,1,13 +DA:128,13 +BRDA:128,3,0,1 +BRDA:128,3,1,12 +DA:131,12 +DA:132,12 +BRDA:132,4,0,8 +BRDA:132,4,1,4 +DA:134,4 +DA:135,3 +BRDA:135,5,0,1 +BRDA:135,5,1,2 +DA:136,2 +BRDA:136,6,0,1 +BRDA:136,6,1,1 +DA:138,1 +DA:146,1 +FN:152,ModuleManager._storeModuleRecord +FNDA:146,ModuleManager._storeModuleRecord +DA:162,146 +BRDA:162,7,0,- +BRDA:162,7,1,146 +DA:164,146 +BRDA:164,8,0,1 +BRDA:164,8,1,145 +DA:165,1 +DA:169,145 +BRDA:169,9,0,- +BRDA:169,9,1,145 +DA:172,145 +DA:176,145 +DA:179,145 +FN:185,ModuleManager.findModule +FNDA:1,ModuleManager.findModule +DA:190,1 +FNF:6 +FNH:6 +LF:28 +LH:28 +BRF:20 +BRH:16 end_of_record TN: SF:src/core/ResolverManager.sol -FN:37,ResolverManager.registerResolver -FNDA:42,ResolverManager.registerResolver -DA:43,42 -DA:44,42 -DA:47,42 -DA:50,42 -BRDA:50,0,0,- -BRDA:50,0,1,42 -DA:51,0 -DA:55,42 -DA:57,42 -FN:60,ResolverManager.setResolver -FNDA:0,ResolverManager.setResolver -DA:68,0 -DA:69,0 -DA:70,0 -FNF:2 -FNH:1 -LF:10 -LH:6 +FN:50,ResolverManager.registerResolver +FNDA:153,ResolverManager.registerResolver +DA:56,153 +DA:57,153 +DA:60,153 +DA:63,153 +BRDA:63,0,0,1 +BRDA:63,0,1,152 +DA:64,1 +DA:68,152 +DA:70,152 +FN:76,ResolverManager.setResolver +FNDA:2,ResolverManager.setResolver +DA:84,1 +DA:85,1 +DA:86,1 +FN:92,ResolverManager.findResolver +FNDA:0,ResolverManager.findResolver +DA:93,0 +FNF:3 +FNH:2 +LF:11 +LH:10 BRF:2 -BRH:1 +BRH:2 end_of_record TN: SF:src/core/SchemaManager.sol -FN:17,SchemaManager.registerSchema -FNDA:42,SchemaManager.registerSchema -DA:27,42 -DA:28,42 -DA:31,42 -DA:33,42 -BRDA:33,0,0,- -BRDA:33,0,1,42 -DA:36,42 -DA:38,42 -FNF:1 -FNH:1 -LF:6 -LH:6 +FN:29,SchemaManager.registerSchema +FNDA:91,SchemaManager.registerSchema +DA:37,91 +DA:38,91 +DA:41,91 +DA:43,91 +BRDA:43,0,0,- +BRDA:43,0,1,91 +DA:46,91 +DA:48,91 +FN:67,SchemaManager.findSchema +FNDA:24,SchemaManager.findSchema +DA:68,24 +FNF:2 +FNH:2 +LF:7 +LH:7 BRF:2 BRH:1 end_of_record TN: SF:src/core/SignedAttestation.sol -FN:19,SignedAttestation.attest -FNDA:8,SignedAttestation.attest -DA:28,8 -DA:29,8 -DA:30,8 -DA:31,8 -BRDA:31,0,0,4 -BRDA:31,0,1,4 -DA:33,4 -FN:36,SignedAttestation.attest -FNDA:8,SignedAttestation.attest -DA:44,8 -DA:45,8 -DA:46,8 -DA:47,8 -BRDA:47,1,0,4 -BRDA:47,1,1,4 -DA:49,4 -FN:52,SignedAttestation.revoke -FNDA:0,SignedAttestation.revoke -DA:59,0 -DA:60,0 -DA:61,0 -DA:62,0 -BRDA:62,2,0,- -BRDA:62,2,1,- -DA:64,0 -FN:67,SignedAttestation.revoke -FNDA:0,SignedAttestation.revoke -DA:74,0 -DA:75,0 -DA:76,0 -DA:77,0 -BRDA:77,3,0,- -BRDA:77,3,1,- -DA:79,0 -FN:82,SignedAttestation._domainNameAndVersion +FN:22,SignedAttestation.attest +FNDA:11,SignedAttestation.attest +DA:30,11 +DA:31,11 +DA:32,11 +DA:33,11 +BRDA:33,0,0,4 +BRDA:33,0,1,7 +DA:35,7 +FN:41,SignedAttestation.attest +FNDA:10,SignedAttestation.attest +DA:49,10 +DA:50,10 +DA:51,10 +DA:52,10 +BRDA:52,1,0,4 +BRDA:52,1,1,6 +DA:54,6 +FN:60,SignedAttestation.revoke +FNDA:2,SignedAttestation.revoke +DA:67,2 +DA:68,2 +DA:69,2 +DA:70,2 +BRDA:70,2,0,- +BRDA:70,2,1,2 +DA:72,2 +FN:78,SignedAttestation.revoke +FNDA:2,SignedAttestation.revoke +DA:85,2 +DA:86,2 +DA:87,2 +DA:88,2 +BRDA:88,3,0,- +BRDA:88,3,1,2 +DA:90,2 +FN:100,SignedAttestation._domainNameAndVersion FNDA:0,SignedAttestation._domainNameAndVersion -DA:89,0 -DA:90,0 -FN:93,SignedAttestation.getDigest -FNDA:4,SignedAttestation.getDigest -DA:101,4 -FN:104,SignedAttestation.getDigest -FNDA:4,SignedAttestation.getDigest -DA:112,4 -FNF:7 -FNH:4 -LF:24 -LH:12 +DA:107,0 +DA:108,0 +FN:111,SignedAttestation.getDigest +FNDA:7,SignedAttestation.getDigest +DA:119,7 +FN:122,SignedAttestation.getDigest +FNDA:6,SignedAttestation.getDigest +DA:130,6 +FN:133,SignedAttestation.getDigest +FNDA:2,SignedAttestation.getDigest +DA:141,2 +FN:144,SignedAttestation.getDigest +FNDA:2,SignedAttestation.getDigest +DA:152,2 +FNF:9 +FNH:8 +LF:26 +LH:24 BRF:8 -BRH:4 +BRH:6 end_of_record TN: SF:src/core/TrustManager.sol -FN:33,TrustManager.trustAttesters -FNDA:258,TrustManager.trustAttesters -DA:34,258 -DA:35,258 -DA:36,258 -DA:37,258 -BRDA:37,0,0,- -BRDA:37,0,1,258 -DA:38,258 -BRDA:38,1,0,1 -BRDA:38,1,1,257 -DA:41,257 -DA:43,257 -BRDA:43,2,0,- -BRDA:43,2,1,257 -DA:44,0 -DA:47,257 -DA:48,257 -DA:49,257 -DA:51,257 -DA:52,257 -DA:53,12627 -DA:54,12627 -BRDA:54,3,0,- -BRDA:54,3,1,12627 -DA:55,12627 -FN:59,TrustManager.getTrustedAttesters -FNDA:257,TrustManager.getTrustedAttesters -DA:60,257 -FN:63,TrustManager.getTrustedAttesters -FNDA:0,TrustManager.getTrustedAttesters -DA:68,257 -DA:69,257 -DA:70,257 -DA:71,257 -DA:73,257 -DA:75,12627 -FN:79,TrustManager.check +FN:37,TrustManager.trustAttesters +FNDA:259,TrustManager.trustAttesters +DA:38,259 +DA:39,259 +DA:40,259 +DA:41,259 +BRDA:41,0,0,- +BRDA:41,0,1,259 +DA:42,259 +BRDA:42,1,0,1 +BRDA:42,1,1,258 +DA:45,258 +DA:47,258 +BRDA:47,2,0,- +BRDA:47,2,1,258 +DA:48,0 +DA:51,258 +DA:52,258 +DA:53,258 +DA:55,258 +DA:56,258 +DA:57,12854 +DA:59,12854 +BRDA:59,3,0,- +BRDA:59,3,1,12854 +DA:60,12854 +DA:62,258 +FN:68,TrustManager.check FNDA:1,TrustManager.check -DA:80,1 -FN:83,TrustManager.checkForAccount +DA:69,1 +FN:75,TrustManager.checkForAccount FNDA:1,TrustManager.checkForAccount -DA:84,1 -FN:87,TrustManager.check -FNDA:1,TrustManager.check -DA:88,1 -FN:91,TrustManager.checkForAccount +DA:76,1 +FN:82,TrustManager.check +FNDA:4,TrustManager.check +DA:83,4 +FN:89,TrustManager.checkForAccount FNDA:1,TrustManager.checkForAccount -DA:99,1 -FN:102,TrustManager._check -FNDA:2,TrustManager._check -DA:103,2 -DA:105,2 -DA:106,2 -DA:107,2 -DA:110,2 -BRDA:110,4,0,- -BRDA:110,4,1,2 +DA:97,1 +FN:100,TrustManager._check +FNDA:7,TrustManager._check +DA:101,7 +DA:103,7 +DA:104,7 +DA:105,7 +DA:108,7 +BRDA:108,4,0,- +BRDA:108,4,1,7 +DA:109,0 +DA:113,7 +BRDA:113,5,0,3 +BRDA:113,5,1,4 +DA:114,3 +DA:115,3 +DA:116,3 +DA:121,4 +DA:122,4 +DA:123,4 +DA:124,0 +DA:125,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:131,0 +BRDA:131,6,0,- +BRDA:131,6,1,- +FN:142,TrustManager._requireValidAttestation +FNDA:7,TrustManager._requireValidAttestation +DA:149,7 +DA:150,7 +DA:151,7 +DA:152,7 +DA:167,7 +DA:168,7 +DA:169,7 +DA:170,7 +DA:171,7 +DA:172,7 +DA:173,7 +DA:177,7 +BRDA:177,7,0,4 +BRDA:177,7,1,3 +DA:178,4 +DA:182,3 +BRDA:182,8,0,- +BRDA:182,8,1,3 +DA:183,0 +DA:187,3 +BRDA:187,9,0,- +BRDA:187,9,1,3 +DA:188,0 +DA:192,3 +BRDA:192,10,0,1 +BRDA:192,10,1,2 +DA:193,1 +FN:200,TrustManager.findTrustedAttesters +FNDA:257,TrustManager.findTrustedAttesters +DA:205,257 +DA:206,257 +DA:207,257 +DA:208,257 +DA:209,257 +DA:211,257 +DA:213,12853 +FNF:8 +FNH:8 +LF:66 +LH:56 +BRF:22 +BRH:14 +end_of_record +TN: +SF:src/core/TrustManagerExternalAttesterList.sol +FN:9,TrustManagerExternalAttesterList.check +FNDA:0,TrustManagerExternalAttesterList.check +DA:10,0 +DA:13,0 +DA:14,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:29,0 +BRDA:29,0,0,- +BRDA:29,0,1,- +DA:30,0 +DA:33,0 +BRDA:33,1,0,- +BRDA:33,1,1,- +DA:34,0 +BRDA:34,2,0,- +BRDA:34,2,1,- +DA:35,0 +DA:39,0 +BRDA:39,3,0,- +BRDA:39,3,1,- +DA:40,0 +FN:44,TrustManagerExternalAttesterList.checkN +FNDA:0,TrustManagerExternalAttesterList.checkN +DA:53,0 +DA:54,0 +BRDA:54,4,0,- +BRDA:54,4,1,- +DA:55,0 +DA:58,0 +DA:59,0 +DA:61,0 +DA:62,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:81,0 +BRDA:81,5,0,- +BRDA:81,5,1,- +DA:82,0 +DA:85,0 +BRDA:85,6,0,- +BRDA:85,6,1,- +DA:86,0 +BRDA:86,7,0,- +BRDA:86,7,1,- +DA:87,0 +DA:91,0 +DA:93,0 +BRDA:93,8,0,- +BRDA:93,8,1,- +DA:94,0 +BRDA:94,9,0,- +BRDA:94,9,1,- +DA:96,0 +BRDA:96,10,0,- +BRDA:96,10,1,- +DA:97,0 +FN:100,TrustManagerExternalAttesterList.checkNUnsafe +FNDA:0,TrustManagerExternalAttesterList.checkNUnsafe +DA:109,0 +DA:110,0 +BRDA:110,11,0,- +BRDA:110,11,1,- DA:111,0 -DA:115,2 -BRDA:115,5,0,- -BRDA:115,5,1,2 -DA:116,0 +DA:114,0 +DA:115,0 DA:117,0 DA:118,0 -DA:123,2 -DA:124,2 -DA:125,2 -DA:126,0 -DA:127,0 -DA:129,0 +DA:120,0 +DA:121,0 +DA:122,0 DA:130,0 DA:131,0 +DA:132,0 DA:133,0 -BRDA:133,6,0,- -BRDA:133,6,1,- -FN:138,TrustManager._check -FNDA:2,TrustManager._check -DA:139,2 -DA:141,2 -DA:142,2 -DA:143,2 -DA:146,2 -BRDA:146,7,0,- -BRDA:146,7,1,2 +DA:134,0 +DA:137,0 +BRDA:137,12,0,- +BRDA:137,12,1,- +DA:138,0 +DA:139,0 +DA:142,0 +DA:144,0 +BRDA:144,13,0,- +BRDA:144,13,1,- +DA:145,0 +BRDA:145,14,0,- +BRDA:145,14,1,- +DA:146,0 DA:147,0 -DA:151,2 -BRDA:151,8,0,- -BRDA:151,8,1,2 +DA:151,0 +BRDA:151,15,0,- +BRDA:151,15,1,- DA:152,0 -DA:153,0 +BRDA:152,16,0,- +BRDA:152,16,1,- DA:154,0 -DA:159,2 -DA:160,2 -DA:161,2 -DA:162,0 -DA:163,0 -DA:165,0 -DA:166,0 -DA:167,0 -DA:169,0 -BRDA:169,9,0,- -BRDA:169,9,1,- -FN:174,TrustManager._requireValidAttestation -FNDA:2,TrustManager._requireValidAttestation -DA:182,2 -DA:183,2 -DA:184,2 -DA:185,2 -DA:187,2 -BRDA:187,10,0,2 -BRDA:187,10,1,- -DA:188,2 -DA:191,0 -BRDA:191,11,0,- -BRDA:191,11,1,- -DA:192,0 -DA:195,0 -BRDA:195,12,0,- -BRDA:195,12,1,- -DA:196,0 -DA:198,0 -BRDA:198,13,0,- -BRDA:198,13,1,- -DA:199,0 -FN:203,TrustManager._requireValidAttestation -FNDA:2,TrustManager._requireValidAttestation -DA:205,2 -DA:206,2 -DA:207,2 -DA:209,2 -BRDA:209,14,0,2 -BRDA:209,14,1,- -DA:210,2 -DA:213,0 -BRDA:213,15,0,- -BRDA:213,15,1,- -DA:214,0 -DA:217,0 -BRDA:217,16,0,- -BRDA:217,16,1,- -DA:218,0 -FNF:11 -FNH:10 -LF:86 -LH:55 -BRF:34 -BRH:11 +BRDA:154,17,0,- +BRDA:154,17,1,- +DA:155,0 +FNF:3 +FNH:0 +LF:67 +LH:0 +BRF:36 +BRH:0 +end_of_record +TN: +SF:src/external/examples/ERC7512Schema.sol +FN:49,ERC7512SchemaValidator.supportsInterface +FNDA:0,ERC7512SchemaValidator.supportsInterface +DA:50,0 +FN:53,ERC7512SchemaValidator.validateSchema +FNDA:0,ERC7512SchemaValidator.validateSchema +DA:59,0 +DA:60,0 +BRDA:60,0,0,- +BRDA:60,0,1,- +DA:61,0 +DA:63,0 +BRDA:63,1,0,- +BRDA:63,1,1,- +DA:64,0 +DA:67,0 +FN:72,ERC7512SchemaValidator.validateSchema +FNDA:0,ERC7512SchemaValidator.validateSchema +FNF:3 +FNH:0 +LF:7 +LH:0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:src/external/examples/TokenizedResolver.sol +FN:15,TokenizedResolver.supportsInterface +FNDA:0,TokenizedResolver.supportsInterface +FN:17,TokenizedResolver.resolveAttestation +FNDA:0,TokenizedResolver.resolveAttestation +FN:25,TokenizedResolver.resolveAttestation +FNDA:0,TokenizedResolver.resolveAttestation +FN:33,TokenizedResolver.resolveRevocation +FNDA:0,TokenizedResolver.resolveRevocation +FN:41,TokenizedResolver.resolveRevocation +FNDA:0,TokenizedResolver.resolveRevocation +FN:49,TokenizedResolver.resolveModuleRegistration +FNDA:0,TokenizedResolver.resolveModuleRegistration +DA:60,0 +FNF:6 +FNH:0 +LF:1 +LH:0 +BRF:0 +BRH:0 end_of_record TN: SF:src/lib/AttestationLib.sol @@ -11010,10 +11179,10 @@ FNDA:0,AttestationLib.hash DA:56,0 FN:59,AttestationLib.hash FNDA:0,AttestationLib.hash -DA:67,0 -FN:70,AttestationLib.hash +DA:68,0 +FN:71,AttestationLib.hash FNDA:0,AttestationLib.hash -DA:78,0 +DA:80,0 FNF:7 FNH:0 LF:7 @@ -11026,9 +11195,9 @@ SF:src/lib/Helpers.sol FN:14,UIDLib.getUID FNDA:0,UIDLib.getUID DA:15,0 -FN:27,UIDLib.getUID +FN:29,UIDLib.getUID FNDA:0,UIDLib.getUID -DA:28,0 +DA:30,0 FNF:2 FNH:0 LF:2 @@ -11038,29 +11207,22 @@ BRH:0 end_of_record TN: SF:src/lib/ModuleDeploymentLib.sol -FN:17,ModuleDeploymentLib.codeHash -FNDA:0,ModuleDeploymentLib.codeHash -DA:20,0 -BRDA:20,0,0,- -DA:21,0 -FN:38,ModuleDeploymentLib.deploy +FN:19,ModuleDeploymentLib.deploy FNDA:0,ModuleDeploymentLib.deploy -DA:47,0 -DA:50,0 -DA:56,0 -DA:58,0 -BRDA:58,1,0,- -DA:59,0 -FN:75,ModuleDeploymentLib.calcAddress +DA:20,0 +DA:23,0 +DA:25,0 +BRDA:25,0,0,- +FN:41,ModuleDeploymentLib.calcAddress FNDA:0,ModuleDeploymentLib.calcAddress -DA:76,0 -DA:77,0 -DA:79,0 -FNF:3 +DA:42,0 +DA:43,0 +DA:45,0 +FNF:2 FNH:0 -LF:10 +LF:6 LH:0 -BRF:2 +BRF:1 BRH:0 end_of_record TN: @@ -11068,147 +11230,270 @@ SF:src/lib/ModuleTypeLib.sol FN:8,ModuleTypeLib.isType FNDA:0,ModuleTypeLib.isType DA:9,0 -FN:12,ModuleTypeLib.pack -FNDA:0,ModuleTypeLib.pack +FN:12,ModuleTypeLib.isType +FNDA:0,ModuleTypeLib.isType DA:13,0 -DA:14,0 -DA:15,0 -DA:16,0 +FN:16,ModuleTypeLib.pack +FNDA:0,ModuleTypeLib.pack +DA:17,0 DA:18,0 -FN:21,ModuleTypeLib.packCalldata -FNDA:0,ModuleTypeLib.packCalldata -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -BRDA:29,0,0,- -BRDA:29,0,1,- -DA:30,0 -DA:32,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +BRDA:22,0,0,- +BRDA:22,0,1,- +DA:23,0 +DA:25,0 FNF:3 FNH:0 -LF:12 +LF:10 LH:0 BRF:2 BRH:0 end_of_record TN: SF:src/lib/StubLib.sol -FN:12,StubLib.requireExternalSchemaValidation +FN:20,StubLib.requireExternalSchemaValidation FNDA:0,StubLib.requireExternalSchemaValidation -DA:20,0 -BRDA:20,0,0,- -BRDA:20,0,1,- -DA:22,0 -DA:25,0 -DA:26,0 -BRDA:24,1,0,- -BRDA:24,1,1,- DA:28,0 -FN:32,StubLib.requireExternalSchemaValidation +BRDA:28,0,0,- +BRDA:28,0,1,- +DA:30,0 +DA:33,0 +DA:34,0 +BRDA:32,1,0,- +BRDA:32,1,1,- +DA:36,0 +FN:40,StubLib.requireExternalSchemaValidation FNDA:0,StubLib.requireExternalSchemaValidation -DA:40,0 -BRDA:40,2,0,- -BRDA:40,2,1,- -DA:42,0 -DA:45,0 -DA:46,0 -BRDA:44,3,0,- -BRDA:44,3,1,- DA:48,0 -FN:52,StubLib.requireExternalResolverOnAttestation +BRDA:48,2,0,- +BRDA:48,2,1,- +DA:50,0 +DA:53,0 +DA:54,0 +BRDA:52,3,0,- +BRDA:52,3,1,- +DA:56,0 +FN:60,StubLib.requireExternalResolverOnAttestation FNDA:0,StubLib.requireExternalResolverOnAttestation -DA:58,0 -DA:60,0 -BRDA:60,4,0,- -BRDA:60,4,1,- -DA:61,0 -BRDA:61,5,0,- -BRDA:61,5,1,- -DA:62,0 -FN:66,StubLib.requireExternalResolverOnAttestation +DA:66,0 +DA:68,0 +BRDA:68,4,0,- +BRDA:68,4,1,- +DA:69,0 +BRDA:69,5,0,- +BRDA:69,5,1,- +DA:70,0 +FN:74,StubLib.requireExternalResolverOnAttestation FNDA:0,StubLib.requireExternalResolverOnAttestation -DA:72,0 -DA:74,0 -BRDA:74,6,0,- -BRDA:74,6,1,- -DA:76,0 -BRDA:76,7,0,- -BRDA:76,7,1,- -DA:77,0 -FN:81,StubLib.requireExternalResolverOnRevocation -FNDA:0,StubLib.requireExternalResolverOnRevocation -DA:87,0 -DA:89,0 -BRDA:89,8,0,- -BRDA:89,8,1,- -DA:90,0 -BRDA:90,9,0,- -BRDA:90,9,1,- -DA:91,0 -FN:95,StubLib.requireExternalResolverOnRevocation -FNDA:0,StubLib.requireExternalResolverOnRevocation -DA:101,0 -DA:103,0 -BRDA:103,10,0,- -BRDA:103,10,1,- -DA:105,0 -BRDA:105,11,0,- -BRDA:105,11,1,- -DA:106,0 -FN:110,StubLib.requireExternalResolverOnModuleRegistration -FNDA:0,StubLib.requireExternalResolverOnModuleRegistration +DA:80,0 +DA:82,0 +BRDA:82,6,0,- +BRDA:82,6,1,- +DA:84,0 +BRDA:84,7,0,- +BRDA:84,7,1,- +DA:85,0 +FN:89,StubLib.tryExternalResolverOnRevocation +FNDA:0,StubLib.tryExternalResolverOnRevocation +DA:96,0 +DA:98,0 +BRDA:98,8,0,- +BRDA:98,8,1,- +DA:99,0 +FN:107,StubLib.tryExternalResolverOnRevocation +FNDA:0,StubLib.tryExternalResolverOnRevocation +DA:114,0 DA:116,0 -DA:118,0 -BRDA:118,12,0,- -BRDA:118,12,1,- -DA:120,0 -BRDA:120,13,0,- -BRDA:120,13,1,- -DA:121,0 +BRDA:116,9,0,- +BRDA:116,9,1,- +DA:117,0 +FN:125,StubLib.requireExternalResolverOnModuleRegistration +FNDA:0,StubLib.requireExternalResolverOnModuleRegistration +DA:132,0 +DA:134,0 +BRDA:134,10,0,- +BRDA:134,10,1,- +DA:137,0 +BRDA:136,11,0,- +BRDA:136,11,1,- +DA:143,0 FNF:7 FNH:0 -LF:30 +LF:28 LH:0 -BRF:28 +BRF:24 BRH:0 end_of_record TN: SF:test/Base.t.sol -FN:44,BaseTest.setUp -FNDA:0,BaseTest.setUp -DA:45,0 -DA:46,0 -DA:48,0 -DA:49,0 -DA:51,0 -DA:52,0 -DA:54,0 -DA:55,0 -DA:57,0 -DA:58,0 -DA:60,0 -DA:61,0 -DA:63,0 -DA:64,0 -DA:66,0 -DA:67,0 -DA:69,0 -DA:70,0 -DA:71,0 -DA:73,0 -FN:82,BaseTest.initDefaultEnv -FNDA:0,BaseTest.initDefaultEnv -DA:83,0 -DA:84,0 -DA:85,0 -DA:86,0 -DA:90,0 -DA:91,0 +FN:45,BaseTest.setUp +FNDA:4,BaseTest.setUp +DA:46,4 +DA:47,4 +DA:49,4 +DA:50,4 +DA:52,4 +DA:53,4 +DA:54,4 +DA:56,4 +DA:57,4 +DA:59,4 +DA:60,4 +DA:62,4 +DA:63,4 +DA:65,4 +DA:66,4 +DA:68,4 +DA:69,4 +DA:71,4 +DA:72,4 +DA:73,4 +DA:75,4 +FN:84,BaseTest.initDefaultEnv +FNDA:4,BaseTest.initDefaultEnv +DA:85,4 +DA:86,4 +DA:87,4 +DA:88,4 +DA:92,4 +DA:93,4 +DA:94,4 +DA:95,4 +DA:97,4 +DA:104,4 +DA:105,4 FNF:2 -FNH:0 -LF:26 -LH:0 +FNH:2 +LF:32 +LH:32 +BRF:0 +BRH:0 +end_of_record +TN: +SF:test/ModuleRegistration.t.sol +FN:10,Factory.setReturnAddress +FNDA:3,Factory.setReturnAddress +DA:11,3 +FN:14,Factory.deployFn +FNDA:3,Factory.deployFn +DA:15,3 +FNF:2 +FNH:2 +LF:2 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: +SF:test/invariance/Handler.t.sol +FN:25,Handler.handle_registerResolver +FNDA:4,Handler.handle_registerResolver +DA:26,84 +DA:27,84 +FN:30,Handler.handle_setResolver +FNDA:0,Handler.handle_setResolver +DA:31,0 +DA:32,0 +DA:34,0 +FN:37,Handler._pickRandomSchemaUID +FNDA:4,Handler._pickRandomSchemaUID +DA:38,4 +DA:39,4 +DA:40,4 +DA:41,4 +DA:42,4 +DA:43,4 +DA:45,4 +FN:48,Handler._pickRandomResolverUID +FNDA:16,Handler._pickRandomResolverUID +DA:49,16 +DA:50,16 +DA:51,16 +DA:52,16 +DA:53,16 +DA:54,16 +DA:56,16 +FN:59,Handler.handle_registerSchema +FNDA:4,Handler.handle_registerSchema +DA:60,24 +DA:61,24 +DA:63,24 +FN:66,Handler.handle_registerModule +FNDA:4,Handler.handle_registerModule +DA:74,8 +DA:75,8 +DA:77,8 +FN:80,Handler._pickTypes +FNDA:4,Handler._pickTypes +DA:81,4 +DA:82,4 +DA:83,4 +DA:84,4 +FN:87,Handler.handle_attest +FNDA:4,Handler.handle_attest +DA:96,4 +DA:97,4 +DA:99,4 +DA:100,4 +DA:102,4 +FN:105,Handler.handle_attests +FNDA:0,Handler.handle_attests +DA:120,0 +DA:122,0 +FN:125,Handler.handle_registerModuleWithFactory +FNDA:8,Handler.handle_registerModuleWithFactory +DA:132,8 +DA:133,8 +DA:134,8 +FNF:10 +FNH:8 +LF:39 +LH:34 +BRF:0 +BRH:0 +end_of_record +TN: +SF:test/invariance/invariant_ImmutableData.t.sol +FN:9,ImmutableData.setUp +FNDA:4,ImmutableData.setUp +DA:10,4 +DA:11,4 +DA:13,4 +DA:14,4 +DA:15,4 +DA:17,4 +DA:18,4 +DA:19,4 +DA:20,4 +DA:21,4 +DA:22,4 +DA:24,4 +DA:27,4 +DA:28,4 +FN:31,ImmutableData.invariant_resolver_immutable +FNDA:0,ImmutableData.invariant_resolver_immutable +FN:33,ImmutableData.invariant_schema_immutable +FNDA:0,ImmutableData.invariant_schema_immutable +DA:34,0 +DA:35,0 +FN:38,ImmutableData.invariant_attestation_immutable +FNDA:0,ImmutableData.invariant_attestation_immutable +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +FN:47,ImmutableData.invariant_balance +FNDA:0,ImmutableData.invariant_balance +DA:48,0 +FNF:5 +FNH:1 +LF:23 +LH:14 BRF:0 BRH:0 end_of_record @@ -11228,13 +11513,30 @@ BRF:2 BRH:2 end_of_record TN: +SF:test/mocks/MockFactory.sol +FN:5,MockFactory.deploy +FNDA:0,MockFactory.deploy +DA:8,0 +DA:9,0 +BRDA:9,0,0,- +FNF:1 +FNH:0 +LF:2 +LH:0 +BRF:1 +BRH:0 +end_of_record +TN: SF:test/mocks/MockModule.sol -FN:5,MockModule.foo +FN:17,MockModule.foo FNDA:0,MockModule.foo -DA:6,0 -FNF:1 +DA:18,0 +FN:11,MockModuleWithArgs.readValue +FNDA:0,MockModuleWithArgs.readValue +DA:12,0 +FNF:2 FNH:0 -LF:1 +LF:2 LH:0 BRF:0 BRH:0 @@ -11242,55 +11544,57 @@ end_of_record TN: SF:test/mocks/MockResolver.sol FN:17,MockResolver.reset -FNDA:2,MockResolver.reset -DA:18,2 -DA:19,2 -DA:20,2 +FNDA:14,MockResolver.reset +DA:18,14 +DA:19,14 +DA:20,14 FN:23,MockResolver.supportsInterface -FNDA:42,MockResolver.supportsInterface -DA:24,42 -BRDA:24,0,0,42 +FNDA:155,MockResolver.supportsInterface +DA:24,155 +BRDA:24,0,0,155 BRDA:24,0,1,- FN:27,MockResolver.resolveAttestation -FNDA:0,MockResolver.resolveAttestation -DA:33,0 -DA:34,0 +FNDA:87,MockResolver.resolveAttestation +DA:33,87 +DA:34,87 FN:37,MockResolver.resolveAttestation -FNDA:0,MockResolver.resolveAttestation -DA:43,0 -DA:44,0 +FNDA:18,MockResolver.resolveAttestation +DA:43,18 +DA:44,18 FN:47,MockResolver.resolveRevocation -FNDA:0,MockResolver.resolveRevocation -DA:53,0 +FNDA:12,MockResolver.resolveRevocation +DA:53,12 DA:54,0 -FN:57,MockResolver.resolveRevocation -FNDA:0,MockResolver.resolveRevocation -DA:63,0 -DA:64,0 -FN:67,MockResolver.resolveModuleRegistration -FNDA:0,MockResolver.resolveModuleRegistration -DA:73,0 -DA:74,0 +DA:55,0 +FN:58,MockResolver.resolveRevocation +FNDA:14,MockResolver.resolveRevocation +DA:64,14 +DA:65,0 +DA:66,0 +FN:69,MockResolver.resolveModuleRegistration +FNDA:8,MockResolver.resolveModuleRegistration +DA:79,8 +DA:80,8 FNF:7 -FNH:2 -LF:14 -LH:4 +FNH:7 +LF:16 +LH:12 BRF:2 BRH:1 end_of_record TN: SF:test/mocks/MockSchemaValidator.sol FN:13,MockSchemaValidator.supportsInterface -FNDA:42,MockSchemaValidator.supportsInterface -DA:14,42 -BRDA:14,0,0,42 +FNDA:89,MockSchemaValidator.supportsInterface +DA:14,89 +BRDA:14,0,0,89 BRDA:14,0,1,- FN:17,MockSchemaValidator.validateSchema -FNDA:518,MockSchemaValidator.validateSchema -DA:23,518 +FNDA:83,MockSchemaValidator.validateSchema +DA:23,83 FN:26,MockSchemaValidator.validateSchema -FNDA:4,MockSchemaValidator.validateSchema -DA:32,4 +FNDA:6,MockSchemaValidator.validateSchema +DA:32,6 FNF:3 FNH:3 LF:3 diff --git a/src/IRegistry.sol b/src/IRegistry.sol index afab6aa5..e319ba48 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -8,6 +8,7 @@ import { ModuleType, ModuleRecord, ResolverUID, + ResolverRecord, RevocationRequest, SchemaUID, SchemaRecord @@ -81,7 +82,7 @@ interface IRegistry is IERC7484 { * Get trusted attester for a specific smartAccount * @param smartAccount The address of the smartAccount */ - function getTrustedAttesters(address smartAccount) + function findTrustedAttesters(address smartAccount) external view returns (address[] memory attesters); @@ -286,6 +287,27 @@ interface IRegistry is IERC7484 { payable returns (address moduleAddr); + /** + * Registry can use other factories to deploy the module + */ + function deployViaFactory( + address factory, + bytes calldata callOnFactory, + bytes calldata metadata, + ResolverUID resolverUID + ) + external + payable + returns (address moduleAddress); + + /** + * Already deployed module addresses can be registered on the registry + * @param resolverUID The resolverUID to be used for the module + * @param moduleAddress The address of the module to be registered + * @param metadata The metadata to be stored on the registry. + * This field is optional, and might be used by the module developer to store additional + * information about the module or facilitate business logic with the Resolver stub + */ function registerModule( ResolverUID resolverUID, address moduleAddress, @@ -293,6 +315,13 @@ interface IRegistry is IERC7484 { ) external; + /** + * in conjunction with the deployModule() function, this function let's you + * predict the address of a CREATE2 module deployment + * @param salt CREATE2 salt + * @param initCode module initcode + * @return moduleAddress counterfactual address of the module deployment + */ function calcModuleAddress( bytes32 salt, bytes calldata initCode @@ -301,7 +330,11 @@ interface IRegistry is IERC7484 { view returns (address); - function getRegisteredModule(address moduleAddress) + /** + * Getter function to get the stored ModuleRecord for a specific module address. + * @param moduleAddress The address of the module + */ + function findModule(address moduleAddress) external view returns (ModuleRecord memory moduleRecord); @@ -317,6 +350,16 @@ interface IRegistry is IERC7484 { error InvalidSchema(); error InvalidSchemaValidator(IExternalSchemaValidator validator); + /** + * Register Schema and (optional) external IExternalSchemaValidator + * Schemas describe the structure of the data of attestations + * every attestation made on this registry, will reference a SchemaUID to + * make it possible to decode attestation data in human readable form + * @param schema ABI schema used to encode attestations that are made with this schema + * @param validator (optional) external schema validator that will be used to validate attestations. + * use address(0), if you dont need an external validator + * @return uid SchemaUID of the registered schema + */ function registerSchema( string calldata schema, IExternalSchemaValidator validator // OPTIONAL @@ -324,7 +367,10 @@ interface IRegistry is IERC7484 { external returns (SchemaUID uid); - function findSchema(SchemaUID uid) external returns (SchemaRecord memory record); + /** + * getter function to retrieve SchemaRecord + */ + function findSchema(SchemaUID uid) external view returns (SchemaRecord memory record); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Manage Resolvers */ @@ -338,6 +384,8 @@ interface IRegistry is IERC7484 { function setResolver(ResolverUID uid, IExternalResolver resolver) external; + function findResolver(ResolverUID uid) external view returns (ResolverRecord memory record); + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Stub Errors */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ diff --git a/src/core/Attestation.sol b/src/core/Attestation.sol index d5b997d1..e1f2cbe3 100644 --- a/src/core/Attestation.sol +++ b/src/core/Attestation.sol @@ -8,22 +8,37 @@ import { AttestationManager } from "./AttestationManager.sol"; import { IRegistry } from "../IRegistry.sol"; abstract contract Attestation is IRegistry, AttestationManager { + /** + * @inheritdoc IRegistry + */ function attest(SchemaUID schemaUID, AttestationRequest calldata request) external { _attest(msg.sender, schemaUID, request); } + /** + * @inheritdoc IRegistry + */ function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external { _attest(msg.sender, schemaUID, requests); } + /** + * @inheritdoc IRegistry + */ function revoke(RevocationRequest calldata request) external { _revoke(msg.sender, request); } + /** + * @inheritdoc IRegistry + */ function revoke(RevocationRequest[] calldata requests) external { _revoke(msg.sender, requests); } + /** + * @inheritdoc IRegistry + */ function findAttestation( address module, address attester @@ -35,6 +50,9 @@ abstract contract Attestation is IRegistry, AttestationManager { attestation = _getAttestation(module, attester); } + /** + * @inheritdoc IRegistry + */ function findAttestations( address module, address[] calldata attesters diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 3b29f968..2b764a18 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -180,8 +180,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, function _revoke(address attester, RevocationRequest calldata request) internal { (AttestationRecord memory record, ResolverUID resolverUID) = _storeRevocation(attester, request); - // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? - record.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); + record.tryExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); } /** @@ -204,9 +203,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, } // No schema validation required during revocation. the attestation data was already checked against - - // TODO: what if this fails? it would stop attesters from revoking. Is this wanted behavior? - records.requireExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); + records.tryExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); } /** diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 506ec83a..e3b1f7b5 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -12,7 +12,9 @@ import { IRegistry } from "../IRegistry.sol"; /** * @title Module * - * @dev The Module contract is responsible for handling the registration, storage and retrieval of modules on the Registry. This contract inherits from the IModule interface + * @dev The Module contract is responsible for handling the registration, + * storage and retrieval of modules on the Registry. + * This contract inherits from the IModule interface * * @dev The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract * that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, @@ -33,6 +35,9 @@ abstract contract ModuleManager is IRegistry, ResolverManager { mapping(address moduleAddress => ModuleRecord moduleRecord) internal _moduleAddrToRecords; + /** + * @inheritdoc IRegistry + */ function deployModule( bytes32 salt, ResolverUID resolverUID, @@ -63,6 +68,9 @@ abstract contract ModuleManager is IRegistry, ResolverManager { }); } + /** + * @inheritdoc IRegistry + */ function calcModuleAddress( bytes32 salt, bytes calldata initCode @@ -74,6 +82,9 @@ abstract contract ModuleManager is IRegistry, ResolverManager { return initCode.calcAddress(salt); } + /** + * @inheritdoc IRegistry + */ function registerModule( ResolverUID resolverUID, address moduleAddress, @@ -98,6 +109,9 @@ abstract contract ModuleManager is IRegistry, ResolverManager { }); } + /** + * @inheritdoc IRegistry + */ function deployViaFactory( address factory, bytes calldata callOnFactory, @@ -165,7 +179,10 @@ abstract contract ModuleManager is IRegistry, ResolverManager { emit ModuleRegistration(moduleAddress, sender, ResolverUID.unwrap(resolverUID)); } - function getRegisteredModule(address moduleAddress) + /** + * @inheritdoc IRegistry + */ + function findModule(address moduleAddress) external view returns (ModuleRecord memory moduleRecord) diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index 36fd9be3..e7b98696 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -10,7 +10,7 @@ import { IRegistry } from "../IRegistry.sol"; abstract contract ResolverManager is IRegistry { using UIDLib for ResolverRecord; - mapping(ResolverUID uid => ResolverRecord resolver) public resolvers; + mapping(ResolverUID uid => ResolverRecord resolver) internal resolvers; /** * @dev Modifier to require that the caller is the owner of a resolver @@ -44,6 +44,9 @@ abstract contract ResolverManager is IRegistry { _; } + /** + * @inheritdoc IRegistry + */ function registerResolver(IExternalResolver resolver) external onlyResolver(resolver) @@ -67,6 +70,9 @@ abstract contract ResolverManager is IRegistry { emit NewResolver(uid, address(resolver)); } + /** + * @inheritdoc IRegistry + */ function setResolver( ResolverUID uid, IExternalResolver resolver @@ -79,4 +85,11 @@ abstract contract ResolverManager is IRegistry { referrer.resolver = resolver; emit NewResolver(uid, address(resolver)); } + + /** + * @inheritdoc IRegistry + */ + function findResolver(ResolverUID uid) external view returns (ResolverRecord memory) { + return resolvers[uid]; + } } diff --git a/src/core/SchemaManager.sol b/src/core/SchemaManager.sol index b85f4fc8..6702e08c 100644 --- a/src/core/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -23,6 +23,9 @@ abstract contract SchemaManager is IRegistry { mapping(SchemaUID uid => SchemaRecord schemaRecord) internal schemas; + /** + * @inheritdoc IRegistry + */ function registerSchema( string calldata schema, IExternalSchemaValidator validator // OPTIONAL @@ -58,6 +61,9 @@ abstract contract SchemaManager is IRegistry { _; } + /** + * @inheritdoc IRegistry + */ function findSchema(SchemaUID uid) external view override returns (SchemaRecord memory) { return schemas[uid]; } diff --git a/src/core/SignedAttestation.sol b/src/core/SignedAttestation.sol index 5bba8c4b..0e62161f 100644 --- a/src/core/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -17,10 +17,7 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { mapping(address attester => uint256 nonce) public attesterNonce; /** - * Attestation can be made on any SchemaUID. - * @dev the registry, will forward your AttestationRequest.Data to the SchemaManager to - * check if the schema exists. - * @param schemaUID The SchemaUID of the schema to be attested. + * @inheritdoc IRegistry */ function attest( SchemaUID schemaUID, @@ -38,6 +35,9 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { _attest({ attester: attester, schemaUID: schemaUID, request: request }); } + /** + * @inheritdoc IRegistry + */ function attest( SchemaUID schemaUID, address attester, @@ -54,6 +54,9 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { _attest({ attester: attester, schemaUID: schemaUID, requests: requests }); } + /** + * @inheritdoc IRegistry + */ function revoke( address attester, RevocationRequest calldata request, @@ -69,6 +72,9 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { _revoke({ attester: attester, request: request }); } + /** + * @inheritdoc IRegistry + */ function revoke( address attester, RevocationRequest[] calldata requests, @@ -84,6 +90,13 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { _revoke({ attester: attester, requests: requests }); } + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* EIP712 Digest Helpers */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /** + * override thats used by Solady's EIP712 cache (constructor) + */ function _domainNameAndVersion() internal view diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 7f3df26c..544128e3 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -8,7 +8,8 @@ import { TrustedAttesterRecord } from "../DataTypes.sol"; import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE, ZERO_ADDRESS } from "../Common.sol"; -import { IRegistry } from "../IRegistry.sol"; +// solhint-disable-next-line no-unused-import +import { IRegistry, IERC7484 } from "../IRegistry.sol"; import { TrustManagerExternalAttesterList } from "./TrustManagerExternalAttesterList.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; @@ -29,7 +30,10 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { mapping(address account => TrustedAttesterRecord attesters) internal _accountToAttester; - // Deliberately using memory here, so we can sort the array + /** + * @inheritdoc IRegistry + * @dev delibrately using memory here, so we can sort the array + */ function trustAttesters(uint8 threshold, address[] memory attesters) external { uint256 attestersLength = attesters.length; attesters.sort(); @@ -58,18 +62,30 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { emit NewTrustedAttesters(); } + /** + * @inheritdoc IERC7484 + */ function check(address module) external view { _check(msg.sender, module, ZERO_MODULE_TYPE); } + /** + * @inheritdoc IERC7484 + */ function checkForAccount(address smartAccount, address module) external view { _check(smartAccount, module, ZERO_MODULE_TYPE); } + /** + * @inheritdoc IERC7484 + */ function check(address module, ModuleType moduleType) external view { _check(msg.sender, module, moduleType); } + /** + * @inheritdoc IERC7484 + */ function checkForAccount( address smartAccount, address module, @@ -81,6 +97,13 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { _check(smartAccount, module, moduleType); } + /** + * Internal helper function to check for module's security attestations on behalf of a SmartAccount + * will use registy's storage to get the trusted attester(s) of a smart account, and check if the module was attested + * @param smartAccount the smart account to check for + * @param module address of the module to check + * @param moduleType (optional param), setting moduleType = 0, will ignore moduleTypes in attestations + */ function _check(address smartAccount, address module, ModuleType moduleType) internal view { TrustedAttesterRecord storage trustedAttesters = _accountToAttester[smartAccount]; // SLOAD from one slot @@ -178,7 +201,10 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { } } - function getTrustedAttesters(address smartAccount) + /** + * @inheritdoc IRegistry + */ + function findTrustedAttesters(address smartAccount) public view returns (address[] memory attesters) diff --git a/src/lib/ModuleTypeLib.sol b/src/lib/ModuleTypeLib.sol index 013d01cc..efe7a614 100644 --- a/src/lib/ModuleTypeLib.sol +++ b/src/lib/ModuleTypeLib.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.21; import { PackedModuleTypes, ModuleType } from "../DataTypes.sol"; -import { LibSort } from "solady/utils/LibSort.sol"; import { IRegistry } from "../IRegistry.sol"; library ModuleTypeLib { diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index ff5d85a5..7f3caf50 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -7,9 +7,16 @@ import { IExternalResolver } from "../external/IExternalResolver.sol"; import { ZERO_ADDRESS, ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; +/** + * @title StubLib + * @dev A library that interacts with IExternalResolver and IExternalSchemaValidator + */ library StubLib { event ResolverRevocationError(IExternalResolver resolver); + /** + * @notice if Schema Validator is set, it will call validateSchema() on the validator + */ function requireExternalSchemaValidation( AttestationRecord memory attestationRecord, SchemaRecord storage schema @@ -79,7 +86,7 @@ library StubLib { } } - function requireExternalResolverOnRevocation( + function tryExternalResolverOnRevocation( AttestationRecord memory attestationRecord, ResolverRecord storage resolver ) @@ -97,7 +104,7 @@ library StubLib { } } - function requireExternalResolverOnRevocation( + function tryExternalResolverOnRevocation( AttestationRecord[] memory attestationRecords, ResolverRecord storage resolver ) diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index b22e4826..e38616b2 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -23,7 +23,7 @@ contract ModuleRegistrationTest is BaseTest { bytes memory bytecode = type(MockModule).creationCode; address moduleAddr = registry.deployModule(salt, defaultResolverUID, bytecode, ""); - ModuleRecord memory record = registry.getRegisteredModule(moduleAddr); + ModuleRecord memory record = registry.findModule(moduleAddr); assertTrue(record.resolverUID == defaultResolverUID); } diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol index 3a85959e..1df8297e 100644 --- a/test/TrustDelegation.t.sol +++ b/test/TrustDelegation.t.sol @@ -26,7 +26,7 @@ contract TrustTest is AttestationTest { address[] memory trustedAttesters = new address[](1); trustedAttesters[0] = address(attester1.addr); registry.trustAttesters(1, trustedAttesters); - address[] memory result = registry.getTrustedAttesters(smartAccount1.addr); + address[] memory result = registry.findTrustedAttesters(smartAccount1.addr); assertEq(result.length, 1); assertEq(result[0], address(attester1.addr)); } @@ -46,7 +46,7 @@ contract TrustTest is AttestationTest { registry.trustAttesters(uint8(attesters.length), attesters); // It should set. // It should emit event. - address[] memory result = registry.getTrustedAttesters(smartAccount1.addr); + address[] memory result = registry.findTrustedAttesters(smartAccount1.addr); assertEq(result.length, attesters.length); for (uint256 i; i < attesters.length; i++) { diff --git a/test/invariance/Handler.t.sol b/test/invariance/Handler.t.sol index 5594ce24..01040345 100644 --- a/test/invariance/Handler.t.sol +++ b/test/invariance/Handler.t.sol @@ -102,6 +102,26 @@ contract Handler is CommonBase, StdCheats, StdUtils { REGISTRY.attest(uid, request); } + function handle_attests( + uint256 randResolv, + bytes calldata bytecode, + bytes calldata metadata, + uint256 randomSchemaUID, + AttestationRequest[] memory requests + ) + public + { + // for (uint256 i = 0; i < requests.length; i++) { + // bound(requests[i].expirationTime, block.timestamp + 1, type(uint48).max); + // requests[i].moduleTypes = _pickTypes(); + // handle_registerModule(randResolv, requests[i].moduleAddr, bytecode, metadata); + // } + // + SchemaUID uid = _pickRandomSchemaUID(randomSchemaUID); + + REGISTRY.attest(uid, requests); + } + function handle_registerModuleWithFactory( uint256 randomResolverNr, bytes calldata bytecode, diff --git a/test/invariance/invariant_ImmutableData.t.sol b/test/invariance/invariant_ImmutableData.t.sol index ba08feea..98ae32ec 100644 --- a/test/invariance/invariant_ImmutableData.t.sol +++ b/test/invariance/invariant_ImmutableData.t.sol @@ -20,6 +20,7 @@ contract ImmutableData is BaseTest { targetSelectors[2] = Handler.handle_setResolver.selector; targetSelectors[3] = Handler.handle_registerModule.selector; targetSelectors[4] = Handler.handle_attest.selector; + // targetSelectors[5] = Handler.handle_attests.selector; targetSelectors[5] = Handler.handle_registerModuleWithFactory.selector; // targetSelectors[3] = Handler.handle_revoke.selector; From 4c35e249831451a761920c52d7b4610af52df080 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 13 Feb 2024 14:02:03 +0700 Subject: [PATCH 50/84] chore: rename vars & gas savings --- .gas-snapshot | 106 ++++++++++++------------ src/DataTypes.sol | 2 +- src/core/AttestationManager.sol | 22 ++--- src/core/ModuleManager.sol | 20 +++-- src/core/TrustManager.sol | 46 +++++----- src/external/examples/ERC7512Schema.sol | 13 ++- src/lib/StubLib.sol | 32 +++---- 7 files changed, 125 insertions(+), 116 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 22e92d2e..a64c9c03 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,64 +1,64 @@ -AttestationTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23722, ~: 23351) -AttestationTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16632, ~: 16589) -AttestationTest:test_WhenAttestingWithNoAttestationData() (gas: 275754) -AttestationTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18340, ~: 18746) -AttestationTest:test_WhenAttesting_ShouldCallResolver() (gas: 180377) -AttestationTest:test_WhenReAttestingToARevokedAttestation() (gas: 338657) -AttestationTest:test_WhenRevokingAttestationThatDoesntExist(address) (runs: 256, μ: 28076, ~: 28076) -AttestationTest:test_WhenRevokingWithValidECDSA() (gas: 242527) -AttestationTest:test_WhenRevokingWithValidECDSAMulti() (gas: 409188) +AttestationTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23693, ~: 23329) +AttestationTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16592, ~: 16549) +AttestationTest:test_WhenAttestingWithNoAttestationData() (gas: 275817) +AttestationTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18309, ~: 18706) +AttestationTest:test_WhenAttesting_ShouldCallResolver() (gas: 180325) +AttestationTest:test_WhenReAttestingToARevokedAttestation() (gas: 336670) +AttestationTest:test_WhenRevokingAttestationThatDoesntExist(address) (runs: 256, μ: 28098, ~: 28098) +AttestationTest:test_WhenRevokingWithValidECDSA() (gas: 240190) +AttestationTest:test_WhenRevokingWithValidECDSAMulti() (gas: 409757) AttestationTest:test_WhenTokensAreNotPaid() (gas: 210) AttestationTest:test_WhenTokensArePaid() (gas: 232) -AttestationTest:test_WhenUsingInvalidECDSA() (gas: 51781) -AttestationTest:test_WhenUsingInvalidECDSAMulti() (gas: 60278) -AttestationTest:test_WhenUsingInvalidERC1271() (gas: 45423) -AttestationTest:test_WhenUsingInvalidERC1271Multi() (gas: 51256) -AttestationTest:test_WhenUsingValidECDSA() (gas: 224301) -AttestationTest:test_WhenUsingValidECDSAMulti() (gas: 385255) -AttestationTest:test_WhenUsingValidERC1271() (gas: 211515) -AttestationTest:test_WhenUsingValidERC1271Multi() (gas: 371452) -ImmutableData:invariant_attestation_immutable() (runs: 1200, calls: 24000, reverts: 1442) -ImmutableData:invariant_balance() (runs: 1200, calls: 24000, reverts: 1442) -ImmutableData:invariant_resolver_immutable() (runs: 1200, calls: 24000, reverts: 1442) -ImmutableData:invariant_schema_immutable() (runs: 1200, calls: 24000, reverts: 1442) -ModuleRegistrationTest:test_WhenDeployingViaRegistry() (gas: 130550) -ModuleRegistrationTest:test_WhenDeployingViaRegistryWithArgs() (gas: 153624) +AttestationTest:test_WhenUsingInvalidECDSA() (gas: 51597) +AttestationTest:test_WhenUsingInvalidECDSAMulti() (gas: 59947) +AttestationTest:test_WhenUsingInvalidERC1271() (gas: 45292) +AttestationTest:test_WhenUsingInvalidERC1271Multi() (gas: 51080) +AttestationTest:test_WhenUsingValidECDSA() (gas: 224126) +AttestationTest:test_WhenUsingValidECDSAMulti() (gas: 385144) +AttestationTest:test_WhenUsingValidERC1271() (gas: 211437) +AttestationTest:test_WhenUsingValidERC1271Multi() (gas: 371364) +ImmutableData:invariant_attestation_immutable() (runs: 1200, calls: 24000, reverts: 7296) +ImmutableData:invariant_balance() (runs: 1200, calls: 24000, reverts: 7296) +ImmutableData:invariant_resolver_immutable() (runs: 1200, calls: 24000, reverts: 7296) +ImmutableData:invariant_schema_immutable() (runs: 1200, calls: 24000, reverts: 7296) +ModuleRegistrationTest:test_WhenDeployingViaRegistry() (gas: 130600) +ModuleRegistrationTest:test_WhenDeployingViaRegistryWithArgs() (gas: 153719) ModuleRegistrationTest:test_WhenRegisteringAModuleOnAInValidResolverUID() (gas: 74823) -ModuleRegistrationTest:test_WhenRegisteringAModuleOnAValidResolverUID() (gas: 105879) +ModuleRegistrationTest:test_WhenRegisteringAModuleOnAValidResolverUID() (gas: 105929) ModuleRegistrationTest:test_WhenRegisteringAModuleOnAnInvalidResolverUID() (gas: 81381) -ModuleRegistrationTest:test_WhenRegisteringTwoModulesWithTheSameBytecode() (gas: 108658) -ModuleRegistrationTest:test_WhenRegisteringViaFactory() (gas: 223948) +ModuleRegistrationTest:test_WhenRegisteringTwoModulesWithTheSameBytecode() (gas: 108758) +ModuleRegistrationTest:test_WhenRegisteringViaFactory() (gas: 223998) ModuleRegistrationTest:test_WhenUsingInvalidFactory() (gas: 18581) ModuleRegistrationTest:test_WhenUsingRegistryASFactory() (gas: 15736) -ResolverTest:test_WhenNewResolver() (gas: 306513) -ResolverTest:test_WhenResolverAlreadyRegistered() (gas: 304095) -ResolverTest:test_WhenUsingAuthorizedAccount() (gas: 554370) -ResolverTest:test_WhenUsingUnauthorizedAccount() (gas: 306456) -SchemaValidationTest:test_WhenSchemaAlreadyRegistered() (gas: 107982) -SchemaValidationTest:test_WhenSchemaNew() (gas: 53734) +ResolverTest:test_WhenNewResolver() (gas: 285233) +ResolverTest:test_WhenResolverAlreadyRegistered() (gas: 282816) +ResolverTest:test_WhenUsingAuthorizedAccount() (gas: 511780) +ResolverTest:test_WhenUsingUnauthorizedAccount() (gas: 285133) +SchemaValidationTest:test_WhenSchemaAlreadyRegistered() (gas: 108072) +SchemaValidationTest:test_WhenSchemaNew() (gas: 53779) TrustTest:test_WhenAttesterSetButNoAttestationMade() (gas: 253) -TrustTest:test_WhenAttestersSetAndAllOk() (gas: 285372) +TrustTest:test_WhenAttestersSetAndAllOk() (gas: 285080) TrustTest:test_WhenAttestersSetButThresholdTooLow() (gas: 188) -TrustTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23743, ~: 23379) -TrustTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16610, ~: 16567) -TrustTest:test_WhenAttestingWithNoAttestationData() (gas: 275732) -TrustTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18362, ~: 18768) -TrustTest:test_WhenAttesting_ShouldCallResolver() (gas: 180444) -TrustTest:test_WhenNoAttestersSet() (gas: 39247) -TrustTest:test_WhenReAttestingToARevokedAttestation() (gas: 338679) -TrustTest:test_WhenRevokingAttestationThatDoesntExist(address) (runs: 256, μ: 28098, ~: 28098) -TrustTest:test_WhenRevokingWithValidECDSA() (gas: 242505) -TrustTest:test_WhenRevokingWithValidECDSAMulti() (gas: 409254) -TrustTest:test_WhenSupplyingManyAttesters(address[]) (runs: 256, μ: 1326591, ~: 1326627) -TrustTest:test_WhenSupplyingOneAttester() (gas: 42672) +TrustTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23718, ~: 23357) +TrustTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16570, ~: 16527) +TrustTest:test_WhenAttestingWithNoAttestationData() (gas: 275795) +TrustTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18331, ~: 18728) +TrustTest:test_WhenAttesting_ShouldCallResolver() (gas: 180392) +TrustTest:test_WhenNoAttestersSet() (gas: 39131) +TrustTest:test_WhenReAttestingToARevokedAttestation() (gas: 336692) +TrustTest:test_WhenRevokingAttestationThatDoesntExist(address) (runs: 256, μ: 28120, ~: 28120) +TrustTest:test_WhenRevokingWithValidECDSA() (gas: 240168) +TrustTest:test_WhenRevokingWithValidECDSAMulti() (gas: 409823) +TrustTest:test_WhenSupplyingManyAttesters(address[]) (runs: 256, μ: 1327355, ~: 1326333) +TrustTest:test_WhenSupplyingOneAttester() (gas: 42650) TrustTest:test_WhenSupplyingSameAttesterMultipleTimes() (gas: 13159) TrustTest:test_WhenTokensAreNotPaid() (gas: 210) TrustTest:test_WhenTokensArePaid() (gas: 232) -TrustTest:test_WhenUsingInvalidECDSA() (gas: 51803) -TrustTest:test_WhenUsingInvalidECDSAMulti() (gas: 60256) -TrustTest:test_WhenUsingInvalidERC1271() (gas: 45423) -TrustTest:test_WhenUsingInvalidERC1271Multi() (gas: 51345) -TrustTest:test_WhenUsingValidECDSA() (gas: 224301) -TrustTest:test_WhenUsingValidECDSAMulti() (gas: 385233) -TrustTest:test_WhenUsingValidERC1271() (gas: 211515) -TrustTest:test_WhenUsingValidERC1271Multi() (gas: 371430) \ No newline at end of file +TrustTest:test_WhenUsingInvalidECDSA() (gas: 51619) +TrustTest:test_WhenUsingInvalidECDSAMulti() (gas: 59925) +TrustTest:test_WhenUsingInvalidERC1271() (gas: 45292) +TrustTest:test_WhenUsingInvalidERC1271Multi() (gas: 51169) +TrustTest:test_WhenUsingValidECDSA() (gas: 224126) +TrustTest:test_WhenUsingValidECDSAMulti() (gas: 385122) +TrustTest:test_WhenUsingValidERC1271() (gas: 211437) +TrustTest:test_WhenUsingValidERC1271Multi() (gas: 371342) \ No newline at end of file diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 478c3413..a3a45538 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -43,7 +43,7 @@ struct TrustedAttesterRecord { uint8 attesterCount; // number of attesters in the linked list uint8 threshold; // minimum number of attesters required address attester; // first attester in linked list. (packed to save gas) - mapping(address attester => address linkedAttester) linkedAttesters; + mapping(address attester => address linkedAttester) linkedAttesters; // linked list } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 2b764a18..6fed50dc 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -57,8 +57,8 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, (AttestationRecord memory record, ResolverUID resolverUID) = _storeAttestation({ schemaUID: schemaUID, attester: attester, request: request }); - record.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); - record.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); + record.requireExternalSchemaValidation({ $schema: schemas[schemaUID] }); + record.requireExternalResolverOnAttestation({ $resolver: resolvers[resolverUID] }); } /** @@ -96,8 +96,8 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, } // Use StubLib to call schema Validation and resolver if needed - records.requireExternalSchemaValidation({ schema: schemas[schemaUID] }); - records.requireExternalResolverOnAttestation({ resolver: resolvers[resolverUID] }); + records.requireExternalSchemaValidation({ $schema: schemas[schemaUID] }); + records.requireExternalResolverOnAttestation({ $resolver: resolvers[resolverUID] }); } /** @@ -180,7 +180,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, function _revoke(address attester, RevocationRequest calldata request) internal { (AttestationRecord memory record, ResolverUID resolverUID) = _storeRevocation(attester, request); - record.tryExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); + record.tryExternalResolverOnRevocation({ $resolver: resolvers[resolverUID] }); } /** @@ -203,7 +203,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, } // No schema validation required during revocation. the attestation data was already checked against - records.tryExternalResolverOnRevocation({ resolver: resolvers[resolverUID] }); + records.tryExternalResolverOnRevocation({ $resolver: resolvers[resolverUID] }); } /** @@ -220,11 +220,11 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, internal returns (AttestationRecord memory record, ResolverUID resolverUID) { - AttestationRecord storage attestationStorage = + AttestationRecord storage $attestation = _moduleToAttesterToAttestations[request.moduleAddr][revoker]; // SLOAD entire record. This will later be passed to the resolver - record = attestationStorage; + record = $attestation; resolverUID = _moduleAddrToRecords[request.moduleAddr].resolverUID; // Ensure that we aren't attempting to revoke a non-existing attestation. @@ -243,7 +243,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, } // SSTORE revocation time to registry storage - attestationStorage.revocationTime = _time(); + $attestation.revocationTime = _time(); // set revocation time to NOW emit Revoked({ moduleAddr: request.moduleAddr, revoker: revoker, schema: record.schemaUID }); } @@ -259,8 +259,8 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, internal view override - returns (AttestationRecord storage) + returns (AttestationRecord storage $attestation) { - return _moduleToAttesterToAttestations[module][attester]; + $attestation = _moduleToAttesterToAttestations[module][attester]; } } diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index e3b1f7b5..d274b0ce 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -48,8 +48,8 @@ abstract contract ModuleManager is IRegistry, ResolverManager { payable returns (address moduleAddress) { - ResolverRecord storage resolver = resolvers[resolverUID]; - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); + ResolverRecord storage $resolver = resolvers[resolverUID]; + if ($resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver($resolver.resolver); // address predictedModuleAddress = code.calculateAddress(deployParams, salt); @@ -64,7 +64,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { }); record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, - resolver: resolver + $resolver: $resolver }); } @@ -92,10 +92,10 @@ abstract contract ModuleManager is IRegistry, ResolverManager { ) external { - ResolverRecord storage resolver = resolvers[resolverUID]; + ResolverRecord storage $resolver = resolvers[resolverUID]; // ensure that non-zero resolverUID was provided - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver(resolver.resolver); + if ($resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver($resolver.resolver); ModuleRecord memory record = _storeModuleRecord({ moduleAddress: moduleAddress, @@ -103,9 +103,11 @@ abstract contract ModuleManager is IRegistry, ResolverManager { resolverUID: resolverUID, metadata: metadata }); + + // resolve module registration record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, - resolver: resolver + $resolver: $resolver }); } @@ -122,8 +124,8 @@ abstract contract ModuleManager is IRegistry, ResolverManager { payable returns (address moduleAddress) { - ResolverRecord memory resolver = resolvers[resolverUID]; - if (resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolverUID(resolverUID); + ResolverRecord storage $resolver = resolvers[resolverUID]; + if ($resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolverUID(resolverUID); // prevent someone from calling a registry function pretending its a factory if (factory == address(this)) revert FactoryCallFailed(factory); // call external factory to deploy module @@ -145,7 +147,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, - resolver: resolver + $resolver: $resolver }); } diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 544128e3..3ada4fd4 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -42,22 +42,22 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { if (attesters.length != attestersLength) revert InvalidTrustedAttesterInput(); // sort attesters - TrustedAttesterRecord storage _att = _accountToAttester[msg.sender]; + TrustedAttesterRecord storage $trustedAttester = _accountToAttester[msg.sender]; // threshold cannot be greater than the number of attesters if (threshold > attestersLength) { threshold = uint8(attestersLength); } // - _att.attesterCount = uint8(attestersLength); - _att.threshold = threshold; - _att.attester = attesters[0]; + $trustedAttester.attesterCount = uint8(attestersLength); + $trustedAttester.threshold = threshold; + $trustedAttester.attester = attesters[0]; attestersLength--; for (uint256 i; i < attestersLength; i++) { address _attester = attesters[i]; // user could have set attester to address(0) if (_attester == ZERO_ADDRESS) revert InvalidTrustedAttesterInput(); - _att.linkedAttesters[_attester] = attesters[i + 1]; + $trustedAttester.linkedAttesters[_attester] = attesters[i + 1]; } emit NewTrustedAttesters(); } @@ -105,11 +105,11 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { * @param moduleType (optional param), setting moduleType = 0, will ignore moduleTypes in attestations */ function _check(address smartAccount, address module, ModuleType moduleType) internal view { - TrustedAttesterRecord storage trustedAttesters = _accountToAttester[smartAccount]; + TrustedAttesterRecord storage $trustedAttesters = _accountToAttester[smartAccount]; // SLOAD from one slot - uint256 attesterCount = trustedAttesters.attesterCount; - uint256 threshold = trustedAttesters.threshold; - address attester = trustedAttesters.attester; + uint256 attesterCount = $trustedAttesters.attesterCount; + uint256 threshold = $trustedAttesters.threshold; + address attester = $trustedAttesters.attester; // smart account has no trusted attesters set if (attester == ZERO_ADDRESS && threshold != 0) { @@ -118,22 +118,22 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { // smart account only has ONE trusted attester // use this condition to save gas else if (threshold == 1) { - AttestationRecord storage record = + AttestationRecord storage $attestation = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); + _requireValidAttestation(moduleType, $attestation); } // smart account has more than one trusted attester else { // loop though list and check if the attestation is valid - AttestationRecord storage record = + AttestationRecord storage $attestation = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); + _requireValidAttestation(moduleType, $attestation); for (uint256 i = 1; i < attesterCount; i++) { threshold--; // get next attester from linked List - attester = trustedAttesters.linkedAttesters[attester]; - record = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, record); + attester = $trustedAttesters.linkedAttesters[attester]; + $attestation = _getAttestation({ module: module, attester: attester }); + _requireValidAttestation(moduleType, $attestation); // if threshold reached, exit loop if (threshold == 0) return; } @@ -148,7 +148,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { */ function _requireValidAttestation( ModuleType expectedType, - AttestationRecord storage record + AttestationRecord storage $attestation ) internal view @@ -170,7 +170,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff - let slot := sload(record.slot) + let slot := sload($attestation.slot) attestedAt := and(mask, slot) slot := shr(48, slot) expirationTime := and(mask, slot) @@ -192,7 +192,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { // check if attestation has been revoked if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(record.attester); + revert RevokedAttestation($attestation.attester); } // if a expectedType is set, check if the attestation is for the correct module type // if no expectedType is set, module type is not checked @@ -209,15 +209,15 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { view returns (address[] memory attesters) { - TrustedAttesterRecord storage trustedAttesters = _accountToAttester[smartAccount]; - uint256 count = trustedAttesters.attesterCount; - address attester0 = trustedAttesters.attester; + TrustedAttesterRecord storage $trustedAttesters = _accountToAttester[smartAccount]; + uint256 count = $trustedAttesters.attesterCount; + address attester0 = $trustedAttesters.attester; attesters = new address[](count); attesters[0] = attester0; for (uint256 i = 1; i < count; i++) { // get next attester from linked List - attesters[i] = trustedAttesters.linkedAttesters[attesters[i - 1]]; + attesters[i] = $trustedAttesters.linkedAttesters[attesters[i - 1]]; } } } diff --git a/src/external/examples/ERC7512Schema.sol b/src/external/examples/ERC7512Schema.sol index 32fa083d..9b3fc36c 100644 --- a/src/external/examples/ERC7512Schema.sol +++ b/src/external/examples/ERC7512Schema.sol @@ -51,7 +51,7 @@ contract ERC7512SchemaValidator is IExternalSchemaValidator, ERC7512 { } function validateSchema(AttestationRecord calldata attestation) - external + public view override returns (bool valid) @@ -73,6 +73,13 @@ contract ERC7512SchemaValidator is IExternalSchemaValidator, ERC7512 { external view override - returns (bool) - { } + returns (bool valid) + { + uint256 length = attestations.length; + for (uint256 i = 0; i < length; i++) { + if (!validateSchema(attestations[i])) { + return false; + } + } + } } diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index 7f3caf50..3d7f3eba 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -19,15 +19,15 @@ library StubLib { */ function requireExternalSchemaValidation( AttestationRecord memory attestationRecord, - SchemaRecord storage schema + SchemaRecord storage $schema ) internal view { // only run this function if the selected schemaUID exists - if (schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); + if ($schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); // validate Schema - IExternalSchemaValidator validator = schema.validator; + IExternalSchemaValidator validator = $schema.validator; // if validator is set, call the validator if ( address(validator) != ZERO_ADDRESS @@ -39,15 +39,15 @@ library StubLib { function requireExternalSchemaValidation( AttestationRecord[] memory attestationRecords, - SchemaRecord storage schema + SchemaRecord storage $schema ) internal view { // only run this function if the selected schemaUID exists - if (schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); + if ($schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); // validate Schema - IExternalSchemaValidator validator = schema.validator; + IExternalSchemaValidator validator = $schema.validator; // if validator is set, call the validator if ( address(validator) != ZERO_ADDRESS @@ -59,11 +59,11 @@ library StubLib { function requireExternalResolverOnAttestation( AttestationRecord memory attestationRecord, - ResolverRecord storage resolver + ResolverRecord storage $resolver ) internal { - IExternalResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = $resolver.resolver; if (address(resolverContract) == ZERO_ADDRESS) return; if (resolverContract.resolveAttestation(attestationRecord) == false) { @@ -73,11 +73,11 @@ library StubLib { function requireExternalResolverOnAttestation( AttestationRecord[] memory attestationRecords, - ResolverRecord storage resolver + ResolverRecord storage $resolver ) internal { - IExternalResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = $resolver.resolver; if (address(resolverContract) == ZERO_ADDRESS) return; @@ -88,12 +88,12 @@ library StubLib { function tryExternalResolverOnRevocation( AttestationRecord memory attestationRecord, - ResolverRecord storage resolver + ResolverRecord storage $resolver ) internal returns (bool resolved) { - IExternalResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = $resolver.resolver; if (address(resolverContract) == ZERO_ADDRESS) return true; try resolverContract.resolveRevocation(attestationRecord) returns (bool _resolved) { @@ -106,12 +106,12 @@ library StubLib { function tryExternalResolverOnRevocation( AttestationRecord[] memory attestationRecords, - ResolverRecord storage resolver + ResolverRecord storage $resolver ) internal returns (bool resolved) { - IExternalResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = $resolver.resolver; if (address(resolverContract) == ZERO_ADDRESS) return true; try resolverContract.resolveRevocation(attestationRecords) returns (bool _resolved) { @@ -125,11 +125,11 @@ library StubLib { function requireExternalResolverOnModuleRegistration( ModuleRecord memory moduleRecord, address moduleAddress, - ResolverRecord memory resolver + ResolverRecord storage $resolver ) internal { - IExternalResolver resolverContract = resolver.resolver; + IExternalResolver resolverContract = $resolver.resolver; if (address(resolverContract) != ZERO_ADDRESS) return; From 1521cb69059b4c2d9d70c5e1484175ead919a962 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 13 Feb 2024 14:08:52 +0700 Subject: [PATCH 51/84] chore: remove unneeded code --- src/core/ResolverManager.sol | 9 +-------- src/core/TrustManagerExternalAttesterList.sol | 18 +++++++++--------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index e7b98696..97075076 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; -import { EMPTY_RESOLVER_UID, ZERO_ADDRESS } from "../Common.sol"; +import { ZERO_ADDRESS } from "../Common.sol"; import { IExternalResolver } from "../external/IExternalResolver.sol"; import { UIDLib } from "../lib/Helpers.sol"; import { IRegistry } from "../IRegistry.sol"; @@ -24,13 +24,6 @@ abstract contract ResolverManager is IRegistry { _; } - modifier notZero(ResolverUID uid) { - if (uid == EMPTY_RESOLVER_UID) { - revert InvalidResolverUID(uid); - } - _; - } - /** * If a resolver is not address(0), we check if it supports the IExternalResolver interface */ diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index 29e73506..a2b45569 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -7,7 +7,7 @@ import { IRegistry } from "../IRegistry.sol"; abstract contract TrustManagerExternalAttesterList is IRegistry { function check(address module, address attester) external view returns (uint256 attestedAt) { - AttestationRecord storage attestation = _getAttestation(module, attester); + AttestationRecord storage $attestation = _getAttestation(module, attester); // attestedAt = attestation.time; uint256 expirationTime; // = attestation.expirationTime; @@ -18,7 +18,7 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff - let times := sload(attestation.slot) + let times := sload($attestation.slot) attestedAt := and(mask, times) times := shr(48, times) expirationTime := and(mask, times) @@ -37,7 +37,7 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { } if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(attestation.attester); + revert RevokedAttestation($attestation.attester); } } @@ -59,7 +59,7 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { attestedAtArray = new uint256[](attestersLength); for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage attestation = _getAttestation(module, attesters[i]); + AttestationRecord storage $attestation = _getAttestation(module, attesters[i]); uint256 attestationTime; // = attestation.time; uint256 expirationTime; // = attestation.expirationTime; @@ -70,7 +70,7 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff - let times := sload(attestation.slot) + let times := sload($attestation.slot) attestationTime := and(mask, times) times := shr(48, times) expirationTime := and(mask, times) @@ -79,7 +79,7 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { } if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation(attestation.attester); + revert RevokedAttestation($attestation.attester); } if (expirationTime != ZERO_TIMESTAMP) { @@ -115,7 +115,7 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { attestedAtArray = new uint256[](attestersLength); for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage attestation = _getAttestation(module, attesters[i]); + AttestationRecord storage $attestation = _getAttestation(module, attesters[i]); uint256 attestationTime; // = attestation.time; uint256 expirationTime; // = attestation.expirationTime; @@ -126,7 +126,7 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff - let times := sload(attestation.slot) + let times := sload($attestation.slot) attestationTime := and(mask, times) times := shr(48, times) expirationTime := and(mask, times) @@ -162,5 +162,5 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { internal view virtual - returns (AttestationRecord storage attestation); + returns (AttestationRecord storage $attestation); } From 2b5ba6362e5a75dad5612ad07b0a798fe6764de7 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 13 Feb 2024 14:26:10 +0700 Subject: [PATCH 52/84] gas --- .gas-snapshot | 38 +++++++++++++++++++------------------- src/DataTypes.sol | 2 +- src/IRegistry.sol | 7 ++----- src/core/ModuleManager.sol | 9 ++++++--- src/core/SchemaManager.sol | 4 +++- src/core/TrustManager.sol | 3 +-- src/lib/AttestationLib.sol | 2 -- 7 files changed, 32 insertions(+), 33 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index a64c9c03..03f59103 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,9 +1,9 @@ -AttestationTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23693, ~: 23329) -AttestationTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16592, ~: 16549) -AttestationTest:test_WhenAttestingWithNoAttestationData() (gas: 275817) +AttestationTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23689, ~: 23329) +AttestationTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16593, ~: 16549) +AttestationTest:test_WhenAttestingWithNoAttestationData() (gas: 275618) AttestationTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18309, ~: 18706) AttestationTest:test_WhenAttesting_ShouldCallResolver() (gas: 180325) -AttestationTest:test_WhenReAttestingToARevokedAttestation() (gas: 336670) +AttestationTest:test_WhenReAttestingToARevokedAttestation() (gas: 336471) AttestationTest:test_WhenRevokingAttestationThatDoesntExist(address) (runs: 256, μ: 28098, ~: 28098) AttestationTest:test_WhenRevokingWithValidECDSA() (gas: 240190) AttestationTest:test_WhenRevokingWithValidECDSAMulti() (gas: 409757) @@ -17,19 +17,19 @@ AttestationTest:test_WhenUsingValidECDSA() (gas: 224126) AttestationTest:test_WhenUsingValidECDSAMulti() (gas: 385144) AttestationTest:test_WhenUsingValidERC1271() (gas: 211437) AttestationTest:test_WhenUsingValidERC1271Multi() (gas: 371364) -ImmutableData:invariant_attestation_immutable() (runs: 1200, calls: 24000, reverts: 7296) -ImmutableData:invariant_balance() (runs: 1200, calls: 24000, reverts: 7296) -ImmutableData:invariant_resolver_immutable() (runs: 1200, calls: 24000, reverts: 7296) -ImmutableData:invariant_schema_immutable() (runs: 1200, calls: 24000, reverts: 7296) -ModuleRegistrationTest:test_WhenDeployingViaRegistry() (gas: 130600) -ModuleRegistrationTest:test_WhenDeployingViaRegistryWithArgs() (gas: 153719) +ImmutableData:invariant_attestation_immutable() (runs: 1200, calls: 24000, reverts: 7425) +ImmutableData:invariant_balance() (runs: 1200, calls: 24000, reverts: 7425) +ImmutableData:invariant_resolver_immutable() (runs: 1200, calls: 24000, reverts: 7425) +ImmutableData:invariant_schema_immutable() (runs: 1200, calls: 24000, reverts: 7425) +ModuleRegistrationTest:test_WhenDeployingViaRegistry() (gas: 130398) +ModuleRegistrationTest:test_WhenDeployingViaRegistryWithArgs() (gas: 153516) ModuleRegistrationTest:test_WhenRegisteringAModuleOnAInValidResolverUID() (gas: 74823) -ModuleRegistrationTest:test_WhenRegisteringAModuleOnAValidResolverUID() (gas: 105929) +ModuleRegistrationTest:test_WhenRegisteringAModuleOnAValidResolverUID() (gas: 105730) ModuleRegistrationTest:test_WhenRegisteringAModuleOnAnInvalidResolverUID() (gas: 81381) -ModuleRegistrationTest:test_WhenRegisteringTwoModulesWithTheSameBytecode() (gas: 108758) -ModuleRegistrationTest:test_WhenRegisteringViaFactory() (gas: 223998) -ModuleRegistrationTest:test_WhenUsingInvalidFactory() (gas: 18581) -ModuleRegistrationTest:test_WhenUsingRegistryASFactory() (gas: 15736) +ModuleRegistrationTest:test_WhenRegisteringTwoModulesWithTheSameBytecode() (gas: 108559) +ModuleRegistrationTest:test_WhenRegisteringViaFactory() (gas: 219570) +ModuleRegistrationTest:test_WhenUsingInvalidFactory() (gas: 16406) +ModuleRegistrationTest:test_WhenUsingRegistryASFactory() (gas: 13561) ResolverTest:test_WhenNewResolver() (gas: 285233) ResolverTest:test_WhenResolverAlreadyRegistered() (gas: 282816) ResolverTest:test_WhenUsingAuthorizedAccount() (gas: 511780) @@ -41,15 +41,15 @@ TrustTest:test_WhenAttestersSetAndAllOk() (gas: 285080) TrustTest:test_WhenAttestersSetButThresholdTooLow() (gas: 188) TrustTest:test_WhenAttestingToNon_existingModule(address,uint48,bytes,uint32[]) (runs: 256, μ: 23718, ~: 23357) TrustTest:test_WhenAttestingWithExpirationTimeInThePast(address,bytes,uint32) (runs: 256, μ: 16570, ~: 16527) -TrustTest:test_WhenAttestingWithNoAttestationData() (gas: 275795) -TrustTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18331, ~: 18728) +TrustTest:test_WhenAttestingWithNoAttestationData() (gas: 275596) +TrustTest:test_WhenAttestingWithTooHighModuleType(address,uint48,bytes,uint32) (runs: 256, μ: 18480, ~: 18728) TrustTest:test_WhenAttesting_ShouldCallResolver() (gas: 180392) TrustTest:test_WhenNoAttestersSet() (gas: 39131) -TrustTest:test_WhenReAttestingToARevokedAttestation() (gas: 336692) +TrustTest:test_WhenReAttestingToARevokedAttestation() (gas: 336493) TrustTest:test_WhenRevokingAttestationThatDoesntExist(address) (runs: 256, μ: 28120, ~: 28120) TrustTest:test_WhenRevokingWithValidECDSA() (gas: 240168) TrustTest:test_WhenRevokingWithValidECDSAMulti() (gas: 409823) -TrustTest:test_WhenSupplyingManyAttesters(address[]) (runs: 256, μ: 1327355, ~: 1326333) +TrustTest:test_WhenSupplyingManyAttesters(address[]) (runs: 256, μ: 1326050, ~: 1326696) TrustTest:test_WhenSupplyingOneAttester() (gas: 42650) TrustTest:test_WhenSupplyingSameAttesterMultipleTimes() (gas: 13159) TrustTest:test_WhenTokensAreNotPaid() (gas: 210) diff --git a/src/DataTypes.sol b/src/DataTypes.sol index a3a45538..6049d2ea 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -14,10 +14,10 @@ struct AttestationRecord { uint48 expirationTime; // The time when the attestation expires (Unix timestamp). uint48 revocationTime; // The time when the attestation was revoked (Unix timestamp). PackedModuleTypes moduleTypes; // bit-wise encoded module types. See ModuleTypeLib - SchemaUID schemaUID; // The unique identifier of the schema. address moduleAddr; // The implementation address of the module that is being attested. address attester; // The attesting account. AttestationDataRef dataPointer; // SSTORE2 pointer to the attestation data. + SchemaUID schemaUID; // The unique identifier of the schema. } // Struct that represents Module artefact. diff --git a/src/IRegistry.sol b/src/IRegistry.sol index e319ba48..f847c07c 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -256,11 +256,7 @@ interface IRegistry is IERC7484 { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Event triggered when a module is deployed. event ModuleRegistration( - address indexed implementation, address indexed sender, bytes32 resolver - ); - event ModuleDeployed(address indexed implementation, bytes32 indexed salt, bytes32 resolver); - event ModuleDeployedExternalFactory( - address indexed implementation, address indexed factory, bytes32 resolver + address indexed implementation, address indexed sender, ResolverUID resolverUID ); error AlreadyRegistered(address module); @@ -355,6 +351,7 @@ interface IRegistry is IERC7484 { * Schemas describe the structure of the data of attestations * every attestation made on this registry, will reference a SchemaUID to * make it possible to decode attestation data in human readable form + * overrwriting a schema is not allowed, and will revert * @param schema ABI schema used to encode attestations that are made with this schema * @param validator (optional) external schema validator that will be used to validate attestations. * use address(0), if you dont need an external validator diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index d274b0ce..841bebf8 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -51,8 +51,6 @@ abstract contract ModuleManager is IRegistry, ResolverManager { ResolverRecord storage $resolver = resolvers[resolverUID]; if ($resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver($resolver.resolver); - // address predictedModuleAddress = code.calculateAddress(deployParams, salt); - moduleAddress = initCode.deploy(salt); // _storeModuleRecord() will check if module is already registered, // which should prevent reentry to any deploy function @@ -62,6 +60,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { resolverUID: resolverUID, metadata: metadata }); + record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, $resolver: $resolver @@ -178,7 +177,11 @@ abstract contract ModuleManager is IRegistry, ResolverManager { _moduleAddrToRecords[moduleAddress] = moduleRegistration; // Emit ModuleRegistration event - emit ModuleRegistration(moduleAddress, sender, ResolverUID.unwrap(resolverUID)); + emit ModuleRegistration({ + implementation: moduleAddress, + sender: sender, + resolverUID: resolverUID + }); } /** diff --git a/src/core/SchemaManager.sol b/src/core/SchemaManager.sol index 6702e08c..b4453502 100644 --- a/src/core/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -28,7 +28,7 @@ abstract contract SchemaManager is IRegistry { */ function registerSchema( string calldata schema, - IExternalSchemaValidator validator // OPTIONAL + IExternalSchemaValidator validator ) external onlySchemaValidator(validator) @@ -40,6 +40,8 @@ abstract contract SchemaManager is IRegistry { // Computing a unique ID for the schema using its properties uid = schemaRecord.getUID(); + // check if schema with this UID already exists. + // overwriting a schema is not allowed if (schemas[uid].registeredAt != ZERO_TIMESTAMP) revert SchemaAlreadyExists(uid); // Storing schema in the _schemas mapping diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 3ada4fd4..6c804700 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -36,18 +36,17 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { */ function trustAttesters(uint8 threshold, address[] memory attesters) external { uint256 attestersLength = attesters.length; + // sort attesters and remove duplicates attesters.sort(); attesters.uniquifySorted(); if (attestersLength == 0) revert InvalidTrustedAttesterInput(); if (attesters.length != attestersLength) revert InvalidTrustedAttesterInput(); - // sort attesters TrustedAttesterRecord storage $trustedAttester = _accountToAttester[msg.sender]; // threshold cannot be greater than the number of attesters if (threshold > attestersLength) { threshold = uint8(attestersLength); } - // $trustedAttester.attesterCount = uint8(attestersLength); $trustedAttester.threshold = threshold; $trustedAttester.attester = attesters[0]; diff --git a/src/lib/AttestationLib.sol b/src/lib/AttestationLib.sol index 4490d379..b8dd2e70 100644 --- a/src/lib/AttestationLib.sol +++ b/src/lib/AttestationLib.sol @@ -64,7 +64,6 @@ library AttestationLib { pure returns (bytes32 _hash) { - // TODO: check if this is correct _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); } @@ -76,7 +75,6 @@ library AttestationLib { pure returns (bytes32 _hash) { - // TODO: check if this is correct _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); } } From fa05695cd8fb0d7f71357d25bf344a2202fe944d Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 13 Feb 2024 14:30:54 +0700 Subject: [PATCH 53/84] bump version --- package.json | 8 +++---- pnpm-lock.yaml | 22 +++++++++---------- script/DeployRegistry.s.sol.bak | 2 +- src/Common.sol | 2 +- src/DataTypes.sol | 4 ++-- src/IRegistry.sol | 2 +- src/Registry.sol | 2 +- src/core/Attestation.sol | 2 +- src/core/AttestationManager.sol | 2 +- src/core/ModuleManager.sol | 2 +- src/core/ResolverManager.sol | 2 +- src/core/SchemaManager.sol | 2 +- src/core/SignedAttestation.sol | 2 +- src/core/TrustManager.sol | 2 +- src/core/TrustManagerExternalAttesterList.sol | 2 +- src/external/IExternalSchemaValidator.sol | 2 +- src/external/examples/ERC7512Schema.sol | 2 +- src/external/examples/ResolverBase.sol | 2 +- src/external/examples/TokenizedResolver.sol | 2 +- src/lib/AttestationLib.sol | 2 +- src/lib/Helpers.sol | 2 +- src/lib/ModuleDeploymentLib.sol | 2 +- src/lib/StubLib.sol | 2 +- test/Base.t.sol | 2 +- test/mocks/MockFactory.sol | 2 +- 25 files changed, 39 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index db876f74..a2c8c564 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,11 @@ "@openzeppelin/contracts": "5.0.1" }, "devDependencies": { - "ds-test": "github:dapphub/ds-test", - "forge-std": "github:foundry-rs/forge-std", + "ds-test": "github:dapphub/ds-test#e282159d5170298eb2455a6c05280ab5a73a4ef0", + "forge-std": "github:foundry-rs/forge-std#v1.7.6", "prettier": "^2.8.8", - "solmate": "github:transmissions11/solmate", - "solady": "github:vectorized/solady", + "solmate": "github:transmissions11/solmate#c892309933b25c03d32b1b0d674df7ae292ba925", + "solady": "github:vectorized/solady#9deb9ed36a27261a8745db5b7cd7f4cdc3b1cd4e", "solhint": "^4.1.1" }, "files": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5affa9bb..ab03e4a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,22 +11,22 @@ dependencies: devDependencies: ds-test: - specifier: github:dapphub/ds-test + specifier: github:dapphub/ds-test#e282159d5170298eb2455a6c05280ab5a73a4ef0 version: github.com/dapphub/ds-test/e282159d5170298eb2455a6c05280ab5a73a4ef0 forge-std: - specifier: github:foundry-rs/forge-std - version: github.com/foundry-rs/forge-std/4513bc2063f23c57bee6558799584b518d387a39 + specifier: github:foundry-rs/forge-std#v1.7.6 + version: github.com/foundry-rs/forge-std/ae570fec082bfe1c1f45b0acca4a2b4f84d345ce prettier: specifier: ^2.8.8 version: 2.8.8 solady: - specifier: github:vectorized/solady - version: github.com/vectorized/solady/330d9aceb8086f2ac85bb5dbfb9991642ae944e6 + specifier: github:vectorized/solady#9deb9ed36a27261a8745db5b7cd7f4cdc3b1cd4e + version: github.com/vectorized/solady/9deb9ed36a27261a8745db5b7cd7f4cdc3b1cd4e solhint: specifier: ^4.1.1 version: 4.1.1 solmate: - specifier: github:transmissions11/solmate + specifier: github:transmissions11/solmate#c892309933b25c03d32b1b0d674df7ae292ba925 version: github.com/transmissions11/solmate/c892309933b25c03d32b1b0d674df7ae292ba925 packages: @@ -724,8 +724,8 @@ packages: version: 1.0.0 dev: true - github.com/foundry-rs/forge-std/4513bc2063f23c57bee6558799584b518d387a39: - resolution: {tarball: https://codeload.github.com/foundry-rs/forge-std/tar.gz/4513bc2063f23c57bee6558799584b518d387a39} + github.com/foundry-rs/forge-std/ae570fec082bfe1c1f45b0acca4a2b4f84d345ce: + resolution: {tarball: https://codeload.github.com/foundry-rs/forge-std/tar.gz/ae570fec082bfe1c1f45b0acca4a2b4f84d345ce} name: forge-std version: 1.7.6 dev: true @@ -736,8 +736,8 @@ packages: version: 6.2.0 dev: true - github.com/vectorized/solady/330d9aceb8086f2ac85bb5dbfb9991642ae944e6: - resolution: {tarball: https://codeload.github.com/vectorized/solady/tar.gz/330d9aceb8086f2ac85bb5dbfb9991642ae944e6} + github.com/vectorized/solady/9deb9ed36a27261a8745db5b7cd7f4cdc3b1cd4e: + resolution: {tarball: https://codeload.github.com/vectorized/solady/tar.gz/9deb9ed36a27261a8745db5b7cd7f4cdc3b1cd4e} name: solady - version: 0.0.167 + version: 0.0.168 dev: true diff --git a/script/DeployRegistry.s.sol.bak b/script/DeployRegistry.s.sol.bak index 39228d8d..8b8fc4bd 100644 --- a/script/DeployRegistry.s.sol.bak +++ b/script/DeployRegistry.s.sol.bak @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { Script } from "forge-std/Script.sol"; import { RegistryTestTools, RegistryInstance } from "../test/utils/BaseUtils.sol"; diff --git a/src/Common.sol b/src/Common.sol index 4b29869d..e2f3ff77 100644 --- a/src/Common.sol +++ b/src/Common.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { ResolverUID, SchemaUID, AttestationDataRef, ModuleType } from "./DataTypes.sol"; diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 6049d2ea..9e425dd6 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { IExternalSchemaValidator } from "./external/IExternalSchemaValidator.sol"; import { IExternalResolver } from "./external/IExternalResolver.sol"; @@ -59,10 +59,10 @@ struct AttestationRequest { bytes data; // Custom attestation data. ModuleType[] moduleTypes; // optional: The type(s) of the module. } + /** * @dev A struct representing the arguments of the revocation request. */ - struct RevocationRequest { address moduleAddr; // The module address. } diff --git a/src/IRegistry.sol b/src/IRegistry.sol index f847c07c..7df601ca 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationDataRef, diff --git a/src/Registry.sol b/src/Registry.sol index 4f47ab4f..59d1dae8 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { SignedAttestation } from "./core/SignedAttestation.sol"; import { IRegistry } from "./IRegistry.sol"; diff --git a/src/core/Attestation.sol b/src/core/Attestation.sol index e1f2cbe3..53a7a99b 100644 --- a/src/core/Attestation.sol +++ b/src/core/Attestation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationRecord, AttestationRequest, RevocationRequest, SchemaUID diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 6fed50dc..da3e1473 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationDataRef, diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 841bebf8..5745ea19 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { ModuleDeploymentLib } from "../lib/ModuleDeploymentLib.sol"; import { StubLib } from "../lib/StubLib.sol"; diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index 97075076..3bd19011 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { ResolverRecord, ResolverUID } from "../DataTypes.sol"; import { ZERO_ADDRESS } from "../Common.sol"; diff --git a/src/core/SchemaManager.sol b/src/core/SchemaManager.sol index b4453502..7efee3e2 100644 --- a/src/core/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { SchemaRecord, SchemaUID } from "../DataTypes.sol"; import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; diff --git a/src/core/SignedAttestation.sol b/src/core/SignedAttestation.sol index 0e62161f..25e39daf 100644 --- a/src/core/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { Attestation } from "./Attestation.sol"; import { AttestationRequest, RevocationRequest, SchemaUID } from "../DataTypes.sol"; diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 6c804700..3bd1cccc 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationRecord, diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index a2b45569..0b1598c4 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationRecord } from "../DataTypes.sol"; import { ZERO_TIMESTAMP } from "../Common.sol"; diff --git a/src/external/IExternalSchemaValidator.sol b/src/external/IExternalSchemaValidator.sol index 68bd61dd..e43ba503 100644 --- a/src/external/IExternalSchemaValidator.sol +++ b/src/external/IExternalSchemaValidator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationRecord } from "../DataTypes.sol"; import { IERC165 } from "forge-std/interfaces/IERC165.sol"; diff --git a/src/external/examples/ERC7512Schema.sol b/src/external/examples/ERC7512Schema.sol index 9b3fc36c..4cf76a1d 100644 --- a/src/external/examples/ERC7512Schema.sol +++ b/src/external/examples/ERC7512Schema.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { IExternalSchemaValidator } from "../IExternalSchemaValidator.sol"; import { AttestationRecord, AttestationDataRef } from "../../DataTypes.sol"; diff --git a/src/external/examples/ResolverBase.sol b/src/external/examples/ResolverBase.sol index cd04a0f7..bd44f1b0 100644 --- a/src/external/examples/ResolverBase.sol +++ b/src/external/examples/ResolverBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { IExternalResolver } from "../IExternalResolver.sol"; import { IRegistry } from "../../IRegistry.sol"; diff --git a/src/external/examples/TokenizedResolver.sol b/src/external/examples/TokenizedResolver.sol index e11b72cd..79c45c07 100644 --- a/src/external/examples/TokenizedResolver.sol +++ b/src/external/examples/TokenizedResolver.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import "./ResolverBase.sol"; import "forge-std/interfaces/IERC20.sol"; diff --git a/src/lib/AttestationLib.sol b/src/lib/AttestationLib.sol index b8dd2e70..88d51378 100644 --- a/src/lib/AttestationLib.sol +++ b/src/lib/AttestationLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationRequest, RevocationRequest, AttestationDataRef } from "../DataTypes.sol"; import { SSTORE2 } from "solady/utils/SSTORE2.sol"; diff --git a/src/lib/Helpers.sol b/src/lib/Helpers.sol index 82ce56e1..2ea60941 100644 --- a/src/lib/Helpers.sol +++ b/src/lib/Helpers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { ResolverRecord, SchemaRecord, SchemaUID, ResolverUID } from "../DataTypes.sol"; diff --git a/src/lib/ModuleDeploymentLib.sol b/src/lib/ModuleDeploymentLib.sol index ee46a4a5..5ca38693 100644 --- a/src/lib/ModuleDeploymentLib.sol +++ b/src/lib/ModuleDeploymentLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; /** * @title ModuleDeploymentLib diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index 3d7f3eba..0d8cd1c5 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import { AttestationRecord, ResolverRecord, SchemaRecord, ModuleRecord } from "../DataTypes.sol"; import { IExternalSchemaValidator } from "../external/IExternalSchemaValidator.sol"; diff --git a/test/Base.t.sol b/test/Base.t.sol index 6d70dc08..adcd66a8 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; import "forge-std/Test.sol"; import "src/Registry.sol"; diff --git a/test/mocks/MockFactory.sol b/test/mocks/MockFactory.sol index 1463c1c8..6af84c2d 100644 --- a/test/mocks/MockFactory.sol +++ b/test/mocks/MockFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity ^0.8.24; contract MockFactory { function deploy(bytes memory bytecode) external returns (address addr) { From 974ae5ca727643d44c102101d0d456d552aad60b Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 13 Feb 2024 14:37:21 +0700 Subject: [PATCH 54/84] chore: renaming storage --- script/DeployRegistry.s.sol.bak | 4 +--- src/core/AttestationManager.sol | 20 ++++++++++---------- src/core/ModuleManager.sol | 14 +++++++------- src/core/ResolverManager.sol | 12 ++++++------ src/core/TrustManager.sol | 8 ++++---- 5 files changed, 28 insertions(+), 30 deletions(-) diff --git a/script/DeployRegistry.s.sol.bak b/script/DeployRegistry.s.sol.bak index 8b8fc4bd..30f5f4de 100644 --- a/script/DeployRegistry.s.sol.bak +++ b/script/DeployRegistry.s.sol.bak @@ -2,8 +2,6 @@ pragma solidity ^0.8.24; import { Script } from "forge-std/Script.sol"; -import { RegistryTestTools, RegistryInstance } from "../test/utils/BaseUtils.sol"; -import { DebugResolver } from "../src/external/examples/DebugResolver.sol"; import { IExternalResolver } from "../src/external/IExternalResolver.sol"; import { ResolverUID } from "../src/DataTypes.sol"; import { console2 } from "forge-std/console2.sol"; @@ -12,7 +10,7 @@ import { console2 } from "forge-std/console2.sol"; * @title DeployRegistryScript * @author zeroknots */ -contract DeployRegistryScript is Script, RegistryTestTools { +contract DeployRegistryScript is Script { function run() public { bytes32 salt = bytes32(uint256(2)); diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index da3e1473..14c50e42 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -33,7 +33,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, using ModuleTypeLib for ModuleType[]; mapping(address module => mapping(address attester => AttestationRecord attestation)) internal - _moduleToAttesterToAttestations; + $moduleToAttesterToAttestations; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Attestation */ @@ -58,7 +58,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, _storeAttestation({ schemaUID: schemaUID, attester: attester, request: request }); record.requireExternalSchemaValidation({ $schema: schemas[schemaUID] }); - record.requireExternalResolverOnAttestation({ $resolver: resolvers[resolverUID] }); + record.requireExternalResolverOnAttestation({ $resolver: $resolvers[resolverUID] }); } /** @@ -97,7 +97,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, // Use StubLib to call schema Validation and resolver if needed records.requireExternalSchemaValidation({ $schema: schemas[schemaUID] }); - records.requireExternalResolverOnAttestation({ $resolver: resolvers[resolverUID] }); + records.requireExternalResolverOnAttestation({ $resolver: $resolvers[resolverUID] }); } /** @@ -135,7 +135,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, // caching module address. address module = request.moduleAddr; // SLOAD the resolverUID from the moduleRecord - resolverUID = _moduleAddrToRecords[module].resolverUID; + resolverUID = $moduleAddrToRecords[module].resolverUID; // Ensure that attestation is for module that was registered. if (resolverUID == EMPTY_RESOLVER_UID) { revert ModuleNotFoundInRegistry(module); @@ -158,7 +158,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, dataPointer: sstore2Pointer }); // SSTORE attestation to registry storage - _moduleToAttesterToAttestations[request.moduleAddr][attester] = record; + $moduleToAttesterToAttestations[request.moduleAddr][attester] = record; emit Attested({ moduleAddr: module, @@ -180,7 +180,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, function _revoke(address attester, RevocationRequest calldata request) internal { (AttestationRecord memory record, ResolverUID resolverUID) = _storeRevocation(attester, request); - record.tryExternalResolverOnRevocation({ $resolver: resolvers[resolverUID] }); + record.tryExternalResolverOnRevocation({ $resolver: $resolvers[resolverUID] }); } /** @@ -203,7 +203,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, } // No schema validation required during revocation. the attestation data was already checked against - records.tryExternalResolverOnRevocation({ $resolver: resolvers[resolverUID] }); + records.tryExternalResolverOnRevocation({ $resolver: $resolvers[resolverUID] }); } /** @@ -221,11 +221,11 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, returns (AttestationRecord memory record, ResolverUID resolverUID) { AttestationRecord storage $attestation = - _moduleToAttesterToAttestations[request.moduleAddr][revoker]; + $moduleToAttesterToAttestations[request.moduleAddr][revoker]; // SLOAD entire record. This will later be passed to the resolver record = $attestation; - resolverUID = _moduleAddrToRecords[request.moduleAddr].resolverUID; + resolverUID = $moduleAddrToRecords[request.moduleAddr].resolverUID; // Ensure that we aren't attempting to revoke a non-existing attestation. if (record.dataPointer == EMPTY_ATTESTATION_REF) { @@ -261,6 +261,6 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, override returns (AttestationRecord storage $attestation) { - $attestation = _moduleToAttesterToAttestations[module][attester]; + $attestation = $moduleToAttesterToAttestations[module][attester]; } } diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 5745ea19..78bd6927 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -33,7 +33,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { using ModuleDeploymentLib for address; using StubLib for *; - mapping(address moduleAddress => ModuleRecord moduleRecord) internal _moduleAddrToRecords; + mapping(address moduleAddress => ModuleRecord moduleRecord) internal $moduleAddrToRecords; /** * @inheritdoc IRegistry @@ -48,7 +48,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { payable returns (address moduleAddress) { - ResolverRecord storage $resolver = resolvers[resolverUID]; + ResolverRecord storage $resolver = $resolvers[resolverUID]; if ($resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver($resolver.resolver); moduleAddress = initCode.deploy(salt); @@ -91,7 +91,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { ) external { - ResolverRecord storage $resolver = resolvers[resolverUID]; + ResolverRecord storage $resolver = $resolvers[resolverUID]; // ensure that non-zero resolverUID was provided if ($resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolver($resolver.resolver); @@ -123,7 +123,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { payable returns (address moduleAddress) { - ResolverRecord storage $resolver = resolvers[resolverUID]; + ResolverRecord storage $resolver = $resolvers[resolverUID]; if ($resolver.resolverOwner == ZERO_ADDRESS) revert InvalidResolverUID(resolverUID); // prevent someone from calling a registry function pretending its a factory if (factory == address(this)) revert FactoryCallFailed(factory); @@ -162,7 +162,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { // ensure that non-zero resolverUID was provided if (resolverUID == EMPTY_RESOLVER_UID) revert InvalidDeployment(); // ensure moduleAddress is not already registered - if (_moduleAddrToRecords[moduleAddress].resolverUID != EMPTY_RESOLVER_UID) { + if ($moduleAddrToRecords[moduleAddress].resolverUID != EMPTY_RESOLVER_UID) { revert AlreadyRegistered(moduleAddress); } // revert if moduleAddress is NOT a contract @@ -174,7 +174,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { ModuleRecord({ resolverUID: resolverUID, sender: sender, metadata: metadata }); // Store module record in _modules mapping - _moduleAddrToRecords[moduleAddress] = moduleRegistration; + $moduleAddrToRecords[moduleAddress] = moduleRegistration; // Emit ModuleRegistration event emit ModuleRegistration({ @@ -192,6 +192,6 @@ abstract contract ModuleManager is IRegistry, ResolverManager { view returns (ModuleRecord memory moduleRecord) { - return _moduleAddrToRecords[moduleAddress]; + return $moduleAddrToRecords[moduleAddress]; } } diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index 3bd19011..e4a76393 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -10,7 +10,7 @@ import { IRegistry } from "../IRegistry.sol"; abstract contract ResolverManager is IRegistry { using UIDLib for ResolverRecord; - mapping(ResolverUID uid => ResolverRecord resolver) internal resolvers; + mapping(ResolverUID uid => ResolverRecord resolver) internal $resolvers; /** * @dev Modifier to require that the caller is the owner of a resolver @@ -18,7 +18,7 @@ abstract contract ResolverManager is IRegistry { * @param uid The UID of the resolver. */ modifier onlyResolverOwner(ResolverUID uid) { - if (resolvers[uid].resolverOwner != msg.sender) { + if ($resolvers[uid].resolverOwner != msg.sender) { revert AccessDenied(); } _; @@ -53,12 +53,12 @@ abstract contract ResolverManager is IRegistry { uid = resolverRecord.getUID(); // Checking if a schema with this UID already exists -> resolver can never be ZERO_ADDRESS - if (address(resolvers[uid].resolver) != ZERO_ADDRESS) { + if (address($resolvers[uid].resolver) != ZERO_ADDRESS) { revert ResolverAlreadyExists(); } // SSTORE schema in the resolvers mapping - resolvers[uid] = resolverRecord; + $resolvers[uid] = resolverRecord; emit NewResolver(uid, address(resolver)); } @@ -74,7 +74,7 @@ abstract contract ResolverManager is IRegistry { onlyResolver(resolver) onlyResolverOwner(uid) // authorization control { - ResolverRecord storage referrer = resolvers[uid]; + ResolverRecord storage referrer = $resolvers[uid]; referrer.resolver = resolver; emit NewResolver(uid, address(resolver)); } @@ -83,6 +83,6 @@ abstract contract ResolverManager is IRegistry { * @inheritdoc IRegistry */ function findResolver(ResolverUID uid) external view returns (ResolverRecord memory) { - return resolvers[uid]; + return $resolvers[uid]; } } diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 3bd1cccc..3e56a41f 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -28,7 +28,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { using ModuleTypeLib for PackedModuleTypes; using LibSort for address[]; - mapping(address account => TrustedAttesterRecord attesters) internal _accountToAttester; + mapping(address account => TrustedAttesterRecord attesters) internal $accountToAttester; /** * @inheritdoc IRegistry @@ -42,7 +42,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { if (attestersLength == 0) revert InvalidTrustedAttesterInput(); if (attesters.length != attestersLength) revert InvalidTrustedAttesterInput(); - TrustedAttesterRecord storage $trustedAttester = _accountToAttester[msg.sender]; + TrustedAttesterRecord storage $trustedAttester = $accountToAttester[msg.sender]; // threshold cannot be greater than the number of attesters if (threshold > attestersLength) { threshold = uint8(attestersLength); @@ -104,7 +104,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { * @param moduleType (optional param), setting moduleType = 0, will ignore moduleTypes in attestations */ function _check(address smartAccount, address module, ModuleType moduleType) internal view { - TrustedAttesterRecord storage $trustedAttesters = _accountToAttester[smartAccount]; + TrustedAttesterRecord storage $trustedAttesters = $accountToAttester[smartAccount]; // SLOAD from one slot uint256 attesterCount = $trustedAttesters.attesterCount; uint256 threshold = $trustedAttesters.threshold; @@ -208,7 +208,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { view returns (address[] memory attesters) { - TrustedAttesterRecord storage $trustedAttesters = _accountToAttester[smartAccount]; + TrustedAttesterRecord storage $trustedAttesters = $accountToAttester[smartAccount]; uint256 count = $trustedAttesters.attesterCount; address attester0 = $trustedAttesters.attester; attesters = new address[](count); From 4502efb6cdbf142f8e1b3a6ed8252f73edebd0e5 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 13 Feb 2024 15:10:32 +0700 Subject: [PATCH 55/84] feat: create2 factory code rewritten to allow tooling for vanity addresses --- src/lib/ModuleDeploymentLib.sol | 86 ++++++++++++++++++++++++--------- test/ModuleRegistration.t.sol | 4 +- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/lib/ModuleDeploymentLib.sol b/src/lib/ModuleDeploymentLib.sol index 5ca38693..c56a74e1 100644 --- a/src/lib/ModuleDeploymentLib.sol +++ b/src/lib/ModuleDeploymentLib.sol @@ -7,23 +7,45 @@ pragma solidity ^0.8.24; * @author zeroknots */ library ModuleDeploymentLib { - /** - * @notice Creates a new contract using CREATE2 opcode. - * @dev This method uses the CREATE2 opcode to deploy a new contract with a deterministic address. - * - * @param initCode The creationCode for the contract. - * this MUST be provided. Function will fail if params are abi.encodePacked in createCode. - * - * @return moduleAddress The address of the deployed contract. - */ - function deploy(bytes memory initCode, bytes32 salt) internal returns (address moduleAddress) { - uint256 value = msg.value; - // solhint-disable-next-line no-inline-assembly + // source: https://github.com/0age/metamorphic/blob/master/contracts/ImmutableCreate2Factory.sol#L194-L203 + modifier containsCaller(bytes32 salt) { + // prevent contract submissions from being stolen from tx.pool by requiring + // that the first 20 bytes of the submitted salt match msg.sender. + require( + (address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)), "Invalid salt" + ); + _; + } + + function deploy( + bytes calldata _initCode, + bytes32 salt + ) + internal + containsCaller(salt) + returns (address deploymentAddress) + { + // move the initialization code from calldata to memory. + bytes memory initCode = _initCode; + + // determine the target address for contract deployment. + address targetDeploymentAddress = calcAddress(_initCode, salt); + + // using inline assembly: load data and length of data, then call CREATE2. assembly { - moduleAddress := create2(value, add(initCode, 0x20), mload(initCode), salt) - // If the contract was not created successfully, the transaction is reverted. - if iszero(extcodesize(moduleAddress)) { revert(0, 0) } + let encoded_data := add(0x20, initCode) // load initialization code. + let encoded_size := mload(initCode) // load the init code's length. + deploymentAddress := + create2( // call CREATE2 with 4 arguments. + callvalue(), // forward any attached value. + encoded_data, // pass in initialization code. + encoded_size, // pass in init code's length. + salt // pass in the salt value. + ) } + + // check address against target to ensure that deployment was successful. + require(deploymentAddress == targetDeploymentAddress, "invalid address"); } /** @@ -31,18 +53,36 @@ library ModuleDeploymentLib { * @dev The calculated address is based on the contract's code, a salt, and the address of the current contract. * @dev This function uses the formula specified in EIP-1014 (https://eips.ethereum.org/EIPS/eip-1014). * - * @param _code The contract code that would be deployed. - * @param _salt A salt used for the address calculation. + * @param initCode The contract code that would be deployed. + * @param salt A salt used for the address calculation. * This must be the same salt that would be passed to the CREATE2 opcode. * - * @return The address that the contract would be deployed + * @return targetDeploymentAddress The address that the contract would be deployed * at if the CREATE2 opcode was called with the specified _code and _salt. */ - function calcAddress(bytes calldata _code, bytes32 _salt) internal view returns (address) { - bytes32 hash = - keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(_code))); - // NOTE: cast last 20 bytes of hash to address - return address(uint160(uint256(hash))); + function calcAddress( + bytes calldata initCode, + bytes32 salt + ) + internal + view + returns (address targetDeploymentAddress) + { + targetDeploymentAddress = address( + uint160( // downcast to match the address type. + uint256( // convert to uint to truncate upper digits. + keccak256( // compute the CREATE2 hash using 4 inputs. + abi.encodePacked( // pack all inputs to the hash together. + hex"ff", // start with 0xff to distinguish from RLP. + address(this), // this contract will be the caller. + salt, // pass in the supplied salt value. + keccak256( // pass in the hash of initialization code. + abi.encodePacked(initCode)) + ) + ) + ) + ) + ); } error InvalidDeployment(); diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index e38616b2..be6a5ed1 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -18,7 +18,7 @@ contract Factory { contract ModuleRegistrationTest is BaseTest { function test_WhenDeployingViaRegistry() public prankWithAccount(moduleDev1) { - bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); + bytes32 salt = bytes32(abi.encodePacked(address(moduleDev1.addr), bytes12(0))); bytes memory bytecode = type(MockModule).creationCode; @@ -28,7 +28,7 @@ contract ModuleRegistrationTest is BaseTest { } function test_WhenDeployingViaRegistryWithArgs() public prankWithAccount(moduleDev1) { - bytes32 salt = keccak256(abi.encodePacked("ModuleRegistration", address(this))); + bytes32 salt = bytes32(abi.encodePacked(address(moduleDev1.addr), bytes12(0))); bytes memory bytecode = type(MockModuleWithArgs).creationCode; bytes memory initCode = abi.encodePacked(bytecode, abi.encode(313_131)); From 462376e26ea2815835c3e9d95a082c6331e78784 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 14 Feb 2024 08:21:14 +0700 Subject: [PATCH 56/84] adding transferownership to resolver --- foundry.toml | 2 +- src/DataTypes.sol | 8 +- src/IRegistry.sol | 112 +++++++----------- src/core/Attestation.sol | 13 +- src/core/AttestationManager.sol | 48 ++------ src/core/ModuleManager.sol | 48 ++------ src/core/ResolverManager.sol | 22 ++-- src/core/SchemaManager.sol | 8 +- src/core/SignedAttestation.sol | 78 ++---------- src/core/TrustManager.sol | 36 +----- src/core/TrustManagerExternalAttesterList.sol | 9 +- src/external/IExternalResolver.sol | 20 +--- src/external/IExternalSchemaValidator.sol | 7 +- src/external/examples/ERC7512Schema.sol | 18 +-- src/external/examples/TokenizedResolver.sol | 32 +---- src/lib/AttestationLib.sol | 47 +------- src/lib/Helpers.sol | 6 +- src/lib/ModuleDeploymentLib.sol | 22 +--- src/lib/StubLib.sol | 42 ++----- test/Attestation.t.sol | 62 +++------- test/Base.t.sol | 4 +- test/ModuleRegistration.t.sol | 36 ++---- test/Resolver.t.sol | 28 +++++ test/SchemaValidation.t.sol | 3 +- test/TrustDelegation.t.sol | 12 +- test/invariance/Handler.t.sol | 24 +--- test/invariance/invariant_ImmutableData.t.sol | 6 +- test/mocks/MockResolver.sol | 28 +---- test/mocks/MockSchemaValidator.sol | 14 +-- 29 files changed, 187 insertions(+), 608 deletions(-) diff --git a/foundry.toml b/foundry.toml index 1a276d6e..e554080e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,7 +7,7 @@ evm_version = 'paris' [fmt] bracket_spacing = true -line_length = 100 +line_length = 140 multiline_func_header = "all" number_underscore="thousands" diff --git a/src/DataTypes.sol b/src/DataTypes.sol index 9e425dd6..d5e95e07 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -103,13 +103,7 @@ type AttestationDataRef is address; using { attestationDataRefEq as == } for AttestationDataRef global; -function attestationDataRefEq( - AttestationDataRef uid1, - AttestationDataRef uid2 -) - pure - returns (bool) -{ +function attestationDataRefEq(AttestationDataRef uid1, AttestationDataRef uid2) pure returns (bool) { return AttestationDataRef.unwrap(uid1) == AttestationDataRef.unwrap(uid2); } diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 7df601ca..81645cf8 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -27,13 +27,7 @@ interface IERC7484 { function check(address module, ModuleType moduleType) external view; - function checkForAccount( - address smartAccount, - address module, - ModuleType moduleType - ) - external - view; + function checkForAccount(address smartAccount, address module, ModuleType moduleType) external view; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Check with external attester(s) */ @@ -82,22 +76,14 @@ interface IRegistry is IERC7484 { * Get trusted attester for a specific smartAccount * @param smartAccount The address of the smartAccount */ - function findTrustedAttesters(address smartAccount) - external - view - returns (address[] memory attesters); + function findTrustedAttesters(address smartAccount) external view returns (address[] memory attesters); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Attestations */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ event Revoked(address indexed moduleAddr, address indexed revoker, SchemaUID schema); - event Attested( - address indexed moduleAddr, - address indexed attester, - SchemaUID schemaUID, - AttestationDataRef indexed sstore2Pointer - ); + event Attested(address indexed moduleAddr, address indexed attester, SchemaUID schemaUID, AttestationDataRef indexed sstore2Pointer); error AlreadyRevoked(); error ModuleNotFoundInRegistry(address module); @@ -147,13 +133,7 @@ interface IRegistry is IERC7484 { * @param request An AttestationRequest * @param signature The signature of the attester. ECDSA or ERC1271 */ - function attest( - SchemaUID schemaUID, - address attester, - AttestationRequest calldata request, - bytes calldata signature - ) - external; + function attest(SchemaUID schemaUID, address attester, AttestationRequest calldata request, bytes calldata signature) external; /** * Allows attester to attest by signing an AttestationRequest (ECDSA or ERC1271) @@ -168,24 +148,12 @@ interface IRegistry is IERC7484 { * @param requests An array of AttestationRequest * @param signature The signature of the attester. ECDSA or ERC1271 */ - function attest( - SchemaUID schemaUID, - address attester, - AttestationRequest[] calldata requests, - bytes calldata signature - ) - external; + function attest(SchemaUID schemaUID, address attester, AttestationRequest[] calldata requests, bytes calldata signature) external; /** * Getter function to get AttestationRequest made by one attester */ - function findAttestation( - address module, - address attester - ) - external - view - returns (AttestationRecord memory attestation); + function findAttestation(address module, address attester) external view returns (AttestationRecord memory attestation); /** * Getter function to get AttestationRequest made by multiple attesters @@ -229,12 +197,7 @@ interface IRegistry is IERC7484 { * @param request the RevocationRequest * @param signature ECDSA or ERC1271 signature */ - function revoke( - address attester, - RevocationRequest calldata request, - bytes calldata signature - ) - external; + function revoke(address attester, RevocationRequest calldata request, bytes calldata signature) external; /** * Allows attester to revoke an attestation by signing an RevocationRequest (ECDSA or ERC1271) @@ -244,20 +207,13 @@ interface IRegistry is IERC7484 { * @param requests array of RevocationRequests * @param signature ECDSA or ERC1271 signature */ - function revoke( - address attester, - RevocationRequest[] calldata requests, - bytes calldata signature - ) - external; + function revoke(address attester, RevocationRequest[] calldata requests, bytes calldata signature) external; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Module Registration */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Event triggered when a module is deployed. - event ModuleRegistration( - address indexed implementation, address indexed sender, ResolverUID resolverUID - ); + event ModuleRegistration(address indexed implementation, address indexed sender, ResolverUID resolverUID); error AlreadyRegistered(address module); error InvalidDeployment(); @@ -285,6 +241,9 @@ interface IRegistry is IERC7484 { /** * Registry can use other factories to deploy the module + * @notice This function is used to deploy and register a module using a factory contract. + * Since one of the parameters of this function is a unique resolverUID and any + * registered module address can only be registered once, using this function is of risk for a frontrun attack */ function deployViaFactory( address factory, @@ -298,18 +257,16 @@ interface IRegistry is IERC7484 { /** * Already deployed module addresses can be registered on the registry + * @notice This function is used to deploy and register an already deployed module. + * Since one of the parameters of this function is a unique resolverUID and any + * registered module address can only be registered once, using this function is of risk for a frontrun attack * @param resolverUID The resolverUID to be used for the module * @param moduleAddress The address of the module to be registered * @param metadata The metadata to be stored on the registry. * This field is optional, and might be used by the module developer to store additional * information about the module or facilitate business logic with the Resolver stub */ - function registerModule( - ResolverUID resolverUID, - address moduleAddress, - bytes calldata metadata - ) - external; + function registerModule(ResolverUID resolverUID, address moduleAddress, bytes calldata metadata) external; /** * in conjunction with the deployModule() function, this function let's you @@ -318,22 +275,13 @@ interface IRegistry is IERC7484 { * @param initCode module initcode * @return moduleAddress counterfactual address of the module deployment */ - function calcModuleAddress( - bytes32 salt, - bytes calldata initCode - ) - external - view - returns (address); + function calcModuleAddress(bytes32 salt, bytes calldata initCode) external view returns (address); /** * Getter function to get the stored ModuleRecord for a specific module address. * @param moduleAddress The address of the module */ - function findModule(address moduleAddress) - external - view - returns (ModuleRecord memory moduleRecord); + function findModule(address moduleAddress) external view returns (ModuleRecord memory moduleRecord); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Manage Schemas */ @@ -374,13 +322,35 @@ interface IRegistry is IERC7484 { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ event NewResolver(ResolverUID indexed uid, address indexed resolver); + event NewResolverOwner(ResolverUID indexed uid, address newOwner); error ResolverAlreadyExists(); - function registerResolver(IExternalResolver _resolver) external returns (ResolverUID uid); + /** + * Allows Marketplace Agents to register external resolvers. + * @param resolver external resolver contract + * @return uid ResolverUID of the registered resolver + */ + function registerResolver(IExternalResolver resolver) external returns (ResolverUID uid); + /** + * Entities that previously regsitered an external resolver, may update the implementation address. + * @param uid The UID of the resolver. + * @param resolver The new resolver implementation address. + */ function setResolver(ResolverUID uid, IExternalResolver resolver) external; + /** + * Transfer ownership of resolverUID to a new address + * @param uid The UID of the resolver to transfer ownership for + * @param newOwner The address of the new owner + */ + function transferResolverOwnership(ResolverUID uid, address newOwner) external; + + /** + * Getter function to get the ResolverRecord of a registerd resolver + * @param uid The UID of the resolver. + */ function findResolver(ResolverUID uid) external view returns (ResolverRecord memory record); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ diff --git a/src/core/Attestation.sol b/src/core/Attestation.sol index 53a7a99b..0d7ffea0 100644 --- a/src/core/Attestation.sol +++ b/src/core/Attestation.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.24; -import { - AttestationRecord, AttestationRequest, RevocationRequest, SchemaUID -} from "../DataTypes.sol"; +import { AttestationRecord, AttestationRequest, RevocationRequest, SchemaUID } from "../DataTypes.sol"; import { AttestationManager } from "./AttestationManager.sol"; import { IRegistry } from "../IRegistry.sol"; @@ -39,14 +37,7 @@ abstract contract Attestation is IRegistry, AttestationManager { /** * @inheritdoc IRegistry */ - function findAttestation( - address module, - address attester - ) - external - view - returns (AttestationRecord memory attestation) - { + function findAttestation(address module, address attester) external view returns (AttestationRecord memory attestation) { attestation = _getAttestation(module, attester); } diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 14c50e42..9e55ff3f 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -32,8 +32,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, using AttestationLib for address; using ModuleTypeLib for ModuleType[]; - mapping(address module => mapping(address attester => AttestationRecord attestation)) internal - $moduleToAttesterToAttestations; + mapping(address module => mapping(address attester => AttestationRecord attestation)) internal $moduleToAttesterToAttestations; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Attestation */ @@ -47,13 +46,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, * function will get the external IExternalResolver for the module - that the attestation is for * and call it, if an external Resolver was set */ - function _attest( - address attester, - SchemaUID schemaUID, - AttestationRequest calldata request - ) - internal - { + function _attest(address attester, SchemaUID schemaUID, AttestationRequest calldata request) internal { (AttestationRecord memory record, ResolverUID resolverUID) = _storeAttestation({ schemaUID: schemaUID, attester: attester, request: request }); @@ -69,13 +62,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, * function will get the external IExternalResolver for the module - that the attestation is for * and call it, if an external Resolver was set */ - function _attest( - address attester, - SchemaUID schemaUID, - AttestationRequest[] calldata requests - ) - internal - { + function _attest(address attester, SchemaUID schemaUID, AttestationRequest[] calldata requests) internal { uint256 length = requests.length; AttestationRecord[] memory records = new AttestationRecord[](length); // loop will check that the batched attestation is made ONLY for the same resolver @@ -83,11 +70,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, for (uint256 i; i < length; i++) { ResolverUID resolverUID_cache; // save the attestation record into records array. - (records[i], resolverUID_cache) = _storeAttestation({ - schemaUID: schemaUID, - attester: attester, - request: requests[i] - }); + (records[i], resolverUID_cache) = _storeAttestation({ schemaUID: schemaUID, attester: attester, request: requests[i] }); // cache the first resolverUID and compare it to the rest // If the resolverUID is different, revert // @dev if you want to use different resolvers, make different attestation calls @@ -160,12 +143,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, // SSTORE attestation to registry storage $moduleToAttesterToAttestations[request.moduleAddr][attester] = record; - emit Attested({ - moduleAddr: module, - attester: attester, - schemaUID: schemaUID, - sstore2Pointer: sstore2Pointer - }); + emit Attested({ moduleAddr: module, attester: attester, schemaUID: schemaUID, sstore2Pointer: sstore2Pointer }); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -178,8 +156,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, * and pass the RevocationRecord to the resolver to check if the revocation is valid */ function _revoke(address attester, RevocationRequest calldata request) internal { - (AttestationRecord memory record, ResolverUID resolverUID) = - _storeRevocation(attester, request); + (AttestationRecord memory record, ResolverUID resolverUID) = _storeRevocation(attester, request); record.tryExternalResolverOnRevocation({ $resolver: $resolvers[resolverUID] }); } @@ -220,8 +197,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, internal returns (AttestationRecord memory record, ResolverUID resolverUID) { - AttestationRecord storage $attestation = - $moduleToAttesterToAttestations[request.moduleAddr][revoker]; + AttestationRecord storage $attestation = $moduleToAttesterToAttestations[request.moduleAddr][revoker]; // SLOAD entire record. This will later be passed to the resolver record = $attestation; @@ -252,15 +228,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, * Returns the attestation records for the given module and attesters. * This function is expected to be used by TrustManager and TrustManagerExternalAttesterList */ - function _getAttestation( - address module, - address attester - ) - internal - view - override - returns (AttestationRecord storage $attestation) - { + function _getAttestation(address module, address attester) internal view override returns (AttestationRecord storage $attestation) { $attestation = $moduleToAttesterToAttestations[module][attester]; } } diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 78bd6927..6e77682a 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -61,36 +61,20 @@ abstract contract ModuleManager is IRegistry, ResolverManager { metadata: metadata }); - record.requireExternalResolverOnModuleRegistration({ - moduleAddress: moduleAddress, - $resolver: $resolver - }); + record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, $resolver: $resolver }); } /** * @inheritdoc IRegistry */ - function calcModuleAddress( - bytes32 salt, - bytes calldata initCode - ) - external - view - returns (address) - { + function calcModuleAddress(bytes32 salt, bytes calldata initCode) external view returns (address) { return initCode.calcAddress(salt); } /** * @inheritdoc IRegistry */ - function registerModule( - ResolverUID resolverUID, - address moduleAddress, - bytes calldata metadata - ) - external - { + function registerModule(ResolverUID resolverUID, address moduleAddress, bytes calldata metadata) external { ResolverRecord storage $resolver = $resolvers[resolverUID]; // ensure that non-zero resolverUID was provided @@ -104,10 +88,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { }); // resolve module registration - record.requireExternalResolverOnModuleRegistration({ - moduleAddress: moduleAddress, - $resolver: $resolver - }); + record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, $resolver: $resolver }); } /** @@ -144,10 +125,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { metadata: metadata }); - record.requireExternalResolverOnModuleRegistration({ - moduleAddress: moduleAddress, - $resolver: $resolver - }); + record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, $resolver: $resolver }); } function _storeModuleRecord( @@ -170,28 +148,20 @@ abstract contract ModuleManager is IRegistry, ResolverManager { if (!_isContract(moduleAddress)) revert InvalidDeployment(); // Store module metadata in _modules mapping - moduleRegistration = - ModuleRecord({ resolverUID: resolverUID, sender: sender, metadata: metadata }); + moduleRegistration = ModuleRecord({ resolverUID: resolverUID, sender: sender, metadata: metadata }); // Store module record in _modules mapping $moduleAddrToRecords[moduleAddress] = moduleRegistration; // Emit ModuleRegistration event - emit ModuleRegistration({ - implementation: moduleAddress, - sender: sender, - resolverUID: resolverUID - }); + // TODO: add flag to event to indicate if module was deployed or registered + emit ModuleRegistration({ implementation: moduleAddress, sender: sender, resolverUID: resolverUID }); } /** * @inheritdoc IRegistry */ - function findModule(address moduleAddress) - external - view - returns (ModuleRecord memory moduleRecord) - { + function findModule(address moduleAddress) external view returns (ModuleRecord memory moduleRecord) { return $moduleAddrToRecords[moduleAddress]; } } diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index e4a76393..f4e4f065 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -28,10 +28,7 @@ abstract contract ResolverManager is IRegistry { * If a resolver is not address(0), we check if it supports the IExternalResolver interface */ modifier onlyResolver(IExternalResolver resolver) { - if ( - address(resolver) == address(0) - || !resolver.supportsInterface(type(IExternalResolver).interfaceId) - ) { + if (address(resolver) == address(0) || !resolver.supportsInterface(type(IExternalResolver).interfaceId)) { revert InvalidResolver(resolver); } _; @@ -40,14 +37,9 @@ abstract contract ResolverManager is IRegistry { /** * @inheritdoc IRegistry */ - function registerResolver(IExternalResolver resolver) - external - onlyResolver(resolver) - returns (ResolverUID uid) - { + function registerResolver(IExternalResolver resolver) external onlyResolver(resolver) returns (ResolverUID uid) { // build a ResolverRecord from the input - ResolverRecord memory resolverRecord = - ResolverRecord({ resolver: resolver, resolverOwner: msg.sender }); + ResolverRecord memory resolverRecord = ResolverRecord({ resolver: resolver, resolverOwner: msg.sender }); // Computing a unique ID for the schema using its properties uid = resolverRecord.getUID(); @@ -79,6 +71,14 @@ abstract contract ResolverManager is IRegistry { emit NewResolver(uid, address(resolver)); } + /** + * @inheritdoc IRegistry + */ + function transferResolverOwnership(ResolverUID uid, address newOwner) external onlyResolverOwner(uid) { + $resolvers[uid].resolverOwner = newOwner; + emit NewResolverOwner(uid, newOwner); + } + /** * @inheritdoc IRegistry */ diff --git a/src/core/SchemaManager.sol b/src/core/SchemaManager.sol index 7efee3e2..6bafe409 100644 --- a/src/core/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -34,8 +34,7 @@ abstract contract SchemaManager is IRegistry { onlySchemaValidator(validator) returns (SchemaUID uid) { - SchemaRecord memory schemaRecord = - SchemaRecord({ validator: validator, registeredAt: _time(), schema: schema }); + SchemaRecord memory schemaRecord = SchemaRecord({ validator: validator, registeredAt: _time(), schema: schema }); // Computing a unique ID for the schema using its properties uid = schemaRecord.getUID(); @@ -54,10 +53,7 @@ abstract contract SchemaManager is IRegistry { * If a validator is not address(0), we check if it supports the IExternalSchemaValidator interface */ modifier onlySchemaValidator(IExternalSchemaValidator validator) { - if ( - address(validator) != address(0) - && !validator.supportsInterface(type(IExternalSchemaValidator).interfaceId) - ) { + if (address(validator) != address(0) && !validator.supportsInterface(type(IExternalSchemaValidator).interfaceId)) { revert InvalidSchemaValidator(validator); } _; diff --git a/src/core/SignedAttestation.sol b/src/core/SignedAttestation.sol index 25e39daf..27b3f7cb 100644 --- a/src/core/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -19,14 +19,7 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { /** * @inheritdoc IRegistry */ - function attest( - SchemaUID schemaUID, - address attester, - AttestationRequest calldata request, - bytes calldata signature - ) - external - { + function attest(SchemaUID schemaUID, address attester, AttestationRequest calldata request, bytes calldata signature) external { uint256 nonce = ++attesterNonce[attester]; bytes32 digest = _hashTypedData(request.hash(nonce)); bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); @@ -38,14 +31,7 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { /** * @inheritdoc IRegistry */ - function attest( - SchemaUID schemaUID, - address attester, - AttestationRequest[] calldata requests, - bytes calldata signature - ) - external - { + function attest(SchemaUID schemaUID, address attester, AttestationRequest[] calldata requests, bytes calldata signature) external { uint256 nonce = ++attesterNonce[attester]; bytes32 digest = _hashTypedData(requests.hash(nonce)); bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); @@ -57,13 +43,7 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { /** * @inheritdoc IRegistry */ - function revoke( - address attester, - RevocationRequest calldata request, - bytes calldata signature - ) - external - { + function revoke(address attester, RevocationRequest calldata request, bytes calldata signature) external { uint256 nonce = ++attesterNonce[attester]; bytes32 digest = _hashTypedData(request.hash(nonce)); bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); @@ -75,13 +55,7 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { /** * @inheritdoc IRegistry */ - function revoke( - address attester, - RevocationRequest[] calldata requests, - bytes calldata signature - ) - external - { + function revoke(address attester, RevocationRequest[] calldata requests, bytes calldata signature) external { uint256 nonce = ++attesterNonce[attester]; bytes32 digest = _hashTypedData(requests.hash(nonce)); bool valid = SignatureCheckerLib.isValidSignatureNow(attester, digest, signature); @@ -97,58 +71,24 @@ contract SignedAttestation is IRegistry, Attestation, EIP712 { /** * override thats used by Solady's EIP712 cache (constructor) */ - function _domainNameAndVersion() - internal - view - virtual - override - returns (string memory name, string memory version) - { + function _domainNameAndVersion() internal view virtual override returns (string memory name, string memory version) { name = "RhinestoneRegistry"; version = "v1.0"; } - function getDigest( - AttestationRequest calldata request, - address attester - ) - external - view - returns (bytes32 digest) - { + function getDigest(AttestationRequest calldata request, address attester) external view returns (bytes32 digest) { digest = _hashTypedData(request.hash(attesterNonce[attester] + 1)); } - function getDigest( - AttestationRequest[] calldata requests, - address attester - ) - external - view - returns (bytes32 digest) - { + function getDigest(AttestationRequest[] calldata requests, address attester) external view returns (bytes32 digest) { digest = _hashTypedData(requests.hash(attesterNonce[attester] + 1)); } - function getDigest( - RevocationRequest calldata request, - address attester - ) - external - view - returns (bytes32 digest) - { + function getDigest(RevocationRequest calldata request, address attester) external view returns (bytes32 digest) { digest = _hashTypedData(request.hash(attesterNonce[attester] + 1)); } - function getDigest( - RevocationRequest[] calldata requests, - address attester - ) - external - view - returns (bytes32 digest) - { + function getDigest(RevocationRequest[] calldata requests, address attester) external view returns (bytes32 digest) { digest = _hashTypedData(requests.hash(attesterNonce[attester] + 1)); } } diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 3e56a41f..0d18c342 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -1,12 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.24; -import { - AttestationRecord, - PackedModuleTypes, - ModuleType, - TrustedAttesterRecord -} from "../DataTypes.sol"; +import { AttestationRecord, PackedModuleTypes, ModuleType, TrustedAttesterRecord } from "../DataTypes.sol"; import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE, ZERO_ADDRESS } from "../Common.sol"; // solhint-disable-next-line no-unused-import import { IRegistry, IERC7484 } from "../IRegistry.sol"; @@ -85,14 +80,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { /** * @inheritdoc IERC7484 */ - function checkForAccount( - address smartAccount, - address module, - ModuleType moduleType - ) - external - view - { + function checkForAccount(address smartAccount, address module, ModuleType moduleType) external view { _check(smartAccount, module, moduleType); } @@ -117,15 +105,13 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { // smart account only has ONE trusted attester // use this condition to save gas else if (threshold == 1) { - AttestationRecord storage $attestation = - _getAttestation({ module: module, attester: attester }); + AttestationRecord storage $attestation = _getAttestation({ module: module, attester: attester }); _requireValidAttestation(moduleType, $attestation); } // smart account has more than one trusted attester else { // loop though list and check if the attestation is valid - AttestationRecord storage $attestation = - _getAttestation({ module: module, attester: attester }); + AttestationRecord storage $attestation = _getAttestation({ module: module, attester: attester }); _requireValidAttestation(moduleType, $attestation); for (uint256 i = 1; i < attesterCount; i++) { threshold--; @@ -145,13 +131,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { * - not expired * - correct module type (if not ZERO_MODULE_TYPE) */ - function _requireValidAttestation( - ModuleType expectedType, - AttestationRecord storage $attestation - ) - internal - view - { + function _requireValidAttestation(ModuleType expectedType, AttestationRecord storage $attestation) internal view { uint256 attestedAt; uint256 expirationTime; uint256 revocationTime; @@ -203,11 +183,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { /** * @inheritdoc IRegistry */ - function findTrustedAttesters(address smartAccount) - public - view - returns (address[] memory attesters) - { + function findTrustedAttesters(address smartAccount) public view returns (address[] memory attesters) { TrustedAttesterRecord storage $trustedAttesters = $accountToAttester[smartAccount]; uint256 count = $trustedAttesters.attesterCount; address attester0 = $trustedAttesters.attester; diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index 0b1598c4..4fcbeb85 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -155,12 +155,5 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { revert InsufficientAttestations(); } - function _getAttestation( - address module, - address attester - ) - internal - view - virtual - returns (AttestationRecord storage $attestation); + function _getAttestation(address module, address attester) internal view virtual returns (AttestationRecord storage $attestation); } diff --git a/src/external/IExternalResolver.sol b/src/external/IExternalResolver.sol index 0f5beb33..a2dea004 100644 --- a/src/external/IExternalResolver.sol +++ b/src/external/IExternalResolver.sol @@ -18,15 +18,9 @@ interface IExternalResolver is IERC165 { * * @return Whether the attestation is valid. */ - function resolveAttestation(AttestationRecord calldata attestation) - external - payable - returns (bool); + function resolveAttestation(AttestationRecord calldata attestation) external payable returns (bool); - function resolveAttestation(AttestationRecord[] calldata attestation) - external - payable - returns (bool); + function resolveAttestation(AttestationRecord[] calldata attestation) external payable returns (bool); /** * @dev Processes an attestation revocation and verifies if it can be revoked. @@ -35,14 +29,8 @@ interface IExternalResolver is IERC165 { * * @return Whether the attestation can be revoked. */ - function resolveRevocation(AttestationRecord calldata attestation) - external - payable - returns (bool); - function resolveRevocation(AttestationRecord[] calldata attestation) - external - payable - returns (bool); + function resolveRevocation(AttestationRecord calldata attestation) external payable returns (bool); + function resolveRevocation(AttestationRecord[] calldata attestation) external payable returns (bool); /** * @dev Processes a Module Registration diff --git a/src/external/IExternalSchemaValidator.sol b/src/external/IExternalSchemaValidator.sol index e43ba503..96c722c8 100644 --- a/src/external/IExternalSchemaValidator.sol +++ b/src/external/IExternalSchemaValidator.sol @@ -11,13 +11,10 @@ interface IExternalSchemaValidator is IERC165 { /** * @notice Validates an attestation request. */ - function validateSchema(AttestationRecord calldata attestation) external view returns (bool); + function validateSchema(AttestationRecord calldata attestation) external returns (bool); /** * @notice Validates an array of attestation requests. */ - function validateSchema(AttestationRecord[] calldata attestations) - external - view - returns (bool); + function validateSchema(AttestationRecord[] calldata attestations) external returns (bool); } diff --git a/src/external/examples/ERC7512Schema.sol b/src/external/examples/ERC7512Schema.sol index 4cf76a1d..9d0619f9 100644 --- a/src/external/examples/ERC7512Schema.sol +++ b/src/external/examples/ERC7512Schema.sol @@ -50,12 +50,7 @@ contract ERC7512SchemaValidator is IExternalSchemaValidator, ERC7512 { return (interfaceID == type(IExternalSchemaValidator).interfaceId); } - function validateSchema(AttestationRecord calldata attestation) - public - view - override - returns (bool valid) - { + function validateSchema(AttestationRecord calldata attestation) public view override returns (bool valid) { AuditSummary memory summary = abi.decode(attestation.dataPointer.sload2(), (AuditSummary)); if (summary.auditedContract.deployment != attestation.moduleAddr) { return false; @@ -64,17 +59,10 @@ contract ERC7512SchemaValidator is IExternalSchemaValidator, ERC7512 { return false; } - valid = SignatureCheckerLib.isValidSignatureNow( - summary.auditorSignature.signer, summary.auditHash, summary.auditorSignature.data - ); + valid = SignatureCheckerLib.isValidSignatureNow(summary.auditorSignature.signer, summary.auditHash, summary.auditorSignature.data); } - function validateSchema(AttestationRecord[] calldata attestations) - external - view - override - returns (bool valid) - { + function validateSchema(AttestationRecord[] calldata attestations) external view override returns (bool valid) { uint256 length = attestations.length; for (uint256 i = 0; i < length; i++) { if (!validateSchema(attestations[i])) { diff --git a/src/external/examples/TokenizedResolver.sol b/src/external/examples/TokenizedResolver.sol index 79c45c07..73b1eff1 100644 --- a/src/external/examples/TokenizedResolver.sol +++ b/src/external/examples/TokenizedResolver.sol @@ -14,37 +14,13 @@ contract TokenizedResolver is ResolverBase { function supportsInterface(bytes4 interfaceID) external view override returns (bool) { } - function resolveAttestation(AttestationRecord calldata attestation) - external - payable - override - onlyRegistry - returns (bool) - { } + function resolveAttestation(AttestationRecord calldata attestation) external payable override onlyRegistry returns (bool) { } - function resolveAttestation(AttestationRecord[] calldata attestation) - external - payable - override - onlyRegistry - returns (bool) - { } + function resolveAttestation(AttestationRecord[] calldata attestation) external payable override onlyRegistry returns (bool) { } - function resolveRevocation(AttestationRecord calldata attestation) - external - payable - override - onlyRegistry - returns (bool) - { } + function resolveRevocation(AttestationRecord calldata attestation) external payable override onlyRegistry returns (bool) { } - function resolveRevocation(AttestationRecord[] calldata attestation) - external - payable - override - onlyRegistry - returns (bool) - { } + function resolveRevocation(AttestationRecord[] calldata attestation) external payable override onlyRegistry returns (bool) { } function resolveModuleRegistration( address sender, diff --git a/src/lib/AttestationLib.sol b/src/lib/AttestationLib.sol index 88d51378..fec4db1a 100644 --- a/src/lib/AttestationLib.sol +++ b/src/lib/AttestationLib.sol @@ -6,8 +6,7 @@ import { SSTORE2 } from "solady/utils/SSTORE2.sol"; library AttestationLib { // The hash of the data type used to relay calls to the attest function. It's the value of - bytes32 internal constant ATTEST_TYPEHASH = - keccak256("AttestationRequest(address,uint48,bytes,uint256[])"); + bytes32 internal constant ATTEST_TYPEHASH = keccak256("AttestationRequest(address,uint48,bytes,uint256[])"); // The hash of the data type used to relay calls to the revoke function. It's the value of bytes32 internal constant REVOKE_TYPEHASH = keccak256("RevocationRequest(address)"); @@ -16,13 +15,7 @@ library AttestationLib { data = SSTORE2.read(AttestationDataRef.unwrap(dataPointer)); } - function sstore2( - AttestationRequest calldata request, - bytes32 salt - ) - internal - returns (AttestationDataRef dataPointer) - { + function sstore2(AttestationRequest calldata request, bytes32 salt) internal returns (AttestationDataRef dataPointer) { /** * @dev We are using CREATE2 to deterministically generate the address of the attestation data. * Checking if an attestation pointer already exists, would cost more GAS in the average case. @@ -34,47 +27,19 @@ library AttestationLib { salt = keccak256(abi.encodePacked(attester, module, block.timestamp, block.chainid)); } - function hash( - AttestationRequest calldata data, - uint256 nonce - ) - internal - pure - returns (bytes32 _hash) - { + function hash(AttestationRequest calldata data, uint256 nonce) internal pure returns (bytes32 _hash) { _hash = keccak256(abi.encode(ATTEST_TYPEHASH, keccak256(abi.encode(data)), nonce)); } - function hash( - AttestationRequest[] calldata data, - uint256 nonce - ) - internal - pure - returns (bytes32 _hash) - { + function hash(AttestationRequest[] calldata data, uint256 nonce) internal pure returns (bytes32 _hash) { _hash = keccak256(abi.encode(ATTEST_TYPEHASH, keccak256(abi.encode(data)), nonce)); } - function hash( - RevocationRequest calldata data, - uint256 nonce - ) - internal - pure - returns (bytes32 _hash) - { + function hash(RevocationRequest calldata data, uint256 nonce) internal pure returns (bytes32 _hash) { _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); } - function hash( - RevocationRequest[] calldata data, - uint256 nonce - ) - internal - pure - returns (bytes32 _hash) - { + function hash(RevocationRequest[] calldata data, uint256 nonce) internal pure returns (bytes32 _hash) { _hash = keccak256(abi.encode(REVOKE_TYPEHASH, keccak256(abi.encode(data)), nonce)); } } diff --git a/src/lib/Helpers.sol b/src/lib/Helpers.sol index 2ea60941..86d8d156 100644 --- a/src/lib/Helpers.sol +++ b/src/lib/Helpers.sol @@ -12,11 +12,7 @@ library UIDLib { * @return schema UID. */ function getUID(SchemaRecord memory schemaRecord) internal view returns (SchemaUID) { - return SchemaUID.wrap( - keccak256( - abi.encodePacked(msg.sender, schemaRecord.schema, address(schemaRecord.validator)) - ) - ); + return SchemaUID.wrap(keccak256(abi.encodePacked(msg.sender, schemaRecord.schema, address(schemaRecord.validator)))); } /** diff --git a/src/lib/ModuleDeploymentLib.sol b/src/lib/ModuleDeploymentLib.sol index c56a74e1..5cb9eb79 100644 --- a/src/lib/ModuleDeploymentLib.sol +++ b/src/lib/ModuleDeploymentLib.sol @@ -11,20 +11,11 @@ library ModuleDeploymentLib { modifier containsCaller(bytes32 salt) { // prevent contract submissions from being stolen from tx.pool by requiring // that the first 20 bytes of the submitted salt match msg.sender. - require( - (address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)), "Invalid salt" - ); + require((address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)), "Invalid salt"); _; } - function deploy( - bytes calldata _initCode, - bytes32 salt - ) - internal - containsCaller(salt) - returns (address deploymentAddress) - { + function deploy(bytes calldata _initCode, bytes32 salt) internal containsCaller(salt) returns (address deploymentAddress) { // move the initialization code from calldata to memory. bytes memory initCode = _initCode; @@ -60,14 +51,7 @@ library ModuleDeploymentLib { * @return targetDeploymentAddress The address that the contract would be deployed * at if the CREATE2 opcode was called with the specified _code and _salt. */ - function calcAddress( - bytes calldata initCode, - bytes32 salt - ) - internal - view - returns (address targetDeploymentAddress) - { + function calcAddress(bytes calldata initCode, bytes32 salt) internal view returns (address targetDeploymentAddress) { targetDeploymentAddress = address( uint160( // downcast to match the address type. uint256( // convert to uint to truncate upper digits. diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index 0d8cd1c5..a79a6db7 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -17,52 +17,29 @@ library StubLib { /** * @notice if Schema Validator is set, it will call validateSchema() on the validator */ - function requireExternalSchemaValidation( - AttestationRecord memory attestationRecord, - SchemaRecord storage $schema - ) - internal - view - { + function requireExternalSchemaValidation(AttestationRecord memory attestationRecord, SchemaRecord storage $schema) internal { // only run this function if the selected schemaUID exists if ($schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); // validate Schema IExternalSchemaValidator validator = $schema.validator; // if validator is set, call the validator - if ( - address(validator) != ZERO_ADDRESS - && validator.validateSchema(attestationRecord) == false - ) { + if (address(validator) != ZERO_ADDRESS && validator.validateSchema(attestationRecord) == false) { revert IRegistry.ExternalError_SchemaValidation(); } } - function requireExternalSchemaValidation( - AttestationRecord[] memory attestationRecords, - SchemaRecord storage $schema - ) - internal - view - { + function requireExternalSchemaValidation(AttestationRecord[] memory attestationRecords, SchemaRecord storage $schema) internal { // only run this function if the selected schemaUID exists if ($schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); // validate Schema IExternalSchemaValidator validator = $schema.validator; // if validator is set, call the validator - if ( - address(validator) != ZERO_ADDRESS - && validator.validateSchema(attestationRecords) == false - ) { + if (address(validator) != ZERO_ADDRESS && validator.validateSchema(attestationRecords) == false) { revert IRegistry.ExternalError_SchemaValidation(); } } - function requireExternalResolverOnAttestation( - AttestationRecord memory attestationRecord, - ResolverRecord storage $resolver - ) - internal - { + function requireExternalResolverOnAttestation(AttestationRecord memory attestationRecord, ResolverRecord storage $resolver) internal { IExternalResolver resolverContract = $resolver.resolver; if (address(resolverContract) == ZERO_ADDRESS) return; @@ -133,13 +110,8 @@ library StubLib { if (address(resolverContract) != ZERO_ADDRESS) return; - if ( - resolverContract.resolveModuleRegistration({ - sender: msg.sender, - moduleAddress: moduleAddress, - record: moduleRecord - }) == false - ) { + if (resolverContract.resolveModuleRegistration({ sender: msg.sender, moduleAddress: moduleAddress, record: moduleRecord }) == false) + { revert IRegistry.ExternalError_ModuleRegistration(); } } diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 62807e35..2d06a87a 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -23,19 +23,10 @@ contract AttestationTest is BaseTest { for (uint256 i; i < types.length; i++) { typesEnc[i] = ModuleType.wrap(types[i]); } - request = AttestationRequest({ - moduleAddr: module, - expirationTime: expirationTime, - data: data, - moduleTypes: typesEnc - }); + request = AttestationRequest({ moduleAddr: module, expirationTime: expirationTime, data: data, moduleTypes: typesEnc }); } - function mockRevocation(address module) - internal - pure - returns (RevocationRequest memory request) - { + function mockRevocation(address module) internal pure returns (RevocationRequest memory request) { request = RevocationRequest({ moduleAddr: module }); } @@ -43,8 +34,7 @@ contract AttestationTest is BaseTest { address module = address(new MockModule()); registry.registerModule(defaultResolverUID, module, ""); uint32[] memory types = new uint32[](1); - AttestationRequest memory request = - mockAttestation(module, uint48(block.timestamp + 1), "", types); + AttestationRequest memory request = mockAttestation(module, uint48(block.timestamp + 1), "", types); // It should store. registry.attest(defaultSchemaUID, request); AttestationRecord memory record = registry.findAttestation(module, attester1.addr); @@ -59,8 +49,7 @@ contract AttestationTest is BaseTest { address module = address(new MockModule()); registry.registerModule(defaultResolverUID, module, ""); uint32[] memory types = new uint32[](1); - AttestationRequest memory request = - mockAttestation(module, uint48(block.timestamp + 1), "", types); + AttestationRequest memory request = mockAttestation(module, uint48(block.timestamp + 1), "", types); // It should store. registry.attest(defaultSchemaUID, request); AttestationRecord memory record = registry.findAttestation(module, attester1.addr); @@ -91,13 +80,7 @@ contract AttestationTest is BaseTest { assertEq(record.attester, attester1.addr); } - function test_WhenAttestingWithExpirationTimeInThePast( - address module, - bytes memory data, - uint32 moduleType - ) - external - { + function test_WhenAttestingWithExpirationTimeInThePast(address module, bytes memory data, uint32 moduleType) external { vm.assume(moduleType > 31); uint48 expirationTime = uint48(block.timestamp - 1000); @@ -151,10 +134,7 @@ contract AttestationTest is BaseTest { registry.attest(defaultSchemaUID, request); } - function test_WhenRevokingAttestationThatDoesntExist(address module) - external - prankWithAccount(attester1) - { + function test_WhenRevokingAttestationThatDoesntExist(address module) external prankWithAccount(attester1) { // It should revert. vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); registry.revoke(mockRevocation(module)); @@ -165,8 +145,7 @@ contract AttestationTest is BaseTest { // It should call ExternalResolver. uint32[] memory types = new uint32[](1); - AttestationRequest memory request = - mockAttestation(address(module1), uint48(block.timestamp + 1), "", types); + AttestationRequest memory request = mockAttestation(address(module1), uint48(block.timestamp + 1), "", types); // It should store. registry.attest(defaultSchemaUID, request); @@ -199,8 +178,7 @@ contract AttestationTest is BaseTest { uint32[] memory types = new uint32[](2); types[0] = 1; types[1] = 2; - AttestationRequest memory request = - mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + AttestationRequest memory request = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); bytes32 digest = registry.getDigest(request, attester1.addr); bytes memory sig = ecdsaSign(attester1.key, digest); @@ -262,8 +240,7 @@ contract AttestationTest is BaseTest { function test_WhenUsingInvalidECDSA() external whenAttestingWithSignature { uint32[] memory types = new uint32[](1); - AttestationRequest memory request = - mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + AttestationRequest memory request = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); bytes32 digest = registry.getDigest(request, attester1.addr); bytes memory sig = ecdsaSign(attester1.key, digest); @@ -289,14 +266,12 @@ contract AttestationTest is BaseTest { function test_WhenUsingValidERC1271() external whenAttestingWithSignature { uint32[] memory types = new uint32[](1); - AttestationRequest memory request = - mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + AttestationRequest memory request = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); bytes memory sig = "signature"; registry.attest(defaultSchemaUID, address(erc1271AttesterTrue), request, sig); - AttestationRecord memory record = - registry.findAttestation(address(module1), address(erc1271AttesterTrue)); + AttestationRecord memory record = registry.findAttestation(address(module1), address(erc1271AttesterTrue)); assertEq(record.time, block.timestamp); assertEq(record.expirationTime, request.expirationTime); @@ -313,8 +288,7 @@ contract AttestationTest is BaseTest { bytes memory sig = "signature"; registry.attest(defaultSchemaUID, address(erc1271AttesterTrue), requests, sig); - AttestationRecord memory record = - registry.findAttestation(address(module1), address(erc1271AttesterTrue)); + AttestationRecord memory record = registry.findAttestation(address(module1), address(erc1271AttesterTrue)); assertEq(record.time, block.timestamp); assertEq(record.expirationTime, requests[0].expirationTime); @@ -338,22 +312,14 @@ contract AttestationTest is BaseTest { function test_WhenUsingInvalidERC1271() external whenAttestingWithSignature { // It should revert. uint32[] memory types = new uint32[](1); - AttestationRequest memory request = - mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + AttestationRequest memory request = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); bytes memory sig = "signature"; vm.expectRevert(abi.encodeWithSelector(IRegistry.InvalidSignature.selector)); registry.attest(defaultSchemaUID, address(erc1271AttesterFalse), request, sig); } - function ecdsaSign( - uint256 privKey, - bytes32 digest - ) - internal - pure - returns (bytes memory signature) - { + function ecdsaSign(uint256 privKey, bytes32 digest) internal pure returns (bytes memory signature) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(privKey, digest); return abi.encodePacked(r, s, v); } diff --git a/test/Base.t.sol b/test/Base.t.sol index adcd66a8..bbbae36c 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -85,9 +85,7 @@ contract BaseTest is Test { vm.prank(opsEntity1.addr); defaultResolverUID = registry.registerResolver(IExternalResolver(address(resolverTrue))); vm.prank(opsEntity1.addr); - defaultSchemaUID = registry.registerSchema( - defaultSchema, IExternalSchemaValidator(address(schemaValidatorTrue)) - ); + defaultSchemaUID = registry.registerSchema(defaultSchema, IExternalSchemaValidator(address(schemaValidatorTrue))); vm.prank(moduleDev1.addr); registry.registerModule(defaultResolverUID, address(module1), ""); diff --git a/test/ModuleRegistration.t.sol b/test/ModuleRegistration.t.sol index be6a5ed1..ecfad1b7 100644 --- a/test/ModuleRegistration.t.sol +++ b/test/ModuleRegistration.t.sol @@ -39,10 +39,7 @@ contract ModuleRegistrationTest is BaseTest { assertTrue(moduleAddr == moduleAddrCalc); } - function test_WhenRegisteringAModuleOnAnInvalidResolverUID() - external - prankWithAccount(moduleDev1) - { + function test_WhenRegisteringAModuleOnAnInvalidResolverUID() external prankWithAccount(moduleDev1) { MockModule newModule = new MockModule(); // It should revert. ResolverUID invalidUID = ResolverUID.wrap(hex"00"); @@ -54,20 +51,14 @@ contract ModuleRegistrationTest is BaseTest { registry.registerModule(invalidUID, address(newModule), ""); } - function test_WhenRegisteringAModuleOnAValidResolverUID() - external - prankWithAccount(moduleDev1) - { + function test_WhenRegisteringAModuleOnAValidResolverUID() external prankWithAccount(moduleDev1) { // It should register. MockModule newModule = new MockModule(); registry.registerModule(defaultResolverUID, address(newModule), ""); } - function test_WhenRegisteringAModuleOnAInValidResolverUID() - external - prankWithAccount(moduleDev1) - { + function test_WhenRegisteringAModuleOnAInValidResolverUID() external prankWithAccount(moduleDev1) { // It should revert MockModule newModule = new MockModule(); @@ -75,17 +66,12 @@ contract ModuleRegistrationTest is BaseTest { registry.registerModule(ResolverUID.wrap(bytes32("foobar")), address(newModule), ""); } - function test_WhenRegisteringTwoModulesWithTheSameBytecode() - external - prankWithAccount(moduleDev1) - { + function test_WhenRegisteringTwoModulesWithTheSameBytecode() external prankWithAccount(moduleDev1) { MockModule newModule = new MockModule(); // It should revert. registry.registerModule(defaultResolverUID, address(newModule), ""); - vm.expectRevert( - abi.encodeWithSelector(IRegistry.AlreadyRegistered.selector, address(newModule)) - ); + vm.expectRevert(abi.encodeWithSelector(IRegistry.AlreadyRegistered.selector, address(newModule))); registry.registerModule(defaultResolverUID, address(newModule), ""); } @@ -96,22 +82,16 @@ contract ModuleRegistrationTest is BaseTest { factory.setReturnAddress(address(0)); vm.expectRevert(); - registry.deployViaFactory( - address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID - ); + registry.deployViaFactory(address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID); factory.setReturnAddress(address(1)); vm.expectRevert(); - registry.deployViaFactory( - address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID - ); + registry.deployViaFactory(address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID); MockModule newModule = new MockModule(); factory.setReturnAddress(address(newModule)); - registry.deployViaFactory( - address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID - ); + registry.deployViaFactory(address(factory), abi.encodeCall(factory.deployFn, ()), "", defaultResolverUID); } function test_WhenUsingInvalidFactory() public { diff --git a/test/Resolver.t.sol b/test/Resolver.t.sol index c69f0df1..d1c5fe36 100644 --- a/test/Resolver.t.sol +++ b/test/Resolver.t.sol @@ -50,4 +50,32 @@ contract ResolverTest is BaseTest { // ResolverRecord memory record = registry.resolvers(resolverUID); // assertEq(address(record.resolver), address(newResolver2)); } + + function test_WhenUpdatingOwnership_Authorized() external whenUpdatingResolver { + MockResolver newResolver = new MockResolver(false); + vm.prank(opsEntity1.addr); + ResolverUID resolverUID = registry.registerResolver(IExternalResolver(address(newResolver))); + ResolverRecord memory record = registry.findResolver(resolverUID); + assertEq(record.resolverOwner, opsEntity1.addr); + + vm.prank(opsEntity1.addr); + registry.transferResolverOwnership(resolverUID, opsEntity2.addr); + + record = registry.findResolver(resolverUID); + assertEq(record.resolverOwner, opsEntity2.addr); + } + + function test_WhenUpdatingOwnership_NotAuthorized() external whenUpdatingResolver { + MockResolver newResolver = new MockResolver(false); + vm.prank(opsEntity1.addr); + ResolverUID resolverUID = registry.registerResolver(IExternalResolver(address(newResolver))); + ResolverRecord memory record = registry.findResolver(resolverUID); + assertEq(record.resolverOwner, opsEntity1.addr); + + vm.prank(opsEntity2.addr); + vm.expectRevert(); + registry.transferResolverOwnership(resolverUID, opsEntity2.addr); + + + } } diff --git a/test/SchemaValidation.t.sol b/test/SchemaValidation.t.sol index 63b649d9..39a829b7 100644 --- a/test/SchemaValidation.t.sol +++ b/test/SchemaValidation.t.sol @@ -11,8 +11,7 @@ contract SchemaValidationTest is BaseTest { function test_WhenSchemaAlreadyRegistered() external whenRegisteringNewSchema { string memory schema = "schema"; SchemaUID uid = registry.registerSchema(schema, IExternalSchemaValidator(address(0))); - SchemaUID uid1 = - registry.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorFalse))); + SchemaUID uid1 = registry.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorFalse))); assertTrue(uid != uid1); } diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol index 1df8297e..6db7c1ca 100644 --- a/test/TrustDelegation.t.sol +++ b/test/TrustDelegation.t.sol @@ -17,11 +17,7 @@ contract TrustTest is AttestationTest { _; } - function test_WhenSupplyingOneAttester() - external - whenSettingAttester - prankWithAccount(smartAccount1) - { + function test_WhenSupplyingOneAttester() external whenSettingAttester prankWithAccount(smartAccount1) { // It should set. address[] memory trustedAttesters = new address[](1); trustedAttesters[0] = address(attester1.addr); @@ -31,11 +27,7 @@ contract TrustTest is AttestationTest { assertEq(result[0], address(attester1.addr)); } - function test_WhenSupplyingManyAttesters(address[] memory attesters) - external - whenSettingAttester - prankWithAccount(smartAccount1) - { + function test_WhenSupplyingManyAttesters(address[] memory attesters) external whenSettingAttester prankWithAccount(smartAccount1) { vm.assume(attesters.length < 100); vm.assume(attesters.length > 0); for (uint256 i; i < attesters.length; i++) { diff --git a/test/invariance/Handler.t.sol b/test/invariance/Handler.t.sol index 01040345..985d8977 100644 --- a/test/invariance/Handler.t.sol +++ b/test/invariance/Handler.t.sol @@ -58,19 +58,11 @@ contract Handler is CommonBase, StdCheats, StdUtils { function handle_registerSchema(string memory schema) public returns (SchemaUID uid) { MockSchemaValidator schemaValidatorTrue = new MockSchemaValidator(true); - uid = - REGISTRY.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorTrue))); + uid = REGISTRY.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorTrue))); SchemaRecord memory record = REGISTRY.findSchema(uid); } - function handle_registerModule( - uint256 randomResolverNr, - address moduleAddr, - bytes calldata bytecode, - bytes calldata metadata - ) - public - { + function handle_registerModule(uint256 randomResolverNr, address moduleAddr, bytes calldata bytecode, bytes calldata metadata) public { vm.etch(moduleAddr, bytecode); ResolverUID uid = _pickRandomResolverUID(randomResolverNr); @@ -122,17 +114,9 @@ contract Handler is CommonBase, StdCheats, StdUtils { REGISTRY.attest(uid, requests); } - function handle_registerModuleWithFactory( - uint256 randomResolverNr, - bytes calldata bytecode, - uint256 value - ) - external - { + function handle_registerModuleWithFactory(uint256 randomResolverNr, bytes calldata bytecode, uint256 value) external { vm.deal(address(this), value); ResolverUID uid = _pickRandomResolverUID(randomResolverNr); - REGISTRY.deployViaFactory{ value: value }( - address(FACTORY), abi.encodeCall(MockFactory.deploy, (bytecode)), "", uid - ); + REGISTRY.deployViaFactory{ value: value }(address(FACTORY), abi.encodeCall(MockFactory.deploy, (bytecode)), "", uid); } } diff --git a/test/invariance/invariant_ImmutableData.t.sol b/test/invariance/invariant_ImmutableData.t.sol index 98ae32ec..345c0216 100644 --- a/test/invariance/invariant_ImmutableData.t.sol +++ b/test/invariance/invariant_ImmutableData.t.sol @@ -10,8 +10,7 @@ contract ImmutableData is BaseTest { super.setUp(); handler = new Handler(registry); - AttestationRecord memory attRecord = - registry.findAttestation(address(module1), invarAttester.addr); + AttestationRecord memory attRecord = registry.findAttestation(address(module1), invarAttester.addr); defaultDataRef = attRecord.dataPointer; bytes4[] memory targetSelectors = new bytes4[](6); @@ -36,8 +35,7 @@ contract ImmutableData is BaseTest { } function invariant_attestation_immutable() public { - AttestationRecord memory record = - registry.findAttestation(address(module1), invarAttester.addr); + AttestationRecord memory record = registry.findAttestation(address(module1), invarAttester.addr); assertTrue(record.dataPointer == defaultDataRef); assertEq(record.moduleAddr, address(module1)); assertEq(record.attester, invarAttester.addr); diff --git a/test/mocks/MockResolver.sol b/test/mocks/MockResolver.sol index b6943c3a..27b8f326 100644 --- a/test/mocks/MockResolver.sol +++ b/test/mocks/MockResolver.sol @@ -24,43 +24,23 @@ contract MockResolver is IExternalResolver { if (interfaceId == type(IExternalResolver).interfaceId) return true; } - function resolveAttestation(AttestationRecord calldata attestation) - external - payable - override - returns (bool) - { + function resolveAttestation(AttestationRecord calldata attestation) external payable override returns (bool) { onAttestCalled = true; return returnVal; } - function resolveAttestation(AttestationRecord[] calldata attestation) - external - payable - override - returns (bool) - { + function resolveAttestation(AttestationRecord[] calldata attestation) external payable override returns (bool) { onAttestCalled = true; return returnVal; } - function resolveRevocation(AttestationRecord calldata attestation) - external - payable - override - returns (bool) - { + function resolveRevocation(AttestationRecord calldata attestation) external payable override returns (bool) { revert(); onRevokeCalled = true; return returnVal; } - function resolveRevocation(AttestationRecord[] calldata attestation) - external - payable - override - returns (bool) - { + function resolveRevocation(AttestationRecord[] calldata attestation) external payable override returns (bool) { revert(); onRevokeCalled = true; return returnVal; diff --git a/test/mocks/MockSchemaValidator.sol b/test/mocks/MockSchemaValidator.sol index 2e0c9b7c..7c98940d 100644 --- a/test/mocks/MockSchemaValidator.sol +++ b/test/mocks/MockSchemaValidator.sol @@ -14,21 +14,11 @@ contract MockSchemaValidator is IExternalSchemaValidator { if (interfaceId == type(IExternalSchemaValidator).interfaceId) return true; } - function validateSchema(AttestationRecord calldata attestation) - external - view - override - returns (bool) - { + function validateSchema(AttestationRecord calldata attestation) external view override returns (bool) { return returnVal; } - function validateSchema(AttestationRecord[] calldata attestations) - external - view - override - returns (bool) - { + function validateSchema(AttestationRecord[] calldata attestations) external view override returns (bool) { return returnVal; } } From e874a7720a216dd989d872a64c05c45186425518 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 14 Feb 2024 09:56:26 +0700 Subject: [PATCH 57/84] WIP --- .solhint.json | 7 +- package.json | 4 - pnpm-lock.yaml | 5 +- script/Create2Factory.sol | 150 ++++++++++++++++++ script/DeployRegistry.s.sol | 36 +++++ script/DeployRegistry.s.sol.bak | 28 ---- src/IRegistry.sol | 10 +- src/core/TrustManager.sol | 1 - src/core/TrustManagerExternalAttesterList.sol | 3 - src/lib/ModuleDeploymentLib.sol | 7 +- test/Base.t.sol | 4 +- test/Factory.t.sol | 32 ++++ test/Resolver.t.sol | 2 - test/mocks/MockFactory.sol | 1 - 14 files changed, 238 insertions(+), 52 deletions(-) create mode 100644 script/Create2Factory.sol create mode 100644 script/DeployRegistry.s.sol delete mode 100644 script/DeployRegistry.s.sol.bak create mode 100644 test/Factory.t.sol diff --git a/.solhint.json b/.solhint.json index f24c4323..9cfc1b73 100644 --- a/.solhint.json +++ b/.solhint.json @@ -6,10 +6,11 @@ "compiler-version": ["error", ">=0.8.0"], "contract-name-camelcase": "off", "const-name-snakecase": "off", - "custom-errors": "off", - "func-name-mixedcase": "off", + "custom-errors": "error", + "no-inline-assembly": "off", + "func-name-mixedcase": "error", "func-visibility": ["error", { "ignoreConstructors": true }], - "max-line-length": ["error", 123], + "max-line-length": ["error", 140], "named-parameters-mapping": "warn", "no-empty-blocks": "off", "not-rely-on-time": "off", diff --git a/package.json b/package.json index a2c8c564..f03faec7 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "devDependencies": { "ds-test": "github:dapphub/ds-test#e282159d5170298eb2455a6c05280ab5a73a4ef0", "forge-std": "github:foundry-rs/forge-std#v1.7.6", - "prettier": "^2.8.8", "solmate": "github:transmissions11/solmate#c892309933b25c03d32b1b0d674df7ae292ba925", "solady": "github:vectorized/solady#9deb9ed36a27261a8745db5b7cd7f4cdc3b1cd4e", "solhint": "^4.1.1" @@ -49,11 +48,8 @@ "gas:report": "forge test --gas-report --mp \"./test/integration/**/*.sol\" --nmt \"test(Fuzz)?_RevertWhen_\\w{1,}?\"", "gas:snapshot": "forge snapshot --mp \"./test/integration/**/*.sol\" --nmt \"test(Fuzz)?_RevertWhen_\\w{1,}?\"", "gas:snapshot:optimized": "pnpm run build:optimized && FOUNDRY_PROFILE=test-optimized forge snapshot --mp \"./test/integration/**/*.sol\" --nmt \"test(Fork)?(Fuzz)?_RevertWhen_\\w{1,}?\"", - "lint": "pnpm run lint:sol && bun run prettier:check", "lint:sol": "forge fmt --check && pnpm solhint \"{script,src,test}/**/*.sol\"", "prepack": "pnpm install", - "prettier:check": "prettier --check \"**/*.{json,md,svg,yml}\"", - "prettier:write": "prettier --write \"**/*.{json,md,svg,yml}\"", "test": "forge test", "test:lite": "FOUNDRY_PROFILE=lite forge test", "test:optimized": "pnpm run build:optimized && FOUNDRY_PROFILE=test-optimized forge test" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab03e4a5..9180337a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,9 +16,6 @@ devDependencies: forge-std: specifier: github:foundry-rs/forge-std#v1.7.6 version: github.com/foundry-rs/forge-std/ae570fec082bfe1c1f45b0acca4a2b4f84d345ce - prettier: - specifier: ^2.8.8 - version: 2.8.8 solady: specifier: github:vectorized/solady#9deb9ed36a27261a8745db5b7cd7f4cdc3b1cd4e version: github.com/vectorized/solady/9deb9ed36a27261a8745db5b7cd7f4cdc3b1cd4e @@ -548,7 +545,9 @@ packages: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} hasBin: true + requiresBuild: true dev: true + optional: true /proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} diff --git a/script/Create2Factory.sol b/script/Create2Factory.sol new file mode 100644 index 00000000..88c9ef2b --- /dev/null +++ b/script/Create2Factory.sol @@ -0,0 +1,150 @@ +pragma solidity 0.8.24; + +/** + * @title Immutable Create2 Contract Factory + * @author 0age + * @notice This contract provides a safeCreate2 function that takes a salt value + * and a block of initialization code as arguments and passes them into inline + * assembly. The contract prevents redeploys by maintaining a mapping of all + * contracts that have already been deployed, and prevents frontrunning or other + * collisions by requiring that the first 20 bytes of the salt are equal to the + * address of the caller (this can be bypassed by setting the first 20 bytes to + * the null address). There is also a view function that computes the address of + * the contract that will be created when submitting a given salt or nonce along + * with a given block of initialization code. + * @dev CREATE2 will not be available on mainnet until (at least) block + * 7,280,000. This contract has not yet been fully tested or audited - proceed + * with caution and please share any exploits or optimizations you discover. + */ +contract ImmutableCreate2Factory { + // mapping to track which addresses have already been deployed. + mapping(address => bool) private _deployed; + + function safeCreate2( + bytes32 salt, + bytes calldata initializationCode + ) + external + payable + containsCaller(salt) + returns (address deploymentAddress) + { + // move the initialization code from calldata to memory. + bytes memory initCode = initializationCode; + + // determine the target address for contract deployment. + address targetDeploymentAddress = address( + uint160( // downcast to match the address type. + uint256( // convert to uint to truncate upper digits. + keccak256( // compute the CREATE2 hash using 4 inputs. + abi.encodePacked( // pack all inputs to the hash together. + hex"ff", // start with 0xff to distinguish from RLP. + address(this), // this contract will be the caller. + salt, // pass in the supplied salt value. + keccak256( // pass in the hash of initialization code. + abi.encodePacked(initCode)) + ) + ) + ) + ) + ); + + // ensure that a contract hasn't been previously deployed to target address. + require(!_deployed[targetDeploymentAddress], "Invalid contract creation - contract has already been deployed."); + + // using inline assembly: load data and length of data, then call CREATE2. + assembly { + // solhint-disable-line + let encoded_data := add(0x20, initCode) // load initialization code. + let encoded_size := mload(initCode) // load the init code's length. + deploymentAddress := + create2( // call CREATE2 with 4 arguments. + callvalue(), // forward any attached value. + encoded_data, // pass in initialization code. + encoded_size, // pass in init code's length. + salt // pass in the salt value. + ) + } + + // check address against target to ensure that deployment was successful. + require(deploymentAddress == targetDeploymentAddress, "Failed to deploy contract using provided salt and initialization code."); + + // record the deployment of the contract to prevent redeploys. + _deployed[deploymentAddress] = true; + } + + function findCreate2Address(bytes32 salt, bytes calldata initCode) external view returns (address deploymentAddress) { + // determine the address where the contract will be deployed. + deploymentAddress = address( + uint160( // downcast to match the address type. + uint256( // convert to uint to truncate upper digits. + keccak256( // compute the CREATE2 hash using 4 inputs. + abi.encodePacked( // pack all inputs to the hash together. + hex"ff", // start with 0xff to distinguish from RLP. + address(this), // this contract will be the caller. + salt, // pass in the supplied salt value. + keccak256( // pass in the hash of initialization code. + abi.encodePacked(initCode)) + ) + ) + ) + ) + ); + + // return null address to signify failure if contract has been deployed. + if (_deployed[deploymentAddress]) { + return address(0); + } + } + + function findCreate2AddressViaHash(bytes32 salt, bytes32 initCodeHash) external view returns (address deploymentAddress) { + // determine the address where the contract will be deployed. + deploymentAddress = address( + uint160( // downcast to match the address type. + uint256( // convert to uint to truncate upper digits. + keccak256( // compute the CREATE2 hash using 4 inputs. + abi.encodePacked( // pack all inputs to the hash together. + hex"ff", // start with 0xff to distinguish from RLP. + address(this), // this contract will be the caller. + salt, // pass in the supplied salt value. + initCodeHash // pass in the hash of initialization code. + ) + ) + ) + ) + ); + + // return null address to signify failure if contract has been deployed. + if (_deployed[deploymentAddress]) { + return address(0); + } + } + + /** + * @dev Determine if a contract has already been deployed by the factory to a + * given address. + * @param deploymentAddress address The contract address to check. + * @return True if the contract has been deployed, false otherwise. + */ + function hasBeenDeployed(address deploymentAddress) external view returns (bool) { + // determine if a contract has been deployed to the provided address. + return _deployed[deploymentAddress]; + } + + /** + * @dev Modifier to ensure that the first 20 bytes of a submitted salt match + * those of the calling account. This provides protection against the salt + * being stolen by frontrunners or other attackers. The protection can also be + * bypassed if desired by setting each of the first 20 bytes to zero. + * @param salt bytes32 The salt value to check against the calling address. + */ + modifier containsCaller(bytes32 salt) { + // prevent contract submissions from being stolen from tx.pool by requiring + // that the first 20 bytes of the submitted salt match msg.sender. + require( + (address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)), + "Invalid salt - first 20 bytes of the salt must match calling address." + ); + _; + } +} diff --git a/script/DeployRegistry.s.sol b/script/DeployRegistry.s.sol new file mode 100644 index 00000000..ce0acc2f --- /dev/null +++ b/script/DeployRegistry.s.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { Script } from "forge-std/Script.sol"; +import { IExternalResolver } from "../src/external/IExternalResolver.sol"; +import { Registry } from "../src/Registry.sol"; +import { ResolverUID } from "../src/DataTypes.sol"; +import { console2 } from "forge-std/console2.sol"; +import "./Create2Factory.sol"; + +/** + * @title DeployRegistryScript + * @author zeroknots + */ +contract DeployRegistryScript is Script { + function run() public { + bytes32 salt = 0x05a40beaf368eb6b2bc5665901a885c044c19346fc828ba80a35fe4cc30d0000; + + bytes memory initcode = type(Registry).creationCode; + bytes32 initcodeHash = keccak256(initcode); + console2.log("initcodeHash:"); + console2.logBytes32(initcodeHash); + + ImmutableCreate2Factory factory = new ImmutableCreate2Factory(); + bytes memory code = address(factory).code; + + vm.startBroadcast(vm.envUint("PK")); + address factoryAddress = address(0x0000000000FFe8B47B3e2130213B802212439497); + vm.etch(factoryAddress, code); + factory = ImmutableCreate2Factory(factoryAddress); + + Registry registry = Registry(factory.safeCreate2(salt, initcode)); + + console2.log("Deployed Registry @", address(registry)); + } +} diff --git a/script/DeployRegistry.s.sol.bak b/script/DeployRegistry.s.sol.bak deleted file mode 100644 index 30f5f4de..00000000 --- a/script/DeployRegistry.s.sol.bak +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import { Script } from "forge-std/Script.sol"; -import { IExternalResolver } from "../src/external/IExternalResolver.sol"; -import { ResolverUID } from "../src/DataTypes.sol"; -import { console2 } from "forge-std/console2.sol"; - -/** - * @title DeployRegistryScript - * @author zeroknots - */ -contract DeployRegistryScript is Script { - function run() public { - bytes32 salt = bytes32(uint256(2)); - - vm.startBroadcast(vm.envUint("PK")); - - // Deploy Registry - RegistryInstance memory instance = _setupInstance({ name: "Registry", salt: salt }); - - // Set up default resolver - DebugResolver debugResolver = new DebugResolver{ salt: salt }(address(instance.registry)); - instance.registry.registerResolver(IExternalResolver(address(debugResolver))); - - vm.stopBroadcast(); - } -} diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 81645cf8..80de16bb 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -222,7 +222,9 @@ interface IRegistry is IERC7484 { /** * This registry implements a CREATE2 factory, that allows module developers to register and deploy module bytecode - * @param salt The salt to be used in the CREATE2 factory + * @param salt The salt to be used in the CREATE2 factory. This adheres to Pr000xy/Create2Factory.sol salt formatting. + * The salt's first bytes20 should be the address of the sender + * or bytes20(0) to bypass the check (this will lose replay protection) * @param resolverUID The resolverUID to be used in the CREATE2 factory * @param initCode The initCode to be used in the CREATE2 factory * @param metadata The metadata to be stored on the registry. @@ -243,7 +245,8 @@ interface IRegistry is IERC7484 { * Registry can use other factories to deploy the module * @notice This function is used to deploy and register a module using a factory contract. * Since one of the parameters of this function is a unique resolverUID and any - * registered module address can only be registered once, using this function is of risk for a frontrun attack + * registered module address can only be registered once, + * using this function is of risk for a frontrun attack */ function deployViaFactory( address factory, @@ -259,7 +262,8 @@ interface IRegistry is IERC7484 { * Already deployed module addresses can be registered on the registry * @notice This function is used to deploy and register an already deployed module. * Since one of the parameters of this function is a unique resolverUID and any - * registered module address can only be registered once, using this function is of risk for a frontrun attack + * registered module address can only be registered once, + * using this function is of risk for a frontrun attack * @param resolverUID The resolverUID to be used for the module * @param moduleAddress The address of the module to be registered * @param metadata The metadata to be stored on the registry. diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 0d18c342..77647cb3 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -146,7 +146,6 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { * PackedModuleTypes packedModuleType = record.moduleTypes; */ - // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let slot := sload($attestation.slot) diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index 4fcbeb85..816992f4 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -15,7 +15,6 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload($attestation.slot) @@ -67,7 +66,6 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload($attestation.slot) @@ -123,7 +121,6 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables // @dev the solidity version of the assembly code is above - // solhint-disable-next-line no-inline-assembly assembly { let mask := 0xffffffffffff let times := sload($attestation.slot) diff --git a/src/lib/ModuleDeploymentLib.sol b/src/lib/ModuleDeploymentLib.sol index 5cb9eb79..b9b41b8b 100644 --- a/src/lib/ModuleDeploymentLib.sol +++ b/src/lib/ModuleDeploymentLib.sol @@ -7,11 +7,14 @@ pragma solidity ^0.8.24; * @author zeroknots */ library ModuleDeploymentLib { + error InvalidSalt(); + error InvalidAddress(); // source: https://github.com/0age/metamorphic/blob/master/contracts/ImmutableCreate2Factory.sol#L194-L203 + modifier containsCaller(bytes32 salt) { // prevent contract submissions from being stolen from tx.pool by requiring // that the first 20 bytes of the submitted salt match msg.sender. - require((address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)), "Invalid salt"); + if ((address(bytes20(salt)) != msg.sender) && (bytes20(salt) != bytes20(0))) revert InvalidSalt(); _; } @@ -36,7 +39,7 @@ library ModuleDeploymentLib { } // check address against target to ensure that deployment was successful. - require(deploymentAddress == targetDeploymentAddress, "invalid address"); + if (deploymentAddress != targetDeploymentAddress) revert InvalidAddress(); } /** diff --git a/test/Base.t.sol b/test/Base.t.sol index bbbae36c..2f15a898 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -10,7 +10,7 @@ import "./mocks/MockERC1271Attester.sol"; import "./mocks/MockModule.sol"; contract BaseTest is Test { - Registry registry; + Registry internal registry; Account smartAccount1; Account smartAccount2; @@ -40,7 +40,7 @@ contract BaseTest is Test { string defaultSchema = "Foobar"; SchemaUID defaultSchemaUID; - ResolverUID defaultResolverUID; + ResolverUID internal defaultResolverUID; function setUp() public virtual { vm.warp(1_641_070_800); diff --git a/test/Factory.t.sol b/test/Factory.t.sol new file mode 100644 index 00000000..dba04d4c --- /dev/null +++ b/test/Factory.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "./Base.t.sol"; +import "../script/Create2Factory.sol"; + +contract MockModuleFoo { + uint256 public value; + + constructor() { + value = 1; + } +} + +contract FactoryTest is BaseTest { + ImmutableCreate2Factory factory; + + function setUp() public override { + super.setUp(); + } + + function test_EnsureFactoryCalc() public { + bytes32 salt = 0x05a40beaf368eb6b2bc5665901a885c044c19346fc828ba80a35fe4cc30d0000; + + vm.startPrank(address(0x05a40beAF368EB6b2bc5665901a885C044C19346)); + address moduleAddr = registry.calcModuleAddress(salt, type(MockModuleFoo).creationCode); + + address deployedAddr = registry.deployModule(salt, defaultResolverUID, type(MockModuleFoo).creationCode, ""); + vm.stopPrank(); + assertTrue(moduleAddr == deployedAddr); + } +} diff --git a/test/Resolver.t.sol b/test/Resolver.t.sol index d1c5fe36..c3b55982 100644 --- a/test/Resolver.t.sol +++ b/test/Resolver.t.sol @@ -75,7 +75,5 @@ contract ResolverTest is BaseTest { vm.prank(opsEntity2.addr); vm.expectRevert(); registry.transferResolverOwnership(resolverUID, opsEntity2.addr); - - } } diff --git a/test/mocks/MockFactory.sol b/test/mocks/MockFactory.sol index 6af84c2d..09d3ef47 100644 --- a/test/mocks/MockFactory.sol +++ b/test/mocks/MockFactory.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.24; contract MockFactory { function deploy(bytes memory bytecode) external returns (address addr) { - // solhint-disable-next-line no-inline-assembly assembly { addr := create(0, add(bytecode, 0x20), mload(bytecode)) if iszero(extcodesize(addr)) { revert(0, 0) } From fa68503860d5d9b2acd552d107a1be093fe8a799 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 14 Feb 2024 12:48:03 +0700 Subject: [PATCH 58/84] feat: adding event that shows if module registration was done via registry or externally --- src/IRegistry.sol | 2 +- src/Registry.sol | 5 ++++- src/core/ModuleManager.sol | 13 +++---------- test/Attestation.t.sol | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 80de16bb..e9d3a417 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -213,7 +213,7 @@ interface IRegistry is IERC7484 { /* Module Registration */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Event triggered when a module is deployed. - event ModuleRegistration(address indexed implementation, address indexed sender, ResolverUID resolverUID); + event ModuleRegistration(address indexed implementation, ResolverUID resolverUID, bool deployedViaRegistry); error AlreadyRegistered(address module); error InvalidDeployment(); diff --git a/src/Registry.sol b/src/Registry.sol index 59d1dae8..25e3b0d2 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -7,4 +7,7 @@ import { IRegistry } from "./IRegistry.sol"; * @author zeroknots */ -contract Registry is IRegistry, SignedAttestation { } +contract Registry is IRegistry, SignedAttestation { +// TODO: should we create a default resolverUID thats address(0). +// this will allow the registry to be usable right after deployment without any resolver +} diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 6e77682a..4da58042 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -54,12 +54,8 @@ abstract contract ModuleManager is IRegistry, ResolverManager { moduleAddress = initCode.deploy(salt); // _storeModuleRecord() will check if module is already registered, // which should prevent reentry to any deploy function - ModuleRecord memory record = _storeModuleRecord({ - moduleAddress: moduleAddress, // TODO: is this reentrancy? - sender: msg.sender, - resolverUID: resolverUID, - metadata: metadata - }); + ModuleRecord memory record = + _storeModuleRecord({ moduleAddress: moduleAddress, sender: msg.sender, resolverUID: resolverUID, metadata: metadata }); record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, $resolver: $resolver }); } @@ -109,7 +105,6 @@ abstract contract ModuleManager is IRegistry, ResolverManager { // prevent someone from calling a registry function pretending its a factory if (factory == address(this)) revert FactoryCallFailed(factory); // call external factory to deploy module - // TODO: this could be reentrancy since its not using CEI (bool ok, bytes memory returnData) = factory.call{ value: msg.value }(callOnFactory); if (!ok) revert FactoryCallFailed(factory); @@ -119,7 +114,6 @@ abstract contract ModuleManager is IRegistry, ResolverManager { ModuleRecord memory record = _storeModuleRecord({ moduleAddress: moduleAddress, - // TODO: should we use msg.sender or the factory address? sender: ZERO_ADDRESS, // setting sender to address(0) since anyone can invoke this function resolverUID: resolverUID, metadata: metadata @@ -154,8 +148,7 @@ abstract contract ModuleManager is IRegistry, ResolverManager { $moduleAddrToRecords[moduleAddress] = moduleRegistration; // Emit ModuleRegistration event - // TODO: add flag to event to indicate if module was deployed or registered - emit ModuleRegistration({ implementation: moduleAddress, sender: sender, resolverUID: resolverUID }); + emit ModuleRegistration({ implementation: moduleAddress, resolverUID: resolverUID, deployedViaRegistry: sender == msg.sender }); } /** diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 2d06a87a..5d6beb12 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -130,7 +130,7 @@ contract AttestationTest is BaseTest { expirationTime = uint48(block.timestamp + expirationTime + 100); AttestationRequest memory request = mockAttestation(module, expirationTime, data, types); // It should revert. - vm.expectRevert(); // TODO: should we allow counterfactual? + vm.expectRevert(); registry.attest(defaultSchemaUID, request); } From afa47334a9dca2cd094ad6ad5139b417f133b953 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 14 Feb 2024 13:49:08 +0700 Subject: [PATCH 59/84] chore: docs and removing unsafe fn --- src/IRegistry.sol | 1 + src/core/TrustManager.sol | 13 +++- src/core/TrustManagerExternalAttesterList.sol | 70 ++++--------------- 3 files changed, 24 insertions(+), 60 deletions(-) diff --git a/src/IRegistry.sol b/src/IRegistry.sol index e9d3a417..48cb272b 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -65,6 +65,7 @@ interface IRegistry is IERC7484 { /** * Allows smartaccounts - the end users of the registry - to appoint * one or many attesters as trusted. + * @notice this function reverts, if address(0), or duplicates are provided in attesters[] * * @param threshold The minimum number of attestations required for a module * to be considered secure. diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 77647cb3..1f81be4f 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -27,13 +27,18 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { /** * @inheritdoc IRegistry - * @dev delibrately using memory here, so we can sort the array */ - function trustAttesters(uint8 threshold, address[] memory attesters) external { + function trustAttesters( + uint8 threshold, + address[] memory attesters // deliberately using memory to allow sorting and uniquifying + ) + external + { uint256 attestersLength = attesters.length; // sort attesters and remove duplicates attesters.sort(); attesters.uniquifySorted(); + // if attesters array has duplicates, revert if (attestersLength == 0) revert InvalidTrustedAttesterInput(); if (attesters.length != attestersLength) revert InvalidTrustedAttesterInput(); @@ -47,6 +52,7 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { $trustedAttester.attester = attesters[0]; attestersLength--; + // setup the linked list of trusted attesters for (uint256 i; i < attestersLength; i++) { address _attester = attesters[i]; // user could have set attester to address(0) @@ -130,6 +136,9 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { * - not revoked * - not expired * - correct module type (if not ZERO_MODULE_TYPE) + * @notice this function reverts if the attestationRecord is not valid + * @param expectedType the expected module type. if this is ZERO_MODULE_TYPE, types specified in the attestation are ignored + * @param $attestation the storage reference of the attestation record to check */ function _requireValidAttestation(ModuleType expectedType, AttestationRecord storage $attestation) internal view { uint256 attestedAt; diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index 816992f4..8a1b1051 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -3,9 +3,17 @@ pragma solidity ^0.8.24; import { AttestationRecord } from "../DataTypes.sol"; import { ZERO_TIMESTAMP } from "../Common.sol"; -import { IRegistry } from "../IRegistry.sol"; +// solhint-disable-next-line no-unused-import +import { IRegistry, IERC7484 } from "../IRegistry.sol"; +/** + * If smart accounts want to query the registry, and supply a list of trusted attesters in calldata, this component can be used + * @dev This contract is abstract and provides utility functions to query attestations with a calldata provided list of attesters + */ abstract contract TrustManagerExternalAttesterList is IRegistry { + /** + * @inheritdoc IERC7484 + */ function check(address module, address attester) external view returns (uint256 attestedAt) { AttestationRecord storage $attestation = _getAttestation(module, attester); @@ -40,6 +48,9 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { } } + /** + * @inheritdoc IERC7484 + */ function checkN( address module, address[] calldata attesters, @@ -95,62 +106,5 @@ abstract contract TrustManagerExternalAttesterList is IRegistry { revert InsufficientAttestations(); } - function checkNUnsafe( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray) - { - uint256 attestersLength = attesters.length; - if (attestersLength < threshold || threshold == 0) { - threshold = attestersLength; - } - - uint256 timeNow = block.timestamp; - attestedAtArray = new uint256[](attestersLength); - - for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage $attestation = _getAttestation(module, attesters[i]); - - uint256 attestationTime; // = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - assembly { - let mask := 0xffffffffffff - let times := sload($attestation.slot) - attestationTime := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (revocationTime != ZERO_TIMESTAMP) { - attestedAtArray[i] = 0; - continue; - } - - attestedAtArray[i] = attestationTime; - - if (expirationTime != ZERO_TIMESTAMP) { - if (timeNow > expirationTime) { - attestedAtArray[i] = 0; - continue; - } - } - - if (attestationTime == ZERO_TIMESTAMP) continue; - if (threshold != 0) --threshold; - } - if (threshold == 0) return attestedAtArray; - revert InsufficientAttestations(); - } - function _getAttestation(address module, address attester) internal view virtual returns (AttestationRecord storage $attestation); } From 350cdd9001705a91cd42a82c8ee3e0cd055714e5 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Fri, 16 Feb 2024 12:56:45 +0700 Subject: [PATCH 60/84] feat: update CI/CD --- .github/workflows/ci.yaml | 19 +++++++++++++++++++ .github/workflows/foundryTest.yml | 19 ------------------- .github/workflows/slither.yml | 8 -------- 3 files changed, 19 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/ci.yaml delete mode 100644 .github/workflows/foundryTest.yml delete mode 100644 .github/workflows/slither.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..44450bfc --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,19 @@ +on: + - push + - pull_request + +jobs: + lint: + uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-lint.yaml@main" + + build: + uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-build.yaml@main" + + test-unit: + needs: ["build"] + uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-test.yaml@main" + with: + foundry-fuzz-runs: 5000 + foundry-profile: "test" + match-path: "test/**/*.sol" + name: "Unit tests" diff --git a/.github/workflows/foundryTest.yml b/.github/workflows/foundryTest.yml deleted file mode 100644 index 78dc9449..00000000 --- a/.github/workflows/foundryTest.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Foundry Tests -on: [push] -jobs: - check: - name: Foundry project - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - - - name: Run tests - run: forge test -vvv - - - name: Run snapshot - run: forge snapshot diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml deleted file mode 100644 index 47e01e81..00000000 --- a/.github/workflows/slither.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Slither Analysis -on: [push] -jobs: - analyze: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: crytic/slither-action@v0.3.1 From d24b4addea899d7503ad64d3f4ca700041031de6 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Mon, 19 Feb 2024 21:08:45 +0700 Subject: [PATCH 61/84] docs: adding nods --- .github/workflows/ci.yaml | 2 +- .github/workflows/slither.yaml | 8 + docs/.gitignore | 1 + docs/book.css | 13 + docs/book.toml | 12 + docs/solidity.min.js | 74 ++ docs/src/README.md | 111 +++ docs/src/SUMMARY.md | 51 ++ docs/src/src/Common.sol/constants.Common.md | 45 ++ .../src/Common.sol/function._isContract.md | 22 + docs/src/src/Common.sol/function._time.md | 11 + .../function.attestationDataRefEq.md | 8 + .../DataTypes.sol/function.moduleTypeEq.md | 8 + .../DataTypes.sol/function.moduleTypeNeq.md | 8 + .../src/DataTypes.sol/function.resolverEq.md | 8 + .../DataTypes.sol/function.resolverNotEq.md | 8 + .../src/DataTypes.sol/function.schemaEq.md | 8 + .../src/DataTypes.sol/function.schemaNotEq.md | 8 + .../DataTypes.sol/struct.AttestationRecord.md | 17 + .../struct.AttestationRequest.md | 15 + .../src/DataTypes.sol/struct.ModuleRecord.md | 12 + .../DataTypes.sol/struct.ResolverRecord.md | 11 + .../DataTypes.sol/struct.RevocationRequest.md | 12 + .../src/DataTypes.sol/struct.SchemaRecord.md | 12 + .../struct.TrustedAttesterRecord.md | 13 + .../DataTypes.sol/type.AttestationDataRef.md | 8 + docs/src/src/DataTypes.sol/type.ModuleType.md | 8 + .../DataTypes.sol/type.PackedModuleTypes.md | 8 + .../src/src/DataTypes.sol/type.ResolverUID.md | 8 + docs/src/src/DataTypes.sol/type.SchemaUID.md | 8 + .../src/IRegistry.sol/interface.IERC7484.md | 47 ++ .../src/IRegistry.sol/interface.IRegistry.md | 645 ++++++++++++++++++ docs/src/src/README.md | 31 + .../src/src/Registry.sol/contract.Registry.md | 10 + .../Attestation.sol/abstract.Attestation.md | 102 +++ .../abstract.AttestationManager.md | 146 ++++ .../abstract.ModuleManager.md | 145 ++++ docs/src/src/core/README.md | 11 + .../abstract.ResolverManager.md | 108 +++ .../abstract.SchemaManager.md | 69 ++ .../contract.SignedAttestation.md | 137 ++++ .../TrustManager.sol/abstract.TrustManager.md | 121 ++++ ...stract.TrustManagerExternalAttesterList.md | 33 + .../interface.IExternalResolver.md | 91 +++ .../interface.IExternalSchemaValidator.md | 26 + docs/src/src/external/README.md | 6 + .../contract.ERC7512SchemaValidator.md | 29 + .../ERC7512Schema.sol/interface.ERC7512.md | 66 ++ docs/src/src/external/examples/README.md | 7 + .../ResolverBase.sol/abstract.ResolverBase.md | 30 + .../contract.TokenizedResolver.md | 81 +++ .../library.AttestationLib.md | 72 ++ .../src/src/lib/Helpers.sol/library.UIDLib.md | 47 ++ .../library.ModuleDeploymentLib.md | 69 ++ .../library.ModuleTypeLib.md | 26 + docs/src/src/lib/README.md | 8 + .../src/lib/StubLib.sol/library.StubLib.md | 80 +++ foundry.toml | 3 + slither.config.json | 9 + src/core/AttestationManager.sol | 3 + 60 files changed, 2795 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/slither.yaml create mode 100644 docs/.gitignore create mode 100644 docs/book.css create mode 100644 docs/book.toml create mode 100644 docs/solidity.min.js create mode 100644 docs/src/README.md create mode 100644 docs/src/SUMMARY.md create mode 100644 docs/src/src/Common.sol/constants.Common.md create mode 100644 docs/src/src/Common.sol/function._isContract.md create mode 100644 docs/src/src/Common.sol/function._time.md create mode 100644 docs/src/src/DataTypes.sol/function.attestationDataRefEq.md create mode 100644 docs/src/src/DataTypes.sol/function.moduleTypeEq.md create mode 100644 docs/src/src/DataTypes.sol/function.moduleTypeNeq.md create mode 100644 docs/src/src/DataTypes.sol/function.resolverEq.md create mode 100644 docs/src/src/DataTypes.sol/function.resolverNotEq.md create mode 100644 docs/src/src/DataTypes.sol/function.schemaEq.md create mode 100644 docs/src/src/DataTypes.sol/function.schemaNotEq.md create mode 100644 docs/src/src/DataTypes.sol/struct.AttestationRecord.md create mode 100644 docs/src/src/DataTypes.sol/struct.AttestationRequest.md create mode 100644 docs/src/src/DataTypes.sol/struct.ModuleRecord.md create mode 100644 docs/src/src/DataTypes.sol/struct.ResolverRecord.md create mode 100644 docs/src/src/DataTypes.sol/struct.RevocationRequest.md create mode 100644 docs/src/src/DataTypes.sol/struct.SchemaRecord.md create mode 100644 docs/src/src/DataTypes.sol/struct.TrustedAttesterRecord.md create mode 100644 docs/src/src/DataTypes.sol/type.AttestationDataRef.md create mode 100644 docs/src/src/DataTypes.sol/type.ModuleType.md create mode 100644 docs/src/src/DataTypes.sol/type.PackedModuleTypes.md create mode 100644 docs/src/src/DataTypes.sol/type.ResolverUID.md create mode 100644 docs/src/src/DataTypes.sol/type.SchemaUID.md create mode 100644 docs/src/src/IRegistry.sol/interface.IERC7484.md create mode 100644 docs/src/src/IRegistry.sol/interface.IRegistry.md create mode 100644 docs/src/src/README.md create mode 100644 docs/src/src/Registry.sol/contract.Registry.md create mode 100644 docs/src/src/core/Attestation.sol/abstract.Attestation.md create mode 100644 docs/src/src/core/AttestationManager.sol/abstract.AttestationManager.md create mode 100644 docs/src/src/core/ModuleManager.sol/abstract.ModuleManager.md create mode 100644 docs/src/src/core/README.md create mode 100644 docs/src/src/core/ResolverManager.sol/abstract.ResolverManager.md create mode 100644 docs/src/src/core/SchemaManager.sol/abstract.SchemaManager.md create mode 100644 docs/src/src/core/SignedAttestation.sol/contract.SignedAttestation.md create mode 100644 docs/src/src/core/TrustManager.sol/abstract.TrustManager.md create mode 100644 docs/src/src/core/TrustManagerExternalAttesterList.sol/abstract.TrustManagerExternalAttesterList.md create mode 100644 docs/src/src/external/IExternalResolver.sol/interface.IExternalResolver.md create mode 100644 docs/src/src/external/IExternalSchemaValidator.sol/interface.IExternalSchemaValidator.md create mode 100644 docs/src/src/external/README.md create mode 100644 docs/src/src/external/examples/ERC7512Schema.sol/contract.ERC7512SchemaValidator.md create mode 100644 docs/src/src/external/examples/ERC7512Schema.sol/interface.ERC7512.md create mode 100644 docs/src/src/external/examples/README.md create mode 100644 docs/src/src/external/examples/ResolverBase.sol/abstract.ResolverBase.md create mode 100644 docs/src/src/external/examples/TokenizedResolver.sol/contract.TokenizedResolver.md create mode 100644 docs/src/src/lib/AttestationLib.sol/library.AttestationLib.md create mode 100644 docs/src/src/lib/Helpers.sol/library.UIDLib.md create mode 100644 docs/src/src/lib/ModuleDeploymentLib.sol/library.ModuleDeploymentLib.md create mode 100644 docs/src/src/lib/ModuleTypeLib.sol/library.ModuleTypeLib.md create mode 100644 docs/src/src/lib/README.md create mode 100644 docs/src/src/lib/StubLib.sol/library.StubLib.md create mode 100644 slither.config.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 44450bfc..99bb03c0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,7 +10,7 @@ jobs: uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-build.yaml@main" test-unit: - needs: ["build"] + needs: ["lint", "build"] uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-test.yaml@main" with: foundry-fuzz-runs: 5000 diff --git a/.github/workflows/slither.yaml b/.github/workflows/slither.yaml new file mode 100644 index 00000000..47e01e81 --- /dev/null +++ b/.github/workflows/slither.yaml @@ -0,0 +1,8 @@ +name: Slither Analysis +on: [push] +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: crytic/slither-action@v0.3.1 diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..4e42a1bc --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book/ \ No newline at end of file diff --git a/docs/book.css b/docs/book.css new file mode 100644 index 00000000..b5ce903f --- /dev/null +++ b/docs/book.css @@ -0,0 +1,13 @@ +table { + margin: 0 auto; + border-collapse: collapse; + width: 100%; +} + +table td:first-child { + width: 15%; +} + +table td:nth-child(2) { + width: 25%; +} \ No newline at end of file diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 00000000..50520fda --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,12 @@ +[book] +src = "src" +title = "Rhinestone Registry" + +[output.html] +no-section-label = true +additional-js = ["solidity.min.js"] +additional-css = ["book.css"] +git-repository-url = "https://github.com/rhinestonewtf/registry" + +[output.html.fold] +enable = true diff --git a/docs/solidity.min.js b/docs/solidity.min.js new file mode 100644 index 00000000..19249329 --- /dev/null +++ b/docs/solidity.min.js @@ -0,0 +1,74 @@ +hljs.registerLanguage("solidity",(()=>{"use strict";function e(){try{return!0 +}catch(e){return!1}} +var a=/-?(\b0[xX]([a-fA-F0-9]_?)*[a-fA-F0-9]|(\b[1-9](_?\d)*(\.((\d_?)*\d)?)?|\.\d(_?\d)*)([eE][-+]?\d(_?\d)*)?|\b0)(?!\w|\$)/ +;e()&&(a=a.source.replace(/\\b/g,"(?{ +var a=r(e),o=l(e),c=/[A-Za-z_$][A-Za-z_$0-9.]*/,d=e.inherit(e.TITLE_MODE,{ +begin:/[A-Za-z$_][0-9A-Za-z$_]*/,lexemes:c,keywords:n}),u={className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,lexemes:c,keywords:n, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,o,s]},_={ +className:"operator",begin:/:=|->/};return{keywords:n,lexemes:c, +contains:[a,o,i,t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,_,{ +className:"function",lexemes:c,beginKeywords:"function",end:"{",excludeEnd:!0, +contains:[d,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,_]}]}}, +solAposStringMode:r,solQuoteStringMode:l,HEX_APOS_STRING_MODE:i, +HEX_QUOTE_STRING_MODE:t,SOL_NUMBER:s,isNegativeLookbehindAvailable:e} +;const{baseAssembly:c,solAposStringMode:d,solQuoteStringMode:u,HEX_APOS_STRING_MODE:_,HEX_QUOTE_STRING_MODE:m,SOL_NUMBER:b,isNegativeLookbehindAvailable:E}=o +;return e=>{for(var a=d(e),s=u(e),n=[],i=0;i<32;i++)n[i]=i+1 +;var t=n.map((e=>8*e)),r=[];for(i=0;i<=80;i++)r[i]=i +;var l=n.map((e=>"bytes"+e)).join(" ")+" ",o=t.map((e=>"uint"+e)).join(" ")+" ",g=t.map((e=>"int"+e)).join(" ")+" ",M=[].concat.apply([],t.map((e=>r.map((a=>e+"x"+a))))),p={ +keyword:"var bool string int uint "+g+o+"byte bytes "+l+"fixed ufixed "+M.map((e=>"fixed"+e)).join(" ")+" "+M.map((e=>"ufixed"+e)).join(" ")+" enum struct mapping address new delete if else for while continue break return throw emit try catch revert unchecked _ function modifier event constructor fallback receive error virtual override constant immutable anonymous indexed storage memory calldata external public internal payable pure view private returns import from as using pragma contract interface library is abstract type assembly", +literal:"true false wei gwei szabo finney ether seconds minutes hours days weeks years", +built_in:"self this super selfdestruct suicide now msg block tx abi blockhash gasleft assert require Error Panic sha3 sha256 keccak256 ripemd160 ecrecover addmod mulmod log0 log1 log2 log3 log4" +},O={className:"operator",begin:/[+\-!~*\/%<>&^|=]/ +},C=/[A-Za-z_$][A-Za-z_$0-9]*/,N={className:"params",begin:/\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,lexemes:C,keywords:p, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,s,b,"self"]},f={ +begin:/\.\s*/,end:/[^A-Za-z0-9$_\.]/,excludeBegin:!0,excludeEnd:!0,keywords:{ +built_in:"gas value selector address length push pop send transfer call callcode delegatecall staticcall balance code codehash wrap unwrap name creationCode runtimeCode interfaceId min max" +},relevance:2},y=e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/, +lexemes:C,keywords:p}),w={className:"built_in", +begin:(E()?"(? + +# Registry [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ![solidity](https://img.shields.io/badge/solidity-^0.8.22-lightgrey) [![Foundry][foundry-badge]][foundry] + +[foundry]: https://getfoundry.sh +[foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg + +This Contract is in active development. Do not use this in Prod! + +## Overview + +Account abstraction (or smart accounts) will deliver three key enhancements for the Ethereum ecosystem: +improved UX, enhanced user security and greater wallet extensibility. Modular smart accounts are the next +frontier for achieving these goals. However, it also opens up a number of new challenges that +could drastically undermine the objective by opening up a plethora of new attack vectors and security concerns for accounts. + +The Registry aims to solve this concern by providing a means of verifying the legitimacy and +security of independently built smart account modules for installation and use across any integrated +smart account. It allows entities to attest to statements about modules and smart accounts to query these at module nstallation and/or execution time. The Registry is a Singleton that is free, open and permissionless. It also serves as the reference implementation for [ERC-7484](https://eips.ethereum.org/EIPS/eip-7484). + +## Core Concepts + +### Attestations + +Attestations on the Registry represent statements about Modules. An Attestation is made using a particular [Schema](./Schemas.md) that is used to encode and decode the Attestation data. The most important usecase for Attestations is to make statements about the security of a Module. + +An attestation consists of two primary elements: the Schema and the +Attestation data. The Schema acts as a standardized structure for +creating and validating Attestations, defining how the Attestation data is encoded and decoded. + +### Schemas + +[Schemas](./docs/Schema.md) represent predefined structures utilized for the formation and +verification of Attestation data. Using flexible Schemas rather than a single, fixed Schema allows Attesters to encode their data in a custom way, providing flexibility when creating Attestations. For example, the data of an Attestation about the outcome of the formal verification on a Module will have a very format than the data of an Attestation about what interfaces a Module supports. + +### Resolvers + +Resolvers are external contracts that are tied to Modules and called when specific Registry actions are executed. These actions are: + +- attestation +- revocation +- module registration / deployment + +This architectural design aims to provide entities like Smart Account vendors or DAOs, with the +flexibility to incorporate custom business logic while maintaining the +robustness and security of the core functionalities implemented by the Registry + +### Attesters + +Attesters are individuals or organizations responsible for +creating and signing Attestations. They add the Attestation to the +Registry, making it available for verification. + +### Modules + +Modules are smart contracts that act as modular components that can be added to Smart Accounts. +The registry is agnostic towards Smart Account or Module implementations. Only Module addresses and +deployment metadata are stored on the registry. + +Modules are registered on the Registry either during, using `CREATE2`, `CREATE3` or a custom deployment factory, or after deployment. + +## Architecture + +![Sequence Diagram](./public/docs/all.svg) + +## Gas comparison + +The following is a table of the gas differences between the Registry and a minimal [ERC-7484](https://eips.ethereum.org/EIPS/eip-7484) registry that only has one attester. As you can see, the gas difference is negligible for 1 or 2 attesters, but the Registry scales much better than using multiple single attester registries. + +To run the tests yourself, run `forge test --mc RegistryGasComparisonTest -vv`. + +_Note:_ The gas calculation numbers include the gas cost for `CALL` + +| # of Attesters | Registry | Minimal7484Registry | Difference | +| ----------------- | ------------ | ------------------- | ---------- | +| 1 | 7983 | 7706 | +277 | +| 2 | 15472 | 15418 | +54 | +| 3 | 20823 | 23124 | -2301 | +| n (approximation) | 5299n + 4901 | 7709n | | + +## Deployments + +Current address: [0x500684cBaa280aDf80d5ACf7A32Daebb23162e63](https://blockscan.com/address/0x500684cBaa280aDf80d5ACf7A32Daebb23162e63) + +## Contribute + +For feature or change requests, feel free to open a PR or get in touch with us. + +## Credits & Special Thanks + +For the continious support and constructive feedback, we would like to thank: + +- [Ethereum Foundation](https://erc4337.mirror.xyz/hRn_41cef8oKn44ZncN9pXvY3VID6LZOtpLlktXYtmA) +- ERC-4337 Team +- Richard Meissner (Safe) @rimeissner +- Taek @taek.eth +- Biconomy +- Heavily inspired by EAS + +## Authors ✨ + + + + + + + + + + +

zeroknots

💻

Konrad

💻
diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 00000000..4eb7b9b3 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,51 @@ +# Summary +- [Home](README.md) +# src + - [❱ core](src/core/README.md) + - [Attestation](src/core/Attestation.sol/abstract.Attestation.md) + - [AttestationManager](src/core/AttestationManager.sol/abstract.AttestationManager.md) + - [ModuleManager](src/core/ModuleManager.sol/abstract.ModuleManager.md) + - [ResolverManager](src/core/ResolverManager.sol/abstract.ResolverManager.md) + - [SchemaManager](src/core/SchemaManager.sol/abstract.SchemaManager.md) + - [SignedAttestation](src/core/SignedAttestation.sol/contract.SignedAttestation.md) + - [TrustManager](src/core/TrustManager.sol/abstract.TrustManager.md) + - [TrustManagerExternalAttesterList](src/core/TrustManagerExternalAttesterList.sol/abstract.TrustManagerExternalAttesterList.md) + - [❱ external](src/external/README.md) + - [❱ examples](src/external/examples/README.md) + - [ERC7512](src/external/examples/ERC7512Schema.sol/interface.ERC7512.md) + - [ERC7512SchemaValidator](src/external/examples/ERC7512Schema.sol/contract.ERC7512SchemaValidator.md) + - [ResolverBase](src/external/examples/ResolverBase.sol/abstract.ResolverBase.md) + - [TokenizedResolver](src/external/examples/TokenizedResolver.sol/contract.TokenizedResolver.md) + - [IExternalResolver](src/external/IExternalResolver.sol/interface.IExternalResolver.md) + - [IExternalSchemaValidator](src/external/IExternalSchemaValidator.sol/interface.IExternalSchemaValidator.md) + - [❱ lib](src/lib/README.md) + - [AttestationLib](src/lib/AttestationLib.sol/library.AttestationLib.md) + - [UIDLib](src/lib/Helpers.sol/library.UIDLib.md) + - [ModuleDeploymentLib](src/lib/ModuleDeploymentLib.sol/library.ModuleDeploymentLib.md) + - [ModuleTypeLib](src/lib/ModuleTypeLib.sol/library.ModuleTypeLib.md) + - [StubLib](src/lib/StubLib.sol/library.StubLib.md) + - [_isContract](src/Common.sol/function._isContract.md) + - [_time](src/Common.sol/function._time.md) + - [Common constants](src/Common.sol/constants.Common.md) + - [AttestationRecord](src/DataTypes.sol/struct.AttestationRecord.md) + - [ModuleRecord](src/DataTypes.sol/struct.ModuleRecord.md) + - [SchemaRecord](src/DataTypes.sol/struct.SchemaRecord.md) + - [ResolverRecord](src/DataTypes.sol/struct.ResolverRecord.md) + - [TrustedAttesterRecord](src/DataTypes.sol/struct.TrustedAttesterRecord.md) + - [AttestationRequest](src/DataTypes.sol/struct.AttestationRequest.md) + - [RevocationRequest](src/DataTypes.sol/struct.RevocationRequest.md) + - [SchemaUID](src/DataTypes.sol/type.SchemaUID.md) + - [ResolverUID](src/DataTypes.sol/type.ResolverUID.md) + - [AttestationDataRef](src/DataTypes.sol/type.AttestationDataRef.md) + - [PackedModuleTypes](src/DataTypes.sol/type.PackedModuleTypes.md) + - [ModuleType](src/DataTypes.sol/type.ModuleType.md) + - [attestationDataRefEq](src/DataTypes.sol/function.attestationDataRefEq.md) + - [resolverEq](src/DataTypes.sol/function.resolverEq.md) + - [schemaEq](src/DataTypes.sol/function.schemaEq.md) + - [moduleTypeEq](src/DataTypes.sol/function.moduleTypeEq.md) + - [resolverNotEq](src/DataTypes.sol/function.resolverNotEq.md) + - [moduleTypeNeq](src/DataTypes.sol/function.moduleTypeNeq.md) + - [schemaNotEq](src/DataTypes.sol/function.schemaNotEq.md) + - [IERC7484](src/IRegistry.sol/interface.IERC7484.md) + - [IRegistry](src/IRegistry.sol/interface.IRegistry.md) + - [Registry](src/Registry.sol/contract.Registry.md) diff --git a/docs/src/src/Common.sol/constants.Common.md b/docs/src/src/Common.sol/constants.Common.md new file mode 100644 index 00000000..b5424767 --- /dev/null +++ b/docs/src/src/Common.sol/constants.Common.md @@ -0,0 +1,45 @@ +# Constants +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/Common.sol) + +### EMPTY_UID + +```solidity +bytes32 constant EMPTY_UID = 0; +``` + +### EMPTY_RESOLVER_UID + +```solidity +ResolverUID constant EMPTY_RESOLVER_UID = ResolverUID.wrap(EMPTY_UID); +``` + +### EMPTY_SCHEMA_UID + +```solidity +SchemaUID constant EMPTY_SCHEMA_UID = SchemaUID.wrap(EMPTY_UID); +``` + +### ZERO_TIMESTAMP + +```solidity +uint256 constant ZERO_TIMESTAMP = 0; +``` + +### ZERO_ADDRESS + +```solidity +address constant ZERO_ADDRESS = address(0); +``` + +### ZERO_MODULE_TYPE + +```solidity +ModuleType constant ZERO_MODULE_TYPE = ModuleType.wrap(0); +``` + +### EMPTY_ATTESTATION_REF + +```solidity +AttestationDataRef constant EMPTY_ATTESTATION_REF = AttestationDataRef.wrap(address(0)); +``` + diff --git a/docs/src/src/Common.sol/function._isContract.md b/docs/src/src/Common.sol/function._isContract.md new file mode 100644 index 00000000..cdd7fc51 --- /dev/null +++ b/docs/src/src/Common.sol/function._isContract.md @@ -0,0 +1,22 @@ +# _isContract +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/Common.sol) + +*Returns whether an address is a contract.* + + +```solidity +function _isContract(address addr) view returns (bool); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`addr`|`address`|The address to check.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`bool`|true if `account` is a contract, false otherwise.| + + diff --git a/docs/src/src/Common.sol/function._time.md b/docs/src/src/Common.sol/function._time.md new file mode 100644 index 00000000..f8a6e019 --- /dev/null +++ b/docs/src/src/Common.sol/function._time.md @@ -0,0 +1,11 @@ +# _time +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/Common.sol) + +*Returns the current's block timestamp. This method is overridden during tests and used to simulate the +current block time.* + + +```solidity +function _time() view returns (uint48); +``` + diff --git a/docs/src/src/DataTypes.sol/function.attestationDataRefEq.md b/docs/src/src/DataTypes.sol/function.attestationDataRefEq.md new file mode 100644 index 00000000..aa9ca0a7 --- /dev/null +++ b/docs/src/src/DataTypes.sol/function.attestationDataRefEq.md @@ -0,0 +1,8 @@ +# attestationDataRefEq +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +function attestationDataRefEq(AttestationDataRef uid1, AttestationDataRef uid2) pure returns (bool); +``` + diff --git a/docs/src/src/DataTypes.sol/function.moduleTypeEq.md b/docs/src/src/DataTypes.sol/function.moduleTypeEq.md new file mode 100644 index 00000000..fe49717e --- /dev/null +++ b/docs/src/src/DataTypes.sol/function.moduleTypeEq.md @@ -0,0 +1,8 @@ +# moduleTypeEq +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +function moduleTypeEq(ModuleType uid1, ModuleType uid2) pure returns (bool); +``` + diff --git a/docs/src/src/DataTypes.sol/function.moduleTypeNeq.md b/docs/src/src/DataTypes.sol/function.moduleTypeNeq.md new file mode 100644 index 00000000..51b28040 --- /dev/null +++ b/docs/src/src/DataTypes.sol/function.moduleTypeNeq.md @@ -0,0 +1,8 @@ +# moduleTypeNeq +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +function moduleTypeNeq(ModuleType uid1, ModuleType uid2) pure returns (bool); +``` + diff --git a/docs/src/src/DataTypes.sol/function.resolverEq.md b/docs/src/src/DataTypes.sol/function.resolverEq.md new file mode 100644 index 00000000..cfac154e --- /dev/null +++ b/docs/src/src/DataTypes.sol/function.resolverEq.md @@ -0,0 +1,8 @@ +# resolverEq +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +function resolverEq(ResolverUID uid1, ResolverUID uid2) pure returns (bool); +``` + diff --git a/docs/src/src/DataTypes.sol/function.resolverNotEq.md b/docs/src/src/DataTypes.sol/function.resolverNotEq.md new file mode 100644 index 00000000..5ef3a771 --- /dev/null +++ b/docs/src/src/DataTypes.sol/function.resolverNotEq.md @@ -0,0 +1,8 @@ +# resolverNotEq +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +function resolverNotEq(ResolverUID uid1, ResolverUID uid2) pure returns (bool); +``` + diff --git a/docs/src/src/DataTypes.sol/function.schemaEq.md b/docs/src/src/DataTypes.sol/function.schemaEq.md new file mode 100644 index 00000000..7bda8a33 --- /dev/null +++ b/docs/src/src/DataTypes.sol/function.schemaEq.md @@ -0,0 +1,8 @@ +# schemaEq +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +function schemaEq(SchemaUID uid1, SchemaUID uid) pure returns (bool); +``` + diff --git a/docs/src/src/DataTypes.sol/function.schemaNotEq.md b/docs/src/src/DataTypes.sol/function.schemaNotEq.md new file mode 100644 index 00000000..0548aa05 --- /dev/null +++ b/docs/src/src/DataTypes.sol/function.schemaNotEq.md @@ -0,0 +1,8 @@ +# schemaNotEq +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +function schemaNotEq(SchemaUID uid1, SchemaUID uid) pure returns (bool); +``` + diff --git a/docs/src/src/DataTypes.sol/struct.AttestationRecord.md b/docs/src/src/DataTypes.sol/struct.AttestationRecord.md new file mode 100644 index 00000000..3d663e79 --- /dev/null +++ b/docs/src/src/DataTypes.sol/struct.AttestationRecord.md @@ -0,0 +1,17 @@ +# AttestationRecord +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +struct AttestationRecord { + uint48 time; + uint48 expirationTime; + uint48 revocationTime; + PackedModuleTypes moduleTypes; + address moduleAddr; + address attester; + AttestationDataRef dataPointer; + SchemaUID schemaUID; +} +``` + diff --git a/docs/src/src/DataTypes.sol/struct.AttestationRequest.md b/docs/src/src/DataTypes.sol/struct.AttestationRequest.md new file mode 100644 index 00000000..33eeb91f --- /dev/null +++ b/docs/src/src/DataTypes.sol/struct.AttestationRequest.md @@ -0,0 +1,15 @@ +# AttestationRequest +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + +*A struct representing the arguments of the attestation request.* + + +```solidity +struct AttestationRequest { + address moduleAddr; + uint48 expirationTime; + bytes data; + ModuleType[] moduleTypes; +} +``` + diff --git a/docs/src/src/DataTypes.sol/struct.ModuleRecord.md b/docs/src/src/DataTypes.sol/struct.ModuleRecord.md new file mode 100644 index 00000000..b9bf67a2 --- /dev/null +++ b/docs/src/src/DataTypes.sol/struct.ModuleRecord.md @@ -0,0 +1,12 @@ +# ModuleRecord +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +struct ModuleRecord { + ResolverUID resolverUID; + address sender; + bytes metadata; +} +``` + diff --git a/docs/src/src/DataTypes.sol/struct.ResolverRecord.md b/docs/src/src/DataTypes.sol/struct.ResolverRecord.md new file mode 100644 index 00000000..eda16b1b --- /dev/null +++ b/docs/src/src/DataTypes.sol/struct.ResolverRecord.md @@ -0,0 +1,11 @@ +# ResolverRecord +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +struct ResolverRecord { + IExternalResolver resolver; + address resolverOwner; +} +``` + diff --git a/docs/src/src/DataTypes.sol/struct.RevocationRequest.md b/docs/src/src/DataTypes.sol/struct.RevocationRequest.md new file mode 100644 index 00000000..34ba0c5f --- /dev/null +++ b/docs/src/src/DataTypes.sol/struct.RevocationRequest.md @@ -0,0 +1,12 @@ +# RevocationRequest +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + +*A struct representing the arguments of the revocation request.* + + +```solidity +struct RevocationRequest { + address moduleAddr; +} +``` + diff --git a/docs/src/src/DataTypes.sol/struct.SchemaRecord.md b/docs/src/src/DataTypes.sol/struct.SchemaRecord.md new file mode 100644 index 00000000..d9b13ca8 --- /dev/null +++ b/docs/src/src/DataTypes.sol/struct.SchemaRecord.md @@ -0,0 +1,12 @@ +# SchemaRecord +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +struct SchemaRecord { + uint48 registeredAt; + IExternalSchemaValidator validator; + string schema; +} +``` + diff --git a/docs/src/src/DataTypes.sol/struct.TrustedAttesterRecord.md b/docs/src/src/DataTypes.sol/struct.TrustedAttesterRecord.md new file mode 100644 index 00000000..f54dd620 --- /dev/null +++ b/docs/src/src/DataTypes.sol/struct.TrustedAttesterRecord.md @@ -0,0 +1,13 @@ +# TrustedAttesterRecord +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +struct TrustedAttesterRecord { + uint8 attesterCount; + uint8 threshold; + address attester; + mapping(address attester => address linkedAttester) linkedAttesters; +} +``` + diff --git a/docs/src/src/DataTypes.sol/type.AttestationDataRef.md b/docs/src/src/DataTypes.sol/type.AttestationDataRef.md new file mode 100644 index 00000000..36a93cdb --- /dev/null +++ b/docs/src/src/DataTypes.sol/type.AttestationDataRef.md @@ -0,0 +1,8 @@ +# AttestationDataRef +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +type AttestationDataRef is address; +``` + diff --git a/docs/src/src/DataTypes.sol/type.ModuleType.md b/docs/src/src/DataTypes.sol/type.ModuleType.md new file mode 100644 index 00000000..81167a93 --- /dev/null +++ b/docs/src/src/DataTypes.sol/type.ModuleType.md @@ -0,0 +1,8 @@ +# ModuleType +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +type ModuleType is uint256; +``` + diff --git a/docs/src/src/DataTypes.sol/type.PackedModuleTypes.md b/docs/src/src/DataTypes.sol/type.PackedModuleTypes.md new file mode 100644 index 00000000..e44e1d91 --- /dev/null +++ b/docs/src/src/DataTypes.sol/type.PackedModuleTypes.md @@ -0,0 +1,8 @@ +# PackedModuleTypes +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +type PackedModuleTypes is uint32; +``` + diff --git a/docs/src/src/DataTypes.sol/type.ResolverUID.md b/docs/src/src/DataTypes.sol/type.ResolverUID.md new file mode 100644 index 00000000..f9915362 --- /dev/null +++ b/docs/src/src/DataTypes.sol/type.ResolverUID.md @@ -0,0 +1,8 @@ +# ResolverUID +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +type ResolverUID is bytes32; +``` + diff --git a/docs/src/src/DataTypes.sol/type.SchemaUID.md b/docs/src/src/DataTypes.sol/type.SchemaUID.md new file mode 100644 index 00000000..44f13e00 --- /dev/null +++ b/docs/src/src/DataTypes.sol/type.SchemaUID.md @@ -0,0 +1,8 @@ +# SchemaUID +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/DataTypes.sol) + + +```solidity +type SchemaUID is bytes32; +``` + diff --git a/docs/src/src/IRegistry.sol/interface.IERC7484.md b/docs/src/src/IRegistry.sol/interface.IERC7484.md new file mode 100644 index 00000000..a3ec35a0 --- /dev/null +++ b/docs/src/src/IRegistry.sol/interface.IERC7484.md @@ -0,0 +1,47 @@ +# IERC7484 +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/IRegistry.sol) + + +## Functions +### check + + +```solidity +function check(address module) external view; +``` + +### checkForAccount + + +```solidity +function checkForAccount(address smartAccount, address module) external view; +``` + +### check + + +```solidity +function check(address module, ModuleType moduleType) external view; +``` + +### checkForAccount + + +```solidity +function checkForAccount(address smartAccount, address module, ModuleType moduleType) external view; +``` + +### check + + +```solidity +function check(address module, address attester) external view returns (uint256 attestedAt); +``` + +### checkN + + +```solidity +function checkN(address module, address[] calldata attesters, uint256 threshold) external view returns (uint256[] memory attestedAtArray); +``` + diff --git a/docs/src/src/IRegistry.sol/interface.IRegistry.md b/docs/src/src/IRegistry.sol/interface.IRegistry.md new file mode 100644 index 00000000..62183bcf --- /dev/null +++ b/docs/src/src/IRegistry.sol/interface.IRegistry.md @@ -0,0 +1,645 @@ +# IRegistry +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/IRegistry.sol) + +**Inherits:** +[IERC7484](/src/IRegistry.sol/interface.IERC7484.md) + + +## Functions +### trustAttesters + +Allows smartaccounts - the end users of the registry - to appoint +one or many attesters as trusted. + +this function reverts, if address(0), or duplicates are provided in attesters[] + + +```solidity +function trustAttesters(uint8 threshold, address[] calldata attesters) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`threshold`|`uint8`|The minimum number of attestations required for a module to be considered secure.| +|`attesters`|`address[]`|The addresses of the attesters to be trusted.| + + +### findTrustedAttesters + +Get trusted attester for a specific smartAccount + + +```solidity +function findTrustedAttesters(address smartAccount) external view returns (address[] memory attesters); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`smartAccount`|`address`|The address of the smartAccount| + + +### attest + +Allows msg.sender to attest to multiple modules' security status. +The AttestationRequest.Data provided should match the attestation +schema defined by the Schema corresponding to the SchemaUID + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|The SchemaUID of the schema the attestation is based on.| +|`request`|`AttestationRequest`|a single AttestationRequest| + + +### attest + +Allows msg.sender to attest to multiple modules' security status. +The AttestationRequest.Data provided should match the attestation +schema defined by the Schema corresponding to the SchemaUID + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|The SchemaUID of the schema the attestation is based on.| +|`requests`|`AttestationRequest[]`|An array of AttestationRequest| + + +### attest + +Allows attester to attest by signing an AttestationRequest (ECDSA or ERC1271) +The AttestationRequest.Data provided should match the attestation +schema defined by the Schema corresponding to the SchemaUID + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function attest(SchemaUID schemaUID, address attester, AttestationRequest calldata request, bytes calldata signature) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|The SchemaUID of the schema the attestation is based on.| +|`attester`|`address`|The address of the attester| +|`request`|`AttestationRequest`|An AttestationRequest| +|`signature`|`bytes`|The signature of the attester. ECDSA or ERC1271| + + +### attest + +Allows attester to attest by signing an AttestationRequest (ECDSA or ERC1271) +The AttestationRequest.Data provided should match the attestation +schema defined by the Schema corresponding to the SchemaUID + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function attest(SchemaUID schemaUID, address attester, AttestationRequest[] calldata requests, bytes calldata signature) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|The SchemaUID of the schema the attestation is based on.| +|`attester`|`address`|The address of the attester| +|`requests`|`AttestationRequest[]`|An array of AttestationRequest| +|`signature`|`bytes`|The signature of the attester. ECDSA or ERC1271| + + +### findAttestation + +Getter function to get AttestationRequest made by one attester + + +```solidity +function findAttestation(address module, address attester) external view returns (AttestationRecord memory attestation); +``` + +### findAttestations + +Getter function to get AttestationRequest made by multiple attesters + + +```solidity +function findAttestations(address module, address[] calldata attesters) external view returns (AttestationRecord[] memory attestations); +``` + +### revoke + +Allows msg.sender to revoke an attstation made by the same msg.sender + +*this function will revert if the attestation is not found* + +*this function will revert if the attestation is already revoked* + + +```solidity +function revoke(RevocationRequest calldata request) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`request`|`RevocationRequest`| the RevocationRequest| + + +### revoke + +Allows msg.sender to revoke multiple attstations made by the same msg.sender + +*this function will revert if the attestation is not found* + +*this function will revert if the attestation is already revoked* + + +```solidity +function revoke(RevocationRequest[] calldata requests) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`requests`|`RevocationRequest[]`|the RevocationRequests| + + +### revoke + +Allows attester to revoke an attestation by signing an RevocationRequest (ECDSA or ERC1271) + + +```solidity +function revoke(address attester, RevocationRequest calldata request, bytes calldata signature) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`attester`|`address`|the signer / revoker| +|`request`|`RevocationRequest`|the RevocationRequest| +|`signature`|`bytes`|ECDSA or ERC1271 signature| + + +### revoke + +Allows attester to revoke an attestation by signing an RevocationRequest (ECDSA or ERC1271) + +*if you want to revoke multiple attestations, but from different attesters, call this function multiple times* + + +```solidity +function revoke(address attester, RevocationRequest[] calldata requests, bytes calldata signature) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`attester`|`address`|the signer / revoker| +|`requests`|`RevocationRequest[]`|array of RevocationRequests| +|`signature`|`bytes`|ECDSA or ERC1271 signature| + + +### deployModule + +This registry implements a CREATE2 factory, that allows module developers to register and deploy module bytecode + + +```solidity +function deployModule( + bytes32 salt, + ResolverUID resolverUID, + bytes calldata initCode, + bytes calldata metadata +) + external + payable + returns (address moduleAddr); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`salt`|`bytes32`|The salt to be used in the CREATE2 factory. This adheres to Pr000xy/Create2Factory.sol salt formatting. The salt's first bytes20 should be the address of the sender or bytes20(0) to bypass the check (this will lose replay protection)| +|`resolverUID`|`ResolverUID`|The resolverUID to be used in the CREATE2 factory| +|`initCode`|`bytes`|The initCode to be used in the CREATE2 factory| +|`metadata`|`bytes`|The metadata to be stored on the registry. This field is optional, and might be used by the module developer to store additional information about the module or facilitate business logic with the Resolver stub| + + +### deployViaFactory + +Registry can use other factories to deploy the module + +This function is used to deploy and register a module using a factory contract. +Since one of the parameters of this function is a unique resolverUID and any +registered module address can only be registered once, +using this function is of risk for a frontrun attack + + +```solidity +function deployViaFactory( + address factory, + bytes calldata callOnFactory, + bytes calldata metadata, + ResolverUID resolverUID +) + external + payable + returns (address moduleAddress); +``` + +### registerModule + +Already deployed module addresses can be registered on the registry + +This function is used to deploy and register an already deployed module. +Since one of the parameters of this function is a unique resolverUID and any +registered module address can only be registered once, +using this function is of risk for a frontrun attack + + +```solidity +function registerModule(ResolverUID resolverUID, address moduleAddress, bytes calldata metadata) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`resolverUID`|`ResolverUID`|The resolverUID to be used for the module| +|`moduleAddress`|`address`|The address of the module to be registered| +|`metadata`|`bytes`|The metadata to be stored on the registry. This field is optional, and might be used by the module developer to store additional information about the module or facilitate business logic with the Resolver stub| + + +### calcModuleAddress + +in conjunction with the deployModule() function, this function let's you +predict the address of a CREATE2 module deployment + + +```solidity +function calcModuleAddress(bytes32 salt, bytes calldata initCode) external view returns (address); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`salt`|`bytes32`|CREATE2 salt| +|`initCode`|`bytes`|module initcode| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`address`|moduleAddress counterfactual address of the module deployment| + + +### findModule + +Getter function to get the stored ModuleRecord for a specific module address. + + +```solidity +function findModule(address moduleAddress) external view returns (ModuleRecord memory moduleRecord); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`moduleAddress`|`address`|The address of the module| + + +### registerSchema + +Register Schema and (optional) external IExternalSchemaValidator +Schemas describe the structure of the data of attestations +every attestation made on this registry, will reference a SchemaUID to +make it possible to decode attestation data in human readable form +overrwriting a schema is not allowed, and will revert + + +```solidity +function registerSchema(string calldata schema, IExternalSchemaValidator validator) external returns (SchemaUID uid); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schema`|`string`|ABI schema used to encode attestations that are made with this schema| +|`validator`|`IExternalSchemaValidator`|(optional) external schema validator that will be used to validate attestations. use address(0), if you dont need an external validator| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`SchemaUID`|SchemaUID of the registered schema| + + +### findSchema + +getter function to retrieve SchemaRecord + + +```solidity +function findSchema(SchemaUID uid) external view returns (SchemaRecord memory record); +``` + +### registerResolver + +Allows Marketplace Agents to register external resolvers. + + +```solidity +function registerResolver(IExternalResolver resolver) external returns (ResolverUID uid); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`resolver`|`IExternalResolver`|external resolver contract| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|ResolverUID of the registered resolver| + + +### setResolver + +Entities that previously regsitered an external resolver, may update the implementation address. + + +```solidity +function setResolver(ResolverUID uid, IExternalResolver resolver) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|The UID of the resolver.| +|`resolver`|`IExternalResolver`|The new resolver implementation address.| + + +### transferResolverOwnership + +Transfer ownership of resolverUID to a new address + + +```solidity +function transferResolverOwnership(ResolverUID uid, address newOwner) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|The UID of the resolver to transfer ownership for| +|`newOwner`|`address`|The address of the new owner| + + +### findResolver + +Getter function to get the ResolverRecord of a registerd resolver + + +```solidity +function findResolver(ResolverUID uid) external view returns (ResolverRecord memory record); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|The UID of the resolver.| + + +## Events +### NewTrustedAttesters + +```solidity +event NewTrustedAttesters(); +``` + +### Revoked + +```solidity +event Revoked(address indexed moduleAddr, address indexed revoker, SchemaUID schema); +``` + +### Attested + +```solidity +event Attested(address indexed moduleAddr, address indexed attester, SchemaUID schemaUID, AttestationDataRef indexed sstore2Pointer); +``` + +### ModuleRegistration + +```solidity +event ModuleRegistration(address indexed implementation, ResolverUID resolverUID, bool deployedViaRegistry); +``` + +### SchemaRegistered + +```solidity +event SchemaRegistered(SchemaUID indexed uid, address indexed registerer); +``` + +### NewResolver + +```solidity +event NewResolver(ResolverUID indexed uid, address indexed resolver); +``` + +### NewResolverOwner + +```solidity +event NewResolverOwner(ResolverUID indexed uid, address newOwner); +``` + +## Errors +### InvalidResolver + +```solidity +error InvalidResolver(IExternalResolver resolver); +``` + +### InvalidResolverUID + +```solidity +error InvalidResolverUID(ResolverUID uid); +``` + +### InvalidTrustedAttesterInput + +```solidity +error InvalidTrustedAttesterInput(); +``` + +### NoTrustedAttestersFound + +```solidity +error NoTrustedAttestersFound(); +``` + +### RevokedAttestation + +```solidity +error RevokedAttestation(address attester); +``` + +### InvalidModuleType + +```solidity +error InvalidModuleType(); +``` + +### AttestationNotFound + +```solidity +error AttestationNotFound(); +``` + +### InsufficientAttestations + +```solidity +error InsufficientAttestations(); +``` + +### AlreadyRevoked + +```solidity +error AlreadyRevoked(); +``` + +### ModuleNotFoundInRegistry + +```solidity +error ModuleNotFoundInRegistry(address module); +``` + +### AccessDenied + +```solidity +error AccessDenied(); +``` + +### InvalidAttestation + +```solidity +error InvalidAttestation(); +``` + +### InvalidExpirationTime + +```solidity +error InvalidExpirationTime(); +``` + +### DifferentResolvers + +```solidity +error DifferentResolvers(); +``` + +### InvalidSignature + +```solidity +error InvalidSignature(); +``` + +### InvalidModuleTypes + +```solidity +error InvalidModuleTypes(); +``` + +### AlreadyRegistered + +```solidity +error AlreadyRegistered(address module); +``` + +### InvalidDeployment + +```solidity +error InvalidDeployment(); +``` + +### ModuleAddressIsNotContract + +```solidity +error ModuleAddressIsNotContract(address moduleAddress); +``` + +### FactoryCallFailed + +```solidity +error FactoryCallFailed(address factory); +``` + +### SchemaAlreadyExists + +```solidity +error SchemaAlreadyExists(SchemaUID uid); +``` + +### InvalidSchema + +```solidity +error InvalidSchema(); +``` + +### InvalidSchemaValidator + +```solidity +error InvalidSchemaValidator(IExternalSchemaValidator validator); +``` + +### ResolverAlreadyExists + +```solidity +error ResolverAlreadyExists(); +``` + +### ExternalError_SchemaValidation + +```solidity +error ExternalError_SchemaValidation(); +``` + +### ExternalError_ResolveAtteststation + +```solidity +error ExternalError_ResolveAtteststation(); +``` + +### ExternalError_ResolveRevocation + +```solidity +error ExternalError_ResolveRevocation(); +``` + +### ExternalError_ModuleRegistration + +```solidity +error ExternalError_ModuleRegistration(); +``` + diff --git a/docs/src/src/README.md b/docs/src/src/README.md new file mode 100644 index 00000000..73d326e6 --- /dev/null +++ b/docs/src/src/README.md @@ -0,0 +1,31 @@ + + +# Contents +- [core](/src/core) +- [external](/src/external) +- [lib](/src/lib) +- [_isContract](Common.sol/function._isContract.md) +- [_time](Common.sol/function._time.md) +- [Common constants](Common.sol/constants.Common.md) +- [AttestationRecord](DataTypes.sol/struct.AttestationRecord.md) +- [ModuleRecord](DataTypes.sol/struct.ModuleRecord.md) +- [SchemaRecord](DataTypes.sol/struct.SchemaRecord.md) +- [ResolverRecord](DataTypes.sol/struct.ResolverRecord.md) +- [TrustedAttesterRecord](DataTypes.sol/struct.TrustedAttesterRecord.md) +- [AttestationRequest](DataTypes.sol/struct.AttestationRequest.md) +- [RevocationRequest](DataTypes.sol/struct.RevocationRequest.md) +- [SchemaUID](DataTypes.sol/type.SchemaUID.md) +- [ResolverUID](DataTypes.sol/type.ResolverUID.md) +- [AttestationDataRef](DataTypes.sol/type.AttestationDataRef.md) +- [PackedModuleTypes](DataTypes.sol/type.PackedModuleTypes.md) +- [ModuleType](DataTypes.sol/type.ModuleType.md) +- [attestationDataRefEq](DataTypes.sol/function.attestationDataRefEq.md) +- [resolverEq](DataTypes.sol/function.resolverEq.md) +- [schemaEq](DataTypes.sol/function.schemaEq.md) +- [moduleTypeEq](DataTypes.sol/function.moduleTypeEq.md) +- [resolverNotEq](DataTypes.sol/function.resolverNotEq.md) +- [moduleTypeNeq](DataTypes.sol/function.moduleTypeNeq.md) +- [schemaNotEq](DataTypes.sol/function.schemaNotEq.md) +- [IERC7484](IRegistry.sol/interface.IERC7484.md) +- [IRegistry](IRegistry.sol/interface.IRegistry.md) +- [Registry](Registry.sol/contract.Registry.md) diff --git a/docs/src/src/Registry.sol/contract.Registry.md b/docs/src/src/Registry.sol/contract.Registry.md new file mode 100644 index 00000000..c731f3b2 --- /dev/null +++ b/docs/src/src/Registry.sol/contract.Registry.md @@ -0,0 +1,10 @@ +# Registry +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/Registry.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md), [SignedAttestation](/src/core/SignedAttestation.sol/contract.SignedAttestation.md) + +**Author:** +zeroknots + + diff --git a/docs/src/src/core/Attestation.sol/abstract.Attestation.md b/docs/src/src/core/Attestation.sol/abstract.Attestation.md new file mode 100644 index 00000000..aad0467d --- /dev/null +++ b/docs/src/src/core/Attestation.sol/abstract.Attestation.md @@ -0,0 +1,102 @@ +# Attestation +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/core/Attestation.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md), [AttestationManager](/src/core/AttestationManager.sol/abstract.AttestationManager.md) + + +## Functions +### attest + +Allows msg.sender to attest to multiple modules' security status. +The AttestationRequest.Data provided should match the attestation +schema defined by the Schema corresponding to the SchemaUID + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|The SchemaUID of the schema the attestation is based on.| +|`request`|`AttestationRequest`|a single AttestationRequest| + + +### attest + +Allows msg.sender to attest to multiple modules' security status. +The AttestationRequest.Data provided should match the attestation +schema defined by the Schema corresponding to the SchemaUID + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|The SchemaUID of the schema the attestation is based on.| +|`requests`|`AttestationRequest[]`|| + + +### revoke + +Allows msg.sender to revoke an attstation made by the same msg.sender + +*this function will revert if the attestation is not found* + + +```solidity +function revoke(RevocationRequest calldata request) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`request`|`RevocationRequest`| the RevocationRequest| + + +### revoke + +Allows msg.sender to revoke an attstation made by the same msg.sender + +*this function will revert if the attestation is not found* + + +```solidity +function revoke(RevocationRequest[] calldata requests) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`requests`|`RevocationRequest[]`|| + + +### findAttestation + +Getter function to get AttestationRequest made by one attester + + +```solidity +function findAttestation(address module, address attester) external view returns (AttestationRecord memory attestation); +``` + +### findAttestations + +Getter function to get AttestationRequest made by multiple attesters + + +```solidity +function findAttestations(address module, address[] calldata attesters) external view returns (AttestationRecord[] memory attestations); +``` + diff --git a/docs/src/src/core/AttestationManager.sol/abstract.AttestationManager.md b/docs/src/src/core/AttestationManager.sol/abstract.AttestationManager.md new file mode 100644 index 00000000..cdc4d08b --- /dev/null +++ b/docs/src/src/core/AttestationManager.sol/abstract.AttestationManager.md @@ -0,0 +1,146 @@ +# AttestationManager +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/core/AttestationManager.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md), [ModuleManager](/src/core/ModuleManager.sol/abstract.ModuleManager.md), [SchemaManager](/src/core/SchemaManager.sol/abstract.SchemaManager.md), [TrustManager](/src/core/TrustManager.sol/abstract.TrustManager.md) + +AttestationManager handles the registry's internal storage of new attestations and revocation of attestation + +*This contract is abstract and provides utility functions to store attestations and revocations.* + + +## State Variables +### $moduleToAttesterToAttestations + +```solidity +mapping(address module => mapping(address attester => AttestationRecord attestation)) internal $moduleToAttesterToAttestations; +``` + + +## Functions +### _attest + +Processes an attestation request and stores the attestation in the registry. +If the attestation was made for a module that was not registered, the function will revert. +function will get the external Schema Validator for the supplied SchemaUID +and call it, if an external IExternalSchemaValidator was set +function will get the external IExternalResolver for the module - that the attestation is for +and call it, if an external Resolver was set + + +```solidity +function _attest(address attester, SchemaUID schemaUID, AttestationRequest calldata request) internal; +``` + +### _attest + +Processes an array of attestation requests and stores the attestations in the registry. +If the attestation was made for a module that was not registered, the function will revert. +function will get the external Schema Validator for the supplied SchemaUID +and call it, if an external IExternalSchemaValidator was set +function will get the external IExternalResolver for the module - that the attestation is for +and call it, if an external Resolver was set + + +```solidity +function _attest(address attester, SchemaUID schemaUID, AttestationRequest[] calldata requests) internal; +``` + +### _storeAttestation + +Stores an attestation in the registry storage. +The bytes encoded AttestationRequest.Data is not stored directly into the registry storage, +but rather stored with SSTORE2. SSTORE2/SLOAD2 is writing and reading contract storage +paying a fraction of the cost, it uses contract code as storage, writing data takes the +form of contract creations and reading data uses EXTCODECOPY. +since attestation data is supposed to be immutable, it is a good candidate for SSTORE2 + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function _storeAttestation( + SchemaUID schemaUID, + address attester, + AttestationRequest calldata request +) + internal + returns (AttestationRecord memory record, ResolverUID resolverUID); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|| +|`attester`|`address`|The address of the attesting account.| +|`request`|`AttestationRequest`|The AttestationRequest that was supplied via calldata| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`record`|`AttestationRecord`|The AttestationRecord of what was written into registry storage| +|`resolverUID`|`ResolverUID`|The resolverUID in charge for the module| + + +### _revoke + +Revoke a single Revocation Request +This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, +and pass the RevocationRecord to the resolver to check if the revocation is valid + + +```solidity +function _revoke(address attester, RevocationRequest calldata request) internal; +``` + +### _revoke + +Revoke an array Revocation Request +This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, +and pass the RevocationRecord to the resolver to check if the revocation is valid + + +```solidity +function _revoke(address attester, RevocationRequest[] calldata requests) internal; +``` + +### _storeRevocation + +Gets the AttestationRecord for the supplied RevocationRequest and stores the revocation time in the registry storage + + +```solidity +function _storeRevocation( + address revoker, + RevocationRequest calldata request +) + internal + returns (AttestationRecord memory record, ResolverUID resolverUID); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`revoker`|`address`|The address of the attesting account.| +|`request`|`RevocationRequest`|The AttestationRequest that was supplied via calldata| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`record`|`AttestationRecord`|The AttestationRecord of what was written into registry storage| +|`resolverUID`|`ResolverUID`|The resolverUID in charge for the module| + + +### _getAttestation + +Returns the attestation records for the given module and attesters. +This function is expected to be used by TrustManager and TrustManagerExternalAttesterList + + +```solidity +function _getAttestation(address module, address attester) internal view override returns (AttestationRecord storage $attestation); +``` + diff --git a/docs/src/src/core/ModuleManager.sol/abstract.ModuleManager.md b/docs/src/src/core/ModuleManager.sol/abstract.ModuleManager.md new file mode 100644 index 00000000..939452d8 --- /dev/null +++ b/docs/src/src/core/ModuleManager.sol/abstract.ModuleManager.md @@ -0,0 +1,145 @@ +# ModuleManager +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/core/ModuleManager.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md), [ResolverManager](/src/core/ResolverManager.sol/abstract.ResolverManager.md) + +**Author:** +rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + +*The Module contract is responsible for handling the registration, +storage and retrieval of modules on the Registry. +This contract inherits from the IModule interface* + +*The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract +that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, +sender address, deploy parameters hash, and additional metadata are stored in +a struct and mapped to the module's address in +the `_modules` mapping for easy access and management.* + +*In conclusion, the Module is a central part of a system to manage, +deploy, and interact with a set of smart contracts +in a structured and controlled manner.* + + +## State Variables +### $moduleAddrToRecords + +```solidity +mapping(address moduleAddress => ModuleRecord moduleRecord) internal $moduleAddrToRecords; +``` + + +## Functions +### deployModule + +This registry implements a CREATE2 factory, that allows module developers to register and deploy module bytecode + + +```solidity +function deployModule( + bytes32 salt, + ResolverUID resolverUID, + bytes calldata initCode, + bytes calldata metadata +) + external + payable + returns (address moduleAddress); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`salt`|`bytes32`|The salt to be used in the CREATE2 factory. This adheres to Pr000xy/Create2Factory.sol salt formatting. The salt's first bytes20 should be the address of the sender or bytes20(0) to bypass the check (this will lose replay protection)| +|`resolverUID`|`ResolverUID`|The resolverUID to be used in the CREATE2 factory| +|`initCode`|`bytes`|The initCode to be used in the CREATE2 factory| +|`metadata`|`bytes`|The metadata to be stored on the registry. This field is optional, and might be used by the module developer to store additional information about the module or facilitate business logic with the Resolver stub| + + +### calcModuleAddress + +in conjunction with the deployModule() function, this function let's you +predict the address of a CREATE2 module deployment + + +```solidity +function calcModuleAddress(bytes32 salt, bytes calldata initCode) external view returns (address); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`salt`|`bytes32`|CREATE2 salt| +|`initCode`|`bytes`|module initcode| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`address`|moduleAddress counterfactual address of the module deployment| + + +### registerModule + +Already deployed module addresses can be registered on the registry + + +```solidity +function registerModule(ResolverUID resolverUID, address moduleAddress, bytes calldata metadata) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`resolverUID`|`ResolverUID`|The resolverUID to be used for the module| +|`moduleAddress`|`address`|The address of the module to be registered| +|`metadata`|`bytes`|The metadata to be stored on the registry. This field is optional, and might be used by the module developer to store additional information about the module or facilitate business logic with the Resolver stub| + + +### deployViaFactory + +Registry can use other factories to deploy the module + + +```solidity +function deployViaFactory( + address factory, + bytes calldata callOnFactory, + bytes calldata metadata, + ResolverUID resolverUID +) + external + payable + returns (address moduleAddress); +``` + +### _storeModuleRecord + + +```solidity +function _storeModuleRecord( + address moduleAddress, + address sender, + ResolverUID resolverUID, + bytes calldata metadata +) + internal + returns (ModuleRecord memory moduleRegistration); +``` + +### findModule + +Getter function to get the stored ModuleRecord for a specific module address. + + +```solidity +function findModule(address moduleAddress) external view returns (ModuleRecord memory moduleRecord); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`moduleAddress`|`address`|The address of the module| + + diff --git a/docs/src/src/core/README.md b/docs/src/src/core/README.md new file mode 100644 index 00000000..dd00cb63 --- /dev/null +++ b/docs/src/src/core/README.md @@ -0,0 +1,11 @@ + + +# Contents +- [Attestation](Attestation.sol/abstract.Attestation.md) +- [AttestationManager](AttestationManager.sol/abstract.AttestationManager.md) +- [ModuleManager](ModuleManager.sol/abstract.ModuleManager.md) +- [ResolverManager](ResolverManager.sol/abstract.ResolverManager.md) +- [SchemaManager](SchemaManager.sol/abstract.SchemaManager.md) +- [SignedAttestation](SignedAttestation.sol/contract.SignedAttestation.md) +- [TrustManager](TrustManager.sol/abstract.TrustManager.md) +- [TrustManagerExternalAttesterList](TrustManagerExternalAttesterList.sol/abstract.TrustManagerExternalAttesterList.md) diff --git a/docs/src/src/core/ResolverManager.sol/abstract.ResolverManager.md b/docs/src/src/core/ResolverManager.sol/abstract.ResolverManager.md new file mode 100644 index 00000000..ddcd72ba --- /dev/null +++ b/docs/src/src/core/ResolverManager.sol/abstract.ResolverManager.md @@ -0,0 +1,108 @@ +# ResolverManager +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/core/ResolverManager.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md) + + +## State Variables +### $resolvers + +```solidity +mapping(ResolverUID uid => ResolverRecord resolver) internal $resolvers; +``` + + +## Functions +### onlyResolverOwner + +*Modifier to require that the caller is the owner of a resolver* + + +```solidity +modifier onlyResolverOwner(ResolverUID uid); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|The UID of the resolver.| + + +### onlyResolver + +If a resolver is not address(0), we check if it supports the IExternalResolver interface + + +```solidity +modifier onlyResolver(IExternalResolver resolver); +``` + +### registerResolver + +Allows Marketplace Agents to register external resolvers. + + +```solidity +function registerResolver(IExternalResolver resolver) external onlyResolver(resolver) returns (ResolverUID uid); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`resolver`|`IExternalResolver`|external resolver contract| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|ResolverUID of the registered resolver| + + +### setResolver + +Entities that previously regsitered an external resolver, may update the implementation address. + + +```solidity +function setResolver(ResolverUID uid, IExternalResolver resolver) external onlyResolver(resolver) onlyResolverOwner(uid); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|The UID of the resolver.| +|`resolver`|`IExternalResolver`|The new resolver implementation address.| + + +### transferResolverOwnership + +Transfer ownership of resolverUID to a new address + + +```solidity +function transferResolverOwnership(ResolverUID uid, address newOwner) external onlyResolverOwner(uid); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|The UID of the resolver to transfer ownership for| +|`newOwner`|`address`|The address of the new owner| + + +### findResolver + +Getter function to get the ResolverRecord of a registerd resolver + + +```solidity +function findResolver(ResolverUID uid) external view returns (ResolverRecord memory); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`ResolverUID`|The UID of the resolver.| + + diff --git a/docs/src/src/core/SchemaManager.sol/abstract.SchemaManager.md b/docs/src/src/core/SchemaManager.sol/abstract.SchemaManager.md new file mode 100644 index 00000000..be54c54e --- /dev/null +++ b/docs/src/src/core/SchemaManager.sol/abstract.SchemaManager.md @@ -0,0 +1,69 @@ +# SchemaManager +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/core/SchemaManager.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md) + +**Author:** +rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + + +## State Variables +### schemas + +```solidity +mapping(SchemaUID uid => SchemaRecord schemaRecord) internal schemas; +``` + + +## Functions +### registerSchema + +Register Schema and (optional) external IExternalSchemaValidator +Schemas describe the structure of the data of attestations +every attestation made on this registry, will reference a SchemaUID to +make it possible to decode attestation data in human readable form +overrwriting a schema is not allowed, and will revert + + +```solidity +function registerSchema( + string calldata schema, + IExternalSchemaValidator validator +) + external + onlySchemaValidator(validator) + returns (SchemaUID uid); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schema`|`string`|ABI schema used to encode attestations that are made with this schema| +|`validator`|`IExternalSchemaValidator`|(optional) external schema validator that will be used to validate attestations. use address(0), if you dont need an external validator| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`uid`|`SchemaUID`|SchemaUID of the registered schema| + + +### onlySchemaValidator + +If a validator is not address(0), we check if it supports the IExternalSchemaValidator interface + + +```solidity +modifier onlySchemaValidator(IExternalSchemaValidator validator); +``` + +### findSchema + +getter function to retrieve SchemaRecord + + +```solidity +function findSchema(SchemaUID uid) external view override returns (SchemaRecord memory); +``` + diff --git a/docs/src/src/core/SignedAttestation.sol/contract.SignedAttestation.md b/docs/src/src/core/SignedAttestation.sol/contract.SignedAttestation.md new file mode 100644 index 00000000..ad52879d --- /dev/null +++ b/docs/src/src/core/SignedAttestation.sol/contract.SignedAttestation.md @@ -0,0 +1,137 @@ +# SignedAttestation +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/core/SignedAttestation.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md), [Attestation](/src/core/Attestation.sol/abstract.Attestation.md), EIP712 + + +## State Variables +### attesterNonce + +```solidity +mapping(address attester => uint256 nonce) public attesterNonce; +``` + + +## Functions +### attest + +Allows msg.sender to attest to multiple modules' security status. +The AttestationRequest.Data provided should match the attestation +schema defined by the Schema corresponding to the SchemaUID + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function attest(SchemaUID schemaUID, address attester, AttestationRequest calldata request, bytes calldata signature) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|The SchemaUID of the schema the attestation is based on.| +|`attester`|`address`|| +|`request`|`AttestationRequest`|a single AttestationRequest| +|`signature`|`bytes`|| + + +### attest + +Allows msg.sender to attest to multiple modules' security status. +The AttestationRequest.Data provided should match the attestation +schema defined by the Schema corresponding to the SchemaUID + +*This function will revert if the same module is attested twice by the same attester. +If you want to re-attest, you have to revoke your attestation first, and then attest again.* + + +```solidity +function attest(SchemaUID schemaUID, address attester, AttestationRequest[] calldata requests, bytes calldata signature) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaUID`|`SchemaUID`|The SchemaUID of the schema the attestation is based on.| +|`attester`|`address`|| +|`requests`|`AttestationRequest[]`|| +|`signature`|`bytes`|| + + +### revoke + +Allows msg.sender to revoke an attstation made by the same msg.sender + +*this function will revert if the attestation is not found* + + +```solidity +function revoke(address attester, RevocationRequest calldata request, bytes calldata signature) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`attester`|`address`|| +|`request`|`RevocationRequest`| the RevocationRequest| +|`signature`|`bytes`|| + + +### revoke + +Allows msg.sender to revoke an attstation made by the same msg.sender + +*this function will revert if the attestation is not found* + + +```solidity +function revoke(address attester, RevocationRequest[] calldata requests, bytes calldata signature) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`attester`|`address`|| +|`requests`|`RevocationRequest[]`|| +|`signature`|`bytes`|| + + +### _domainNameAndVersion + +override thats used by Solady's EIP712 cache (constructor) + + +```solidity +function _domainNameAndVersion() internal view virtual override returns (string memory name, string memory version); +``` + +### getDigest + + +```solidity +function getDigest(AttestationRequest calldata request, address attester) external view returns (bytes32 digest); +``` + +### getDigest + + +```solidity +function getDigest(AttestationRequest[] calldata requests, address attester) external view returns (bytes32 digest); +``` + +### getDigest + + +```solidity +function getDigest(RevocationRequest calldata request, address attester) external view returns (bytes32 digest); +``` + +### getDigest + + +```solidity +function getDigest(RevocationRequest[] calldata requests, address attester) external view returns (bytes32 digest); +``` + diff --git a/docs/src/src/core/TrustManager.sol/abstract.TrustManager.md b/docs/src/src/core/TrustManager.sol/abstract.TrustManager.md new file mode 100644 index 00000000..f0efdb8e --- /dev/null +++ b/docs/src/src/core/TrustManager.sol/abstract.TrustManager.md @@ -0,0 +1,121 @@ +# TrustManager +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/core/TrustManager.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md), [TrustManagerExternalAttesterList](/src/core/TrustManagerExternalAttesterList.sol/abstract.TrustManagerExternalAttesterList.md) + +**Author:** +rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) +Implements EIP-7484 to query attestations stored in the registry. + +*This contract is abstract and provides utility functions to query attestations.* + + +## State Variables +### $accountToAttester + +```solidity +mapping(address account => TrustedAttesterRecord attesters) internal $accountToAttester; +``` + + +## Functions +### trustAttesters + +Allows smartaccounts - the end users of the registry - to appoint +one or many attesters as trusted. + + +```solidity +function trustAttesters(uint8 threshold, address[] memory attesters) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`threshold`|`uint8`|The minimum number of attestations required for a module to be considered secure.| +|`attesters`|`address[]`|The addresses of the attesters to be trusted.| + + +### check + + +```solidity +function check(address module) external view; +``` + +### checkForAccount + + +```solidity +function checkForAccount(address smartAccount, address module) external view; +``` + +### check + + +```solidity +function check(address module, ModuleType moduleType) external view; +``` + +### checkForAccount + + +```solidity +function checkForAccount(address smartAccount, address module, ModuleType moduleType) external view; +``` + +### _check + +Internal helper function to check for module's security attestations on behalf of a SmartAccount +will use registy's storage to get the trusted attester(s) of a smart account, and check if the module was attested + + +```solidity +function _check(address smartAccount, address module, ModuleType moduleType) internal view; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`smartAccount`|`address`|the smart account to check for| +|`module`|`address`|address of the module to check| +|`moduleType`|`ModuleType`|(optional param), setting moduleType = 0, will ignore moduleTypes in attestations| + + +### _requireValidAttestation + +Check that attestationRecord is valid: +- not revoked +- not expired +- correct module type (if not ZERO_MODULE_TYPE) + +this function reverts if the attestationRecord is not valid + + +```solidity +function _requireValidAttestation(ModuleType expectedType, AttestationRecord storage $attestation) internal view; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`expectedType`|`ModuleType`|the expected module type. if this is ZERO_MODULE_TYPE, types specified in the attestation are ignored| +|`$attestation`|`AttestationRecord`|the storage reference of the attestation record to check| + + +### findTrustedAttesters + +Get trusted attester for a specific smartAccount + + +```solidity +function findTrustedAttesters(address smartAccount) public view returns (address[] memory attesters); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`smartAccount`|`address`|The address of the smartAccount| + + diff --git a/docs/src/src/core/TrustManagerExternalAttesterList.sol/abstract.TrustManagerExternalAttesterList.md b/docs/src/src/core/TrustManagerExternalAttesterList.sol/abstract.TrustManagerExternalAttesterList.md new file mode 100644 index 00000000..42918e62 --- /dev/null +++ b/docs/src/src/core/TrustManagerExternalAttesterList.sol/abstract.TrustManagerExternalAttesterList.md @@ -0,0 +1,33 @@ +# TrustManagerExternalAttesterList +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/core/TrustManagerExternalAttesterList.sol) + +**Inherits:** +[IRegistry](/src/IRegistry.sol/interface.IRegistry.md) + +If smart accounts want to query the registry, and supply a list of trusted attesters in calldata, this component can be used + +*This contract is abstract and provides utility functions to query attestations with a calldata provided list of attesters* + + +## Functions +### check + + +```solidity +function check(address module, address attester) external view returns (uint256 attestedAt); +``` + +### checkN + + +```solidity +function checkN(address module, address[] calldata attesters, uint256 threshold) external view returns (uint256[] memory attestedAtArray); +``` + +### _getAttestation + + +```solidity +function _getAttestation(address module, address attester) internal view virtual returns (AttestationRecord storage $attestation); +``` + diff --git a/docs/src/src/external/IExternalResolver.sol/interface.IExternalResolver.md b/docs/src/src/external/IExternalResolver.sol/interface.IExternalResolver.md new file mode 100644 index 00000000..2672560f --- /dev/null +++ b/docs/src/src/external/IExternalResolver.sol/interface.IExternalResolver.md @@ -0,0 +1,91 @@ +# IExternalResolver +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/external/IExternalResolver.sol) + +**Inherits:** +IERC165 + +*The resolver is responsible for validating the schema and attestation data.* + +*The resolver is also responsible for processing the attestation and revocation requests.* + + +## Functions +### resolveAttestation + +*Processes an attestation and verifies whether it's valid.* + + +```solidity +function resolveAttestation(AttestationRecord calldata attestation) external payable returns (bool); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`attestation`|`AttestationRecord`|The new attestation.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`bool`|Whether the attestation is valid.| + + +### resolveAttestation + + +```solidity +function resolveAttestation(AttestationRecord[] calldata attestation) external payable returns (bool); +``` + +### resolveRevocation + +*Processes an attestation revocation and verifies if it can be revoked.* + + +```solidity +function resolveRevocation(AttestationRecord calldata attestation) external payable returns (bool); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`attestation`|`AttestationRecord`|The existing attestation to be revoked.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`bool`|Whether the attestation can be revoked.| + + +### resolveRevocation + + +```solidity +function resolveRevocation(AttestationRecord[] calldata attestation) external payable returns (bool); +``` + +### resolveModuleRegistration + +*Processes a Module Registration* + + +```solidity +function resolveModuleRegistration(address sender, address moduleAddress, ModuleRecord calldata record) external payable returns (bool); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`sender`|`address`|The msg.sender of the module registration| +|`moduleAddress`|`address`|address of the module| +|`record`|`ModuleRecord`|Module registration artefact| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`bool`|Whether the registration is valid| + + diff --git a/docs/src/src/external/IExternalSchemaValidator.sol/interface.IExternalSchemaValidator.md b/docs/src/src/external/IExternalSchemaValidator.sol/interface.IExternalSchemaValidator.md new file mode 100644 index 00000000..b9004f5f --- /dev/null +++ b/docs/src/src/external/IExternalSchemaValidator.sol/interface.IExternalSchemaValidator.md @@ -0,0 +1,26 @@ +# IExternalSchemaValidator +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/external/IExternalSchemaValidator.sol) + +**Inherits:** +IERC165 + + +## Functions +### validateSchema + +Validates an attestation request. + + +```solidity +function validateSchema(AttestationRecord calldata attestation) external returns (bool); +``` + +### validateSchema + +Validates an array of attestation requests. + + +```solidity +function validateSchema(AttestationRecord[] calldata attestations) external returns (bool); +``` + diff --git a/docs/src/src/external/README.md b/docs/src/src/external/README.md new file mode 100644 index 00000000..aaa58c78 --- /dev/null +++ b/docs/src/src/external/README.md @@ -0,0 +1,6 @@ + + +# Contents +- [examples](/src/external/examples) +- [IExternalResolver](IExternalResolver.sol/interface.IExternalResolver.md) +- [IExternalSchemaValidator](IExternalSchemaValidator.sol/interface.IExternalSchemaValidator.md) diff --git a/docs/src/src/external/examples/ERC7512Schema.sol/contract.ERC7512SchemaValidator.md b/docs/src/src/external/examples/ERC7512Schema.sol/contract.ERC7512SchemaValidator.md new file mode 100644 index 00000000..1481e239 --- /dev/null +++ b/docs/src/src/external/examples/ERC7512Schema.sol/contract.ERC7512SchemaValidator.md @@ -0,0 +1,29 @@ +# ERC7512SchemaValidator +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/external/examples/ERC7512Schema.sol) + +**Inherits:** +[IExternalSchemaValidator](/src/external/IExternalSchemaValidator.sol/interface.IExternalSchemaValidator.md), [ERC7512](/src/external/examples/ERC7512Schema.sol/interface.ERC7512.md) + + +## Functions +### supportsInterface + + +```solidity +function supportsInterface(bytes4 interfaceID) external pure override returns (bool); +``` + +### validateSchema + + +```solidity +function validateSchema(AttestationRecord calldata attestation) public view override returns (bool valid); +``` + +### validateSchema + + +```solidity +function validateSchema(AttestationRecord[] calldata attestations) external view override returns (bool valid); +``` + diff --git a/docs/src/src/external/examples/ERC7512Schema.sol/interface.ERC7512.md b/docs/src/src/external/examples/ERC7512Schema.sol/interface.ERC7512.md new file mode 100644 index 00000000..d41a62b6 --- /dev/null +++ b/docs/src/src/external/examples/ERC7512Schema.sol/interface.ERC7512.md @@ -0,0 +1,66 @@ +# ERC7512 +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/external/examples/ERC7512Schema.sol) + + +## Errors +### ERC7512_InvalidModuleAddr + +```solidity +error ERC7512_InvalidModuleAddr(); +``` + +## Structs +### Auditor + +```solidity +struct Auditor { + string name; + string uri; + string[] authors; +} +``` + +### Contract + +```solidity +struct Contract { + bytes32 chainId; + address deployment; +} +``` + +### Signature + +```solidity +struct Signature { + SignatureType sigType; + address signer; + bytes data; +} +``` + +### AuditSummary + +```solidity +struct AuditSummary { + Auditor auditor; + uint256 issuedAt; + uint256[] ercs; + Contract auditedContract; + bytes32 auditHash; + string auditUri; + uint256 signedAt; + Signature auditorSignature; +} +``` + +## Enums +### SignatureType + +```solidity +enum SignatureType { + SECP256K1, + ERC1271 +} +``` + diff --git a/docs/src/src/external/examples/README.md b/docs/src/src/external/examples/README.md new file mode 100644 index 00000000..9affe763 --- /dev/null +++ b/docs/src/src/external/examples/README.md @@ -0,0 +1,7 @@ + + +# Contents +- [ERC7512](ERC7512Schema.sol/interface.ERC7512.md) +- [ERC7512SchemaValidator](ERC7512Schema.sol/contract.ERC7512SchemaValidator.md) +- [ResolverBase](ResolverBase.sol/abstract.ResolverBase.md) +- [TokenizedResolver](TokenizedResolver.sol/contract.TokenizedResolver.md) diff --git a/docs/src/src/external/examples/ResolverBase.sol/abstract.ResolverBase.md b/docs/src/src/external/examples/ResolverBase.sol/abstract.ResolverBase.md new file mode 100644 index 00000000..65f20dcc --- /dev/null +++ b/docs/src/src/external/examples/ResolverBase.sol/abstract.ResolverBase.md @@ -0,0 +1,30 @@ +# ResolverBase +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/external/examples/ResolverBase.sol) + +**Inherits:** +[IExternalResolver](/src/external/IExternalResolver.sol/interface.IExternalResolver.md) + + +## State Variables +### REGISTRY + +```solidity +IRegistry internal immutable REGISTRY; +``` + + +## Functions +### constructor + + +```solidity +constructor(IRegistry _registry); +``` + +### onlyRegistry + + +```solidity +modifier onlyRegistry(); +``` + diff --git a/docs/src/src/external/examples/TokenizedResolver.sol/contract.TokenizedResolver.md b/docs/src/src/external/examples/TokenizedResolver.sol/contract.TokenizedResolver.md new file mode 100644 index 00000000..709a94d2 --- /dev/null +++ b/docs/src/src/external/examples/TokenizedResolver.sol/contract.TokenizedResolver.md @@ -0,0 +1,81 @@ +# TokenizedResolver +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/external/examples/TokenizedResolver.sol) + +**Inherits:** +[ResolverBase](/src/external/examples/ResolverBase.sol/abstract.ResolverBase.md) + + +## State Variables +### TOKEN + +```solidity +IERC20 public immutable TOKEN; +``` + + +### fee + +```solidity +uint256 internal immutable fee = 1e18; +``` + + +## Functions +### constructor + + +```solidity +constructor(IERC20 _token, IRegistry _registry) ResolverBase(_registry); +``` + +### supportsInterface + + +```solidity +function supportsInterface(bytes4 interfaceID) external view override returns (bool); +``` + +### resolveAttestation + + +```solidity +function resolveAttestation(AttestationRecord calldata attestation) external payable override onlyRegistry returns (bool); +``` + +### resolveAttestation + + +```solidity +function resolveAttestation(AttestationRecord[] calldata attestation) external payable override onlyRegistry returns (bool); +``` + +### resolveRevocation + + +```solidity +function resolveRevocation(AttestationRecord calldata attestation) external payable override onlyRegistry returns (bool); +``` + +### resolveRevocation + + +```solidity +function resolveRevocation(AttestationRecord[] calldata attestation) external payable override onlyRegistry returns (bool); +``` + +### resolveModuleRegistration + + +```solidity +function resolveModuleRegistration( + address sender, + address moduleAddress, + ModuleRecord calldata record +) + external + payable + override + onlyRegistry + returns (bool); +``` + diff --git a/docs/src/src/lib/AttestationLib.sol/library.AttestationLib.md b/docs/src/src/lib/AttestationLib.sol/library.AttestationLib.md new file mode 100644 index 00000000..04a030e8 --- /dev/null +++ b/docs/src/src/lib/AttestationLib.sol/library.AttestationLib.md @@ -0,0 +1,72 @@ +# AttestationLib +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/lib/AttestationLib.sol) + + +## State Variables +### ATTEST_TYPEHASH + +```solidity +bytes32 internal constant ATTEST_TYPEHASH = keccak256("AttestationRequest(address,uint48,bytes,uint256[])"); +``` + + +### REVOKE_TYPEHASH + +```solidity +bytes32 internal constant REVOKE_TYPEHASH = keccak256("RevocationRequest(address)"); +``` + + +## Functions +### sload2 + + +```solidity +function sload2(AttestationDataRef dataPointer) internal view returns (bytes memory data); +``` + +### sstore2 + + +```solidity +function sstore2(AttestationRequest calldata request, bytes32 salt) internal returns (AttestationDataRef dataPointer); +``` + +### sstore2Salt + +*We are using CREATE2 to deterministically generate the address of the attestation data. +Checking if an attestation pointer already exists, would cost more GAS in the average case.* + + +```solidity +function sstore2Salt(address attester, address module) internal view returns (bytes32 salt); +``` + +### hash + + +```solidity +function hash(AttestationRequest calldata data, uint256 nonce) internal pure returns (bytes32 _hash); +``` + +### hash + + +```solidity +function hash(AttestationRequest[] calldata data, uint256 nonce) internal pure returns (bytes32 _hash); +``` + +### hash + + +```solidity +function hash(RevocationRequest calldata data, uint256 nonce) internal pure returns (bytes32 _hash); +``` + +### hash + + +```solidity +function hash(RevocationRequest[] calldata data, uint256 nonce) internal pure returns (bytes32 _hash); +``` + diff --git a/docs/src/src/lib/Helpers.sol/library.UIDLib.md b/docs/src/src/lib/Helpers.sol/library.UIDLib.md new file mode 100644 index 00000000..126ea2fb --- /dev/null +++ b/docs/src/src/lib/Helpers.sol/library.UIDLib.md @@ -0,0 +1,47 @@ +# UIDLib +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/lib/Helpers.sol) + + +## Functions +### getUID + +*Calculates a UID for a given schema.* + + +```solidity +function getUID(SchemaRecord memory schemaRecord) internal view returns (SchemaUID); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`schemaRecord`|`SchemaRecord`|The input schema.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`SchemaUID`|schema UID.| + + +### getUID + +*Calculates a UID for a given resolver.* + + +```solidity +function getUID(ResolverRecord memory resolver) internal view returns (ResolverUID); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`resolver`|`ResolverRecord`|The input schema.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`ResolverUID`|ResolverUID.| + + diff --git a/docs/src/src/lib/ModuleDeploymentLib.sol/library.ModuleDeploymentLib.md b/docs/src/src/lib/ModuleDeploymentLib.sol/library.ModuleDeploymentLib.md new file mode 100644 index 00000000..b1da8146 --- /dev/null +++ b/docs/src/src/lib/ModuleDeploymentLib.sol/library.ModuleDeploymentLib.md @@ -0,0 +1,69 @@ +# ModuleDeploymentLib +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/lib/ModuleDeploymentLib.sol) + +**Author:** +zeroknots + +*A library that can be used to deploy the Registry* + + +## Functions +### containsCaller + + +```solidity +modifier containsCaller(bytes32 salt); +``` + +### deploy + + +```solidity +function deploy(bytes calldata _initCode, bytes32 salt) internal containsCaller(salt) returns (address deploymentAddress); +``` + +### calcAddress + +Calculates the deterministic address of a contract that would be deployed using the CREATE2 opcode. + +*The calculated address is based on the contract's code, a salt, and the address of the current contract.* + +*This function uses the formula specified in EIP-1014 (https://eips.ethereum.org/EIPS/eip-1014).* + + +```solidity +function calcAddress(bytes calldata initCode, bytes32 salt) internal view returns (address targetDeploymentAddress); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`initCode`|`bytes`|The contract code that would be deployed.| +|`salt`|`bytes32`|A salt used for the address calculation. This must be the same salt that would be passed to the CREATE2 opcode.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`targetDeploymentAddress`|`address`|The address that the contract would be deployed at if the CREATE2 opcode was called with the specified _code and _salt.| + + +## Errors +### InvalidSalt + +```solidity +error InvalidSalt(); +``` + +### InvalidAddress + +```solidity +error InvalidAddress(); +``` + +### InvalidDeployment + +```solidity +error InvalidDeployment(); +``` + diff --git a/docs/src/src/lib/ModuleTypeLib.sol/library.ModuleTypeLib.md b/docs/src/src/lib/ModuleTypeLib.sol/library.ModuleTypeLib.md new file mode 100644 index 00000000..8215dfa1 --- /dev/null +++ b/docs/src/src/lib/ModuleTypeLib.sol/library.ModuleTypeLib.md @@ -0,0 +1,26 @@ +# ModuleTypeLib +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/lib/ModuleTypeLib.sol) + + +## Functions +### isType + + +```solidity +function isType(PackedModuleTypes self, ModuleType moduleType) internal pure returns (bool); +``` + +### isType + + +```solidity +function isType(uint32 packed, uint256 check) internal pure returns (bool); +``` + +### pack + + +```solidity +function pack(ModuleType[] memory moduleTypes) internal pure returns (PackedModuleTypes); +``` + diff --git a/docs/src/src/lib/README.md b/docs/src/src/lib/README.md new file mode 100644 index 00000000..6071bb1a --- /dev/null +++ b/docs/src/src/lib/README.md @@ -0,0 +1,8 @@ + + +# Contents +- [AttestationLib](AttestationLib.sol/library.AttestationLib.md) +- [UIDLib](Helpers.sol/library.UIDLib.md) +- [ModuleDeploymentLib](ModuleDeploymentLib.sol/library.ModuleDeploymentLib.md) +- [ModuleTypeLib](ModuleTypeLib.sol/library.ModuleTypeLib.md) +- [StubLib](StubLib.sol/library.StubLib.md) diff --git a/docs/src/src/lib/StubLib.sol/library.StubLib.md b/docs/src/src/lib/StubLib.sol/library.StubLib.md new file mode 100644 index 00000000..e556d31c --- /dev/null +++ b/docs/src/src/lib/StubLib.sol/library.StubLib.md @@ -0,0 +1,80 @@ +# StubLib +[Git Source](https://github.com/rhinestonewtf/registry/blob/350cdd9001705a91cd42a82c8ee3e0cd055714e5/src/lib/StubLib.sol) + +*A library that interacts with IExternalResolver and IExternalSchemaValidator* + + +## Functions +### requireExternalSchemaValidation + +if Schema Validator is set, it will call validateSchema() on the validator + + +```solidity +function requireExternalSchemaValidation(AttestationRecord memory attestationRecord, SchemaRecord storage $schema) internal; +``` + +### requireExternalSchemaValidation + + +```solidity +function requireExternalSchemaValidation(AttestationRecord[] memory attestationRecords, SchemaRecord storage $schema) internal; +``` + +### requireExternalResolverOnAttestation + + +```solidity +function requireExternalResolverOnAttestation(AttestationRecord memory attestationRecord, ResolverRecord storage $resolver) internal; +``` + +### requireExternalResolverOnAttestation + + +```solidity +function requireExternalResolverOnAttestation(AttestationRecord[] memory attestationRecords, ResolverRecord storage $resolver) internal; +``` + +### tryExternalResolverOnRevocation + + +```solidity +function tryExternalResolverOnRevocation( + AttestationRecord memory attestationRecord, + ResolverRecord storage $resolver +) + internal + returns (bool resolved); +``` + +### tryExternalResolverOnRevocation + + +```solidity +function tryExternalResolverOnRevocation( + AttestationRecord[] memory attestationRecords, + ResolverRecord storage $resolver +) + internal + returns (bool resolved); +``` + +### requireExternalResolverOnModuleRegistration + + +```solidity +function requireExternalResolverOnModuleRegistration( + ModuleRecord memory moduleRecord, + address moduleAddress, + ResolverRecord storage $resolver +) + internal; +``` + +## Events +### ResolverRevocationError + +```solidity +event ResolverRevocationError(IExternalResolver resolver); +``` + diff --git a/foundry.toml b/foundry.toml index e554080e..32072b05 100644 --- a/foundry.toml +++ b/foundry.toml @@ -11,6 +11,9 @@ line_length = 140 multiline_func_header = "all" number_underscore="thousands" +[doc] +title = "Rhinestone Registry" + [invariant] # fail_on_revert = true runs = 1200 diff --git a/slither.config.json b/slither.config.json new file mode 100644 index 00000000..0abdc2d3 --- /dev/null +++ b/slither.config.json @@ -0,0 +1,9 @@ +{ + "solc_remaps": [ + "@openzeppelin/=node_modules/@openzeppelin/", + "solmate/=node_modules/solmate/src/", + "solady/=node_modules/solady/src/", + "forge-std/=node_modules/forge-std/src/", + "ds-test/=node_modules/ds-test/src/" + ] +} diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 9e55ff3f..40138483 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -45,6 +45,9 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, * and call it, if an external IExternalSchemaValidator was set * function will get the external IExternalResolver for the module - that the attestation is for * and call it, if an external Resolver was set + * @param attester The address of the attesting account. + * @param schemaUID the UID of the schema that the attestation is made for + * @param request AttestationRequest send by attester via calldata */ function _attest(address attester, SchemaUID schemaUID, AttestationRequest calldata request) internal { (AttestationRecord memory record, ResolverUID resolverUID) = From 815fb41a4bd8013554ceefc771d80e92a78e97c1 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 20 Feb 2024 07:21:10 +0700 Subject: [PATCH 62/84] feat: CI coverage --- .github/workflows/ci.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 99bb03c0..2135b9bd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,3 +17,9 @@ jobs: foundry-profile: "test" match-path: "test/**/*.sol" name: "Unit tests" + + coverage: + needs: ["build"] + uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-coverage.yaml@main" + with: + match-path: "test/**/*.sol" From 7a6e61102b5fa202f7c51814733fd2f764420c2e Mon Sep 17 00:00:00 2001 From: zeroknots <102132718+zeroknots@users.noreply.github.com> Date: Wed, 21 Feb 2024 08:03:47 +0700 Subject: [PATCH 63/84] feat: assembly to check attestation now in shared lib (#57) --- lcov.info | 4 +- src/IRegistry.sol | 15 +-- src/core/Attestation.sol | 4 +- src/core/AttestationManager.sol | 6 +- src/core/TrustManager.sol | 76 ++---------- src/core/TrustManagerExternalAttesterList.sol | 111 +++++------------- src/lib/TrustLib.sol | 68 +++++++++++ 7 files changed, 123 insertions(+), 161 deletions(-) create mode 100644 src/lib/TrustLib.sol diff --git a/lcov.info b/lcov.info index 71b48971..310915a6 100644 --- a/lcov.info +++ b/lcov.info @@ -10691,8 +10691,8 @@ BRDA:241,9,1,8 DA:242,0 DA:246,8 DA:248,8 -FN:255,AttestationManager._getAttestation -FNDA:32,AttestationManager._getAttestation +FN:255,AttestationManager.$getAttestation +FNDA:32,AttestationManager.$getAttestation DA:264,32 FNF:7 FNH:7 diff --git a/src/IRegistry.sol b/src/IRegistry.sol index 48cb272b..fcda55ed 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -33,16 +33,13 @@ interface IERC7484 { /* Check with external attester(s) */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - function check(address module, address attester) external view returns (uint256 attestedAt); + function check(address module, address attester) external view; - function checkN( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray); + function check(address module, ModuleType moduleType, address attester) external view; + + function checkN(address module, address[] calldata attesters, uint256 threshold) external view; + + function checkN(address module, ModuleType moduleType, address[] calldata attesters, uint256 threshold) external view; } interface IRegistry is IERC7484 { diff --git a/src/core/Attestation.sol b/src/core/Attestation.sol index 0d7ffea0..034aedfb 100644 --- a/src/core/Attestation.sol +++ b/src/core/Attestation.sol @@ -38,7 +38,7 @@ abstract contract Attestation is IRegistry, AttestationManager { * @inheritdoc IRegistry */ function findAttestation(address module, address attester) external view returns (AttestationRecord memory attestation) { - attestation = _getAttestation(module, attester); + attestation = $getAttestation(module, attester); } /** @@ -55,7 +55,7 @@ abstract contract Attestation is IRegistry, AttestationManager { uint256 length = attesters.length; attestations = new AttestationRecord[](length); for (uint256 i; i < length; i++) { - attestations[i] = _getAttestation(module, attesters[i]); + attestations[i] = $getAttestation(module, attesters[i]); } } } diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index 40138483..f1ae86e9 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -12,7 +12,7 @@ import { } from "../DataTypes.sol"; import { SchemaManager } from "./SchemaManager.sol"; import { ModuleManager } from "./ModuleManager.sol"; -import { TrustManager } from "./TrustManager.sol"; +import { TrustManagerExternalAttesterList } from "./TrustManagerExternalAttesterList.sol"; import { StubLib } from "../lib/StubLib.sol"; import { AttestationLib } from "../lib/AttestationLib.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; @@ -24,7 +24,7 @@ import { EMPTY_ATTESTATION_REF, EMPTY_RESOLVER_UID, _time, ZERO_TIMESTAMP } from * AttestationManager handles the registry's internal storage of new attestations and revocation of attestation * @dev This contract is abstract and provides utility functions to store attestations and revocations. */ -abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManager { +abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManagerExternalAttesterList { using StubLib for *; using AttestationLib for AttestationDataRef; using AttestationLib for AttestationRequest; @@ -231,7 +231,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, * Returns the attestation records for the given module and attesters. * This function is expected to be used by TrustManager and TrustManagerExternalAttesterList */ - function _getAttestation(address module, address attester) internal view override returns (AttestationRecord storage $attestation) { + function $getAttestation(address module, address attester) internal view override returns (AttestationRecord storage $attestation) { $attestation = $moduleToAttesterToAttestations[module][attester]; } } diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 1f81be4f..de6ad627 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -5,8 +5,8 @@ import { AttestationRecord, PackedModuleTypes, ModuleType, TrustedAttesterRecord import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE, ZERO_ADDRESS } from "../Common.sol"; // solhint-disable-next-line no-unused-import import { IRegistry, IERC7484 } from "../IRegistry.sol"; -import { TrustManagerExternalAttesterList } from "./TrustManagerExternalAttesterList.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; +import { TrustLib } from "../lib/TrustLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; /** @@ -19,8 +19,9 @@ import { LibSort } from "solady/utils/LibSort.sol"; * Implements EIP-7484 to query attestations stored in the registry. * @dev This contract is abstract and provides utility functions to query attestations. */ -abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { +abstract contract TrustManager is IRegistry { using ModuleTypeLib for PackedModuleTypes; + using TrustLib for AttestationRecord; using LibSort for address[]; mapping(address account => TrustedAttesterRecord attesters) internal $accountToAttester; @@ -111,83 +112,26 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { // smart account only has ONE trusted attester // use this condition to save gas else if (threshold == 1) { - AttestationRecord storage $attestation = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, $attestation); + AttestationRecord storage $attestation = $getAttestation({ module: module, attester: attester }); + $attestation.enforceValid(moduleType); } // smart account has more than one trusted attester else { // loop though list and check if the attestation is valid - AttestationRecord storage $attestation = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, $attestation); + AttestationRecord storage $attestation = $getAttestation({ module: module, attester: attester }); + $attestation.enforceValid(moduleType); for (uint256 i = 1; i < attesterCount; i++) { threshold--; // get next attester from linked List attester = $trustedAttesters.linkedAttesters[attester]; - $attestation = _getAttestation({ module: module, attester: attester }); - _requireValidAttestation(moduleType, $attestation); + $attestation = $getAttestation({ module: module, attester: attester }); + $attestation.enforceValid(moduleType); // if threshold reached, exit loop if (threshold == 0) return; } } } - /** - * Check that attestationRecord is valid: - * - not revoked - * - not expired - * - correct module type (if not ZERO_MODULE_TYPE) - * @notice this function reverts if the attestationRecord is not valid - * @param expectedType the expected module type. if this is ZERO_MODULE_TYPE, types specified in the attestation are ignored - * @param $attestation the storage reference of the attestation record to check - */ - function _requireValidAttestation(ModuleType expectedType, AttestationRecord storage $attestation) internal view { - uint256 attestedAt; - uint256 expirationTime; - uint256 revocationTime; - PackedModuleTypes packedModuleType; - /* - * Ensure only one SLOAD - * Assembly equiv to: - * - * uint256 attestedAt = record.time; - * uint256 expirationTime = record.expirationTime; - * uint256 revocationTime = record.revocationTime; - * PackedModuleTypes packedModuleType = record.moduleTypes; - */ - - assembly { - let mask := 0xffffffffffff - let slot := sload($attestation.slot) - attestedAt := and(mask, slot) - slot := shr(48, slot) - expirationTime := and(mask, slot) - slot := shr(48, slot) - revocationTime := and(mask, slot) - slot := shr(48, slot) - packedModuleType := and(mask, slot) - } - - // check if any attestation was made - if (attestedAt == ZERO_TIMESTAMP) { - revert AttestationNotFound(); - } - - // check if attestation has expired - if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { - revert AttestationNotFound(); - } - - // check if attestation has been revoked - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation($attestation.attester); - } - // if a expectedType is set, check if the attestation is for the correct module type - // if no expectedType is set, module type is not checked - if (expectedType != ZERO_MODULE_TYPE && !packedModuleType.isType(expectedType)) { - revert InvalidModuleType(); - } - } - /** * @inheritdoc IRegistry */ @@ -203,4 +147,6 @@ abstract contract TrustManager is IRegistry, TrustManagerExternalAttesterList { attesters[i] = $trustedAttesters.linkedAttesters[attesters[i - 1]]; } } + + function $getAttestation(address module, address attester) internal view virtual returns (AttestationRecord storage $attestation); } diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index 8a1b1051..9c6ae0f0 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -1,110 +1,61 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.24; -import { AttestationRecord } from "../DataTypes.sol"; -import { ZERO_TIMESTAMP } from "../Common.sol"; +import { AttestationRecord, ModuleType } from "../DataTypes.sol"; +import { ZERO_MODULE_TYPE, ZERO_TIMESTAMP } from "../Common.sol"; // solhint-disable-next-line no-unused-import import { IRegistry, IERC7484 } from "../IRegistry.sol"; +import { TrustManager } from "./TrustManager.sol"; +import { TrustLib } from "../lib/TrustLib.sol"; /** * If smart accounts want to query the registry, and supply a list of trusted attesters in calldata, this component can be used * @dev This contract is abstract and provides utility functions to query attestations with a calldata provided list of attesters */ -abstract contract TrustManagerExternalAttesterList is IRegistry { +abstract contract TrustManagerExternalAttesterList is IRegistry, TrustManager { + using TrustLib for AttestationRecord; + /** * @inheritdoc IERC7484 */ - function check(address module, address attester) external view returns (uint256 attestedAt) { - AttestationRecord storage $attestation = _getAttestation(module, attester); - - // attestedAt = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - assembly { - let mask := 0xffffffffffff - let times := sload($attestation.slot) - attestedAt := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } + function check(address module, address attester) external view { + $getAttestation(module, attester).enforceValid(ZERO_MODULE_TYPE); + } - if (attestedAt == ZERO_TIMESTAMP) { - revert AttestationNotFound(); - } + /** + * @inheritdoc IERC7484 + */ + function check(address module, ModuleType moduleType, address attester) external view { + $getAttestation(module, attester).enforceValid(moduleType); + } - if (expirationTime != ZERO_TIMESTAMP) { - if (block.timestamp > expirationTime) { - revert AttestationNotFound(); - } - } + /** + * @inheritdoc IERC7484 + */ + function checkN(address module, address[] calldata attesters, uint256 threshold) external view { + uint256 attestersLength = attesters.length; + if (attestersLength < threshold || threshold == 0) threshold = attestersLength; - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation($attestation.attester); + for (uint256 i; i < attestersLength; ++i) { + $getAttestation(module, attesters[i]).enforceValid(ZERO_MODULE_TYPE); + if (threshold != 0) --threshold; } + if (threshold == 0) return; + revert InsufficientAttestations(); } /** * @inheritdoc IERC7484 */ - function checkN( - address module, - address[] calldata attesters, - uint256 threshold - ) - external - view - returns (uint256[] memory attestedAtArray) - { + function checkN(address module, ModuleType moduleType, address[] calldata attesters, uint256 threshold) external view { uint256 attestersLength = attesters.length; - if (attestersLength < threshold || threshold == 0) { - threshold = attestersLength; - } - - uint256 timeNow = block.timestamp; - attestedAtArray = new uint256[](attestersLength); + if (attestersLength < threshold || threshold == 0) threshold = attestersLength; for (uint256 i; i < attestersLength; ++i) { - AttestationRecord storage $attestation = _getAttestation(module, attesters[i]); - - uint256 attestationTime; // = attestation.time; - uint256 expirationTime; // = attestation.expirationTime; - uint256 revocationTime; // = attestation.revocationTime; - - // @dev this loads the three time values from storage, bit shifts them and assigns them to the variables - // @dev the solidity version of the assembly code is above - assembly { - let mask := 0xffffffffffff - let times := sload($attestation.slot) - attestationTime := and(mask, times) - times := shr(48, times) - expirationTime := and(mask, times) - times := shr(48, times) - revocationTime := and(mask, times) - } - - if (revocationTime != ZERO_TIMESTAMP) { - revert RevokedAttestation($attestation.attester); - } - - if (expirationTime != ZERO_TIMESTAMP) { - if (timeNow > expirationTime) { - revert AttestationNotFound(); - } - } - - attestedAtArray[i] = attestationTime; - - if (attestationTime == ZERO_TIMESTAMP) continue; + $getAttestation(module, attesters[i]).enforceValid(moduleType); if (threshold != 0) --threshold; } - if (threshold == 0) return attestedAtArray; + if (threshold == 0) return; revert InsufficientAttestations(); } - - function _getAttestation(address module, address attester) internal view virtual returns (AttestationRecord storage $attestation); } diff --git a/src/lib/TrustLib.sol b/src/lib/TrustLib.sol new file mode 100644 index 00000000..2e34bcd7 --- /dev/null +++ b/src/lib/TrustLib.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.24; + +import { AttestationRecord, PackedModuleTypes, ModuleType } from "../DataTypes.sol"; +import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE } from "../Common.sol"; +import { IRegistry } from "../IRegistry.sol"; +import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; + +library TrustLib { + using ModuleTypeLib for PackedModuleTypes; + /** + * Check that attestationRecord is valid: + * - not revoked + * - not expired + * - correct module type (if not ZERO_MODULE_TYPE) + * @notice this function reverts if the attestationRecord is not valid + * @param expectedType the expected module type. if this is ZERO_MODULE_TYPE, types specified in the attestation are ignored + * @param $attestation the storage reference of the attestation record to check + */ + + function enforceValid(AttestationRecord storage $attestation, ModuleType expectedType) internal view { + uint256 attestedAt; + uint256 expirationTime; + uint256 revocationTime; + PackedModuleTypes packedModuleType; + /* + * Ensure only one SLOAD + * Assembly equiv to: + * + * uint256 attestedAt = record.time; + * uint256 expirationTime = record.expirationTime; + * uint256 revocationTime = record.revocationTime; + * PackedModuleTypes packedModuleType = record.moduleTypes; + */ + + assembly { + let mask := 0xffffffffffff + let slot := sload($attestation.slot) + attestedAt := and(mask, slot) + slot := shr(48, slot) + expirationTime := and(mask, slot) + slot := shr(48, slot) + revocationTime := and(mask, slot) + slot := shr(48, slot) + packedModuleType := and(mask, slot) + } + + // check if any attestation was made + if (attestedAt == ZERO_TIMESTAMP) { + revert IRegistry.AttestationNotFound(); + } + + // check if attestation has expired + if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { + revert IRegistry.AttestationNotFound(); + } + + // check if attestation has been revoked + if (revocationTime != ZERO_TIMESTAMP) { + revert IRegistry.RevokedAttestation($attestation.attester); + } + // if a expectedType is set, check if the attestation is for the correct module type + // if no expectedType is set, module type is not checked + if (expectedType != ZERO_MODULE_TYPE && !packedModuleType.isType(expectedType)) { + revert IRegistry.InvalidModuleType(); + } + } +} From c920db438b7d08497b1e05c5e210480b02e984d9 Mon Sep 17 00:00:00 2001 From: zeroknots <102132718+zeroknots@users.noreply.github.com> Date: Wed, 21 Feb 2024 08:05:03 +0700 Subject: [PATCH 64/84] Update src/Registry.sol Co-authored-by: Konrad --- src/Registry.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Registry.sol b/src/Registry.sol index 25e3b0d2..02f855b4 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -8,6 +8,4 @@ import { IRegistry } from "./IRegistry.sol"; */ contract Registry is IRegistry, SignedAttestation { -// TODO: should we create a default resolverUID thats address(0). -// this will allow the registry to be usable right after deployment without any resolver } From d0f38881a971fc9735ef8954fafe5325296140dd Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 13:18:08 +0700 Subject: [PATCH 65/84] adding code --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2135b9bd..3cd374d3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,4 +22,7 @@ jobs: needs: ["build"] uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-coverage.yaml@main" with: + env: + token: ${{ secrets.CODECOV_TOKEN }} + slug: zeroknots/registry match-path: "test/**/*.sol" From 9773594590aff470e18b823ce38984d4fcb71f79 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 13:22:03 +0700 Subject: [PATCH 66/84] adding inputs --- .github/workflows/ci.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3cd374d3..467c05ff 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,5 @@ jobs: needs: ["build"] uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-coverage.yaml@main" with: - env: - token: ${{ secrets.CODECOV_TOKEN }} - slug: zeroknots/registry match-path: "test/**/*.sol" + coverage-slug: zeroknots/registry From 2989015114252359c9f1de145a19ba7b9529cbf9 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 13:23:23 +0700 Subject: [PATCH 67/84] fix: name --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 467c05ff..df0a5c57 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,4 +23,4 @@ jobs: uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-coverage.yaml@main" with: match-path: "test/**/*.sol" - coverage-slug: zeroknots/registry + codecov-slug: zeroknots/registry From 49742421d8813397dd43f931901447e26e3d5163 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 13:25:40 +0700 Subject: [PATCH 68/84] fix: linting --- src/Registry.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Registry.sol b/src/Registry.sol index 02f855b4..59d1dae8 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -7,5 +7,4 @@ import { IRegistry } from "./IRegistry.sol"; * @author zeroknots */ -contract Registry is IRegistry, SignedAttestation { -} +contract Registry is IRegistry, SignedAttestation { } From 406e8f4769a04be81354c32c9c856451bf784741 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 13:43:29 +0700 Subject: [PATCH 69/84] fix: more coverage fic: more coverage --- test/Attestation.t.sol | 57 +++++++++++++++++++++++++++++++++++++ test/SchemaValidation.t.sol | 11 ++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 5d6beb12..981825f9 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -45,6 +45,63 @@ contract AttestationTest is BaseTest { assertEq(record.attester, attester1.addr); } + function test_WhenUsingValidMulti() public prankWithAccount(attester1) { + // It should recover. + uint32[] memory types = new uint32[](1); + + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module2), uint48(block.timestamp + 100), "", types); + + registry.attest(defaultSchemaUID, requests); + + AttestationRecord memory record = registry.findAttestation(address(module1), attester1.addr); + + assertEq(record.time, block.timestamp); + assertEq(record.expirationTime, requests[0].expirationTime); + assertEq(record.moduleAddr, requests[0].moduleAddr); + assertEq(record.attester, attester1.addr); + } + + function test_WhenUsingValidMulti__Revocation() public { + test_WhenUsingValidMulti(); + + RevocationRequest[] memory requests = new RevocationRequest[](2); + requests[0] = mockRevocation(address(module1)); + requests[1] = mockRevocation(address(module2)); + vm.prank(attester1.addr); + registry.revoke(requests); + } + + function test_findAttestation() public { + // It should recover. + uint32[] memory types = new uint32[](1); + + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module2), uint48(block.timestamp + 100), "", types); + + vm.prank(attester1.addr); + registry.attest(defaultSchemaUID, requests); + vm.prank(attester2.addr); + registry.attest(defaultSchemaUID, requests); + + address[] memory attesters = new address[](2); + attesters[0] = attester1.addr; + attesters[1] = attester2.addr; + AttestationRecord[] memory record = registry.findAttestations(address(module1), attesters); + + assertEq(record[0].time, block.timestamp); + assertEq(record[0].expirationTime, requests[0].expirationTime); + assertEq(record[0].moduleAddr, requests[0].moduleAddr); + assertEq(record[0].attester, attester1.addr); + + assertEq(record[1].time, block.timestamp); + assertEq(record[1].expirationTime, requests[0].expirationTime); + assertEq(record[1].moduleAddr, requests[0].moduleAddr); + assertEq(record[1].attester, attester2.addr); + } + function test_WhenReAttestingToARevokedAttestation() public prankWithAccount(attester1) { address module = address(new MockModule()); registry.registerModule(defaultResolverUID, module, ""); diff --git a/test/SchemaValidation.t.sol b/test/SchemaValidation.t.sol index 39a829b7..ce96eff6 100644 --- a/test/SchemaValidation.t.sol +++ b/test/SchemaValidation.t.sol @@ -12,16 +12,25 @@ contract SchemaValidationTest is BaseTest { string memory schema = "schema"; SchemaUID uid = registry.registerSchema(schema, IExternalSchemaValidator(address(0))); SchemaUID uid1 = registry.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorFalse))); + vm.expectRevert(); + uid1 = registry.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorFalse))); assertTrue(uid != uid1); + } function test_WhenSchemaNew() external whenRegisteringNewSchema { // It should register schema. string memory schema = "schema"; - SchemaUID uid = registry.registerSchema(schema, IExternalSchemaValidator(address(0))); + SchemaUID uid = registry.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorFalse))); assertTrue(uid != SchemaUID.wrap(bytes32(0))); + + + SchemaRecord memory record = registry.findSchema(uid); + assertEq(record.registeredAt, block.timestamp); + assertEq(keccak256(abi.encodePacked(record.schema)), keccak256(abi.encodePacked(schema))); + assertTrue(record.validator == IExternalSchemaValidator(address(schemaValidatorFalse))); } } From 629c25f58c707d2bbfb1783b9407e3d08b7c2eea Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 14:05:16 +0700 Subject: [PATCH 70/84] found bug: external attester --- test/SchemaValidation.t.sol | 2 -- test/TrustDelegation.t.sol | 12 +++++++++-- test/TrustDelegationExternal.t.sol | 34 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 test/TrustDelegationExternal.t.sol diff --git a/test/SchemaValidation.t.sol b/test/SchemaValidation.t.sol index ce96eff6..85f3e3e0 100644 --- a/test/SchemaValidation.t.sol +++ b/test/SchemaValidation.t.sol @@ -16,7 +16,6 @@ contract SchemaValidationTest is BaseTest { uid1 = registry.registerSchema(schema, IExternalSchemaValidator(address(schemaValidatorFalse))); assertTrue(uid != uid1); - } function test_WhenSchemaNew() external whenRegisteringNewSchema { @@ -27,7 +26,6 @@ contract SchemaValidationTest is BaseTest { assertTrue(uid != SchemaUID.wrap(bytes32(0))); - SchemaRecord memory record = registry.findSchema(uid); assertEq(record.registeredAt, block.timestamp); assertEq(keccak256(abi.encodePacked(record.schema)), keccak256(abi.encodePacked(schema))); diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol index 6db7c1ca..83a8d03e 100644 --- a/test/TrustDelegation.t.sol +++ b/test/TrustDelegation.t.sol @@ -27,15 +27,23 @@ contract TrustTest is AttestationTest { assertEq(result[0], address(attester1.addr)); } - function test_WhenSupplyingManyAttesters(address[] memory attesters) external whenSettingAttester prankWithAccount(smartAccount1) { + function test_WhenSupplyingManyAttesters( + uint8 threshold, + address[] memory attesters + ) + external + whenSettingAttester + prankWithAccount(smartAccount1) + { vm.assume(attesters.length < 100); vm.assume(attesters.length > 0); + vm.assume(threshold <= attesters.length + 4); for (uint256 i; i < attesters.length; i++) { vm.assume(attesters[i] != address(0)); } attesters.sort(); attesters.uniquifySorted(); - registry.trustAttesters(uint8(attesters.length), attesters); + registry.trustAttesters(threshold, attesters); // It should set. // It should emit event. address[] memory result = registry.findTrustedAttesters(smartAccount1.addr); diff --git a/test/TrustDelegationExternal.t.sol b/test/TrustDelegationExternal.t.sol new file mode 100644 index 00000000..e3cda8e6 --- /dev/null +++ b/test/TrustDelegationExternal.t.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "./Attestation.t.sol"; +import "src/DataTypes.sol"; +import { LibSort } from "solady/utils/LibSort.sol"; + +contract TrustTestExternal is AttestationTest { + using LibSort for address[]; + + function setUp() public override { + super.setUp(); + // test_WhenAttestingWithNoAttestationData(address(module1)); + } + + modifier whenSettingAttester() { + _; + } + + function test_WhenSupplyingExternal() external whenSettingAttester { + // It should set. + test_WhenUsingValidECDSA(); + address[] memory trustedAttesters = new address[](2); + trustedAttesters[0] = address(attester1.addr); + trustedAttesters[1] = address(attester2.addr); + + registry.check(address(module1), ModuleType.wrap(1), attester1.addr); + registry.check(address(module1), ModuleType.wrap(2), attester1.addr); + vm.expectRevert(); + registry.check(address(module1), ModuleType.wrap(3), attester1.addr); + registry.checkN(address(module1), trustedAttesters, 1); + + } +} From 7ca71860b433729cdc59d1a219e47e6e623f25f8 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 14:28:50 +0700 Subject: [PATCH 71/84] fix: more test coverage --- src/core/TrustManager.sol | 7 ++- src/core/TrustManagerExternalAttesterList.sol | 14 +++--- src/lib/TrustLib.sol | 49 +++++++++++++++++++ test/Attestation.t.sol | 18 ++++--- test/TrustDelegation.t.sol | 8 +-- test/TrustDelegationExternal.t.sol | 21 +++++++- 6 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index de6ad627..2f079486 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -106,7 +106,7 @@ abstract contract TrustManager is IRegistry { address attester = $trustedAttesters.attester; // smart account has no trusted attesters set - if (attester == ZERO_ADDRESS && threshold != 0) { + if (attester == ZERO_ADDRESS || threshold != 0) { revert NoTrustedAttestersFound(); } // smart account only has ONE trusted attester @@ -119,13 +119,12 @@ abstract contract TrustManager is IRegistry { else { // loop though list and check if the attestation is valid AttestationRecord storage $attestation = $getAttestation({ module: module, attester: attester }); - $attestation.enforceValid(moduleType); + if ($attestation.checkValid(moduleType)) threshold--; for (uint256 i = 1; i < attesterCount; i++) { - threshold--; // get next attester from linked List attester = $trustedAttesters.linkedAttesters[attester]; $attestation = $getAttestation({ module: module, attester: attester }); - $attestation.enforceValid(moduleType); + if ($attestation.checkValid(moduleType)) threshold--; // if threshold reached, exit loop if (threshold == 0) return; } diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index 9c6ae0f0..0e7afbbf 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -37,10 +37,11 @@ abstract contract TrustManagerExternalAttesterList is IRegistry, TrustManager { if (attestersLength < threshold || threshold == 0) threshold = attestersLength; for (uint256 i; i < attestersLength; ++i) { - $getAttestation(module, attesters[i]).enforceValid(ZERO_MODULE_TYPE); - if (threshold != 0) --threshold; + if ($getAttestation(module, attesters[i]).checkValid(ZERO_MODULE_TYPE)) { + --threshold; + } + if (threshold == 0) return; } - if (threshold == 0) return; revert InsufficientAttestations(); } @@ -52,10 +53,11 @@ abstract contract TrustManagerExternalAttesterList is IRegistry, TrustManager { if (attestersLength < threshold || threshold == 0) threshold = attestersLength; for (uint256 i; i < attestersLength; ++i) { - $getAttestation(module, attesters[i]).enforceValid(moduleType); - if (threshold != 0) --threshold; + if ($getAttestation(module, attesters[i]).checkValid(moduleType)) { + --threshold; + } + if (threshold == 0) return; } - if (threshold == 0) return; revert InsufficientAttestations(); } } diff --git a/src/lib/TrustLib.sol b/src/lib/TrustLib.sol index 2e34bcd7..d629aea1 100644 --- a/src/lib/TrustLib.sol +++ b/src/lib/TrustLib.sol @@ -65,4 +65,53 @@ library TrustLib { revert IRegistry.InvalidModuleType(); } } + + function checkValid(AttestationRecord storage $attestation, ModuleType expectedType) internal view returns (bool) { + uint256 attestedAt; + uint256 expirationTime; + uint256 revocationTime; + PackedModuleTypes packedModuleType; + /* + * Ensure only one SLOAD + * Assembly equiv to: + * + * uint256 attestedAt = record.time; + * uint256 expirationTime = record.expirationTime; + * uint256 revocationTime = record.revocationTime; + * PackedModuleTypes packedModuleType = record.moduleTypes; + */ + + assembly { + let mask := 0xffffffffffff + let slot := sload($attestation.slot) + attestedAt := and(mask, slot) + slot := shr(48, slot) + expirationTime := and(mask, slot) + slot := shr(48, slot) + revocationTime := and(mask, slot) + slot := shr(48, slot) + packedModuleType := and(mask, slot) + } + + // check if any attestation was made + if (attestedAt == ZERO_TIMESTAMP) { + return false; + } + + // check if attestation has expired + if (expirationTime != ZERO_TIMESTAMP && block.timestamp > expirationTime) { + return false; + } + + // check if attestation has been revoked + if (revocationTime != ZERO_TIMESTAMP) { + return false; + } + // if a expectedType is set, check if the attestation is for the correct module type + // if no expectedType is set, module type is not checked + if (expectedType != ZERO_MODULE_TYPE && !packedModuleType.isType(expectedType)) { + return false; + } + return true; + } } diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 981825f9..2608d58c 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -230,24 +230,28 @@ contract AttestationTest is BaseTest { } function test_WhenUsingValidECDSA() public whenAttestingWithSignature { - uint256 nonceBefore = registry.attesterNonce(attester1.addr); + _make_WhenUsingValidECDSA(attester1); + } + + function _make_WhenUsingValidECDSA(Account memory attester) public whenAttestingWithSignature { + uint256 nonceBefore = registry.attesterNonce(attester.addr); // It should recover. uint32[] memory types = new uint32[](2); types[0] = 1; types[1] = 2; AttestationRequest memory request = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); - bytes32 digest = registry.getDigest(request, attester1.addr); - bytes memory sig = ecdsaSign(attester1.key, digest); - registry.attest(defaultSchemaUID, attester1.addr, request, sig); + bytes32 digest = registry.getDigest(request, attester.addr); + bytes memory sig = ecdsaSign(attester.key, digest); + registry.attest(defaultSchemaUID, attester.addr, request, sig); - AttestationRecord memory record = registry.findAttestation(address(module1), attester1.addr); - uint256 nonceAfter = registry.attesterNonce(attester1.addr); + AttestationRecord memory record = registry.findAttestation(address(module1), attester.addr); + uint256 nonceAfter = registry.attesterNonce(attester.addr); assertEq(record.time, block.timestamp); assertEq(record.expirationTime, request.expirationTime); assertEq(record.moduleAddr, request.moduleAddr); - assertEq(record.attester, attester1.addr); + assertEq(record.attester, attester.addr); assertEq(nonceAfter, nonceBefore + 1); assertEq(PackedModuleTypes.unwrap(record.moduleTypes), 2 ** 1 + 2 ** 2); } diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol index 83a8d03e..417b96b3 100644 --- a/test/TrustDelegation.t.sol +++ b/test/TrustDelegation.t.sol @@ -69,13 +69,13 @@ contract TrustTest is AttestationTest { function test_WhenNoAttestersSet() external whenQueryingRegisty { // It should revert. - vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + vm.expectRevert(); registry.check(address(module1), ModuleType.wrap(1)); - vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + vm.expectRevert(); registry.checkForAccount(makeAddr("foo"), address(module1), ModuleType.wrap(1)); - vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + vm.expectRevert(); registry.check(address(module1)); - vm.expectRevert(abi.encodeWithSelector(IRegistry.AttestationNotFound.selector)); + vm.expectRevert(); registry.checkForAccount(makeAddr("foo"), address(module1)); } diff --git a/test/TrustDelegationExternal.t.sol b/test/TrustDelegationExternal.t.sol index e3cda8e6..a6ca4451 100644 --- a/test/TrustDelegationExternal.t.sol +++ b/test/TrustDelegationExternal.t.sol @@ -5,6 +5,7 @@ import "./Attestation.t.sol"; import "src/DataTypes.sol"; import { LibSort } from "solady/utils/LibSort.sol"; + contract TrustTestExternal is AttestationTest { using LibSort for address[]; @@ -19,7 +20,7 @@ contract TrustTestExternal is AttestationTest { function test_WhenSupplyingExternal() external whenSettingAttester { // It should set. - test_WhenUsingValidECDSA(); + _make_WhenUsingValidECDSA(attester1); address[] memory trustedAttesters = new address[](2); trustedAttesters[0] = address(attester1.addr); trustedAttesters[1] = address(attester2.addr); @@ -29,6 +30,24 @@ contract TrustTestExternal is AttestationTest { vm.expectRevert(); registry.check(address(module1), ModuleType.wrap(3), attester1.addr); registry.checkN(address(module1), trustedAttesters, 1); + registry.checkN(address(module1), ModuleType.wrap(1), trustedAttesters, 1); + vm.expectRevert(); + registry.checkN(address(module1), trustedAttesters, 2); + vm.expectRevert(); + registry.checkN(address(module1), ModuleType.wrap(1), trustedAttesters, 2); + _make_WhenUsingValidECDSA(attester2); + registry.checkN(address(module1), trustedAttesters, 2); + registry.checkN(address(module1), trustedAttesters, 2); + // registry.checkN(address(module1), ModuleType.wrap(1), trustedAttesters, 2); + + trustedAttesters = new address[](4); + Account memory attester3 = makeAccount("attester3"); + Account memory attester4 = makeAccount("attester4"); + trustedAttesters[0] = address(attester1.addr); + trustedAttesters[1] = address(attester3.addr); + trustedAttesters[2] = address(attester4.addr); + trustedAttesters[3] = address(attester2.addr); + registry.checkN(address(module1), trustedAttesters, 2); } } From 12ce8f10306606edea75df4fa23fe34ac5944fb2 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 14:51:56 +0700 Subject: [PATCH 72/84] fix: testing trust manager loop and threshold decrement --- src/core/TrustManager.sol | 6 +++++- test/TrustDelegation.t.sol | 21 +++++++++++++++++++++ test/TrustDelegationExternal.t.sol | 1 - 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 2f079486..68ee5f74 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -9,6 +9,8 @@ import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; import { TrustLib } from "../lib/TrustLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; +import "forge-std/console2.sol"; + /** * @title TrustManager * Allows smart accounts to query the registry for the security status of modules. @@ -106,7 +108,7 @@ abstract contract TrustManager is IRegistry { address attester = $trustedAttesters.attester; // smart account has no trusted attesters set - if (attester == ZERO_ADDRESS || threshold != 0) { + if (attester == ZERO_ADDRESS || threshold == 0) { revert NoTrustedAttestersFound(); } // smart account only has ONE trusted attester @@ -125,9 +127,11 @@ abstract contract TrustManager is IRegistry { attester = $trustedAttesters.linkedAttesters[attester]; $attestation = $getAttestation({ module: module, attester: attester }); if ($attestation.checkValid(moduleType)) threshold--; + console2.log("threshold: ", threshold); // if threshold reached, exit loop if (threshold == 0) return; } + if (threshold > 0) revert InsufficientAttestations(); } } diff --git a/test/TrustDelegation.t.sol b/test/TrustDelegation.t.sol index 417b96b3..e4cfd619 100644 --- a/test/TrustDelegation.t.sol +++ b/test/TrustDelegation.t.sol @@ -54,6 +54,27 @@ contract TrustTest is AttestationTest { } } + function test_ManyAttesters() public { + _make_WhenUsingValidECDSA(attester1); + _make_WhenUsingValidECDSA(attester2); + Account memory attester3 = makeAccount("attester3"); + Account memory attester4 = makeAccount("attester4"); + address[] memory trustedAttesters = new address[](4); + trustedAttesters[0] = address(attester1.addr); + trustedAttesters[1] = address(attester3.addr); + trustedAttesters[2] = address(attester4.addr); + trustedAttesters[3] = address(attester2.addr); + + vm.startPrank(smartAccount1.addr); + registry.trustAttesters(2, trustedAttesters); + + registry.check(address(module1), ModuleType.wrap(1)); + registry.check(address(module1), ModuleType.wrap(2)); + registry.trustAttesters(3, trustedAttesters); + vm.expectRevert(); + registry.check(address(module1), ModuleType.wrap(1)); + } + function test_WhenSupplyingSameAttesterMultipleTimes() external whenSettingAttester { address[] memory attesters = new address[](2); attesters[0] = address(attester1.addr); diff --git a/test/TrustDelegationExternal.t.sol b/test/TrustDelegationExternal.t.sol index a6ca4451..c185d182 100644 --- a/test/TrustDelegationExternal.t.sol +++ b/test/TrustDelegationExternal.t.sol @@ -5,7 +5,6 @@ import "./Attestation.t.sol"; import "src/DataTypes.sol"; import { LibSort } from "solady/utils/LibSort.sol"; - contract TrustTestExternal is AttestationTest { using LibSort for address[]; From 020ae75f0b8da08d02203cc7a12e62e87efd0c46 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 14:53:22 +0700 Subject: [PATCH 73/84] fix: linting --- src/core/TrustManager.sol | 5 +---- src/core/TrustManagerExternalAttesterList.sol | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 68ee5f74..07ed2d16 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -2,15 +2,13 @@ pragma solidity ^0.8.24; import { AttestationRecord, PackedModuleTypes, ModuleType, TrustedAttesterRecord } from "../DataTypes.sol"; -import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE, ZERO_ADDRESS } from "../Common.sol"; +import { ZERO_MODULE_TYPE, ZERO_ADDRESS } from "../Common.sol"; // solhint-disable-next-line no-unused-import import { IRegistry, IERC7484 } from "../IRegistry.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; import { TrustLib } from "../lib/TrustLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; -import "forge-std/console2.sol"; - /** * @title TrustManager * Allows smart accounts to query the registry for the security status of modules. @@ -127,7 +125,6 @@ abstract contract TrustManager is IRegistry { attester = $trustedAttesters.linkedAttesters[attester]; $attestation = $getAttestation({ module: module, attester: attester }); if ($attestation.checkValid(moduleType)) threshold--; - console2.log("threshold: ", threshold); // if threshold reached, exit loop if (threshold == 0) return; } diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index 0e7afbbf..ea78c60c 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.24; import { AttestationRecord, ModuleType } from "../DataTypes.sol"; -import { ZERO_MODULE_TYPE, ZERO_TIMESTAMP } from "../Common.sol"; +import { ZERO_MODULE_TYPE } from "../Common.sol"; // solhint-disable-next-line no-unused-import import { IRegistry, IERC7484 } from "../IRegistry.sol"; import { TrustManager } from "./TrustManager.sol"; From 1b924364a92d9bb11aec1320edaae0b90713de90 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 15:03:22 +0700 Subject: [PATCH 74/84] fix: coverage --- test/Attestation.t.sol | 18 ++++++++++++++++++ test/Base.t.sol | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 2608d58c..62a2309f 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -63,14 +63,32 @@ contract AttestationTest is BaseTest { assertEq(record.attester, attester1.addr); } + function test_WhenUsingValidMultiDifferentResolver__ShouldRevert() public prankWithAccount(attester1) { + // It should recover. + uint32[] memory types = new uint32[](1); + + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module3), uint48(block.timestamp + 100), "", types); + + vm.expectRevert(); + registry.attest(defaultSchemaUID, requests); + } + function test_WhenUsingValidMulti__Revocation() public { test_WhenUsingValidMulti(); RevocationRequest[] memory requests = new RevocationRequest[](2); requests[0] = mockRevocation(address(module1)); requests[1] = mockRevocation(address(module2)); + + vm.prank(attester2.addr); + vm.expectRevert(); + registry.revoke(requests); vm.prank(attester1.addr); registry.revoke(requests); + vm.expectRevert(); + registry.revoke(requests); } function test_findAttestation() public { diff --git a/test/Base.t.sol b/test/Base.t.sol index 2f15a898..ba2c061d 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -28,6 +28,8 @@ contract BaseTest is Test { MockResolver resolverFalse; MockResolver resolverTrue; + MockResolver differentResolver; + MockSchemaValidator schemaValidatorFalse; MockSchemaValidator schemaValidatorTrue; @@ -41,6 +43,7 @@ contract BaseTest is Test { string defaultSchema = "Foobar"; SchemaUID defaultSchemaUID; ResolverUID internal defaultResolverUID; + ResolverUID internal differentResolverUID; function setUp() public virtual { vm.warp(1_641_070_800); @@ -61,6 +64,7 @@ contract BaseTest is Test { resolverFalse = new MockResolver(false); resolverTrue = new MockResolver(true); + differentResolver = new MockResolver(true); schemaValidatorFalse = new MockSchemaValidator(false); schemaValidatorTrue = new MockSchemaValidator(true); @@ -85,12 +89,16 @@ contract BaseTest is Test { vm.prank(opsEntity1.addr); defaultResolverUID = registry.registerResolver(IExternalResolver(address(resolverTrue))); vm.prank(opsEntity1.addr); + differentResolverUID = registry.registerResolver(IExternalResolver(address(differentResolver))); + vm.prank(opsEntity1.addr); defaultSchemaUID = registry.registerSchema(defaultSchema, IExternalSchemaValidator(address(schemaValidatorTrue))); vm.prank(moduleDev1.addr); registry.registerModule(defaultResolverUID, address(module1), ""); vm.prank(moduleDev2.addr); registry.registerModule(defaultResolverUID, address(module2), ""); + vm.prank(moduleDev1.addr); + registry.registerModule(differentResolverUID, address(module3), ""); AttestationRequest memory req = AttestationRequest({ moduleAddr: address(module1), From 9acfdd55ffe8c9236b499729bf759798d7616166 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 15:15:06 +0700 Subject: [PATCH 75/84] adding coverage --- test/Attestation.t.sol | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/Attestation.t.sol b/test/Attestation.t.sol index 62a2309f..1c683e02 100644 --- a/test/Attestation.t.sol +++ b/test/Attestation.t.sol @@ -63,6 +63,38 @@ contract AttestationTest is BaseTest { assertEq(record.attester, attester1.addr); } + function test_WhenUsingInvalidSchemaUIDAttestation() public prankWithAccount(attester1) { + // It should recover. + uint32[] memory types = new uint32[](1); + + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module2), uint48(block.timestamp + 100), "", types); + + vm.expectRevert(); + registry.attest(SchemaUID.wrap(bytes32("1234")), requests); + } + + function test_WhenUsingInvalidSchemaUIDRevocation() public prankWithAccount(attester1) { + // It should recover. + uint32[] memory types = new uint32[](1); + + AttestationRequest[] memory requests = new AttestationRequest[](2); + requests[0] = mockAttestation(address(module1), uint48(block.timestamp + 100), "", types); + requests[1] = mockAttestation(address(module2), uint48(block.timestamp + 100), "", types); + + registry.attest(defaultSchemaUID, requests); + AttestationRequest memory request = mockAttestation(address(module3), uint48(block.timestamp + 100), "", types); + registry.attest(defaultSchemaUID, request); + + RevocationRequest[] memory revocations = new RevocationRequest[](2); + revocations[0] = mockRevocation(address(module2)); + revocations[1] = mockRevocation(address(module3)); + + vm.expectRevert(); + registry.revoke(revocations); + } + function test_WhenUsingValidMultiDifferentResolver__ShouldRevert() public prankWithAccount(attester1) { // It should recover. uint32[] memory types = new uint32[](1); From ff02a655ac11653b93cfdce02e15875d11167463 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 15:21:40 +0700 Subject: [PATCH 76/84] chore: adding codecov config --- codecov.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..36287aaf --- /dev/null +++ b/codecov.yml @@ -0,0 +1,11 @@ +codecov: + require_ci_to_pass: false +comment: false +coverage: + status: + patch: off +ignore: + - "script" + - "src/lib/*" + - "src/external/*" + - "test" From 939bfeebac759a88b3326b616d509ccb92a7f115 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 15:26:51 +0700 Subject: [PATCH 77/84] adding badges --- README.md | 6 +++++- codecov.yml | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7aa16856..d3001116 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ -# Registry [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ![solidity](https://img.shields.io/badge/solidity-^0.8.22-lightgrey) [![Foundry][foundry-badge]][foundry] +# Registry [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ![solidity](https://img.shields.io/badge/solidity-^0.8.24-lightgrey) [![Github Actions][gha-badge]][gha] [![Coverage][codecov-badge]][codecov] [![Foundry][foundry-badge]][foundry] +[gha]: https://github.com/rhinestonewtf/registry/actions +[gha-badge]: https://github.com/rhinestonewtf/registry/actions/workflows/ci.yml/badge.svg +[codecov]: https://codecov.io/gh/rhinestonewtf/registry +[codecov-badge]: https://codecov.io/gh/rhinestonewtf/registry/branch/main/graph/badge.svg [foundry]: https://getfoundry.sh [foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg diff --git a/codecov.yml b/codecov.yml index 36287aaf..d55ccd7b 100644 --- a/codecov.yml +++ b/codecov.yml @@ -6,6 +6,6 @@ coverage: patch: off ignore: - "script" - - "src/lib/*" - - "src/external/*" + - "src/lib" + - "src/external" - "test" From 9bad9b1ae530e2d6a2a50ed5143a664b5d516cd1 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 15:28:33 +0700 Subject: [PATCH 78/84] fix readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d3001116..9e367890 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Registry [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ![solidity](https://img.shields.io/badge/solidity-^0.8.24-lightgrey) [![Github Actions][gha-badge]][gha] [![Coverage][codecov-badge]][codecov] [![Foundry][foundry-badge]][foundry] +# Registry [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ![solidity](https://img.shields.io/badge/solidity-^0.8.24-lightgrey) [![Coverage][codecov-badge]][codecov] [![Foundry][foundry-badge]][foundry] [gha]: https://github.com/rhinestonewtf/registry/actions [gha-badge]: https://github.com/rhinestonewtf/registry/actions/workflows/ci.yml/badge.svg From a675904b5aff7f522d2430079d1c43331eb138ba Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 15:29:49 +0700 Subject: [PATCH 79/84] removing examples from codecov --- codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/codecov.yml b/codecov.yml index d55ccd7b..5563d171 100644 --- a/codecov.yml +++ b/codecov.yml @@ -8,4 +8,5 @@ ignore: - "script" - "src/lib" - "src/external" + - "src/external/examples" - "test" From 04c72b755990b5e63a76fffba42cff4b6078e4e3 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 15:35:59 +0700 Subject: [PATCH 80/84] test with fuzz --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index df0a5c57..fdbb8dc6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,5 +22,7 @@ jobs: needs: ["build"] uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-coverage.yaml@main" with: + foundry-fuzz-runs: 5000 + foundry-profile: "test" match-path: "test/**/*.sol" codecov-slug: zeroknots/registry From 2f14932e038ce0d8ae44ff5c15719e5d2c1b9640 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Wed, 21 Feb 2024 15:36:28 +0700 Subject: [PATCH 81/84] rm --- .github/workflows/ci.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fdbb8dc6..df0a5c57 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,5 @@ jobs: needs: ["build"] uses: "rhinestonewtf/reusable-workflows/.github/workflows/forge-coverage.yaml@main" with: - foundry-fuzz-runs: 5000 - foundry-profile: "test" match-path: "test/**/*.sol" codecov-slug: zeroknots/registry From 6083e21fb3c0de6af33cf7c9f69a96b073ed69fb Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 22 Feb 2024 11:09:49 +0700 Subject: [PATCH 82/84] adding docs --- .github/workflows/gh-pages.yml | 30 +++++++++ .gitignore | 1 + foundry.toml | 1 + package.json | 3 +- src/DataTypes.sol | 2 - src/IRegistry.sol | 67 +++++++++++-------- src/Registry.sol | 11 ++- src/core/Attestation.sol | 5 ++ src/core/AttestationManager.sol | 34 +++++----- src/core/ModuleManager.sol | 28 +++++--- src/core/ResolverManager.sol | 13 +++- src/core/SchemaManager.sol | 1 - src/core/SignedAttestation.sol | 5 ++ src/core/TrustManager.sol | 7 +- src/core/TrustManagerExternalAttesterList.sol | 2 + src/lib/StubLib.sol | 51 +++++++++++++- src/lib/TrustLib.sol | 15 ++++- 17 files changed, 206 insertions(+), 70 deletions(-) create mode 100644 .github/workflows/gh-pages.yml diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 00000000..cda2e14a --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,30 @@ +name: github pages + +on: + push: + branches: + - main + pull_request: + +jobs: + deploy: + runs-on: ubuntu-20.04 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + steps: + - uses: actions/checkout@v2 + + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v1 + with: + mdbook-version: "0.4.10" + # mdbook-version: 'latest' + + - run: cd docs; mdbook build + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/main' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/book diff --git a/.gitignore b/.gitignore index cd56cb3b..259c9313 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ cache/ out/ .DS_Store +docs/ # Ignores development broadcast logs /broadcast diff --git a/foundry.toml b/foundry.toml index 32072b05..31df114b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -13,6 +13,7 @@ number_underscore="thousands" [doc] title = "Rhinestone Registry" +ignore = ["src/DataTypes.sol", "src/Common.sol", "src/external"] [invariant] # fail_on_revert = true diff --git a/package.json b/package.json index f03faec7..1227cd90 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "prepack": "pnpm install", "test": "forge test", "test:lite": "FOUNDRY_PROFILE=lite forge test", - "test:optimized": "pnpm run build:optimized && FOUNDRY_PROFILE=test-optimized forge test" + "test:optimized": "pnpm run build:optimized && FOUNDRY_PROFILE=test-optimized forge test", + "build:docs": "forge doc && cd ./docs && rm -rf ./src/src/DataTypes.sol; mdbook build && cd .." } } diff --git a/src/DataTypes.sol b/src/DataTypes.sol index d5e95e07..4813f234 100644 --- a/src/DataTypes.sol +++ b/src/DataTypes.sol @@ -8,7 +8,6 @@ import { IExternalResolver } from "./external/IExternalResolver.sol"; /* Storage Structs */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ -// Struct that represents an attestation. struct AttestationRecord { uint48 time; // The time when the attestation was created (Unix timestamp). uint48 expirationTime; // The time when the attestation expires (Unix timestamp). @@ -20,7 +19,6 @@ struct AttestationRecord { SchemaUID schemaUID; // The unique identifier of the schema. } -// Struct that represents Module artefact. struct ModuleRecord { ResolverUID resolverUID; // The unique identifier of the resolver. address sender; // The address of the sender who deployed the contract diff --git a/src/IRegistry.sol b/src/IRegistry.sol index fcda55ed..c53162b9 100644 --- a/src/IRegistry.sol +++ b/src/IRegistry.sol @@ -42,6 +42,17 @@ interface IERC7484 { function checkN(address module, ModuleType moduleType, address[] calldata attesters, uint256 threshold) external view; } +/** + * Interface definition of all features of the registry: + * - Register Schemas + * - Register External Resolvers + * - Register Modules + * - Make Attestations + * - Make Revocations + * - Delegate Trust to Attester(s) + * + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + */ interface IRegistry is IERC7484 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* Smart Account - Trust Management */ @@ -60,9 +71,9 @@ interface IRegistry is IERC7484 { error InsufficientAttestations(); /** - * Allows smartaccounts - the end users of the registry - to appoint + * Allows Smart Accounts - the end users of the registry - to appoint * one or many attesters as trusted. - * @notice this function reverts, if address(0), or duplicates are provided in attesters[] + * @dev this function reverts, if address(0), or duplicates are provided in attesters[] * * @param threshold The minimum number of attestations required for a module * to be considered secure. @@ -71,8 +82,8 @@ interface IRegistry is IERC7484 { function trustAttesters(uint8 threshold, address[] calldata attesters) external; /** - * Get trusted attester for a specific smartAccount - * @param smartAccount The address of the smartAccount + * Get trusted attester for a specific Smart Account + * @param smartAccount The address of the Smart Account */ function findTrustedAttesters(address smartAccount) external view returns (address[] memory attesters); @@ -93,8 +104,8 @@ interface IRegistry is IERC7484 { error InvalidModuleTypes(); /** - * Allows msg.sender to attest to multiple modules' security status. - * The AttestationRequest.Data provided should match the attestation + * Allows `msg.sender` to attest to multiple modules' security status. + * The `AttestationRequest.Data` provided should match the attestation * schema defined by the Schema corresponding to the SchemaUID * * @dev This function will revert if the same module is attested twice by the same attester. @@ -106,8 +117,8 @@ interface IRegistry is IERC7484 { function attest(SchemaUID schemaUID, AttestationRequest calldata request) external; /** - * Allows msg.sender to attest to multiple modules' security status. - * The AttestationRequest.Data provided should match the attestation + * Allows `msg.sender` to attest to multiple modules' security status. + * The `AttestationRequest.Data` provided should match the attestation * schema defined by the Schema corresponding to the SchemaUID * * @dev This function will revert if the same module is attested twice by the same attester. @@ -119,8 +130,8 @@ interface IRegistry is IERC7484 { function attest(SchemaUID schemaUID, AttestationRequest[] calldata requests) external; /** - * Allows attester to attest by signing an AttestationRequest (ECDSA or ERC1271) - * The AttestationRequest.Data provided should match the attestation + * Allows attester to attest by signing an `AttestationRequest` (`ECDSA` or `ERC1271`) + * The `AttestationRequest.Data` provided should match the attestation * schema defined by the Schema corresponding to the SchemaUID * * @dev This function will revert if the same module is attested twice by the same attester. @@ -134,8 +145,8 @@ interface IRegistry is IERC7484 { function attest(SchemaUID schemaUID, address attester, AttestationRequest calldata request, bytes calldata signature) external; /** - * Allows attester to attest by signing an AttestationRequest (ECDSA or ERC1271) - * The AttestationRequest.Data provided should match the attestation + * Allows attester to attest by signing an `AttestationRequest` (`ECDSA` or `ERC1271`) + * The `AttestationRequest.Data` provided should match the attestation * schema defined by the Schema corresponding to the SchemaUID * * @dev This function will revert if the same module is attested twice by the same attester. @@ -169,17 +180,17 @@ interface IRegistry is IERC7484 { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /** - * Allows msg.sender to revoke an attstation made by the same msg.sender + * Allows `msg.sender` to revoke an attestation made by the same `msg.sender` * * @dev this function will revert if the attestation is not found * @dev this function will revert if the attestation is already revoked * - * @param request the RevocationRequest + * @param request single RevocationRequest */ function revoke(RevocationRequest calldata request) external; /** - * Allows msg.sender to revoke multiple attstations made by the same msg.sender + * Allows msg.sender to revoke multiple attestation made by the same msg.sender * * @dev this function will revert if the attestation is not found * @dev this function will revert if the attestation is already revoked @@ -189,16 +200,16 @@ interface IRegistry is IERC7484 { function revoke(RevocationRequest[] calldata requests) external; /** - * Allows attester to revoke an attestation by signing an RevocationRequest (ECDSA or ERC1271) + * Allows attester to revoke an attestation by signing an `RevocationRequest` (`ECDSA` or `ERC1271`) * * @param attester the signer / revoker - * @param request the RevocationRequest + * @param request single RevocationRequest * @param signature ECDSA or ERC1271 signature */ function revoke(address attester, RevocationRequest calldata request, bytes calldata signature) external; /** - * Allows attester to revoke an attestation by signing an RevocationRequest (ECDSA or ERC1271) + * Allows attester to revoke an attestation by signing an `RevocationRequest` (`ECDSA` or `ERC1271`) * @dev if you want to revoke multiple attestations, but from different attesters, call this function multiple times * * @param attester the signer / revoker @@ -219,12 +230,12 @@ interface IRegistry is IERC7484 { error FactoryCallFailed(address factory); /** - * This registry implements a CREATE2 factory, that allows module developers to register and deploy module bytecode - * @param salt The salt to be used in the CREATE2 factory. This adheres to Pr000xy/Create2Factory.sol salt formatting. + * This registry implements a `CREATE2` factory, that allows module developers to register and deploy module bytecode + * @param salt The salt to be used in the `CREATE2` factory. This adheres to Pr000xy/Create2Factory.sol salt formatting. * The salt's first bytes20 should be the address of the sender * or bytes20(0) to bypass the check (this will lose replay protection) - * @param resolverUID The resolverUID to be used in the CREATE2 factory - * @param initCode The initCode to be used in the CREATE2 factory + * @param resolverUID The resolverUID to be used in the `CREATE2` factory + * @param initCode The initCode to be used in the `CREATE2` factory * @param metadata The metadata to be stored on the registry. * This field is optional, and might be used by the module developer to store additional * information about the module or facilitate business logic with the Resolver stub @@ -297,14 +308,14 @@ interface IRegistry is IERC7484 { error InvalidSchemaValidator(IExternalSchemaValidator validator); /** - * Register Schema and (optional) external IExternalSchemaValidator - * Schemas describe the structure of the data of attestations + * Register Schema and (optional) external `IExternalSchemaValidator` + * A Schema describe the structure of the data of attestations * every attestation made on this registry, will reference a SchemaUID to * make it possible to decode attestation data in human readable form * overrwriting a schema is not allowed, and will revert * @param schema ABI schema used to encode attestations that are made with this schema * @param validator (optional) external schema validator that will be used to validate attestations. - * use address(0), if you dont need an external validator + * use address(0), if you don't need an external validator * @return uid SchemaUID of the registered schema */ function registerSchema( @@ -315,7 +326,7 @@ interface IRegistry is IERC7484 { returns (SchemaUID uid); /** - * getter function to retrieve SchemaRecord + * Getter function to retrieve SchemaRecord */ function findSchema(SchemaUID uid) external view returns (SchemaRecord memory record); @@ -336,7 +347,7 @@ interface IRegistry is IERC7484 { function registerResolver(IExternalResolver resolver) external returns (ResolverUID uid); /** - * Entities that previously regsitered an external resolver, may update the implementation address. + * Entities that previously registered an external resolver, may update the implementation address. * @param uid The UID of the resolver. * @param resolver The new resolver implementation address. */ @@ -350,7 +361,7 @@ interface IRegistry is IERC7484 { function transferResolverOwnership(ResolverUID uid, address newOwner) external; /** - * Getter function to get the ResolverRecord of a registerd resolver + * Getter function to get the ResolverRecord of a registered resolver * @param uid The UID of the resolver. */ function findResolver(ResolverUID uid) external view returns (ResolverRecord memory record); diff --git a/src/Registry.sol b/src/Registry.sol index 59d1dae8..ba970301 100644 --- a/src/Registry.sol +++ b/src/Registry.sol @@ -3,8 +3,15 @@ pragma solidity ^0.8.24; import { SignedAttestation } from "./core/SignedAttestation.sol"; import { IRegistry } from "./IRegistry.sol"; + /** - * @author zeroknots + * Implementation of all features of the registry: + * - Register Schemas + * - Register External Resolvers + * - Register Modules + * - Make Attestations + * - Make Revocations + * - Delegate Trust to Attester(s) + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) */ - contract Registry is IRegistry, SignedAttestation { } diff --git a/src/core/Attestation.sol b/src/core/Attestation.sol index 034aedfb..a92fa08e 100644 --- a/src/core/Attestation.sol +++ b/src/core/Attestation.sol @@ -5,6 +5,11 @@ import { AttestationRecord, AttestationRequest, RevocationRequest, SchemaUID } f import { AttestationManager } from "./AttestationManager.sol"; import { IRegistry } from "../IRegistry.sol"; +/** + * Abstract contract that implements the `IRegistry` interface + * Allows `msg.sender` to make attestations / revocations directly + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + */ abstract contract Attestation is IRegistry, AttestationManager { /** * @inheritdoc IRegistry diff --git a/src/core/AttestationManager.sol b/src/core/AttestationManager.sol index f1ae86e9..bce64499 100644 --- a/src/core/AttestationManager.sol +++ b/src/core/AttestationManager.sol @@ -22,7 +22,9 @@ import { EMPTY_ATTESTATION_REF, EMPTY_RESOLVER_UID, _time, ZERO_TIMESTAMP } from /** * AttestationManager handles the registry's internal storage of new attestations and revocation of attestation - * @dev This contract is abstract and provides utility functions to store attestations and revocations. + * @dev This contract is abstract and provides internal utility functions to store attestations and revocations. + * + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) */ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, TrustManagerExternalAttesterList { using StubLib for *; @@ -42,12 +44,12 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, * Processes an attestation request and stores the attestation in the registry. * If the attestation was made for a module that was not registered, the function will revert. * function will get the external Schema Validator for the supplied SchemaUID - * and call it, if an external IExternalSchemaValidator was set - * function will get the external IExternalResolver for the module - that the attestation is for + * and call it, if an external `IExternalSchemaValidator` was set + * function will get the external `IExternalResolver` for the module - that the attestation is for * and call it, if an external Resolver was set * @param attester The address of the attesting account. * @param schemaUID the UID of the schema that the attestation is made for - * @param request AttestationRequest send by attester via calldata + * @param request AttestationRequest send by attester via `calldata` */ function _attest(address attester, SchemaUID schemaUID, AttestationRequest calldata request) internal { (AttestationRecord memory record, ResolverUID resolverUID) = @@ -88,11 +90,11 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, /** * Stores an attestation in the registry storage. - * The bytes encoded AttestationRequest.Data is not stored directly into the registry storage, - * but rather stored with SSTORE2. SSTORE2/SLOAD2 is writing and reading contract storage + * The bytes encoded `AttestationRequest.Data` is not stored directly into the registry storage, + * but rather stored with `SSTORE2`. `SSTORE2/SLOAD2` is writing and reading contract storage * paying a fraction of the cost, it uses contract code as storage, writing data takes the - * form of contract creations and reading data uses EXTCODECOPY. - * since attestation data is supposed to be immutable, it is a good candidate for SSTORE2 + * form of contract creations and reading data uses `EXTCODECOPY`. + * since attestation data is supposed to be immutable, it is a good candidate for `SSTORE2` * * @dev This function will revert if the same module is attested twice by the same attester. * If you want to re-attest, you have to revoke your attestation first, and then attest again. @@ -128,7 +130,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, } // use SSTORE2 to store the data in attestationRequest - // @dev this will revert, if in a batched attestation, + // this will revert, if in a batched attestation, // the same data is used twice by the same attester for the same module since the salt will be the same AttestationDataRef sstore2Pointer = request.sstore2({ salt: attester.sstore2Salt(module) }); @@ -155,8 +157,8 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, /** * Revoke a single Revocation Request - * This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, - * and pass the RevocationRecord to the resolver to check if the revocation is valid + * This function will write the `RevocationRequest` into storage, and get the stored `RevocationRecord` back, + * and pass the `RevocationRecord` to the resolver to check if the revocation is valid */ function _revoke(address attester, RevocationRequest calldata request) internal { (AttestationRecord memory record, ResolverUID resolverUID) = _storeRevocation(attester, request); @@ -165,8 +167,8 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, /** * Revoke an array Revocation Request - * This function will write the RevocationRequest into storage, and get the stored RevocationRecord back, - * and pass the RevocationRecord to the resolver to check if the revocation is valid + * This function will write the `RevocationRequest` into storage, and get the stored `RevocationRecord` back, + * and pass the `RevocationRecord` to the resolver to check if the revocation is valid */ function _revoke(address attester, RevocationRequest[] calldata requests) internal { uint256 length = requests.length; @@ -182,7 +184,7 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, else if (resolverUID_cache != resolverUID) revert DifferentResolvers(); } - // No schema validation required during revocation. the attestation data was already checked against + // No schema validation required during revocation. The attestation data was already checked against records.tryExternalResolverOnRevocation({ $resolver: $resolvers[resolverUID] }); } @@ -228,8 +230,8 @@ abstract contract AttestationManager is IRegistry, ModuleManager, SchemaManager, } /** - * Returns the attestation records for the given module and attesters. - * This function is expected to be used by TrustManager and TrustManagerExternalAttesterList + * Returns a storage reference to attestation records for the given module and attesters. + * This function is expected to be used by `TrustManager` and `TrustManagerExternalAttesterList` */ function $getAttestation(address module, address attester) internal view override returns (AttestationRecord storage $attestation) { $attestation = $moduleToAttesterToAttestations[module][attester]; diff --git a/src/core/ModuleManager.sol b/src/core/ModuleManager.sol index 4da58042..dfa69b99 100644 --- a/src/core/ModuleManager.sol +++ b/src/core/ModuleManager.sol @@ -10,21 +10,16 @@ import { ResolverManager } from "./ResolverManager.sol"; import { IRegistry } from "../IRegistry.sol"; /** - * @title Module - * - * @dev The Module contract is responsible for handling the registration, + * The ModuleManager contract is responsible for handling module the registration, * storage and retrieval of modules on the Registry. - * This contract inherits from the IModule interface - * - * @dev The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract - * that has been deployed through the Module. The details of each module, such as its address, code hash, schema ID, + * This contract inherits from the `IModule` interface + * The primary responsibility of the Module is to deploy and manage modules. A module is a smart contract + * that has been deployed through the Module. The details of each module, such as its address, resolver UID * sender address, deploy parameters hash, and additional metadata are stored in * a struct and mapped to the module's address in * the `_modules` mapping for easy access and management. - * - * @dev In conclusion, the Module is a central part of a system to manage, - * deploy, and interact with a set of smart contracts - * in a structured and controlled manner. + * @dev The module developer select the resolver to be used for attestations and revocations made of the module. + * @dev Important: only module registrations made through the `deployModule()` function are frontrun protected. * * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) */ @@ -122,6 +117,17 @@ abstract contract ModuleManager is IRegistry, ResolverManager { record.requireExternalResolverOnModuleRegistration({ moduleAddress: moduleAddress, $resolver: $resolver }); } + /** + * Turns module registration artifacts to `ModuleRecord` to stores it in the registry storage + * @dev if a non-existent resolverUID is provided, this function reverts. + * @dev if moduleAddress is already registered, this function reverts. + * @dev if moduleAddress is not a contract, this function reverts. + * @param moduleAddress the address of the module to register + * @param sender the address of the sender who deployed the module + * @param resolverUID the unique identifier of the resolver + * @param metadata additional data related to the contract deployment. + * This parameter is optional and may be used to facilitate custom business logic on the external resolver + */ function _storeModuleRecord( address moduleAddress, address sender, diff --git a/src/core/ResolverManager.sol b/src/core/ResolverManager.sol index f4e4f065..2b380f34 100644 --- a/src/core/ResolverManager.sol +++ b/src/core/ResolverManager.sol @@ -7,6 +7,17 @@ import { IExternalResolver } from "../external/IExternalResolver.sol"; import { UIDLib } from "../lib/Helpers.sol"; import { IRegistry } from "../IRegistry.sol"; +/** + * The ResolverManager allows entities that operate modular marketplaces to register and + * manager custom `IExternalResolver` implementations. + * This feature may be used to extend the registry's security guarantees with custom business logic + * for example: + * - KYC of module devs / attesters + * - tokenomics of module devs / attestation + * @dev only `msg.sender` and the external `IExternalResolver` address are used to create a unique ID for the resolver + * This allows for a single resolver address to be possible across different chains + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + */ abstract contract ResolverManager is IRegistry { using UIDLib for ResolverRecord; @@ -25,7 +36,7 @@ abstract contract ResolverManager is IRegistry { } /** - * If a resolver is not address(0), we check if it supports the IExternalResolver interface + * If a resolver is not address(0), we check if it supports the `IExternalResolver` interface */ modifier onlyResolver(IExternalResolver resolver) { if (address(resolver) == address(0) || !resolver.supportsInterface(type(IExternalResolver).interfaceId)) { diff --git a/src/core/SchemaManager.sol b/src/core/SchemaManager.sol index 6bafe409..5f1c771a 100644 --- a/src/core/SchemaManager.sol +++ b/src/core/SchemaManager.sol @@ -9,7 +9,6 @@ import { ZERO_TIMESTAMP, _time } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; /** - * @title SchemaManager * Allows the creation registration and creation of schemas. * Schemas are used to describe attestation data. It is recommended to use ABI encoded data as schema. * An external schema validator contract may be provided, diff --git a/src/core/SignedAttestation.sol b/src/core/SignedAttestation.sol index 27b3f7cb..344b83df 100644 --- a/src/core/SignedAttestation.sol +++ b/src/core/SignedAttestation.sol @@ -8,6 +8,11 @@ import { EIP712 } from "solady/utils/EIP712.sol"; import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; import { IRegistry } from "../IRegistry.sol"; +/** + * Implements similar functionality to Attestation.sol, but with the added feature of requiring a signature from the attester. + * Signatures may be provided as `ECDSA` or `ERC-1271` + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + */ contract SignedAttestation is IRegistry, Attestation, EIP712 { using AttestationLib for AttestationRequest; using AttestationLib for RevocationRequest; diff --git a/src/core/TrustManager.sol b/src/core/TrustManager.sol index 07ed2d16..d357bf1c 100644 --- a/src/core/TrustManager.sol +++ b/src/core/TrustManager.sol @@ -10,14 +10,13 @@ import { TrustLib } from "../lib/TrustLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; /** - * @title TrustManager * Allows smart accounts to query the registry for the security status of modules. * Smart accounts may trust a list of attesters to attest to the security status of * modules and configure a minimum threshold of how many attestations have to be in place * to consider a module as "trust worthy" - * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) - * Implements EIP-7484 to query attestations stored in the registry. + * Implements `ERC-7484` to query attestations stored in the registry. * @dev This contract is abstract and provides utility functions to query attestations. + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) */ abstract contract TrustManager is IRegistry { using ModuleTypeLib for PackedModuleTypes; @@ -92,7 +91,7 @@ abstract contract TrustManager is IRegistry { } /** - * Internal helper function to check for module's security attestations on behalf of a SmartAccount + * Internal helper function to check for module's security attestations on behalf of a Smart Account * will use registy's storage to get the trusted attester(s) of a smart account, and check if the module was attested * @param smartAccount the smart account to check for * @param module address of the module to check diff --git a/src/core/TrustManagerExternalAttesterList.sol b/src/core/TrustManagerExternalAttesterList.sol index ea78c60c..caf7aca7 100644 --- a/src/core/TrustManagerExternalAttesterList.sol +++ b/src/core/TrustManagerExternalAttesterList.sol @@ -11,6 +11,8 @@ import { TrustLib } from "../lib/TrustLib.sol"; /** * If smart accounts want to query the registry, and supply a list of trusted attesters in calldata, this component can be used * @dev This contract is abstract and provides utility functions to query attestations with a calldata provided list of attesters + * + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) */ abstract contract TrustManagerExternalAttesterList is IRegistry, TrustManager { using TrustLib for AttestationRecord; diff --git a/src/lib/StubLib.sol b/src/lib/StubLib.sol index a79a6db7..16bd503a 100644 --- a/src/lib/StubLib.sol +++ b/src/lib/StubLib.sol @@ -8,14 +8,17 @@ import { ZERO_ADDRESS, ZERO_TIMESTAMP } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; /** - * @title StubLib - * @dev A library that interacts with IExternalResolver and IExternalSchemaValidator + * Helper library for interacting with `IExternalResolver` and `IExternalSchemaValidator` + * @dev if a certain resolver or validator is not set, the function will return without reverting */ library StubLib { event ResolverRevocationError(IExternalResolver resolver); /** - * @notice if Schema Validator is set, it will call validateSchema() on the validator + * Calls an external schema validator contract to validate the schema for a single attestation + * @dev if Schema Validator is set, it will call `validateSchema()` on the `IExternalSchemaValidator` contract + * @param attestationRecord the data record that will be written into registry for this attestation + * @param $schema the storage reference of the schema record */ function requireExternalSchemaValidation(AttestationRecord memory attestationRecord, SchemaRecord storage $schema) internal { // only run this function if the selected schemaUID exists @@ -28,6 +31,12 @@ library StubLib { } } + /** + * Calls an external schema validator contract to validate the schema for multiple attestation + * @dev if Schema Validator is set, it will call `validateSchema()` on the `IExternalSchemaValidator` contract + * @param attestationRecords the data records that will be written into registry for the attestations + * @param $schema the storage reference of the schema record + */ function requireExternalSchemaValidation(AttestationRecord[] memory attestationRecords, SchemaRecord storage $schema) internal { // only run this function if the selected schemaUID exists if ($schema.registeredAt == ZERO_TIMESTAMP) revert IRegistry.InvalidSchema(); @@ -39,6 +48,12 @@ library StubLib { } } + /** + * Calls an external resolver contract to resolve a single attestation + * @dev if a resolver is set, it will call `resolveAttestation()` on the `IExternalResolver` contract + * @param attestationRecord the data record that will be written into registry for the attestation + * @param $resolver the storage reference of the resolver record used for this attestation + */ function requireExternalResolverOnAttestation(AttestationRecord memory attestationRecord, ResolverRecord storage $resolver) internal { IExternalResolver resolverContract = $resolver.resolver; @@ -48,6 +63,12 @@ library StubLib { } } + /** + * Calls an external resolver contract to resolve multiple attestations + * @dev if a resolver is set, it will call `resolveAttestation()` on the `IExternalResolver` contract + * @param attestationRecords the data records that will be written into registry for the attestation + * @param $resolver the storage reference of the resolver record used for this attestation + */ function requireExternalResolverOnAttestation( AttestationRecord[] memory attestationRecords, ResolverRecord storage $resolver @@ -63,6 +84,14 @@ library StubLib { } } + /** + * Calls an external resolver contract to resolve a single revocation + * @dev if a resolver is set, it will call `resolveRevocation()` on the `IExternalResolver` contract + * @dev if the resolver contract reverts, the function will return without reverting. + * This prevents Resolvers to stop DoS revocations + * @param attestationRecord the data records of the attestation that will be revoked + * @param $resolver the storage reference of the resolver record used for this attestation + */ function tryExternalResolverOnRevocation( AttestationRecord memory attestationRecord, ResolverRecord storage $resolver @@ -81,6 +110,14 @@ library StubLib { } } + /** + * Calls an external resolver contract to resolve multiple revocation + * @dev if a resolver is set, it will call `resolveRevocation()` on the `IExternalResolver` contract + * @dev if the resolver contract reverts, the function will return without reverting. + * This prevents Resolvers to stop DoS revocations + * @param attestationRecords the data records of the attestations that will be revoked + * @param $resolver the storage reference of the resolver record used for this attestation + */ function tryExternalResolverOnRevocation( AttestationRecord[] memory attestationRecords, ResolverRecord storage $resolver @@ -99,6 +136,14 @@ library StubLib { } } + /** + * Calls an external resolver contract to resolve a module registration + * @dev if a resolver is set, it will call `resolveModuleRegistration()` on the `IExternalResolver` contract + * @param moduleRecord the module record that will be written into registry for the module registration + * @param moduleAddress the address of the module to register. + * at the point of this call, the module MUST be already deployed (could be within the current transaction) + * @param $resolver the storage reference of the resolver record used for this module registration + */ function requireExternalResolverOnModuleRegistration( ModuleRecord memory moduleRecord, address moduleAddress, diff --git a/src/lib/TrustLib.sol b/src/lib/TrustLib.sol index d629aea1..aed6fba7 100644 --- a/src/lib/TrustLib.sol +++ b/src/lib/TrustLib.sol @@ -6,8 +6,13 @@ import { ZERO_TIMESTAMP, ZERO_MODULE_TYPE } from "../Common.sol"; import { IRegistry } from "../IRegistry.sol"; import { ModuleTypeLib } from "../lib/ModuleTypeLib.sol"; +/** + * Library implements checks to validat if a storage reference for an `AttestationRecord` is currently valid + * @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat) + */ library TrustLib { using ModuleTypeLib for PackedModuleTypes; + /** * Check that attestationRecord is valid: * - not revoked @@ -17,7 +22,6 @@ library TrustLib { * @param expectedType the expected module type. if this is ZERO_MODULE_TYPE, types specified in the attestation are ignored * @param $attestation the storage reference of the attestation record to check */ - function enforceValid(AttestationRecord storage $attestation, ModuleType expectedType) internal view { uint256 attestedAt; uint256 expirationTime; @@ -66,6 +70,15 @@ library TrustLib { } } + /** + * Check that attestationRecord is valid: + * - not revoked + * - not expired + * - correct module type (if not ZERO_MODULE_TYPE) + * @dev this function DOES NOT revert if the attestationRecord is not valid, but returns false + * @param expectedType the expected module type. if this is ZERO_MODULE_TYPE, types specified in the attestation are ignored + * @param $attestation the storage reference of the attestation record to check + */ function checkValid(AttestationRecord storage $attestation, ModuleType expectedType) internal view returns (bool) { uint256 attestedAt; uint256 expirationTime; From a4f0e722223a8cc775b21da76f3c7e29c0cbc0ff Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 22 Feb 2024 11:10:47 +0700 Subject: [PATCH 83/84] make pages --- .github/workflows/gh-pages.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index cda2e14a..a256df6e 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -1,10 +1,7 @@ name: github pages on: - push: - branches: - - main - pull_request: + - push jobs: deploy: From 11939efb9cf90db069055aa0e38c933c089c6672 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Thu, 22 Feb 2024 11:11:52 +0700 Subject: [PATCH 84/84] rename ci --- .github/workflows/{gh-pages.yml => gh-pages.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{gh-pages.yml => gh-pages.yaml} (100%) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yaml similarity index 100% rename from .github/workflows/gh-pages.yml rename to .github/workflows/gh-pages.yaml