Skip to content

Commit

Permalink
Merge branch 'develop-contracts' of github.com:hypercerts-org/hyperce…
Browse files Browse the repository at this point in the history
…rts into develop-contracts
  • Loading branch information
bitbeckers committed Oct 27, 2023
2 parents b893aef + 70a4bbd commit 73891be
Show file tree
Hide file tree
Showing 128 changed files with 15,972 additions and 3,864 deletions.
8 changes: 3 additions & 5 deletions .github/workflows/ci-default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,19 @@ env:
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
NEXT_PUBLIC_SUPABASE_TABLE: ${{ vars.NEXT_PUBLIC_SUPABASE_TABLE }}
NEXT_PUBLIC_WALLETCONNECT_ID: ${{ secrets.NEXT_PUBLIC_WALLETCONNECT_ID }}
GOERLI_RPC_URL: ${{ vars.GOERLI_RPC_URL }}
MAINNET_RPC_URL: ${{ vars.MAINNET_RPC_URL }}
DOCKER_PLATFORM: "amd64"

# Trigger the workflow when:
on:
# A push occurs to one of the matched branches.
push:
branches:
- main
- develop
# Or when a pull request event occurs for a pull request against one of the
# matched branches.
pull_request:
branches:
- main
- develop
- develop-contracts
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

Expand Down
6 changes: 5 additions & 1 deletion contracts/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
MNEMONIC="test test test test test test test test test test test junk"
MNEMONIC_CELO=="test test test test test test test test test test test junk"
INFURA_API_KEY="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
ALCHEMY_OPTIMISM_URL="https://opt-mainnet.g.alchemy.com/v2/zzzzzzzzzzzzzzzzzzz"

# RPCs
MAINNET_RPC_URL=""
OPTIMISM_RPC_URL=""
GOERLI_RPC_URL=""

# OpenZeppelin
OPENZEPPELIN_API_KEY="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
Expand Down
6 changes: 3 additions & 3 deletions contracts/.solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"plugins": ["prettier"],
"extends": "solhint:recommended",
"rules": {
"code-complexity": ["error", 8],
"compiler-version": ["error", "0.8.16"],
"code-complexity": ["error", 12],
"compiler-version": ["error", ">=0.8.16"],
"func-visibility": ["error", { "ignoreConstructors": true }],
"max-line-length": ["warn", 120],
"max-line-length": ["warn", 200],
"no-console": "off",
"not-rely-on-time": "off",
"reason-string": ["warn", { "maxLength": 64 }],
Expand Down
15 changes: 11 additions & 4 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
# Full reference https://github.com/foundry-rs/foundry/tree/master/config

[profile.default]
auto_detect_solc = false
auto_detect_solc = true
allow_paths = ["../node_modules", "lib"]
bytecode_hash = "none"
force = false
fuzz = { runs = 1025 }
gas_reports = ["*"]
libs = ["lib"]
libraries = []
libs = ["node_modules", "lib"]
optimizer = true
optimizer_runs = 5_000
out = "out"
solc = "0.8.16"
src = "src"
test = "test/foundry"

no_match_test = "testCannotExecuteOrderIfInvalidUserGlobal"

[profile.ci]
fuzz = { runs = 1024 }
verbosity = 1

[rpc_endpoints]
mainnet = "${MAINNET_RPC_URL}"
goerli = "${GOERLI_RPC_URL}"
optimism = "${OPTIMISM_RPC_URL}"
6 changes: 3 additions & 3 deletions contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dotenvConfig({ path: resolve(__dirname, dotenvConfigPath) });
const mnemonic = requireEnv(process.env.MNEMONIC, "MNEMONIC");
const mnemonic_celo = requireEnv(process.env.MNEMONIC_CELO, "MNEMONIC_CELO");
const infuraApiKey = requireEnv(process.env.INFURA_API_KEY, "INFURA_API_KEY");
const alchemyOptimismUrl = requireEnv(process.env.ALCHEMY_OPTIMISM_URL, "ALCHEMY_OPTIMISM_URL");
const optimismRpcUrl = requireEnv(process.env.OPTIMISM_RPC_URL, "OPTIMISM_RPC_URL");

const etherscanApiKey = requireEnv(process.env.ETHERSCAN_API_KEY, "ETHERSCAN_API_KEY");
const optimisticEtherscanApiKey = requireEnv(process.env.OPTIMISTIC_ETHERSCAN_API_KEY, "OPTIMISTIC_ETHERSCAN_API_KEY");
Expand Down Expand Up @@ -147,12 +147,12 @@ const config = {
"optimism-goerli": getChainConfig("optimism-goerli"),
"optimism-mainnet": {
...getChainConfig("optimism-mainnet"),
url: alchemyOptimismUrl,
url: optimismRpcUrl,
},
},
paths: {
cache: "./cache_hardhat", // Use a different cache for Hardhat than Foundry
sources: "./src",
sources: "./src/protocol",
tests: "./test",
},
preprocess: {
Expand Down
31 changes: 13 additions & 18 deletions contracts/lib/openzeppelin-contracts-upgradeable/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
{
"extends" : [
"standard"
],
"plugins": [
"mocha"
],
"extends": ["standard"],
"plugins": ["mocha"],
"env": {
"browser" : true,
"node" : true,
"mocha" : true,
"jest" : true,
"browser": true,
"node": true,
"mocha": true,
"jest": true
},
"globals" : {
"globals": {
"artifacts": false,
"contract": false,
"assert": false,
"web3": false,
"usePlugin": false,
"extendEnvironment": false,
"extendEnvironment": false
},
"rules": {

// Strict mode
"strict": ["error", "global"],

// Code style
"array-bracket-spacing": ["off"],
"camelcase": ["error", {"properties": "always"}],
"camelcase": ["error", { "properties": "always" }],
"comma-dangle": ["error", "always-multiline"],
"comma-spacing": ["error", {"before": false, "after": true}],
"dot-notation": ["error", {"allowKeywords": true, "allowPattern": ""}],
"comma-spacing": ["error", { "before": false, "after": true }],
"dot-notation": ["error", { "allowKeywords": true, "allowPattern": "" }],
"eol-last": ["error", "always"],
"eqeqeq": ["error", "smart"],
"generator-star-spacing": ["error", "before"],
Expand All @@ -40,7 +35,7 @@
"no-dupe-args": "error",
"no-dupe-keys": "error",
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"no-redeclare": ["error", {"builtinGlobals": true}],
"no-redeclare": ["error", { "builtinGlobals": true }],
"no-trailing-spaces": ["error", { "skipBlankLines": false }],
"no-undef": "error",
"no-use-before-define": "off",
Expand All @@ -54,7 +49,7 @@
"mocha/no-exclusive-tests": ["error"],

"promise/always-return": "off",
"promise/avoid-new": "off",
"promise/avoid-new": "off"
},
"parserOptions": {
"ecmaVersion": 2020
Expand Down
8 changes: 6 additions & 2 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,20 @@
"contracts"
],
"devDependencies": {
"@chainlink/contracts": "^0.8.0",
"@commitlint/cli": "^17.1.2",
"@commitlint/config-conventional": "^17.1.0",
"@dlsl/hardhat-markup": "^1.0.0-rc.7",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@looksrare/contracts-libs": "^3.4.0",
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
"@nomicfoundation/hardhat-network-helpers": "^1.0.7",
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
"@nomiclabs/hardhat-ethers": "^2.2.1",
"@nomiclabs/hardhat-etherscan": "^3.1.3",
"@openzeppelin/contracts": "<5.0.0",
"@openzeppelin/hardhat-defender": "^1.8.2",
"@openzeppelin/hardhat-upgrades": "^1.28",
"@primitivefi/hardhat-dodoc": "^0.2.3",
Expand Down Expand Up @@ -82,6 +85,7 @@
"solhint": "^3.6.2",
"solhint-plugin-prettier": "^0.0.5",
"solidity-coverage": "^0.8.2",
"solmate": "^6.2.0",
"ts-node": "^10.9.1",
"typechain": "^8.3.1",
"typescript": "^4.9.4"
Expand All @@ -99,11 +103,11 @@
"scripts": {
"build": "hardhat compile && pnpm tsc -p tsconfig.build.json && rollup -c && pnpm copy:contracts",
"build:forge": "forge build",
"clean": "rimraf cache out dist typechain abi",
"clean": "rimraf cache out dist src/typechain src/abi",
"copy:contracts": "copyfiles -u 1 ./src/**/*.sol ./src/*.sol ./contracts",
"docs": "hardhat dodoc",
"lint": "pnpm lint:sol && pnpm prettier:check",
"lint:sol": "solhint -w 5 \"./{src,test}/**/*.sol\"",
"lint:sol": "solhint -w 60 \"./{src,test/protocol,test/marketplace}/**/*.sol\"",
"prebuild": "pnpm clean",
"prepublish": "pnpm build",
"prettier": "prettier --config \"./.prettierrc.yml\" --write \"**/*.{json,md,sol,yml}\"",
Expand Down
5 changes: 4 additions & 1 deletion contracts/remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ oz-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
oz-contracts/=lib/murky/lib/openzeppelin-contracts/
prb-test/=lib/prb-test/src/
@hypercerts/protocol/=src/protocol/
@hypercerts/marketplace/=src/marketplace/
@hypercerts/marketplace/=src/marketplace/
@looksrare/=node_modules/@looksrare/
hardhat/=node_modules/hardhat/
solmate/=node_modules/solmate/
10 changes: 5 additions & 5 deletions contracts/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import HypercertMinterAbi from "./abi/HypercertMinter.json";
// import { HypercertMinter__factory } from "./types/factories/src/HypercertMinter__factory";
import type { AllowlistMinter } from "./types/src/AllowlistMinter";
import type { HypercertMinter } from "./types/src/HypercertMinter";
import type { IAllowlist } from "./types/src/interfaces/IAllowlist";
import type { IHypercertToken } from "./types/src/interfaces/IHypercertToken";
import type { Errors } from "./types/src/libs/Errors";
import type { AllowlistMinter } from "./types/src/protocol/AllowlistMinter";
import type { HypercertMinter } from "./types/src/protocol/HypercertMinter";
import type { IAllowlist } from "./types/src/protocol/interfaces/IAllowlist";
import type { IHypercertToken } from "./types/src/protocol/interfaces/IHypercertToken";
import type { Errors } from "./types/src/protocol/libs/Errors";
/*
in order to adjust the build folder:
1) import any files here you want in the final build package.
Expand Down
54 changes: 54 additions & 0 deletions contracts/src/marketplace/BatchOrderTypehashRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

// Shared errors
import { MerkleProofTooLarge } from "./errors/SharedErrors.sol";

/**
* @title BatchOrderTypehashRegistry
* @notice The contract generates the batch order hash that is used to compute the digest for signature verification.
* @author LooksRare protocol team (👀,💎)
*/
contract BatchOrderTypehashRegistry {
/**
* @notice This function returns the hash of the concatenation of batch order type hash and merkle root.
* @param root Merkle root
* @param proofLength Merkle proof length
* @return batchOrderHash The batch order hash
*/
function hashBatchOrder(bytes32 root, uint256 proofLength) public pure returns (bytes32 batchOrderHash) {
batchOrderHash = keccak256(abi.encode(_getBatchOrderTypehash(proofLength), root));
}

/**
* @dev It looks like this for each height
* height == 1: BatchOrder(Maker[2] tree)Maker(uint8 quoteType,uint256 globalNonce,uint256 subsetNonce,uint256 orderNonce,uint256 strategyId,uint8 collectionType,address collection,address currency,address signer,uint256 startTime,uint256 endTime,uint256 price,uint256[] itemIds,uint256[] amounts,bytes additionalParameters)
* height == 2: BatchOrder(Maker[2][2] tree)Maker(uint8 quoteType,uint256 globalNonce,uint256 subsetNonce,uint256 orderNonce,uint256 strategyId,uint8 collectionType,address collection,address currency,address signer,uint256 startTime,uint256 endTime,uint256 price,uint256[] itemIds,uint256[] amounts,bytes additionalParameters)
* height == n: BatchOrder(Maker[2]...[2] tree)Maker(uint8 quoteType,uint256 globalNonce,uint256 subsetNonce,uint256 orderNonce,uint256 strategyId,uint8 collectionType,address collection,address currency,address signer,uint256 startTime,uint256 endTime,uint256 price,uint256[] itemIds,uint256[] amounts,bytes additionalParameters)
*/
function _getBatchOrderTypehash(uint256 height) internal pure returns (bytes32 typehash) {
if (height == 1) {
typehash = hex"9661287f7a4aa4867db46a2453ee15bebac4e8fc25667a58718da658f15de643";
} else if (height == 2) {
typehash = hex"a54ab330ea9e1dfccee2b86f3666989e7fbd479704416c757c8de8e820142a08";
} else if (height == 3) {
typehash = hex"93390f5d45ede9dea305f16aec86b2472af4f823851637f1b7019ad0775cea49";
} else if (height == 4) {
typehash = hex"9dda2c8358da895e43d574bb15954ce5727b22e923a2d8f28261f297bce42f0b";
} else if (height == 5) {
typehash = hex"92dc717124e161262f9d10c7079e7d54dc51271893fba54aa4a0f270fecdcc98";
} else if (height == 6) {
typehash = hex"ce02aee5a7a35d40d974463c4c6e5534954fb07a7e7bc966fee268a15337bfd8";
} else if (height == 7) {
typehash = hex"f7a65efd167a18f7091b2bb929d687dd94503cf0a43620487055ed7d6b727559";
} else if (height == 8) {
typehash = hex"def24acacad1318b664520f7c10e8bc6d1e7f6f6f7c8b031e70624ceb42266a6";
} else if (height == 9) {
typehash = hex"4cb4080dc4e7bae88b4dc4307ad5117fa4f26195998a1b5f40368809d7f4c7f2";
} else if (height == 10) {
typehash = hex"f8b1f864164d8d6e0b45f1399bd711223117a4ab0b057a9c2d7779e86a7c88db";
} else {
revert MerkleProofTooLarge(height);
}
}
}
94 changes: 94 additions & 0 deletions contracts/src/marketplace/CreatorFeeManagerWithRebates.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

// LooksRare unopinionated libraries
import { IERC2981 } from "@looksrare/contracts-libs/contracts/interfaces/generic/IERC2981.sol";

// Interfaces
import { ICreatorFeeManager } from "./interfaces/ICreatorFeeManager.sol";
import { IRoyaltyFeeRegistry } from "./interfaces/IRoyaltyFeeRegistry.sol";

// Constants
import { ONE_HUNDRED_PERCENT_IN_BP } from "./constants/NumericConstants.sol";

/**
* @title CreatorFeeManagerWithRebates
* @notice This contract returns the creator fee address and the creator rebate amount.
* @author LooksRare protocol team (👀,💎)
*/
contract CreatorFeeManagerWithRebates is ICreatorFeeManager {
/**
* @notice Standard royalty fee (in basis point).
*/
uint256 public constant STANDARD_ROYALTY_FEE_BP = 50;

/**
* @notice Royalty fee registry interface.
*/
IRoyaltyFeeRegistry public immutable royaltyFeeRegistry;

/**
* @notice Constructor
* @param _royaltyFeeRegistry Royalty fee registry address.
*/
constructor(address _royaltyFeeRegistry) {
royaltyFeeRegistry = IRoyaltyFeeRegistry(_royaltyFeeRegistry);
}

/**
* @inheritdoc ICreatorFeeManager
*/
function viewCreatorFeeInfo(
address collection,
uint256 price,
uint256[] memory itemIds
) external view returns (address creator, uint256 creatorFeeAmount) {
// Check if there is a royalty info in the system
(creator, ) = royaltyFeeRegistry.royaltyInfo(collection, price);

if (creator == address(0)) {
if (IERC2981(collection).supportsInterface(IERC2981.royaltyInfo.selector)) {
uint256 length = itemIds.length;

for (uint256 i; i < length; ) {
try IERC2981(collection).royaltyInfo(itemIds[i], price) returns (
address newCreator,
uint256 /* newCreatorFeeAmount */
) {
if (i == 0) {
creator = newCreator;

unchecked {
++i;
}
continue;
}

if (newCreator != creator) {
revert BundleEIP2981NotAllowed(collection);
}
} catch {
// If creator address is not 0, that means there was at least 1
// successful call. If all royaltyInfo calls fail, it should assume
// 0 royalty.
// If the first call reverts, even if creator is address(0), subsequent
// successful calls will still revert above with BundleEIP2981NotAllowed
// because newCreator will be different from creator.
if (creator != address(0)) {
revert BundleEIP2981NotAllowed(collection);
}
}

unchecked {
++i;
}
}
}
}

// A fixed royalty fee is applied
if (creator != address(0)) {
creatorFeeAmount = (STANDARD_ROYALTY_FEE_BP * price) / ONE_HUNDRED_PERCENT_IN_BP;
}
}
}
Loading

0 comments on commit 73891be

Please sign in to comment.