From 37103fbfb61368e561de2dc3a9c6742d3306fc28 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Tue, 30 Jan 2024 01:52:11 +0000 Subject: [PATCH 1/6] Add core actions registry --- contracts/modules/CoreActions.sol | 328 ++++++++++++++++++ contracts/modules/interfaces/ICoreActions.sol | 201 +++++++++++ 2 files changed, 529 insertions(+) create mode 100644 contracts/modules/CoreActions.sol create mode 100644 contracts/modules/interfaces/ICoreActions.sol diff --git a/contracts/modules/CoreActions.sol b/contracts/modules/CoreActions.sol new file mode 100644 index 00000000..708adc9e --- /dev/null +++ b/contracts/modules/CoreActions.sol @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.16; + +import { ICoreActions } from "@modules/interfaces/ICoreActions.sol"; +import { IAddressAliasRegistry } from "@modules/interfaces/IAddressAliasRegistry.sol"; +import { EnumerableMap } from "openzeppelin/utils/structs/EnumerableMap.sol"; +import { EIP712 } from "solady/utils/EIP712.sol"; +import { LibBitmap } from "solady/utils/LibBitmap.sol"; +import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; +import { LibZip } from "solady/utils/LibZip.sol"; +import { LibMulticaller } from "multicaller/LibMulticaller.sol"; +import { LibOps } from "@core/utils/LibOps.sol"; + +/** + * @title CoreActions + * @dev The registry for social coreActions. + */ +contract CoreActions is ICoreActions, EIP712 { + using LibBitmap for LibBitmap.Bitmap; + using EnumerableMap for EnumerableMap.AddressToUintMap; + + // ============================================================= + // CONSTANTS + // ============================================================= + + /** + * @dev For EIP-712 signature digest calculation. + */ + bytes32 public constant CORE_ACTION_REGISTRATIONS_TYPEHASH = + // prettier-ignore + keccak256( + "CoreActionRegistrations(" + "uint256 coreActionType," + "address[] targets," + "address[][] actors," + "uint256[][] timestamps," + "uint256 nonce" + ")" + ); + + // ============================================================= + // IMMUTABLES + // ============================================================= + + /** + * @dev The address alias registry. + */ + address public immutable addressAliasRegistry; + + // ============================================================= + // STORAGE + // ============================================================= + + /** + * @dev Mapping of `platform` => `coreActionType` => `target` => `actor` => `timestamp`. + */ + mapping(address => mapping(uint256 => mapping(address => EnumerableMap.AddressToUintMap))) internal _coreActions; + + /** + * @dev For storing the invalidated nonces. + */ + mapping(address => LibBitmap.Bitmap) internal _invalidatedNonces; + + /** + * @dev A mapping of `platform` => `platformSigner`. + */ + mapping(address => address) public platformSigner; + + // ============================================================= + // CONSTRUCTOR + // ============================================================= + + constructor(address addressAliasRegistry_) payable { + addressAliasRegistry = addressAliasRegistry_; + } + + // ============================================================= + // PUBLIC / EXTERNAL WRITE FUNCTIONS + // ============================================================= + + /** + * @inheritdoc ICoreActions + */ + function register(CoreActionRegistrations calldata r) + external + returns (address[] memory targetAliases, address[][] memory actorAliases) + { + uint256 n = r.targets.length; + address[] memory resolvedTargets; + address[][] memory resolvedActors = new address[][](n); + actorAliases = new address[][](n); + + // Check input array lengths and resolve aliases. + unchecked { + if (n != r.actors.length) revert ArrayLengthsMismatch(); + if (n != r.timestamps.length) revert ArrayLengthsMismatch(); + + IAddressAliasRegistry registry = IAddressAliasRegistry(addressAliasRegistry); + (resolvedTargets, targetAliases) = registry.resolveAndRegister(r.targets); + + for (uint256 i; i != n; ++i) { + if (r.actors[i].length != r.timestamps[i].length) revert ArrayLengthsMismatch(); + (resolvedActors[i], actorAliases[i]) = registry.resolveAndRegister(actorAliases[i]); + } + } + + // Check the signature and invalidate the nonce. + unchecked { + bytes32 digest = _hashTypedData( + keccak256( + abi.encode( + CORE_ACTION_REGISTRATIONS_TYPEHASH, + r.coreActionType, // uint256 + _hashOf(resolvedTargets), // address[] + _hashOf(resolvedActors), // address[][] + _hashOf(r.timestamps), // uint256[][] + r.nonce // uint256 + ) + ) + ); + + address signer = platformSigner[r.platform]; + if (!SignatureCheckerLib.isValidSignatureNowCalldata(signer, digest, r.signature)) + revert InvalidSignature(); + if (!_invalidatedNonces[signer].toggle(r.nonce)) revert InvalidSignature(); + + uint256[] memory nonces = new uint256[](1); + nonces[0] = r.nonce; + emit NoncesInvalidated(signer, nonces); + } + + // Store and emit events. + unchecked { + for (uint256 i; i != r.targets.length; ++i) { + address target = r.targets[i]; + EnumerableMap.AddressToUintMap storage m = _coreActions[r.platform][r.coreActionType][target]; + for (uint256 j; j != r.actors[i].length; ++j) { + address actor = r.actors[i][j]; + if (!m.contains(actor)) { + uint256 timestamp = r.timestamps[i][j]; + m.set(actor, timestamp); + emit Interacted(r.platform, r.coreActionType, target, actor, timestamp); + } + } + } + } + } + + /** + * @inheritdoc ICoreActions + */ + function invalidateNonces(uint256[] calldata nonces) external { + unchecked { + address sender = LibMulticaller.sender(); + LibBitmap.Bitmap storage s = _invalidatedNonces[sender]; + for (uint256 i; i != nonces.length; ++i) { + s.set(nonces[i]); + } + emit NoncesInvalidated(sender, nonces); + } + } + + /** + * @inheritdoc ICoreActions + */ + function setPlatformSigner(address signer) public { + address sender = LibMulticaller.senderOrSigner(); + platformSigner[sender] = signer; + emit PlatformSignerSet(sender, signer); + } + + // Misc functions: + // --------------- + + /** + * @dev For calldata compression. + */ + fallback() external payable { + LibZip.cdFallback(); + } + + /** + * @dev For calldata compression. + */ + receive() external payable { + LibZip.cdFallback(); + } + + // ============================================================= + // PUBLIC / EXTERNAL VIEW FUNCTIONS + // ============================================================= + + /** + * @inheritdoc ICoreActions + */ + function noncesInvalidated(address signer, uint256[] calldata nonces) public view returns (bool[] memory result) { + unchecked { + result = new bool[](nonces.length); + LibBitmap.Bitmap storage s = _invalidatedNonces[signer]; + for (uint256 i; i != nonces.length; ++i) { + result[i] = s.get(nonces[i]); + } + } + } + + /** + * @inheritdoc ICoreActions + */ + function getCoreActionTimestamp( + address platform, + uint256 coreActionType, + address target, + address actor + ) public view returns (uint256) { + return _coreActions[platform][coreActionType][target].get(actor); + } + + /** + * @inheritdoc ICoreActions + */ + function numCoreActions( + address platform, + uint256 coreActionType, + address target + ) public view returns (uint256) { + return _coreActions[platform][coreActionType][target].length(); + } + + /** + * @inheritdoc ICoreActions + */ + function getCoreActions( + address platform, + uint256 coreActionType, + address target + ) public view returns (address[] memory actors, uint256[] memory timestamps) { + EnumerableMap.AddressToUintMap storage m = _coreActions[platform][coreActionType][target]; + return getCoreActionsIn(platform, coreActionType, target, 0, m.length()); + } + + /** + * @inheritdoc ICoreActions + */ + function getCoreActionsIn( + address platform, + uint256 coreActionType, + address target, + uint256 start, + uint256 stop + ) public view returns (address[] memory actors, uint256[] memory timestamps) { + EnumerableMap.AddressToUintMap storage m = _coreActions[platform][coreActionType][target]; + unchecked { + uint256 l = stop - start; + uint256 n = m.length(); + if (LibOps.or(start > stop, stop > n)) revert InvalidQueryRange(); + actors = new address[](l); + timestamps = new uint256[](l); + for (uint256 i; i != l; ++i) { + (actors[i], timestamps[i]) = m.at(start + i); + } + } + } + + // ============================================================= + // INTERNAL / PRIVATE HELPERS + // ============================================================= + + /** + * @dev Override for EIP-712. + * @return name_ The EIP-712 name. + * @return version_ The EIP-712 version. + */ + function _domainNameAndVersion() + internal + pure + virtual + override + returns (string memory name_, string memory version_) + { + name_ = "CoreActions"; + version_ = "1"; + } + + /** + * @dev Returns the hash of `a`. + */ + function _hashOf(address[] memory a) internal pure freeTempMemory returns (bytes32) { + return keccak256(abi.encodePacked(a)); + } + + /** + * @dev Returns the hash of `a`. + */ + function _hashOf(address[][] memory a) internal pure freeTempMemory returns (bytes32) { + uint256 n = a.length; + bytes32[] memory encoded = new bytes32[](n); + for (uint256 i = 0; i != n; ++i) { + encoded[i] = keccak256(abi.encodePacked(a[i])); + } + return keccak256(abi.encodePacked(encoded)); + } + + /** + * @dev Returns the hash of `a`. + */ + function _hashOf(uint256[][] calldata a) internal pure freeTempMemory returns (bytes32) { + uint256 n = a.length; + bytes32[] memory encoded = new bytes32[](n); + for (uint256 i = 0; i != n; ++i) { + encoded[i] = keccak256(abi.encodePacked(a[i])); + } + return keccak256(abi.encodePacked(encoded)); + } + + /** + * @dev Frees all memory allocated within the scope of the function. + */ + modifier freeTempMemory() { + bytes32 m; + assembly { + m := mload(0x40) + } + _; + assembly { + mstore(0x40, m) + } + } +} diff --git a/contracts/modules/interfaces/ICoreActions.sol b/contracts/modules/interfaces/ICoreActions.sol new file mode 100644 index 00000000..77a0e237 --- /dev/null +++ b/contracts/modules/interfaces/ICoreActions.sol @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.16; + +/** + * @title CoreActions + * @dev The registry for social core actions. + */ +interface ICoreActions { + // ============================================================= + // STRUCTS + // ============================================================= + + /** + * @dev A struct containing the arguments for registering core actions. + */ + struct CoreActionRegistrations { + // The platform. + address platform; + // The core action type. + uint256 coreActionType; + // The list of targets. + address[] targets; + // The list of lists of timestamps. Must have the same dimensions as `actors`. + address[][] actors; + // The list of lists of timestamps. Must have the same dimensions as `actors`. + uint256[][] timestamps; + // The nonce of the signature (per platform's signer). + uint256 nonce; + // A signature by the current `platform` signer to authorize registration. + bytes signature; + } + + // ============================================================= + // EVENTS + // ============================================================= + + /** + * @dev Emitted when `actor` performs a core action with `target`, + * of `coreActionType`, on `platform`, at `timestamp`. + * @param platform The platform address. + * @param coreActionType The core action type. + * @param target The core action target. + * @param actor The core action actor. + * @param timestamp The core action timestamp. + */ + event Interacted( + address indexed platform, + uint256 coreActionType, + address indexed target, + address indexed actor, + uint256 timestamp + ); + + /** + * @dev Emitted when the `nonces` of `signer` are invalidated. + * @param signer The signer of the nonces. + * @param nonces The nonces. + */ + event NoncesInvalidated(address indexed signer, uint256[] nonces); + + /** + * @dev Emitted when the signer for a platform is set. + * @param platform The platform address. + * @param signer The signer for the platform. + */ + event PlatformSignerSet(address indexed platform, address signer); + + // ============================================================= + // ERRORS + // ============================================================= + + /** + * @dev The signature is invalid. + */ + error InvalidSignature(); + + /** + * @dev The length of the input arrays must be the same. + */ + error ArrayLengthsMismatch(); + + /** + * @dev The query range exceeds the bounds. + */ + error InvalidQueryRange(); + + // ============================================================= + // PUBLIC / EXTERNAL WRITE FUNCTIONS + // ============================================================= + + /** + * @dev Registers a batch of core actions. + * @param r The core actions to register. + * @return targetAliases A list of aliases corresponding to `targets`. + * @return actorAliases A list of aliases corresponding to `actors`. + */ + function register(CoreActionRegistrations memory r) + external + returns (address[] memory targetAliases, address[][] memory actorAliases); + + /** + * @dev Allows the platform to set their signer. + * @param signer The signer for the platform. + */ + function setPlatformSigner(address signer) external; + + /** + * @dev Invalidates the nonces for the `msg.sender`. + * @param nonces The nonces. + */ + function invalidateNonces(uint256[] calldata nonces) external; + + // ============================================================= + // PUBLIC / EXTERNAL VIEW FUNCTIONS + // ============================================================= + + /** + * @dev Returns the CoreActionRegistrations struct's EIP-712 typehash. + * @return The constant value. + */ + function CORE_ACTION_REGISTRATIONS_TYPEHASH() external pure returns (bytes32); + + /** + * @dev Returns the configured signer for `platform`. + * @param platform The platform. + * @return The configured value. + */ + function platformSigner(address platform) external view returns (address); + + /** + * @dev Returns whether each of the `nonces` of `signer` has been invalidated. + * @param signer The signer of the signature. + * @param nonces An array of nonces. + * @return A bool array representing whether each nonce has been invalidated. + */ + function noncesInvalidated(address signer, uint256[] calldata nonces) external view returns (bool[] memory); + + /** + * @dev Returns the core action timestamp of `actor` on target`, + * of `coreActionType`, on `platform`. + * @param platform The platform. + * @param coreActionType The core action type. + * @param target The core action target. + * @param actor The actor + * @return The amped timestamp value. + */ + function getCoreActionTimestamp( + address platform, + uint256 coreActionType, + address target, + address actor + ) external view returns (uint256); + + /** + * @dev Returns the number of core actions on `target`, + * of `coreActionType` on `platform`. + * @param platform The platform. + * @param coreActionType The core action type. + * @param target The core action target. + * @return The latest value. + */ + function numCoreActions( + address platform, + uint256 coreActionType, + address target + ) external view returns (uint256); + + /** + * @dev Returns the list of `actors` and `timestamps` + * for coreActions on `target`, of `coreActionType`, on `platform`. + * @param platform The platform. + * @param coreActionType The core action type. + * @param target The core action target. + * @return actors The actors for the coreActions. + * @return timestamps The timestamps of the coreActions. + */ + function getCoreActions( + address platform, + uint256 coreActionType, + address target + ) external view returns (address[] memory actors, uint256[] memory timestamps); + + /** + * @dev Returns the list of `actors` and `timestamps` + * for coreActions on `target`, of `coreActionType`, on `platform`. + * @param platform The platform. + * @param coreActionType The core action type. + * @param target The core action target. + * @param start The start index of the range. + * @param stop The end index of the range (exclusive). + * @return actors The actors for the coreActions. + * @return timestamps The timestamps of the coreActions. + */ + function getCoreActionsIn( + address platform, + uint256 coreActionType, + address target, + uint256 start, + uint256 stop + ) external view returns (address[] memory actors, uint256[] memory timestamps); +} From b45bb967b64109e4deda1309585cdced292d20cb Mon Sep 17 00:00:00 2001 From: Vectorized Date: Tue, 30 Jan 2024 03:53:16 +0000 Subject: [PATCH 2/6] T --- contracts/modules/CoreActions.sol | 68 ++++++++++++++++++------------- tests/modules/CoreActions.t.sol | 53 ++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 tests/modules/CoreActions.t.sol diff --git a/contracts/modules/CoreActions.sol b/contracts/modules/CoreActions.sol index 708adc9e..54e3430f 100644 --- a/contracts/modules/CoreActions.sol +++ b/contracts/modules/CoreActions.sol @@ -105,7 +105,7 @@ contract CoreActions is ICoreActions, EIP712 { } // Check the signature and invalidate the nonce. - unchecked { + { bytes32 digest = _hashTypedData( keccak256( abi.encode( @@ -131,7 +131,7 @@ contract CoreActions is ICoreActions, EIP712 { // Store and emit events. unchecked { - for (uint256 i; i != r.targets.length; ++i) { + for (uint256 i; i != n; ++i) { address target = r.targets[i]; EnumerableMap.AddressToUintMap storage m = _coreActions[r.platform][r.coreActionType][target]; for (uint256 j; j != r.actors[i].length; ++j) { @@ -283,46 +283,56 @@ contract CoreActions is ICoreActions, EIP712 { /** * @dev Returns the hash of `a`. + * @param a The input to hash. + * @return result The hash. */ - function _hashOf(address[] memory a) internal pure freeTempMemory returns (bytes32) { - return keccak256(abi.encodePacked(a)); - } - - /** - * @dev Returns the hash of `a`. - */ - function _hashOf(address[][] memory a) internal pure freeTempMemory returns (bytes32) { - uint256 n = a.length; - bytes32[] memory encoded = new bytes32[](n); - for (uint256 i = 0; i != n; ++i) { - encoded[i] = keccak256(abi.encodePacked(a[i])); + function _hashOf(address[] memory a) internal pure returns (bytes32 result) { + assembly { + result := keccak256(add(0x20, a), shl(5, mload(a))) } - return keccak256(abi.encodePacked(encoded)); } /** * @dev Returns the hash of `a`. + * @param a The input to hash. + * @return result The hash. */ - function _hashOf(uint256[][] calldata a) internal pure freeTempMemory returns (bytes32) { - uint256 n = a.length; - bytes32[] memory encoded = new bytes32[](n); - for (uint256 i = 0; i != n; ++i) { - encoded[i] = keccak256(abi.encodePacked(a[i])); + function _hashOf(address[][] memory a) internal pure returns (bytes32 result) { + assembly { + let m := mload(0x40) + let n := shl(5, mload(a)) + for { + let i := 0 + } iszero(eq(i, n)) { + i := add(i, 0x20) + } { + let o := mload(add(add(a, 0x20), i)) + mstore(add(m, i), keccak256(add(0x20, o), shl(5, mload(o)))) + } + result := keccak256(m, n) } - return keccak256(abi.encodePacked(encoded)); } /** - * @dev Frees all memory allocated within the scope of the function. + * @dev Returns the hash of `a`. + * @param a The input to hash. + * @return result The hash. */ - modifier freeTempMemory() { - bytes32 m; + function _hashOf(uint256[][] calldata a) internal pure returns (bytes32 result) { assembly { - m := mload(0x40) - } - _; - assembly { - mstore(0x40, m) + let m := mload(0x40) + let n := shl(5, a.length) + for { + let i := 0 + } iszero(eq(i, n)) { + i := add(i, 0x20) + } { + let o := add(a.offset, calldataload(add(a.offset, i))) + let p := add(m, i) + calldatacopy(p, add(o, 0x20), shl(5, calldataload(o))) + mstore(p, keccak256(p, shl(5, calldataload(o)))) + } + result := keccak256(m, n) } } } diff --git a/tests/modules/CoreActions.t.sol b/tests/modules/CoreActions.t.sol new file mode 100644 index 00000000..44b09195 --- /dev/null +++ b/tests/modules/CoreActions.t.sol @@ -0,0 +1,53 @@ +pragma solidity ^0.8.16; + +// import { IERC721AUpgradeable, ISoundEditionV2_1, SoundEditionV2_1 } from "@core/SoundEditionV2_1.sol"; +// import { ISuperMinterV2, SuperMinterV2 } from "@modules/SuperMinterV2.sol"; +// import { IPlatformAirdropper, PlatformAirdropper } from "@modules/PlatformAirdropper.sol"; +// import { IAddressAliasRegistry, AddressAliasRegistry } from "@modules/AddressAliasRegistry.sol"; +// import { LibOps } from "@core/utils/LibOps.sol"; +// import { Ownable } from "solady/auth/Ownable.sol"; +// import { LibZip } from "solady/utils/LibZip.sol"; +// import { SafeCastLib } from "solady/utils/SafeCastLib.sol"; +// import { LibSort } from "solady/utils/LibSort.sol"; +import "../TestConfigV2_1.sol"; + +contract CoreActionsTests is TestConfigV2_1 { + function setUp() public virtual override { + super.setUp(); + // ISoundEditionV2_1.EditionInitialization memory init = genericEditionInitialization(); + // init.tierCreations = new ISoundEditionV2_1.TierCreation[](2); + // init.tierCreations[0].tier = 0; + // init.tierCreations[1].tier = 1; + // init.tierCreations[1].maxMintableLower = type(uint32).max; + // init.tierCreations[1].maxMintableUpper = type(uint32).max; + // edition = createSoundEdition(init); + // sm = new SuperMinterV2(); + // edition.grantRoles(address(sm), edition.MINTER_ROLE()); + // aar = new AddressAliasRegistry(); + // pa = new PlatformAirdropper(address(aar)); + } + + // function _computeDigest() + + function _hashOf(address[] memory a) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(a)); + } + + function _hashOf(address[][] memory a) internal pure returns (bytes32) { + uint256 n = a.length; + bytes32[] memory encoded = new bytes32[](n); + for (uint256 i = 0; i != n; ++i) { + encoded[i] = keccak256(abi.encodePacked(a[i])); + } + return keccak256(abi.encodePacked(encoded)); + } + + function _hashOf(uint256[][] calldata a) internal pure returns (bytes32) { + uint256 n = a.length; + bytes32[] memory encoded = new bytes32[](n); + for (uint256 i = 0; i != n; ++i) { + encoded[i] = keccak256(abi.encodePacked(a[i])); + } + return keccak256(abi.encodePacked(encoded)); + } +} From fe9c408e74f64d11df517b38e33836f8e36a78d7 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Tue, 30 Jan 2024 11:20:21 +0000 Subject: [PATCH 3/6] T --- contracts/modules/CoreActions.sol | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts/modules/CoreActions.sol b/contracts/modules/CoreActions.sol index 54e3430f..43b11736 100644 --- a/contracts/modules/CoreActions.sol +++ b/contracts/modules/CoreActions.sol @@ -9,7 +9,6 @@ import { LibBitmap } from "solady/utils/LibBitmap.sol"; import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; import { LibZip } from "solady/utils/LibZip.sol"; import { LibMulticaller } from "multicaller/LibMulticaller.sol"; -import { LibOps } from "@core/utils/LibOps.sol"; /** * @title CoreActions @@ -100,7 +99,7 @@ contract CoreActions is ICoreActions, EIP712 { for (uint256 i; i != n; ++i) { if (r.actors[i].length != r.timestamps[i].length) revert ArrayLengthsMismatch(); - (resolvedActors[i], actorAliases[i]) = registry.resolveAndRegister(actorAliases[i]); + (resolvedActors[i], actorAliases[i]) = registry.resolveAndRegister(r.actors[i]); } } @@ -132,10 +131,10 @@ contract CoreActions is ICoreActions, EIP712 { // Store and emit events. unchecked { for (uint256 i; i != n; ++i) { - address target = r.targets[i]; + address target = resolvedTargets[i]; EnumerableMap.AddressToUintMap storage m = _coreActions[r.platform][r.coreActionType][target]; - for (uint256 j; j != r.actors[i].length; ++j) { - address actor = r.actors[i][j]; + for (uint256 j; j != resolvedActors[i].length; ++j) { + address actor = resolvedActors[i][j]; if (!m.contains(actor)) { uint256 timestamp = r.timestamps[i][j]; m.set(actor, timestamp); @@ -252,7 +251,7 @@ contract CoreActions is ICoreActions, EIP712 { unchecked { uint256 l = stop - start; uint256 n = m.length(); - if (LibOps.or(start > stop, stop > n)) revert InvalidQueryRange(); + if (start > stop || stop > n) revert InvalidQueryRange(); actors = new address[](l); timestamps = new uint256[](l); for (uint256 i; i != l; ++i) { From 15f8e10da5543f6e9e8a80501bea51b216b7f061 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Wed, 31 Jan 2024 18:55:40 +0000 Subject: [PATCH 4/6] T --- contracts/modules/CoreActions.sol | 83 +++++++++++-------- contracts/modules/interfaces/ICoreActions.sol | 15 ++-- 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/contracts/modules/CoreActions.sol b/contracts/modules/CoreActions.sol index 43b11736..dd548763 100644 --- a/contracts/modules/CoreActions.sol +++ b/contracts/modules/CoreActions.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.16; import { ICoreActions } from "@modules/interfaces/ICoreActions.sol"; import { IAddressAliasRegistry } from "@modules/interfaces/IAddressAliasRegistry.sol"; -import { EnumerableMap } from "openzeppelin/utils/structs/EnumerableMap.sol"; import { EIP712 } from "solady/utils/EIP712.sol"; import { LibBitmap } from "solady/utils/LibBitmap.sol"; import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; +import { LibMap } from "solady/utils/LibMap.sol"; import { LibZip } from "solady/utils/LibZip.sol"; import { LibMulticaller } from "multicaller/LibMulticaller.sol"; @@ -16,7 +16,23 @@ import { LibMulticaller } from "multicaller/LibMulticaller.sol"; */ contract CoreActions is ICoreActions, EIP712 { using LibBitmap for LibBitmap.Bitmap; - using EnumerableMap for EnumerableMap.AddressToUintMap; + using LibMap for LibMap.Uint32Map; + + // ============================================================= + // STRUCTS + // ============================================================= + + /** + * @dev Storage struct for storing mapping from `actors` => `timestamps`. + */ + struct ActorsAndTimestamps { + // Number of entries, for enumeration. + uint256 length; + // `actorAlias` => `timestamp`. + LibMap.Uint32Map timestamps; + // `index` => `actorAlias`. + LibMap.Uint32Map actorAliases; + } // ============================================================= // CONSTANTS @@ -32,7 +48,7 @@ contract CoreActions is ICoreActions, EIP712 { "uint256 coreActionType," "address[] targets," "address[][] actors," - "uint256[][] timestamps," + "uint32[][] timestamps," "uint256 nonce" ")" ); @@ -51,9 +67,9 @@ contract CoreActions is ICoreActions, EIP712 { // ============================================================= /** - * @dev Mapping of `platform` => `coreActionType` => `target` => `actor` => `timestamp`. + * @dev Mapping of `platform` => `coreActionType` => `target` => `actorAndTimestamps`. */ - mapping(address => mapping(uint256 => mapping(address => EnumerableMap.AddressToUintMap))) internal _coreActions; + mapping(address => mapping(uint256 => mapping(address => ActorsAndTimestamps))) internal _coreActions; /** * @dev For storing the invalidated nonces. @@ -132,13 +148,15 @@ contract CoreActions is ICoreActions, EIP712 { unchecked { for (uint256 i; i != n; ++i) { address target = resolvedTargets[i]; - EnumerableMap.AddressToUintMap storage m = _coreActions[r.platform][r.coreActionType][target]; + ActorsAndTimestamps storage m = _coreActions[r.platform][r.coreActionType][target]; for (uint256 j; j != resolvedActors[i].length; ++j) { - address actor = resolvedActors[i][j]; - if (!m.contains(actor)) { - uint256 timestamp = r.timestamps[i][j]; - m.set(actor, timestamp); - emit Interacted(r.platform, r.coreActionType, target, actor, timestamp); + uint32 actorAlias = uint32(uint160(actorAliases[i][j])); + if (m.timestamps.get(uint256(actorAlias)) == 0) { + uint32 timestamp = r.timestamps[i][j]; + if (timestamp == 0) revert TimestampIsZero(); + m.timestamps.set(actorAlias, timestamp); + m.actorAliases.set(m.length++, actorAlias); + emit Interacted(r.platform, r.coreActionType, target, resolvedActors[i][j], timestamp); } } } @@ -210,8 +228,10 @@ contract CoreActions is ICoreActions, EIP712 { uint256 coreActionType, address target, address actor - ) public view returns (uint256) { - return _coreActions[platform][coreActionType][target].get(actor); + ) public view returns (uint32) { + ActorsAndTimestamps storage m = _coreActions[platform][coreActionType][target]; + uint256 actorAlias = uint160(IAddressAliasRegistry(addressAliasRegistry).aliasOf(actor)); + return m.timestamps.get(actorAlias); } /** @@ -222,7 +242,7 @@ contract CoreActions is ICoreActions, EIP712 { uint256 coreActionType, address target ) public view returns (uint256) { - return _coreActions[platform][coreActionType][target].length(); + return _coreActions[platform][coreActionType][target].length; } /** @@ -232,9 +252,9 @@ contract CoreActions is ICoreActions, EIP712 { address platform, uint256 coreActionType, address target - ) public view returns (address[] memory actors, uint256[] memory timestamps) { - EnumerableMap.AddressToUintMap storage m = _coreActions[platform][coreActionType][target]; - return getCoreActionsIn(platform, coreActionType, target, 0, m.length()); + ) public view returns (address[] memory actors, uint32[] memory timestamps) { + ActorsAndTimestamps storage m = _coreActions[platform][coreActionType][target]; + return getCoreActionsIn(platform, coreActionType, target, 0, m.length); } /** @@ -246,16 +266,19 @@ contract CoreActions is ICoreActions, EIP712 { address target, uint256 start, uint256 stop - ) public view returns (address[] memory actors, uint256[] memory timestamps) { - EnumerableMap.AddressToUintMap storage m = _coreActions[platform][coreActionType][target]; + ) public view returns (address[] memory actors, uint32[] memory timestamps) { + ActorsAndTimestamps storage m = _coreActions[platform][coreActionType][target]; unchecked { uint256 l = stop - start; - uint256 n = m.length(); + uint256 n = m.length; if (start > stop || stop > n) revert InvalidQueryRange(); actors = new address[](l); - timestamps = new uint256[](l); + timestamps = new uint32[](l); + IAddressAliasRegistry registry = IAddressAliasRegistry(addressAliasRegistry); for (uint256 i; i != l; ++i) { - (actors[i], timestamps[i]) = m.at(start + i); + uint32 actorAlias = m.actorAliases.get(start + i); + actors[i] = registry.addressOf(address(uint160(actorAlias))); + timestamps[i] = m.timestamps.get(actorAlias); } } } @@ -300,11 +323,8 @@ contract CoreActions is ICoreActions, EIP712 { assembly { let m := mload(0x40) let n := shl(5, mload(a)) - for { - let i := 0 - } iszero(eq(i, n)) { - i := add(i, 0x20) - } { + // prettier-ignore + for { let i := 0 } iszero(eq(i, n)) { i := add(i, 0x20) } { let o := mload(add(add(a, 0x20), i)) mstore(add(m, i), keccak256(add(0x20, o), shl(5, mload(o)))) } @@ -317,15 +337,12 @@ contract CoreActions is ICoreActions, EIP712 { * @param a The input to hash. * @return result The hash. */ - function _hashOf(uint256[][] calldata a) internal pure returns (bytes32 result) { + function _hashOf(uint32[][] calldata a) internal pure returns (bytes32 result) { assembly { let m := mload(0x40) let n := shl(5, a.length) - for { - let i := 0 - } iszero(eq(i, n)) { - i := add(i, 0x20) - } { + // prettier-ignore + for { let i := 0 } iszero(eq(i, n)) { i := add(i, 0x20) } { let o := add(a.offset, calldataload(add(a.offset, i))) let p := add(m, i) calldatacopy(p, add(o, 0x20), shl(5, calldataload(o))) diff --git a/contracts/modules/interfaces/ICoreActions.sol b/contracts/modules/interfaces/ICoreActions.sol index 77a0e237..7b295d41 100644 --- a/contracts/modules/interfaces/ICoreActions.sol +++ b/contracts/modules/interfaces/ICoreActions.sol @@ -23,7 +23,7 @@ interface ICoreActions { // The list of lists of timestamps. Must have the same dimensions as `actors`. address[][] actors; // The list of lists of timestamps. Must have the same dimensions as `actors`. - uint256[][] timestamps; + uint32[][] timestamps; // The nonce of the signature (per platform's signer). uint256 nonce; // A signature by the current `platform` signer to authorize registration. @@ -48,7 +48,7 @@ interface ICoreActions { uint256 coreActionType, address indexed target, address indexed actor, - uint256 timestamp + uint32 timestamp ); /** @@ -79,6 +79,11 @@ interface ICoreActions { */ error ArrayLengthsMismatch(); + /** + * @dev The timestamp cannot be zero. + */ + error TimestampIsZero(); + /** * @dev The query range exceeds the bounds. */ @@ -149,7 +154,7 @@ interface ICoreActions { uint256 coreActionType, address target, address actor - ) external view returns (uint256); + ) external view returns (uint32); /** * @dev Returns the number of core actions on `target`, @@ -178,7 +183,7 @@ interface ICoreActions { address platform, uint256 coreActionType, address target - ) external view returns (address[] memory actors, uint256[] memory timestamps); + ) external view returns (address[] memory actors, uint32[] memory timestamps); /** * @dev Returns the list of `actors` and `timestamps` @@ -197,5 +202,5 @@ interface ICoreActions { address target, uint256 start, uint256 stop - ) external view returns (address[] memory actors, uint256[] memory timestamps); + ) external view returns (address[] memory actors, uint32[] memory timestamps); } From 4fffa325315ee998b431bb1883a032b2a1c68cfd Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 5 Feb 2024 22:20:24 +0000 Subject: [PATCH 5/6] Add tests --- contracts/modules/CoreActions.sol | 87 +++++++--- contracts/modules/interfaces/ICoreActions.sol | 7 + tests/modules/CoreActions.t.sol | 151 +++++++++++++++--- 3 files changed, 204 insertions(+), 41 deletions(-) diff --git a/contracts/modules/CoreActions.sol b/contracts/modules/CoreActions.sol index dd548763..e1f9c110 100644 --- a/contracts/modules/CoreActions.sol +++ b/contracts/modules/CoreActions.sol @@ -100,39 +100,25 @@ contract CoreActions is ICoreActions, EIP712 { external returns (address[] memory targetAliases, address[][] memory actorAliases) { + _validateArrayLengths(r); + uint256 n = r.targets.length; address[] memory resolvedTargets; address[][] memory resolvedActors = new address[][](n); actorAliases = new address[][](n); - // Check input array lengths and resolve aliases. + // Resolve and register aliases. unchecked { - if (n != r.actors.length) revert ArrayLengthsMismatch(); - if (n != r.timestamps.length) revert ArrayLengthsMismatch(); - IAddressAliasRegistry registry = IAddressAliasRegistry(addressAliasRegistry); (resolvedTargets, targetAliases) = registry.resolveAndRegister(r.targets); - for (uint256 i; i != n; ++i) { - if (r.actors[i].length != r.timestamps[i].length) revert ArrayLengthsMismatch(); (resolvedActors[i], actorAliases[i]) = registry.resolveAndRegister(r.actors[i]); } } // Check the signature and invalidate the nonce. { - bytes32 digest = _hashTypedData( - keccak256( - abi.encode( - CORE_ACTION_REGISTRATIONS_TYPEHASH, - r.coreActionType, // uint256 - _hashOf(resolvedTargets), // address[] - _hashOf(resolvedActors), // address[][] - _hashOf(r.timestamps), // uint256[][] - r.nonce // uint256 - ) - ) - ); + bytes32 digest = _computeDigest(r, resolvedTargets, resolvedActors); address signer = platformSigner[r.platform]; if (!SignatureCheckerLib.isValidSignatureNowCalldata(signer, digest, r.signature)) @@ -283,10 +269,60 @@ contract CoreActions is ICoreActions, EIP712 { } } + /** + * @inheritdoc ICoreActions + */ + function computeDigest(CoreActionRegistrations calldata r) external view returns (bytes32) { + _validateArrayLengths(r); + + uint256 n = r.targets.length; + address[] memory resolvedTargets; + address[][] memory resolvedActors = new address[][](n); + + // Resolve aliases. + unchecked { + IAddressAliasRegistry registry = IAddressAliasRegistry(addressAliasRegistry); + (resolvedTargets, ) = registry.resolve(r.targets); + for (uint256 i; i != n; ++i) { + if (r.actors[i].length != r.timestamps[i].length) revert ArrayLengthsMismatch(); + (resolvedActors[i], ) = registry.resolve(r.actors[i]); + } + } + + return _computeDigest(r, resolvedTargets, resolvedActors); + } + // ============================================================= // INTERNAL / PRIVATE HELPERS // ============================================================= + /** + * @dev Returns the digest for `r`, with `resolvedTargets` and `resolvedActors`. + * @param r The core actions to register. + * @param resolvedTargets The list of resolved targets. + * @param resolvedActors The list of resolved actors. + * @return The computed digest. + */ + function _computeDigest( + CoreActionRegistrations calldata r, + address[] memory resolvedTargets, + address[][] memory resolvedActors + ) internal view returns (bytes32) { + return + _hashTypedData( + keccak256( + abi.encode( + CORE_ACTION_REGISTRATIONS_TYPEHASH, + r.coreActionType, // uint256 + _hashOf(resolvedTargets), // address[] + _hashOf(resolvedActors), // address[][] + _hashOf(r.timestamps), // uint256[][] + r.nonce // uint256 + ) + ) + ); + } + /** * @dev Override for EIP-712. * @return name_ The EIP-712 name. @@ -303,6 +339,21 @@ contract CoreActions is ICoreActions, EIP712 { version_ = "1"; } + /** + * @dev Validate the array lengths. + * @param r The core actions to register. + */ + function _validateArrayLengths(CoreActionRegistrations calldata r) internal pure { + unchecked { + uint256 n = r.targets.length; + if (n != r.actors.length) revert ArrayLengthsMismatch(); + if (n != r.timestamps.length) revert ArrayLengthsMismatch(); + for (uint256 i; i != n; ++i) { + if (r.actors[i].length != r.timestamps[i].length) revert ArrayLengthsMismatch(); + } + } + } + /** * @dev Returns the hash of `a`. * @param a The input to hash. diff --git a/contracts/modules/interfaces/ICoreActions.sol b/contracts/modules/interfaces/ICoreActions.sol index 7b295d41..72469650 100644 --- a/contracts/modules/interfaces/ICoreActions.sol +++ b/contracts/modules/interfaces/ICoreActions.sol @@ -125,6 +125,13 @@ interface ICoreActions { */ function CORE_ACTION_REGISTRATIONS_TYPEHASH() external pure returns (bytes32); + /** + * @dev Returns the digest for the core actions to register. + * @param r The core actions to register. + * @return The computed value. + */ + function computeDigest(CoreActionRegistrations memory r) external view returns (bytes32); + /** * @dev Returns the configured signer for `platform`. * @param platform The platform. diff --git a/tests/modules/CoreActions.t.sol b/tests/modules/CoreActions.t.sol index 44b09195..32a2c4a2 100644 --- a/tests/modules/CoreActions.t.sol +++ b/tests/modules/CoreActions.t.sol @@ -1,33 +1,138 @@ pragma solidity ^0.8.16; -// import { IERC721AUpgradeable, ISoundEditionV2_1, SoundEditionV2_1 } from "@core/SoundEditionV2_1.sol"; -// import { ISuperMinterV2, SuperMinterV2 } from "@modules/SuperMinterV2.sol"; -// import { IPlatformAirdropper, PlatformAirdropper } from "@modules/PlatformAirdropper.sol"; -// import { IAddressAliasRegistry, AddressAliasRegistry } from "@modules/AddressAliasRegistry.sol"; -// import { LibOps } from "@core/utils/LibOps.sol"; -// import { Ownable } from "solady/auth/Ownable.sol"; -// import { LibZip } from "solady/utils/LibZip.sol"; -// import { SafeCastLib } from "solady/utils/SafeCastLib.sol"; -// import { LibSort } from "solady/utils/LibSort.sol"; +import { ICoreActions, CoreActions } from "@modules/CoreActions.sol"; +import { IAddressAliasRegistry, AddressAliasRegistry } from "@modules/AddressAliasRegistry.sol"; +import { EnumerableMap } from "openzeppelin/utils/structs/EnumerableMap.sol"; +import { LibSort } from "solady/utils/LibSort.sol"; import "../TestConfigV2_1.sol"; contract CoreActionsTests is TestConfigV2_1 { + using EnumerableMap for *; + + AddressAliasRegistry aar; + CoreActions ca; + + EnumerableMap.Bytes32ToUintMap expectedTimestamps; + function setUp() public virtual override { super.setUp(); - // ISoundEditionV2_1.EditionInitialization memory init = genericEditionInitialization(); - // init.tierCreations = new ISoundEditionV2_1.TierCreation[](2); - // init.tierCreations[0].tier = 0; - // init.tierCreations[1].tier = 1; - // init.tierCreations[1].maxMintableLower = type(uint32).max; - // init.tierCreations[1].maxMintableUpper = type(uint32).max; - // edition = createSoundEdition(init); - // sm = new SuperMinterV2(); - // edition.grantRoles(address(sm), edition.MINTER_ROLE()); - // aar = new AddressAliasRegistry(); - // pa = new PlatformAirdropper(address(aar)); - } - - // function _computeDigest() + aar = new AddressAliasRegistry(); + ca = new CoreActions(address(aar)); + } + + struct _TestTemps { + address platform; + uint256 platformSignerPrivateKey; + address platformSigner; + address[] targetAliases; + address[][] actorAliases; + address[] targets; + address[] actors; + uint32[] timestamps; + } + + function testRegisterCoreActions(uint256) public { + _TestTemps memory t; + t.platform = _randomNonZeroAddress(); + (t.platformSigner, t.platformSignerPrivateKey) = _randomSigner(); + + vm.prank(t.platform); + ca.setPlatformSigner(t.platformSigner); + + CoreActions.CoreActionRegistrations memory rs; + rs.platform = t.platform; + rs.coreActionType = _random(); + rs.targets = _randomNonZeroAddressesGreaterThan(); + rs.actors = new address[][](rs.targets.length); + rs.timestamps = new uint32[][](rs.targets.length); + rs.nonce = _random(); + for (uint256 i; i != rs.targets.length; ++i) { + rs.actors[i] = _randomNonZeroAddressesGreaterThan(); + rs.timestamps[i] = _randomTimestamps(rs.actors[i].length); + for (uint256 j; j != rs.actors[i].length; ++j) { + bytes32 h = keccak256(abi.encodePacked(rs.targets[i], rs.actors[i][j])); + if (!expectedTimestamps.contains(h)) { + expectedTimestamps.set(h, rs.timestamps[i][j]); + } + } + } + rs.signature = _generateSignature(rs, t.platformSignerPrivateKey); + + (t.targetAliases, t.actorAliases) = ca.register(rs); + + for (uint256 i; i != rs.targets.length; ++i) { + for (uint256 j; j != rs.actors[i].length; ++j) { + uint32 timestamp = ca.getCoreActionTimestamp( + rs.platform, + rs.coreActionType, + rs.targets[i], + rs.actors[i][j] + ); + bytes32 h = keccak256(abi.encodePacked(rs.targets[i], rs.actors[i][j])); + assertEq(timestamp, expectedTimestamps.get(h)); + } + } + + uint256 actionsSum; + t.targets = LibSort.difference(rs.targets, new address[](0)); + LibSort.sort(t.targets); + LibSort.uniquifySorted(t.targets); + for (uint256 i; i != t.targets.length; ++i) { + (t.actors, t.timestamps) = ca.getCoreActions(rs.platform, rs.coreActionType, t.targets[i]); + assertEq(t.actors.length, t.timestamps.length); + actionsSum += t.actors.length; + for (uint256 j; j != t.actors.length; ++j) { + bytes32 h = keccak256(abi.encodePacked(t.targets[i], t.actors[j])); + assertEq(t.timestamps[j], expectedTimestamps.get(h)); + } + } + assertEq(actionsSum, expectedTimestamps.length()); + + vm.expectRevert(ICoreActions.InvalidSignature.selector); + ca.register(rs); + } + + function _generateSignature(CoreActions.CoreActionRegistrations memory rs, uint256 privateKey) + internal + returns (bytes memory signature) + { + bytes32 digest = ca.computeDigest(rs); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + signature = abi.encodePacked(r, s, v); + } + + function _randomNonZeroAddressesGreaterThan() internal returns (address[] memory a) { + a = _randomNonZeroAddressesGreaterThan(0xffffffff); + } + + function _randomNonZeroAddressesGreaterThan(uint256 t) internal returns (address[] memory a) { + uint256 n = _random() % 4; + if (_random() % 32 == 0) { + n = _random() % 32; + } + a = new address[](n); + require(t != 0, "t must not be zero"); + unchecked { + for (uint256 i; i != n; ++i) { + uint256 r; + if (_random() & 1 == 0) { + while (r <= t) r = uint256(uint160(_random())); + } else { + r = type(uint256).max ^ _bound(_random(), 1, 8); + } + a[i] = address(uint160(r)); + } + } + } + + function _randomTimestamps(uint256 n) internal returns (uint32[] memory a) { + a = new uint32[](n); + unchecked { + for (uint256 i; i != n; ++i) { + a[i] = uint32(_bound(_random(), 1, type(uint32).max)); + } + } + } function _hashOf(address[] memory a) internal pure returns (bytes32) { return keccak256(abi.encodePacked(a)); From 641d8e7d7528661c177e76e701023f72acb38e0e Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 5 Feb 2024 22:39:15 +0000 Subject: [PATCH 6/6] T --- contracts/modules/CoreActions.sol | 11 +++++++---- contracts/modules/interfaces/ICoreActions.sol | 18 +++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/contracts/modules/CoreActions.sol b/contracts/modules/CoreActions.sol index e1f9c110..b57147a5 100644 --- a/contracts/modules/CoreActions.sol +++ b/contracts/modules/CoreActions.sol @@ -215,6 +215,7 @@ contract CoreActions is ICoreActions, EIP712 { address target, address actor ) public view returns (uint32) { + target = IAddressAliasRegistry(addressAliasRegistry).addressOf(target); ActorsAndTimestamps storage m = _coreActions[platform][coreActionType][target]; uint256 actorAlias = uint160(IAddressAliasRegistry(addressAliasRegistry).aliasOf(actor)); return m.timestamps.get(actorAlias); @@ -228,6 +229,7 @@ contract CoreActions is ICoreActions, EIP712 { uint256 coreActionType, address target ) public view returns (uint256) { + target = IAddressAliasRegistry(addressAliasRegistry).addressOf(target); return _coreActions[platform][coreActionType][target].length; } @@ -239,8 +241,7 @@ contract CoreActions is ICoreActions, EIP712 { uint256 coreActionType, address target ) public view returns (address[] memory actors, uint32[] memory timestamps) { - ActorsAndTimestamps storage m = _coreActions[platform][coreActionType][target]; - return getCoreActionsIn(platform, coreActionType, target, 0, m.length); + return getCoreActionsIn(platform, coreActionType, target, 0, type(uint256).max); } /** @@ -253,11 +254,13 @@ contract CoreActions is ICoreActions, EIP712 { uint256 start, uint256 stop ) public view returns (address[] memory actors, uint32[] memory timestamps) { + target = IAddressAliasRegistry(addressAliasRegistry).addressOf(target); ActorsAndTimestamps storage m = _coreActions[platform][coreActionType][target]; unchecked { - uint256 l = stop - start; uint256 n = m.length; - if (start > stop || stop > n) revert InvalidQueryRange(); + if (stop > n) stop = n; + uint256 l = stop - start; + if (start > stop) revert InvalidQueryRange(); actors = new address[](l); timestamps = new uint32[](l); IAddressAliasRegistry registry = IAddressAliasRegistry(addressAliasRegistry); diff --git a/contracts/modules/interfaces/ICoreActions.sol b/contracts/modules/interfaces/ICoreActions.sol index 72469650..7f97e135 100644 --- a/contracts/modules/interfaces/ICoreActions.sol +++ b/contracts/modules/interfaces/ICoreActions.sol @@ -19,10 +19,14 @@ interface ICoreActions { // The core action type. uint256 coreActionType; // The list of targets. + // Can be full addresses or aliases. address[] targets; - // The list of lists of timestamps. Must have the same dimensions as `actors`. + // The list of lists of timestamps. + // Can be full addresses or aliases. + // Must have the same dimensions as `actors`. address[][] actors; - // The list of lists of timestamps. Must have the same dimensions as `actors`. + // The list of lists of timestamps. + // Must have the same dimensions as `actors`. uint32[][] timestamps; // The nonce of the signature (per platform's signer). uint256 nonce; @@ -154,7 +158,7 @@ interface ICoreActions { * @param coreActionType The core action type. * @param target The core action target. * @param actor The actor - * @return The amped timestamp value. + * @return The timestamp value. */ function getCoreActionTimestamp( address platform, @@ -183,8 +187,8 @@ interface ICoreActions { * @param platform The platform. * @param coreActionType The core action type. * @param target The core action target. - * @return actors The actors for the coreActions. - * @return timestamps The timestamps of the coreActions. + * @return actors The actors for the core actions. + * @return timestamps The timestamps of the core actions. */ function getCoreActions( address platform, @@ -200,8 +204,8 @@ interface ICoreActions { * @param target The core action target. * @param start The start index of the range. * @param stop The end index of the range (exclusive). - * @return actors The actors for the coreActions. - * @return timestamps The timestamps of the coreActions. + * @return actors The actors for the core actions. + * @return timestamps The timestamps of the core actions. */ function getCoreActionsIn( address platform,