Skip to content

Commit

Permalink
fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Raul committed Dec 7, 2023
2 parents 9b7d5c0 + 64fe5ee commit 64d7cdb
Show file tree
Hide file tree
Showing 102 changed files with 8,253 additions and 3,179 deletions.
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ SEPOLIA_PRIVATEKEY = 12341234123412341234123412341234
SEPOLIA_ADMIN_ADDRESS = 0x12341234123412341234123412341234

# ETHSCAN
ETHERSCAN_API_KEY = ETHERSCANAPIKEYETHERSCANAPIKEY
ETHERSCAN_API_KEY = ETHERSCANAPIKEYETHERSCANAPIKEY

# PROTOCOL LICENSE URL
PIPL_URL=https://url-to-license-file.pdf

# POLYGON TOKEN ORACLE
POLYGON_TOKEN_ORACLE_CLIENT=0x123412341234123412341234123412341234
POLYGON_TOKEN_ORACLE_COORDINATOR=0x123412341234123412341234123412341234
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ docs/
.env

.idea/
.vscode
.github/
node_modules/

Expand All @@ -28,3 +29,6 @@ deployment-31337.json
# Script data
script/data/

# Coverage
coverage/
lcov.info
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-include .env

.PHONY: all test clean
.PHONY: all test clean coverage

all: clean install build

Expand Down Expand Up @@ -28,6 +28,13 @@ slither :; slither ./contracts

format :; npx prettier --write contracts/**/*.sol && npx prettier --write contracts/*.sol

# remove `test` and `script` folders from coverage
coverage:
mkdir -p coverage
forge coverage --report lcov
lcov --remove lcov.info -o lcov.info 'test/*' 'script/*'
genhtml lcov.info --output-dir coverage

# solhint should be installed globally
lint :; npx solhint contracts/**/*.sol && npx solhint contracts/*.sol

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,13 @@ or
forge test
```

## Coverage

```
make coverage
```
Open `index.html` in `coverage/` folder.

# Deploying to a network

## Setup
Expand Down
682 changes: 682 additions & 0 deletions broadcast/Main.s.sol/11155111/run-1701936223.json

Large diffs are not rendered by default.

1,612 changes: 1,612 additions & 0 deletions broadcast/Main.s.sol/11155111/run-1701936255.json

Large diffs are not rendered by default.

1,612 changes: 1,612 additions & 0 deletions broadcast/Main.s.sol/11155111/run-1701936957.json

Large diffs are not rendered by default.

1,857 changes: 875 additions & 982 deletions broadcast/Main.s.sol/11155111/run-latest.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion contracts/IPAssetRegistry.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { IIPAssetRegistry } from "contracts/interfaces/IIPAssetRegistry.sol";
Expand Down
9 changes: 7 additions & 2 deletions contracts/StoryProtocol.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { IIPOrgController } from "contracts/interfaces/ip-org/IIPOrgController.sol";
Expand Down Expand Up @@ -101,7 +102,7 @@ contract StoryProtocol is Multicall {
);
}

/// @notice Transfers an IP asset to another owner.
/// @notice Registers an IP Asset.
/// @param ipOrg_ The governing IP Org under which the IP asset is registered.
/// @param params_ The registration params, including owner, name, hash.
/// @param preHooksData_ Hooks to embed with the registration pre-call.
Expand All @@ -122,6 +123,10 @@ contract StoryProtocol is Multicall {
preHooksData_,
postHooksData_
);
// If the result is empty, then the registration module is pending for async hook execution.
if (result.length == 0) {
return (0, 0);
}
return abi.decode(result, (uint256, uint256));
}

Expand Down
3 changes: 2 additions & 1 deletion contracts/access-control/AccessControlSingleton.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf

pragma solidity ^0.8.13;

Expand Down
3 changes: 2 additions & 1 deletion contracts/access-control/AccessControlled.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf

pragma solidity ^0.8.9;

Expand Down
3 changes: 2 additions & 1 deletion contracts/access-control/AccessControlledUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";
Expand Down
146 changes: 146 additions & 0 deletions contracts/hooks/PolygonTokenHook.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import { HookResult } from "contracts/interfaces/hooks/base/IHook.sol";
import { AsyncBaseHook } from "contracts/hooks/base/AsyncBaseHook.sol";
import { Errors } from "contracts/lib/Errors.sol";
import { PolygonToken } from "contracts/lib/hooks/PolygonToken.sol";

interface IPolygonTokenClient {
function sendRequest(
bytes32 requestId,
address requester,
address tokenAddress,
address tokenOwnerAddress,
address callbackAddr,
bytes4 callbackFunctionSignature
) external;
}

/// @title PolygonTokenHook
/// @notice This is asynchronous hook used to verify a user owning specific Polygon tokens.
contract PolygonTokenHook is AsyncBaseHook {
/// @notice The address that is allowed to call the callback function.
/// @dev This address is set during contract deployment and cannot be changed afterwards.
address private immutable CALLBACK_CALLER;

address public immutable ORACLE_CLIENT;

/// @notice A counter used to generate unique request IDs for each token request.
uint256 private nonce;

/// @notice A mapping that links each request ID to a PolygonTokenRequest struct.
mapping(bytes32 => PolygonTokenRequest) private requestIdToRequest;

/// @notice A struct used to store information about a token request.
/// @dev It includes the requester's address, the token's address, the token owner's address, a balance threshold,
/// and two boolean flags to indicate whether the request is completed and whether it exists.
struct PolygonTokenRequest {
address requester;
address tokenAddress;
address tokenOwnerAddress;
uint256 balanceThreshold;
bool isRequestCompleted;
bool exists;
}

/// @notice Initializes the contract during deployment.
/// @param accessControl_ The address of the access control contract.
/// @param oracleClient_ The address of the oracle client contract for access Polygon Token info.
/// @param callbackCaller_ The address of the callback caller contract.
constructor(
address accessControl_,
address oracleClient_,
address callbackCaller_
) AsyncBaseHook(accessControl_) {
if (callbackCaller_ == address(0)) revert Errors.ZeroAddress();
if (oracleClient_ == address(0)) revert Errors.ZeroAddress();
CALLBACK_CALLER = callbackCaller_;
ORACLE_CLIENT = oracleClient_;
}

/// @notice Handles the callback of a token request.
/// @param requestId The unique ID of the request.
/// @param balance The balance of the token.
/// @dev This function checks if the request exists and verifies the token balance against the balance threshold.
/// If the balance is less than the threshold, it sets an error message. Otherwise, it sets the isPassed flag to true.
/// It then deletes the request from the requestIdToRequest mapping.
/// Finally, it calls the _handleCallback() function, passing the requestId and the encoded isPassed flag and errorMessage.
/// The encoding is done using abi.encode(isPassed, errorMessage).
function handleCallback(bytes32 requestId, uint256 balance) external {
bool isPassed = false;
string memory errorMessage = "";
require(requestIdToRequest[requestId].exists, "Request not found");
if (balance < requestIdToRequest[requestId].balanceThreshold) {
errorMessage = "Balance of Token is not enough";
} else {
isPassed = true;
}
delete requestIdToRequest[requestId];
_handleCallback(requestId, abi.encode(isPassed, errorMessage));
}

/// @notice Validates the configuration for the hook.
/// @dev This function checks if the tokenAddress and balanceThreshold in the configuration are valid.
/// It reverts if the tokenAddress is the zero address or if the balanceThreshold is zero.
/// @param hookConfig_ The configuration data for the hook, encoded as bytes.
function _validateConfig(bytes memory hookConfig_) internal pure override {
PolygonToken.Config memory config = abi.decode(
hookConfig_,
(PolygonToken.Config)
);
if (config.tokenAddress == address(0)) {
revert Errors.Hook_InvalidHookConfig("tokenAddress is 0");
}
if (config.balanceThreshold == 0) {
revert Errors.Hook_InvalidHookConfig("balanceThreshold is 0");
}
}

/// @dev Internal function to request an asynchronous call,
/// concrete hoot implementation should override the function.
/// The function should revert in case of error.
/// @param hookConfig_ The configuration of the hook.
/// @param hookParams_ The parameters for the hook.
/// @return hookData The data returned by the hook.
/// @return requestId The ID of the request.
function _requestAsyncCall(
bytes memory hookConfig_,
bytes memory hookParams_
) internal override returns (bytes memory hookData, bytes32 requestId) {
PolygonToken.Config memory config = abi.decode(
hookConfig_,
(PolygonToken.Config)
);
PolygonToken.Params memory params = abi.decode(
hookParams_,
(PolygonToken.Params)
);
requestId = keccak256(abi.encodePacked(this, nonce++));
hookData = "";

requestIdToRequest[requestId] = PolygonTokenRequest({
requester: msg.sender,
tokenAddress: config.tokenAddress,
tokenOwnerAddress: params.tokenOwnerAddress,
balanceThreshold: config.balanceThreshold,
isRequestCompleted: false,
exists: true
});

IPolygonTokenClient(ORACLE_CLIENT).sendRequest(
requestId,
msg.sender,
config.tokenAddress,
params.tokenOwnerAddress,
address(this),
this.handleCallback.selector
);
}

/// @notice Returns the address of the callback caller.
/// @return The address of the callback caller.
function _callbackCaller(bytes32) internal view override returns (address) {
return CALLBACK_CALLER;
}
}
14 changes: 14 additions & 0 deletions contracts/interfaces/modules/IModuleRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ interface IModuleRegistry {
bytes params
);

/// @notice Emits when a new hook is added for a specific IP Org.
event HookAdded(
address indexed ipOrg,
string hookKey,
address indexed hook
);

/// @notice Emits when a hook is removed for an IP Org.
event HookRemoved(
address indexed ipOrg,
string hookKey,
address indexed hook
);

/// @notice Fetches the latest protocol module bound to a specific key.
function protocolModule(string calldata moduleKey) external view returns (address);
}
4 changes: 2 additions & 2 deletions contracts/interfaces/modules/base/IModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import { IIPOrg } from "contracts/interfaces/ip-org/IIPOrg.sol";
/// @title IModule
/// @notice Interface for a Story Protocol Module, building block of the protocol functionality.
interface IModule {

/// The execution of the module is pending, and will need to be executed again.
event RequestPending(address indexed sender);
/// Module execution completed successfully.
event RequestCompleted(address indexed sender);
/// Module execution failed.
event RequestFailed(address indexed sender, string reason);

/// @notice Main execution entrypoint.
/// @dev It will verify params, execute pre action hooks, perform the action,
Expand Down Expand Up @@ -43,5 +44,4 @@ interface IModule {
address caller_,
bytes calldata params_
) external returns (bytes memory result);

}
3 changes: 2 additions & 1 deletion contracts/ip-org/IPOrg.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.13;

import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
Expand Down
6 changes: 4 additions & 2 deletions contracts/ip-org/IPOrgController.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { Clones } from '@openzeppelin/contracts/proxy/Clones.sol';
Expand Down Expand Up @@ -137,11 +138,12 @@ contract IPOrgController is
}

// Reset the pending owner.
address prevOwner = record.owner;
delete record.pendingOwner;
record.owner = msg.sender;

emit IPOrgPendingOwnerSet(ipOrg_, address(0));
emit IPOrgTransferred(ipOrg_, record.owner, msg.sender);
emit IPOrgTransferred(ipOrg_, prevOwner, msg.sender);
}

/// @notice Registers a new IP Org.
Expand Down
3 changes: 2 additions & 1 deletion contracts/lib/AccessControl.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

/// @title Access Control Library
Expand Down
13 changes: 11 additions & 2 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { IPAsset } from "contracts/lib/IPAsset.sol";
Expand Down Expand Up @@ -37,7 +38,6 @@ library Errors {

error BaseModule_HooksParamsLengthMismatch(uint8 hookType);
error BaseModule_ZeroIpaRegistry();
error BaseModule_ZeroModuleRegistry();
error BaseModule_ZeroLicenseRegistry();
error BaseModule_OnlyModuleRegistry();

Expand All @@ -57,6 +57,9 @@ library Errors {
error HookRegistry_HooksConfigLengthMismatch();
/// @notice This error is thrown when the provided index is out of bounds of the hooks array.
error HookRegistry_IndexOutOfBounds(uint256 hooksIndex);
error HookRegistry_ZeroModuleRegistry();
error HookRegistry_RegisteringNonWhitelistedHook(address hookAddress);


////////////////////////////////////////////////////////////////////////////
// BaseRelationshipProcessor //
Expand All @@ -71,6 +74,7 @@ library Errors {

error ModuleRegistry_ModuleNotRegistered(string moduleName);
error ModuleRegistry_CallerNotOrgOwner();
error ModuleRegistry_HookNotRegistered(string hookKey);

////////////////////////////////////////////////////////////////////////////
// CollectModule //
Expand Down Expand Up @@ -410,6 +414,11 @@ library Errors {
/// @notice The address is not the owner of the token.
error TokenGatedHook_NotTokenOwner(address tokenAddress, address ownerAddress);

error Hook_AsyncHookError(bytes32 requestId, string reason);

/// @notice Invalid Hook configuration.
error Hook_InvalidHookConfig(string reason);

////////////////////////////////////////////////////////////////////////////
// LicensorApprovalHook //
////////////////////////////////////////////////////////////////////////////
Expand Down
3 changes: 2 additions & 1 deletion contracts/lib/IPAsset.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: UNLICENSED
// See Story Protocol Alpha Agreement: https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { IPAsset } from "contracts/lib/IPAsset.sol";
Expand Down
Loading

0 comments on commit 64d7cdb

Please sign in to comment.