├── .eslintignore
├── .eslintrc.yaml
├── .gitignore
├── .gitmodules
├── .prettierignore
├── .prettierrc
├── .solcover.js
├── .solhint.json
├── README.md
├── contracts
├── FETH.sol
├── FoundationTreasury.sol
├── NFTCollection.sol
├── NFTCollectionFactory.sol
├── NFTDropCollection.sol
├── NFTDropMarket.sol
├── PercentSplitETH.sol
├── interfaces
│ ├── IAdminRole.sol
│ ├── ICollectionFactory.sol
│ ├── IERC20Approve.sol
│ ├── IERC20IncreaseAllowance.sol
│ ├── IFethMarket.sol
│ ├── IGetFees.sol
│ ├── IGetRoyalties.sol
│ ├── INFTCollectionInitializer.sol
│ ├── INFTDropCollectionInitializer.sol
│ ├── INFTDropCollectionMint.sol
│ ├── IOperatorRole.sol
│ ├── IOwnable.sol
│ ├── IProxyCall.sol
│ ├── IRoles.sol
│ ├── IRoyaltyInfo.sol
│ └── ITokenCreator.sol
├── libraries
│ ├── AddressLibrary.sol
│ ├── ArrayLibrary.sol
│ ├── BytesLibrary.sol
│ └── LockedBalance.sol
├── mixins
│ ├── collections
│ │ ├── CollectionRoyalties.sol
│ │ └── SequentialMintCollection.sol
│ ├── nftDropMarket
│ │ ├── NFTDropMarketCore.sol
│ │ └── NFTDropMarketFixedPriceSale.sol
│ ├── roles
│ │ ├── AdminRole.sol
│ │ └── MinterRole.sol
│ ├── shared
│ │ ├── Constants.sol
│ │ ├── ContractFactory.sol
│ │ ├── FETHNode.sol
│ │ ├── FoundationTreasuryNode.sol
│ │ ├── Gap10000.sol
│ │ ├── MarketFees.sol
│ │ ├── MarketSharedCore.sol
│ │ ├── OZERC165Checker.sol
│ │ └── SendValueWithFallbackWithdraw.sol
│ └── treasury
│ │ ├── AdminRole.sol
│ │ ├── CollateralManagement.sol
│ │ ├── OZAccessControlUpgradeable.sol
│ │ └── OperatorRole.sol
└── mocks
│ ├── BasicERC721.sol
│ ├── BasicERC721WithAccessControlMock.sol
│ ├── BasicERC721WithoutOwnerOfRevert.sol
│ ├── EmptyMockContract.sol
│ ├── FETHMarketMock.sol
│ ├── MockNFT.sol
│ ├── MockTreasury.sol
│ ├── NFTDropCollectionUnknownCreatorMock.sol
│ ├── NFTDropCollectionWithoutAccessControl.sol
│ ├── NonReceivableMock.sol
│ ├── ReceivableMock.sol
│ ├── RoyaltyRegistry
│ └── MockRoyaltyRegistry.sol
│ ├── WETH9.sol
│ └── collections
│ └── SequentialMintCollectionMock.sol
├── discord-export
├── Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html
├── Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files
│ ├── 0266e5039778e2135b8ce9b7c0243400-A3510.png
│ ├── 123c800a205b56354c1bd121a4b1b969-621EA.png
│ ├── 1f355-981A7.svg
│ ├── 1f374-505CF.svg
│ ├── 1f389-5C738.svg
│ ├── 1f3c1-445DC.svg
│ ├── 1f3c6-621A1.svg
│ ├── 1f43a-EB486.svg
│ ├── 1f440-6C64D.svg
│ ├── 1f44b-8A059.svg
│ ├── 1f44c-59547.svg
│ ├── 1f44d-27259.svg
│ ├── 1f44f-3D381.svg
│ ├── 1f49c-71A75.svg
│ ├── 1f4af-4CFF5.svg
│ ├── 1f4b0-53FFF.svg
│ ├── 1f4b8-E3468.svg
│ ├── 1f4c6-44E30.svg
│ ├── 1f4db-4C577.svg
│ ├── 1f4dc-AC641.svg
│ ├── 1f506-55112.svg
│ ├── 1f50d-195C0.svg
│ ├── 1f525-8FE4F.svg
│ ├── 1f554-A741F.svg
│ ├── 1f602-168C5.svg
│ ├── 1f604-BF863.svg
│ ├── 1f605-42B43.svg
│ ├── 1f606-BE94E.svg
│ ├── 1f609-9EC67.svg
│ ├── 1f60b-B5303.svg
│ ├── 1f629-B734A.svg
│ ├── 1f62d-02603.svg
│ ├── 1f642-83E8A.svg
│ ├── 1f64f-22B8D.svg
│ ├── 1f680-A35CE.svg
│ ├── 1f6a8-A8AB3.svg
│ ├── 1f7e5-2145C.svg
│ ├── 1f911-F346C.svg
│ ├── 1f914-15707.svg
│ ├── 1f919-1f3fb-2BA09.svg
│ ├── 1f923-5854E.svg
│ ├── 1f926-E188B.svg
│ ├── 1f929-12865.svg
│ ├── 1f972-F415D.svg
│ ├── 1f973-88B39.svg
│ ├── 1f97a-1F57B.svg
│ ├── 1f9d1-5BC80.svg
│ ├── 2696-15F4A.svg
│ ├── 2705-0589F.svg
│ ├── 2764-A3D25.svg
│ ├── 2764-fe0f-200d-1f525-0B2B8.svg
│ ├── 2a9faff195fe333526cfe6ae6fce1420-927E9.png
│ ├── 32-20e3-EF1FD.svg
│ ├── 39a730e4124c3b42df900de7d9d32973-2B76A.png
│ ├── 3b01c38b7c5b905fd8e8a1d72f7d7492-6DE9E.png
│ ├── 4c405c8d602d7b3489d628bf20bfb843-60774.png
│ ├── 4d708938ebe45c5d7a5f46ecbd3b144e-C87A6.png
│ ├── 5b069e074423293b86222b4350052da5-77740.png
│ ├── 5f978694dec96afc1640b5426003aef1-3E74E.png
│ ├── 62208180fa28d8609e6f66d9ad7fca37-00B6C.png
│ ├── 67594ee4b4d1fc03bca468327a0d145b-0C614.png
│ ├── 7295c56e5b235d31dcba650a186322b0-1BF1F.png
│ ├── 754850443909267567-E2D08.gif
│ ├── 773490663245348864-8086D.png
│ ├── 809922172625748019-3756A.png
│ ├── 851893157188599838-C23B5.png
│ ├── 851893827027075142-F23DF.png
│ ├── 851893827089727568-5FD38.png
│ ├── 851893827315826708-F59C0.png
│ ├── 910676187288846397-518CD.png
│ ├── 918262047433691247-911FE.png
│ ├── 927703596944998430-0A6FB.png
│ ├── 9503f567dabd2781b3d25827ceb83075-313E1.png
│ ├── 970d2e2f00cd7ef2134a1a3f21326349-B9EEF.png
│ ├── 977133670429261884-CA8EA.png
│ ├── 9bf2181404e658cab4039c07df56213f-EC9DE.png
│ ├── 9dae367c2914db90ec5f86da55c97b23-D9765.png
│ ├── C4-banner-7C19B.png
│ ├── PolygonScan-logo-circle-6FDB5.jpg
│ ├── a36cb7b26a8fd70f4f296a94c3cf5a9d-2B57B.png
│ ├── aa8a8cf504d883f45a141e445c26ec7b-0AED8.png
│ ├── aba7f6b23cd80ec9e8655016ce6ef443-929E9.png
│ ├── ae70f07a06c1c7e983291bb14a1bed7f-98458.png
│ ├── b07292365785e0b7bd0dab7dd7d9e09f-62888.png
│ ├── bugs-bunny-looney-tunes-BD3E0.mp4
│ ├── c47e074275f9b2c05d095d0847bc18dd-CF0A6.png
│ ├── c9cb30134c634c9e02d0c64df4922803-1AAF2.png
│ ├── cb23e87e4eb33d228ed3294f90188951-D6A20.png
│ ├── d38725fdb3f22651bef3993cf902a61d-F5783.png
│ ├── d6f1f56d219902e83e7f37cc225ffd0f-D4278.png
│ ├── d7e057b14c0a958df354613bb7522913-1FD52.png
│ ├── d7e24e0994d277993ed07845cac9ca8b-E4084.png
│ ├── dfacc77d68962a991f5a4c3b7859b4c2-0A44D.png
│ ├── e8e42b0753ed4170607ecff76b81d17e-80898.png
│ ├── ggsans-italic-400-E988B.woff2
│ ├── ggsans-italic-500-0777F.woff2
│ ├── ggsans-italic-600-CB411.woff2
│ ├── ggsans-italic-700-891AC.woff2
│ ├── ggsans-italic-800-D36B0.woff2
│ ├── ggsans-normal-400-1456D.woff2
│ ├── ggsans-normal-500-89CE5.woff2
│ ├── ggsans-normal-600-C1EA8.woff2
│ ├── ggsans-normal-700-1949A.woff2
│ ├── ggsans-normal-800-58487.woff2
│ ├── highlight.min-D8D27.js
│ ├── lottie.min-99657.js
│ ├── michael-scott-wink-82DFE.mp4
│ ├── oh-hey-ryan-reynolds-13ABB.mp4
│ ├── solarized-dark.min-BA98F.css
│ ├── spongebob-sad-96BA8.mp4
│ ├── unknown-086F2.png
│ ├── unknown-CB0DC.png
│ └── will-ferrell-stressed-out-73DD6.mp4
├── Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt
└── Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files
│ ├── C4-banner-7C19B.png
│ ├── PolygonScan-logo-circle-6FDB5.jpg
│ ├── bugs-bunny-looney-tunes-6263B.png
│ ├── maxresdefault-7ADB4.jpg
│ ├── michael-scott-wink-76E4C.png
│ ├── oh-hey-ryan-reynolds-80E2E.png
│ ├── spongebob-sad-A44ED.png
│ ├── unknown-086F2.png
│ ├── unknown-CB0DC.png
│ └── will-ferrell-stressed-out-65DCF.png
├── foundry.toml
├── gas-stories.txt
├── hardhat.config.ts
├── hardhat.gas-stories.config.ts
├── helpers
├── index.ts
└── utils.ts
├── package.json
├── slither.config.json
├── slither
├── NFTCollection.md
├── NFTCollectionFactory.md
├── NFTDropCollection.md
└── NFTDropMarket.md
├── src
├── helpers
│ ├── bytes.ts
│ ├── process.ts
│ └── providerHelpers.ts
├── testBehaviors
│ ├── index.ts
│ └── supportsInterface.behavior.ts
└── types
│ └── openzeppelin__test-helpers.d.ts
├── test-gas-stories
├── collections.ts
├── gas-stories.ts
└── nftDropCollectionFixedPriceSale.ts
├── test
├── NFTDropMarket
│ ├── fixedPrice
│ │ ├── create.ts
│ │ ├── drop.ts
│ │ ├── funds.ts
│ │ ├── getSaleDetails.ts
│ │ ├── getSellerOf.ts
│ │ ├── mintFromFixedPriceSale.ts
│ │ └── mintWithFETH.ts
│ └── initializer.ts
├── collections
│ ├── NFTCollection
│ │ ├── CollectionContract.ts
│ │ ├── mint.ts
│ │ ├── paymentFactory
│ │ │ └── DrainTokens.ts
│ │ ├── royaltyInfo.ts
│ │ ├── selfDestruct.ts
│ │ └── totalSupply.ts
│ ├── NFTCollectionFactory
│ │ ├── CollectionDropFactory.ts
│ │ ├── collectionFactory.ts
│ │ ├── createNFTDropEvents.ts
│ │ ├── initializer.ts
│ │ └── predictNFTDropCollectionAddress.ts
│ ├── NFTDropCollection
│ │ ├── admin.ts
│ │ ├── reveal.ts
│ │ ├── royaltyInterfaces.ts
│ │ ├── supportsInterfaces.ts
│ │ └── update.ts
│ └── mixins
│ │ └── SequentialMintCollectionInitialize.ts
├── feth
│ ├── deposit.ts
│ ├── marketInteractions.ts
│ └── withdraw.ts
├── fixtures
│ └── nftCollectionFactory.ts
├── foundry
│ ├── BytesLibrary.t.sol
│ └── FixedPriceDrop.sol
└── helpers
│ ├── collectionContract.ts
│ ├── constants.ts
│ ├── deploy.ts
│ ├── feth.ts
│ ├── logs.ts
│ ├── mint.ts
│ ├── snapshot.ts
│ ├── splits.ts
│ ├── testData.ts
│ └── time.ts
├── tsconfig.json
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | # folders
2 | artifacts/
3 | build/
4 | cache/
5 | coverage/
6 | dist/
7 | docs/
8 | lib/
9 | node_modules/
10 | src/typechain/
11 | subgraph/config/
12 | subgraph/build/
13 | subgraph/generated/
14 | subgraph/graph-node/
15 | versions/
16 | deployments/
17 |
18 | # files
19 | .solcover.js
20 | coverage.json
21 |
--------------------------------------------------------------------------------
/.eslintrc.yaml:
--------------------------------------------------------------------------------
1 | extends:
2 | - eslint:recommended
3 | - plugin:@typescript-eslint/eslint-recommended
4 | - plugin:@typescript-eslint/recommended
5 | - prettier
6 | parser: "@typescript-eslint/parser"
7 | parserOptions:
8 | project: tsconfig.json
9 | plugins:
10 | - "@typescript-eslint"
11 | - "no-only-tests"
12 | root: true
13 | rules:
14 | "@typescript-eslint/no-floating-promises":
15 | - error
16 | - ignoreIIFE: true
17 | ignoreVoid: true
18 | "@typescript-eslint/no-inferrable-types": "off"
19 | "@typescript-eslint/no-unused-vars":
20 | - error
21 | - argsIgnorePattern: _
22 | varsIgnorePattern: _
23 | "sort-imports":
24 | - error
25 | - ignoreCase: true
26 | ignoreDeclarationSort: true
27 | "no-only-tests/no-only-tests": "error"
28 | overrides:
29 | - files: subgraph/**/*.ts
30 | rules:
31 | prefer-const: 0
32 | "@typescript-eslint/ban-types": 0
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | typechain
4 | coverage
5 | coverage.json
6 | out
7 |
8 | #Hardhat files
9 | cache
10 | artifacts
11 |
12 | yarn-error.log
13 |
14 | .openzeppelin/
15 |
16 | .DS_Store
17 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/forge-std"]
2 | path = lib/forge-std
3 | url = https://github.com/foundry-rs/forge-std
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # folders
2 | artifacts/
3 | build/
4 | cache/
5 | coverage/
6 | dist/
7 | docs/
8 | lib/
9 | node_modules/
10 | typechain-types/
11 | src/typechain/
12 | .vscode/
13 | deployments/
14 |
15 | # files
16 | coverage.json
17 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "avoid",
3 | "bracketSpacing": true,
4 | "endOfLine": "auto",
5 | "printWidth": 120,
6 | "singleQuote": false,
7 | "tabWidth": 2,
8 | "trailingComma": "all",
9 | "overrides": [
10 | {
11 | "files": "*.sol",
12 | "options": {
13 | "tabWidth": 2
14 | }
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/.solcover.js:
--------------------------------------------------------------------------------
1 | const shell = require("shelljs");
2 |
3 | module.exports = {
4 | istanbulReporter: ["html"],
5 | onCompileComplete: async function (_config) {
6 | await run("typechain");
7 | },
8 | onIstanbulComplete: async function (_config) {
9 | // We need to do this because solcover generates bespoke artifacts.
10 | shell.rm("-rf", "./artifacts");
11 | shell.rm("-rf", "./src/typechain");
12 | },
13 | skipFiles: [
14 | "mocks",
15 | "test",
16 | "FNDMiddleware",
17 | "archive",
18 | "FETH",
19 | "PercentSplitETH",
20 | "FoundationTreasury",
21 | "libraries/BytesLibrary",
22 | "libraries/LockedBalance",
23 | "mixins/treasury",
24 | ],
25 | istanbulReporter: ["lcov", "html", "text-summary"],
26 | mocha: {
27 | fgrep: "[skip-on-coverage]",
28 | invert: true,
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/.solhint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "solhint:recommended",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "code-complexity": ["error", 7],
6 | "compiler-version": ["error", "^0.8.12"],
7 | "const-name-snakecase": "off",
8 | "constructor-syntax": "error",
9 | "func-visibility": [
10 | "error",
11 | {
12 | "ignoreConstructors": true
13 | }
14 | ],
15 | "max-line-length": ["error", 120],
16 | "not-rely-on-time": "off",
17 | "prettier/prettier": [
18 | "error",
19 | {
20 | "endOfLine": "auto"
21 | }
22 | ],
23 | "reason-string": [
24 | "warn",
25 | {
26 | "maxLength": 64
27 | }
28 | ],
29 | "var-name-mixedcase": "off",
30 | "no-inline-assembly": "off"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/contracts/FoundationTreasury.sol:
--------------------------------------------------------------------------------
1 | /*
2 | ・
3 | * ★
4 | ・ 。
5 | ・ ゚☆ 。
6 | * ★ ゚・。 * 。
7 | * ☆ 。・゚*.。
8 | ゚ *.。☆。★ ・
9 | * ゚。·*・。 ゚*
10 | ☆゚・。°*. ゚
11 | ・ ゚*。・゚★。
12 | ・ *゚。 *
13 | ・゚*。★・
14 | ☆∴。 *
15 | * ★ 。
16 | ` .-:::::-.` `-::---...```
17 | `-:` .:+ssssoooo++//:.` .-/+shhhhhhhhhhhhhyyyssooo:
18 | .--::. .+ossso+/////++/:://-` .////+shhhhhhhhhhhhhhhhhhhhhy
19 | `-----::. `/+////+++///+++/:--:/+/- -////+shhhhhhhhhhhhhhhhhhhhhy
20 | `------:::-` `//-.``.-/+ooosso+:-.-/oso- -////+shhhhhhhhhhhhhhhhhhhhhy
21 | .--------:::-` :+:.` .-/osyyyyyyso++syhyo.-////+shhhhhhhhhhhhhhhhhhhhhy
22 | `-----------:::-. +o+:-.-:/oyhhhhhhdhhhhhdddy:-////+shhhhhhhhhhhhhhhhhhhhhy
23 | .------------::::-- `oys+/::/+shhhhhhhdddddddddy/-////+shhhhhhhhhhhhhhhhhhhhhy
24 | .--------------:::::-` +ys+////+yhhhhhhhddddddddhy:-////+yhhhhhhhhhhhhhhhhhhhhhy
25 | `----------------::::::-`.ss+/:::+oyhhhhhhhhhhhhhhho`-////+shhhhhhhhhhhhhhhhhhhhhy
26 | .------------------:::::::.-so//::/+osyyyhhhhhhhhhys` -////+shhhhhhhhhhhhhhhhhhhhhy
27 | `.-------------------::/:::::..+o+////+oosssyyyyyyys+` .////+shhhhhhhhhhhhhhhhhhhhhy
28 | .--------------------::/:::.` -+o++++++oooosssss/. `-//+shhhhhhhhhhhhhhhhhhhhyo
29 | .------- ``````.......--` `-/+ooooosso+/-` `./++++///:::--...``hhhhyo
30 | `````
31 | *
32 | ・ 。
33 | ・ ゚☆ 。
34 | * ★ ゚・。 * 。
35 | * ☆ 。・゚*.。
36 | ゚ *.。☆。★ ・
37 | * ゚。·*・。 ゚*
38 | ☆゚・。°*. ゚
39 | ・ ゚*。・゚★。
40 | ・ *゚。 *
41 | ・゚*。★・
42 | ☆∴。 *
43 | ・ 。
44 | */
45 |
46 | // SPDX-License-Identifier: MIT OR Apache-2.0
47 |
48 | pragma solidity ^0.8.12;
49 |
50 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
51 |
52 | import "./mixins/treasury/AdminRole.sol";
53 | import "./mixins/treasury/CollateralManagement.sol";
54 | import "./mixins/treasury/OperatorRole.sol";
55 |
56 | /**
57 | * @title Manage revenue and roles for Foundation.
58 | * @notice All fees generated by the market are forwarded to this contract.
59 | * It also defines the Admin and Operator roles which are used in other contracts.
60 | */
61 | contract FoundationTreasury is Initializable, AdminRole, OperatorRole, CollateralManagement {
62 | /**
63 | * @notice Called one time after deployment to initialize the contract.
64 | * @param admin The account to add as the initial admin.
65 | */
66 | function initialize(address admin) external initializer {
67 | AdminRole._initializeAdminRole(admin);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/contracts/interfaces/IAdminRole.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @notice Interface for AdminRole which wraps the default admin role from
7 | * OpenZeppelin's AccessControl for easy integration.
8 | */
9 | interface IAdminRole {
10 | function isAdmin(address account) external view returns (bool);
11 | }
12 |
--------------------------------------------------------------------------------
/contracts/interfaces/ICollectionFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "./IRoles.sol";
6 | import "./IProxyCall.sol";
7 |
8 | interface ICollectionFactory {
9 | function rolesContract() external returns (IRoles);
10 | }
11 |
--------------------------------------------------------------------------------
/contracts/interfaces/IERC20Approve.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface IERC20Approve {
6 | function approve(address spender, uint256 amount) external returns (bool);
7 | }
8 |
--------------------------------------------------------------------------------
/contracts/interfaces/IERC20IncreaseAllowance.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface IERC20IncreaseAllowance {
6 | function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
7 | }
8 |
--------------------------------------------------------------------------------
/contracts/interfaces/IFethMarket.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @notice Interface for functions the market uses in FETH.
7 | */
8 | interface IFethMarket {
9 | function depositFor(address account) external payable;
10 |
11 | function marketLockupFor(address account, uint256 amount) external payable returns (uint256 expiration);
12 |
13 | function marketWithdrawFrom(address from, uint256 amount) external;
14 |
15 | function marketWithdrawLocked(
16 | address account,
17 | uint256 expiration,
18 | uint256 amount
19 | ) external;
20 |
21 | function marketUnlockFor(
22 | address account,
23 | uint256 expiration,
24 | uint256 amount
25 | ) external;
26 |
27 | function marketChangeLockup(
28 | address unlockFrom,
29 | uint256 unlockExpiration,
30 | uint256 unlockAmount,
31 | address lockupFor,
32 | uint256 lockupAmount
33 | ) external payable returns (uint256 expiration);
34 | }
35 |
--------------------------------------------------------------------------------
/contracts/interfaces/IGetFees.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @notice An interface for communicating fees to 3rd party marketplaces.
7 | * @dev Originally implemented in mainnet contract 0x44d6e8933f8271abcf253c72f9ed7e0e4c0323b3
8 | */
9 | interface IGetFees {
10 | /**
11 | * @notice Get the recipient addresses to which creator royalties should be sent.
12 | * @dev The expected royalty amounts are communicated with `getFeeBps`.
13 | * @param tokenId The ID of the NFT to get royalties for.
14 | * @return recipients An array of addresses to which royalties should be sent.
15 | */
16 | function getFeeRecipients(uint256 tokenId) external view returns (address payable[] memory recipients);
17 |
18 | /**
19 | * @notice Get the creator royalty amounts to be sent to each recipient, in basis points.
20 | * @dev The expected recipients are communicated with `getFeeRecipients`.
21 | * @param tokenId The ID of the NFT to get royalties for.
22 | * @return royaltiesInBasisPoints The array of fees to be sent to each recipient, in basis points.
23 | */
24 | function getFeeBps(uint256 tokenId) external view returns (uint256[] memory royaltiesInBasisPoints);
25 | }
26 |
--------------------------------------------------------------------------------
/contracts/interfaces/IGetRoyalties.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface IGetRoyalties {
6 | /**
7 | * @notice Get the creator royalties to be sent.
8 | * @dev The data is the same as when calling `getFeeRecipients` and `getFeeBps` separately.
9 | * @param tokenId The ID of the NFT to get royalties for.
10 | * @return recipients An array of addresses to which royalties should be sent.
11 | * @return royaltiesInBasisPoints The array of fees to be sent to each recipient, in basis points.
12 | */
13 | function getRoyalties(uint256 tokenId)
14 | external
15 | view
16 | returns (address payable[] memory recipients, uint256[] memory royaltiesInBasisPoints);
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/interfaces/INFTCollectionInitializer.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface INFTCollectionInitializer {
6 | function initialize(
7 | address payable _creator,
8 | string memory _name,
9 | string memory _symbol
10 | ) external;
11 | }
12 |
--------------------------------------------------------------------------------
/contracts/interfaces/INFTDropCollectionInitializer.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface INFTDropCollectionInitializer {
6 | function initialize(
7 | address payable _creator,
8 | string calldata _name,
9 | string calldata _symbol,
10 | string calldata _baseURI,
11 | bytes32 _postRevealBaseURIHash,
12 | uint32 _maxTokenId,
13 | address _approvedMinter,
14 | address payable _paymentAddress
15 | ) external;
16 | }
17 |
--------------------------------------------------------------------------------
/contracts/interfaces/INFTDropCollectionMint.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @notice The required interface for collections to support the NFTDropMarket.
7 | * @dev This interface must be registered as a ERC165 supported interface to support the NFTDropMarket.
8 | */
9 | interface INFTDropCollectionMint {
10 | function mintCountTo(uint16 count, address to) external returns (uint256 firstTokenId);
11 |
12 | function numberOfTokensAvailableToMint() external view returns (uint256 count);
13 | }
14 |
--------------------------------------------------------------------------------
/contracts/interfaces/IOperatorRole.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @notice Interface for OperatorRole which wraps a role from
7 | * OpenZeppelin's AccessControl for easy integration.
8 | */
9 | interface IOperatorRole {
10 | function isOperator(address account) external view returns (bool);
11 | }
12 |
--------------------------------------------------------------------------------
/contracts/interfaces/IOwnable.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface IOwnable {
6 | /**
7 | * @dev Returns the address of the current owner.
8 | */
9 | function owner() external view returns (address);
10 | }
11 |
--------------------------------------------------------------------------------
/contracts/interfaces/IProxyCall.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface IProxyCall {
6 | function proxyCallAndReturnAddress(address externalContract, bytes memory callData)
7 | external
8 | returns (address payable result);
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/interfaces/IRoles.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @notice Interface for a contract which implements admin roles.
7 | */
8 | interface IRoles {
9 | function isAdmin(address account) external view returns (bool);
10 |
11 | function isOperator(address account) external view returns (bool);
12 | }
13 |
--------------------------------------------------------------------------------
/contracts/interfaces/IRoyaltyInfo.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @notice Interface for EIP-2981: NFT Royalty Standard.
7 | * For more see: https://eips.ethereum.org/EIPS/eip-2981.
8 | */
9 | interface IRoyaltyInfo {
10 | /**
11 | * @notice Get the creator royalties to be sent.
12 | * @param tokenId The ID of the NFT to get royalties for.
13 | * @param salePrice The total price of the sale.
14 | * @return receiver The address to which royalties should be sent.
15 | * @return royaltyAmount The total amount that should be sent to the `receiver`.
16 | */
17 | function royaltyInfo(uint256 tokenId, uint256 salePrice)
18 | external
19 | view
20 | returns (address receiver, uint256 royaltyAmount);
21 | }
22 |
--------------------------------------------------------------------------------
/contracts/interfaces/ITokenCreator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface ITokenCreator {
6 | /**
7 | * @notice Returns the creator of this NFT collection.
8 | * @param tokenId The ID of the NFT to get the creator payment address for.
9 | * @return creator The creator of this collection.
10 | */
11 | function tokenCreator(uint256 tokenId) external view returns (address payable creator);
12 | }
13 |
--------------------------------------------------------------------------------
/contracts/libraries/AddressLibrary.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
6 |
7 | struct CallWithoutValue {
8 | address target;
9 | bytes callData;
10 | }
11 |
12 | /**
13 | * @title A library for address helpers not already covered by the OZ library library.
14 | */
15 | library AddressLibrary {
16 | using AddressUpgradeable for address;
17 | using AddressUpgradeable for address payable;
18 |
19 | /**
20 | * @notice Calls an external contract with arbitrary data and parse the return value into an address.
21 | * @param externalContract The address of the contract to call.
22 | * @param callData The data to send to the contract.
23 | * @return contractAddress The address of the contract returned by the call.
24 | */
25 | function callAndReturnContractAddress(address externalContract, bytes memory callData)
26 | internal
27 | returns (address payable contractAddress)
28 | {
29 | bytes memory returnData = externalContract.functionCall(callData);
30 | contractAddress = abi.decode(returnData, (address));
31 | require(contractAddress.isContract(), "InternalProxyCall: did not return a contract");
32 | }
33 |
34 | function callAndReturnContractAddress(CallWithoutValue memory call)
35 | internal
36 | returns (address payable contractAddress)
37 | {
38 | return callAndReturnContractAddress(call.target, call.callData);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/contracts/libraries/ArrayLibrary.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @title Helper functions for arrays.
7 | */
8 | library ArrayLibrary {
9 | /**
10 | * @notice Reduces the size of an array if it's greater than the specified max size,
11 | * using the first maxSize elements.
12 | */
13 | function capLength(address payable[] memory data, uint256 maxLength) internal pure {
14 | if (data.length > maxLength) {
15 | assembly {
16 | mstore(data, maxLength)
17 | }
18 | }
19 | }
20 |
21 | /**
22 | * @notice Reduces the size of an array if it's greater than the specified max size,
23 | * using the first maxSize elements.
24 | */
25 | function capLength(uint256[] memory data, uint256 maxLength) internal pure {
26 | if (data.length > maxLength) {
27 | assembly {
28 | mstore(data, maxLength)
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/contracts/libraries/BytesLibrary.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | error BytesLibrary_Expected_Address_Not_Found();
6 |
7 | /**
8 | * @title A library for manipulation of byte arrays.
9 | */
10 | library BytesLibrary {
11 | /**
12 | * @dev Replace the address at the given location in a byte array if the contents at that location
13 | * match the expected address.
14 | */
15 | function replaceAtIf(
16 | bytes memory data,
17 | uint256 startLocation,
18 | address expectedAddress,
19 | address newAddress
20 | ) internal pure {
21 | bytes memory expectedData = abi.encodePacked(expectedAddress);
22 | bytes memory newData = abi.encodePacked(newAddress);
23 | unchecked {
24 | // An address is 20 bytes long
25 | for (uint256 i = 0; i < 20; ++i) {
26 | uint256 dataLocation = startLocation + i;
27 | if (data[dataLocation] != expectedData[i]) {
28 | revert BytesLibrary_Expected_Address_Not_Found();
29 | }
30 | data[dataLocation] = newData[i];
31 | }
32 | }
33 | }
34 |
35 | /**
36 | * @dev Checks if the call data starts with the given function signature.
37 | */
38 | function startsWith(bytes memory callData, bytes4 functionSig) internal pure returns (bool) {
39 | // A signature is 4 bytes long
40 | if (callData.length < 4) {
41 | return false;
42 | }
43 | unchecked {
44 | for (uint256 i = 0; i < 4; ++i) {
45 | if (callData[i] != functionSig[i]) {
46 | return false;
47 | }
48 | }
49 | }
50 |
51 | return true;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/contracts/mixins/nftDropMarket/NFTDropMarketCore.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
6 |
7 | /**
8 | * @title A place for common modifiers and functions used by various market mixins, if any.
9 | * @dev This also leaves a gap which can be used to add a new mixin to the top of the inheritance tree.
10 | */
11 | abstract contract NFTDropMarketCore {
12 | /**
13 | * @notice This empty reserved space is put in place to allow future versions to add new
14 | * variables without shifting down storage in the inheritance chain.
15 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
16 | */
17 | uint256[1000] private __gap;
18 | }
19 |
--------------------------------------------------------------------------------
/contracts/mixins/roles/AdminRole.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
7 |
8 | /**
9 | * @title Defines a role for admin accounts.
10 | * @dev Wraps the default admin role from OpenZeppelin's AccessControl for easy integration.
11 | */
12 | abstract contract AdminRole is Initializable, AccessControlUpgradeable {
13 | function _initializeAdminRole(address admin) internal onlyInitializing {
14 | // Grant the role to a specified account
15 | _grantRole(DEFAULT_ADMIN_ROLE, admin);
16 | }
17 |
18 | modifier onlyAdmin() {
19 | require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "AdminRole: caller does not have the Admin role");
20 | _;
21 | }
22 |
23 | /**
24 | * @notice Adds an account as an approved admin.
25 | * @dev Only callable by existing admins, as enforced by `grantRole`.
26 | * @param account The address to be approved.
27 | */
28 | function grantAdmin(address account) external {
29 | grantRole(DEFAULT_ADMIN_ROLE, account);
30 | }
31 |
32 | /**
33 | * @notice Removes an account from the set of approved admins.
34 | * @dev Only callable by existing admins, as enforced by `revokeRole`.
35 | * @param account The address to be removed.
36 | */
37 | function revokeAdmin(address account) external {
38 | revokeRole(DEFAULT_ADMIN_ROLE, account);
39 | }
40 |
41 | /**
42 | * @notice Checks if the account provided is an admin.
43 | * @param account The address to check.
44 | * @return approved True if the account is an admin.
45 | * @dev This call is used by the royalty registry contract.
46 | */
47 | function isAdmin(address account) public view returns (bool approved) {
48 | approved = hasRole(DEFAULT_ADMIN_ROLE, account);
49 | }
50 |
51 | /**
52 | * @notice This empty reserved space is put in place to allow future versions to add new
53 | * variables without shifting down storage in the inheritance chain.
54 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
55 | */
56 | uint256[1000] private __gap;
57 | }
58 |
--------------------------------------------------------------------------------
/contracts/mixins/roles/MinterRole.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
7 |
8 | import "./AdminRole.sol";
9 |
10 | /**
11 | * @title Defines a role for minter accounts.
12 | * @dev Wraps a role from OpenZeppelin's AccessControl for easy integration.
13 | */
14 | abstract contract MinterRole is Initializable, AccessControlUpgradeable, AdminRole {
15 | /**
16 | * @notice The `role` type used for approve minters.
17 | * @return `keccak256("MINTER_ROLE")`
18 | */
19 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
20 |
21 | modifier onlyMinterOrAdmin() {
22 | require(isMinter(msg.sender) || isAdmin(msg.sender), "MinterRole: Must have the minter or admin role");
23 | _;
24 | }
25 |
26 | function _initializeMinterRole(address minter) internal onlyInitializing {
27 | // Grant the role to a specified account
28 | _grantRole(MINTER_ROLE, minter);
29 | }
30 |
31 | /**
32 | * @notice Adds an account as an approved minter.
33 | * @dev Only callable by admins, as enforced by `grantRole`.
34 | * @param account The address to be approved.
35 | */
36 | function grantMinter(address account) external {
37 | grantRole(MINTER_ROLE, account);
38 | }
39 |
40 | /**
41 | * @notice Removes an account from the set of approved minters.
42 | * @dev Only callable by admins, as enforced by `revokeRole`.
43 | * @param account The address to be removed.
44 | */
45 | function revokeMinter(address account) external {
46 | revokeRole(MINTER_ROLE, account);
47 | }
48 |
49 | /**
50 | * @notice Checks if the account provided is an minter.
51 | * @param account The address to check.
52 | * @return approved True if the account is an minter.
53 | */
54 | function isMinter(address account) public view returns (bool approved) {
55 | approved = hasRole(MINTER_ROLE, account);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/contracts/mixins/shared/Constants.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /// Constant values shared across mixins.
6 |
7 | /**
8 | * @dev 100% in basis points.
9 | */
10 | uint256 constant BASIS_POINTS = 10000;
11 |
12 | /**
13 | * @dev The default admin role defined by OZ ACL modules.
14 | */
15 | bytes32 constant DEFAULT_ADMIN_ROLE = 0x00;
16 |
17 | /**
18 | * @dev Cap the number of royalty recipients.
19 | * A cap is required to ensure gas costs are not too high when a sale is settled.
20 | */
21 | uint256 constant MAX_ROYALTY_RECIPIENTS = 5;
22 |
23 | /**
24 | * @dev The minimum increase of 10% required when making an offer or placing a bid.
25 | */
26 | uint256 constant MIN_PERCENT_INCREMENT_DENOMINATOR = BASIS_POINTS / 1000;
27 |
28 | /**
29 | * @dev The gas limit used when making external read-only calls.
30 | * This helps to ensure that external calls does not prevent the market from executing.
31 | */
32 | uint256 constant READ_ONLY_GAS_LIMIT = 40000;
33 |
34 | /**
35 | * @dev Default royalty cut paid out on secondary sales.
36 | * Set to 10% of the secondary sale.
37 | */
38 | uint96 constant ROYALTY_IN_BASIS_POINTS = 1000;
39 |
40 | /**
41 | * @dev 10%, expressed as a denominator for more efficient calculations.
42 | */
43 | uint256 constant ROYALTY_RATIO = BASIS_POINTS / ROYALTY_IN_BASIS_POINTS;
44 |
45 | /**
46 | * @dev The gas limit to send ETH to multiple recipients, enough for a 5-way split.
47 | */
48 | uint256 constant SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS = 210000;
49 |
50 | /**
51 | * @dev The gas limit to send ETH to a single recipient, enough for a contract with a simple receiver.
52 | */
53 | uint256 constant SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT = 20000;
54 |
--------------------------------------------------------------------------------
/contracts/mixins/shared/ContractFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
6 |
7 | import "../../interfaces/ICollectionFactory.sol";
8 |
9 | /**
10 | * @title Stores a reference to the factory which is used to create contract proxies.
11 | */
12 | abstract contract ContractFactory {
13 | using AddressUpgradeable for address;
14 |
15 | /**
16 | * @notice The address of the factory which was used to create this contract.
17 | * @return The factory contract address.
18 | */
19 | address public immutable contractFactory;
20 |
21 | modifier onlyContractFactory() {
22 | require(msg.sender == contractFactory, "ContractFactory: Caller is not the factory");
23 | _;
24 | }
25 |
26 | /**
27 | * @notice Initialize the template's immutable variables.
28 | * @param _contractFactory The factory which will be used to create these contracts.
29 | */
30 | constructor(address _contractFactory) {
31 | require(_contractFactory.isContract(), "ContractFactory: Factory is not a contract");
32 | contractFactory = _contractFactory;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/contracts/mixins/shared/FETHNode.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
6 |
7 | import "../../interfaces/IFethMarket.sol";
8 |
9 | error FETHNode_FETH_Address_Is_Not_A_Contract();
10 | error FETHNode_Only_FETH_Can_Transfer_ETH();
11 |
12 | /**
13 | * @title A mixin for interacting with the FETH contract.
14 | */
15 | abstract contract FETHNode {
16 | using AddressUpgradeable for address;
17 | using AddressUpgradeable for address payable;
18 |
19 | /// @notice The FETH ERC-20 token for managing escrow and lockup.
20 | IFethMarket internal immutable feth;
21 |
22 | constructor(address _feth) {
23 | if (!_feth.isContract()) {
24 | revert FETHNode_FETH_Address_Is_Not_A_Contract();
25 | }
26 |
27 | feth = IFethMarket(_feth);
28 | }
29 |
30 | /**
31 | * @notice Only used by FETH. Any direct transfer from users will revert.
32 | */
33 | receive() external payable {
34 | if (msg.sender != address(feth)) {
35 | revert FETHNode_Only_FETH_Can_Transfer_ETH();
36 | }
37 | }
38 |
39 | /**
40 | * @notice Withdraw the msg.sender's available FETH balance if they requested more than the msg.value provided.
41 | * @dev This may revert if the msg.sender is non-receivable.
42 | * This helper should not be used anywhere that may lead to locked assets.
43 | * @param totalAmount The total amount of ETH required (including the msg.value).
44 | * @param shouldRefundSurplus If true, refund msg.value - totalAmount to the msg.sender.
45 | */
46 | function _tryUseFETHBalance(uint256 totalAmount, bool shouldRefundSurplus) internal {
47 | if (totalAmount > msg.value) {
48 | // Withdraw additional ETH required from the user's available FETH balance.
49 | unchecked {
50 | // The if above ensures delta will not underflow.
51 | uint256 delta = totalAmount - msg.value;
52 | // Withdraw ETH from the user's account in the FETH token contract,
53 | // making the funds available in this contract as ETH.
54 | feth.marketWithdrawFrom(msg.sender, delta);
55 | }
56 | } else if (shouldRefundSurplus && totalAmount < msg.value) {
57 | // Return any surplus ETH to the user.
58 | unchecked {
59 | // The if above ensures this will not underflow
60 | payable(msg.sender).sendValue(msg.value - totalAmount);
61 | }
62 | }
63 | }
64 |
65 | /**
66 | * @notice Gets the FETH contract used to escrow offer funds.
67 | * @return fethAddress The FETH contract address.
68 | */
69 | function getFethAddress() external view returns (address fethAddress) {
70 | fethAddress = address(feth);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/contracts/mixins/shared/FoundationTreasuryNode.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
6 |
7 | import "../../interfaces/IAdminRole.sol";
8 | import "../../interfaces/IOperatorRole.sol";
9 |
10 | error FoundationTreasuryNode_Address_Is_Not_A_Contract();
11 | error FoundationTreasuryNode_Caller_Not_Admin();
12 | error FoundationTreasuryNode_Caller_Not_Operator();
13 |
14 | /**
15 | * @title A mixin that stores a reference to the Foundation treasury contract.
16 | * @notice The treasury collects fees and defines admin/operator roles.
17 | */
18 | abstract contract FoundationTreasuryNode {
19 | using AddressUpgradeable for address payable;
20 |
21 | /// @dev This value was replaced with an immutable version.
22 | address payable private __gap_was_treasury;
23 |
24 | /// @notice The address of the treasury contract.
25 | address payable private immutable treasury;
26 |
27 | /// @notice Requires the caller is a Foundation admin.
28 | modifier onlyFoundationAdmin() {
29 | if (!IAdminRole(treasury).isAdmin(msg.sender)) {
30 | revert FoundationTreasuryNode_Caller_Not_Admin();
31 | }
32 | _;
33 | }
34 |
35 | /// @notice Requires the caller is a Foundation operator.
36 | modifier onlyFoundationOperator() {
37 | if (!IOperatorRole(treasury).isOperator(msg.sender)) {
38 | revert FoundationTreasuryNode_Caller_Not_Operator();
39 | }
40 | _;
41 | }
42 |
43 | /**
44 | * @notice Set immutable variables for the implementation contract.
45 | * @dev Assigns the treasury contract address.
46 | */
47 | constructor(address payable _treasury) {
48 | if (!_treasury.isContract()) {
49 | revert FoundationTreasuryNode_Address_Is_Not_A_Contract();
50 | }
51 | treasury = _treasury;
52 | }
53 |
54 | /**
55 | * @notice Gets the Foundation treasury contract.
56 | * @dev This call is used in the royalty registry contract.
57 | * @return treasuryAddress The address of the Foundation treasury contract.
58 | */
59 | function getFoundationTreasury() public view returns (address payable treasuryAddress) {
60 | return treasury;
61 | }
62 |
63 | /**
64 | * @notice This empty reserved space is put in place to allow future versions to add new
65 | * variables without shifting down storage in the inheritance chain.
66 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
67 | */
68 | uint256[2000] private __gap;
69 | }
70 |
--------------------------------------------------------------------------------
/contracts/mixins/shared/Gap10000.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * @title A placeholder contract leaving room for new mixins to be added to the future.
7 | */
8 | abstract contract Gap10000 {
9 | /**
10 | * @notice This empty reserved space is put in place to allow future versions to add new
11 | * variables without shifting down storage in the inheritance chain.
12 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
13 | */
14 | uint256[10000] private __gap;
15 | }
16 |
--------------------------------------------------------------------------------
/contracts/mixins/shared/MarketSharedCore.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
6 |
7 | import "./FETHNode.sol";
8 |
9 | /**
10 | * @title A place for common modifiers and functions used by various market mixins, if any.
11 | * @dev This also leaves a gap which can be used to add a new mixin to the top of the inheritance tree.
12 | */
13 | abstract contract MarketSharedCore is FETHNode {
14 | /**
15 | * @notice Checks who the seller for an NFT is if listed in this market.
16 | * @param nftContract The address of the NFT contract.
17 | * @param tokenId The id of the NFT.
18 | * @return seller The seller which listed this NFT for sale, or address(0) if not listed.
19 | */
20 | function getSellerOf(address nftContract, uint256 tokenId) external view returns (address payable seller) {
21 | return _getSellerOf(nftContract, tokenId);
22 | }
23 |
24 | /**
25 | * @notice Checks who the seller for an NFT is if listed in this market.
26 | */
27 | function _getSellerOf(address nftContract, uint256 tokenId) internal view virtual returns (address payable seller);
28 |
29 | /**
30 | * @notice Checks who the seller for an NFT is if listed in this market.
31 | */
32 | function _getSellerOrOwnerOf(address nftContract, uint256 tokenId)
33 | internal
34 | view
35 | virtual
36 | returns (address payable sellerOrOwner);
37 |
38 | /**
39 | * @notice This empty reserved space is put in place to allow future versions to add new
40 | * variables without shifting down storage in the inheritance chain.
41 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
42 | */
43 | uint256[500] private __gap;
44 | }
45 |
--------------------------------------------------------------------------------
/contracts/mixins/shared/OZERC165Checker.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | /**
6 | * From https://github.com/OpenZeppelin/openzeppelin-contracts
7 | * Copying the method below which is currently unreleased.
8 | */
9 |
10 | import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
11 |
12 | /**
13 | * @title Library to query ERC165 support.
14 | * @dev Library used to query support of an interface declared via {IERC165}.
15 | *
16 | * Note that these functions return the actual result of the query: they do not
17 | * `revert` if an interface is not supported. It is up to the caller to decide
18 | * what to do in these cases.
19 | */
20 | library OZERC165Checker {
21 | /**
22 | * @notice Query if a contract implements an interface, does not check ERC165 support
23 | * @param account The address of the contract to query for support of an interface
24 | * @param interfaceId The interface identifier, as specified in ERC-165
25 | * @return true if the contract at account indicates support of the interface with
26 | * identifier interfaceId, false otherwise
27 | * @dev Assumes that account contains a contract that supports ERC165, otherwise
28 | * the behavior of this method is undefined. This precondition can be checked
29 | * with {supportsERC165}.
30 | * Interface identification is specified in ERC-165.
31 | */
32 | function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
33 | bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);
34 | (bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams);
35 | if (result.length < 32) return false;
36 | return success && abi.decode(result, (uint256)) > 0;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/contracts/mixins/shared/SendValueWithFallbackWithdraw.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
6 |
7 | import "./FETHNode.sol";
8 |
9 | /**
10 | * @title A mixin for sending ETH with a fallback withdraw mechanism.
11 | * @notice Attempt to send ETH and if the transfer fails or runs out of gas, store the balance
12 | * in the FETH token contract for future withdrawal instead.
13 | * @dev This mixin was recently switched to escrow funds in FETH.
14 | * Once we have confirmed all pending balances have been withdrawn, we can remove the escrow tracking here.
15 | */
16 | abstract contract SendValueWithFallbackWithdraw is FETHNode {
17 | using AddressUpgradeable for address payable;
18 |
19 | /// @dev Removing old unused variables in an upgrade safe way.
20 | uint256 private __gap_was_pendingWithdrawals;
21 |
22 | /**
23 | * @notice Emitted when escrowed funds are withdrawn to FETH.
24 | * @param user The account which has withdrawn ETH.
25 | * @param amount The amount of ETH which has been withdrawn.
26 | */
27 | event WithdrawalToFETH(address indexed user, uint256 amount);
28 |
29 | /**
30 | * @notice Attempt to send a user or contract ETH.
31 | * If it fails store the amount owned for later withdrawal in FETH.
32 | * @dev This may fail when sending ETH to a contract that is non-receivable or exceeds the gas limit specified.
33 | */
34 | function _sendValueWithFallbackWithdraw(
35 | address payable user,
36 | uint256 amount,
37 | uint256 gasLimit
38 | ) internal {
39 | if (amount == 0) {
40 | return;
41 | }
42 | // Cap the gas to prevent consuming all available gas to block a tx from completing successfully
43 | // solhint-disable-next-line avoid-low-level-calls
44 | (bool success, ) = user.call{ value: amount, gas: gasLimit }("");
45 | if (!success) {
46 | // Store the funds that failed to send for the user in the FETH token
47 | feth.depositFor{ value: amount }(user);
48 | emit WithdrawalToFETH(user, amount);
49 | }
50 | }
51 |
52 | /**
53 | * @notice This empty reserved space is put in place to allow future versions to add new
54 | * variables without shifting down storage in the inheritance chain.
55 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
56 | */
57 | uint256[999] private __gap;
58 | }
59 |
--------------------------------------------------------------------------------
/contracts/mixins/treasury/AdminRole.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6 |
7 | import "./OZAccessControlUpgradeable.sol";
8 |
9 | /**
10 | * @title Defines a role for Foundation admin accounts.
11 | * @dev Wraps the default admin role from OpenZeppelin's AccessControl for easy integration.
12 | */
13 | abstract contract AdminRole is Initializable, OZAccessControlUpgradeable {
14 | function _initializeAdminRole(address admin) internal onlyInitializing {
15 | // Grant the role to a specified account
16 | _setupRole(DEFAULT_ADMIN_ROLE, admin);
17 | }
18 |
19 | modifier onlyAdmin() {
20 | require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "AdminRole: caller does not have the Admin role");
21 | _;
22 | }
23 |
24 | /**
25 | * @notice Adds the account to the list of approved admins.
26 | * @dev Only callable by admins as enforced by `grantRole`.
27 | * @param account The address to be approved.
28 | */
29 | function grantAdmin(address account) external {
30 | grantRole(DEFAULT_ADMIN_ROLE, account);
31 | }
32 |
33 | /**
34 | * @notice Removes the account from the list of approved admins.
35 | * @dev Only callable by admins as enforced by `revokeRole`.
36 | * @param account The address to be removed from the approved list.
37 | */
38 | function revokeAdmin(address account) external {
39 | revokeRole(DEFAULT_ADMIN_ROLE, account);
40 | }
41 |
42 | /**
43 | * @notice Returns one of the admins by index.
44 | * @param index The index of the admin to return from 0 to getAdminMemberCount() - 1.
45 | * @return account The address of the admin.
46 | */
47 | function getAdminMember(uint256 index) external view returns (address account) {
48 | account = getRoleMember(DEFAULT_ADMIN_ROLE, index);
49 | }
50 |
51 | /**
52 | * @notice Checks how many accounts have been granted admin access.
53 | * @return count The number of accounts with admin access.
54 | */
55 | function getAdminMemberCount() external view returns (uint256 count) {
56 | count = getRoleMemberCount(DEFAULT_ADMIN_ROLE);
57 | }
58 |
59 | /**
60 | * @notice Checks if the account provided is an admin.
61 | * @param account The address to check.
62 | * @return approved True if the account is an admin.
63 | * @dev This call is used by the royalty registry contract.
64 | */
65 | function isAdmin(address account) external view returns (bool approved) {
66 | approved = hasRole(DEFAULT_ADMIN_ROLE, account);
67 | }
68 |
69 | /**
70 | * @notice This empty reserved space is put in place to allow future versions to add new
71 | * variables without shifting down storage in the inheritance chain.
72 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
73 | */
74 | uint256[1000] private __gap;
75 | }
76 |
--------------------------------------------------------------------------------
/contracts/mixins/treasury/CollateralManagement.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
6 |
7 | import "./AdminRole.sol";
8 |
9 | error CollateralManagement_Cannot_Withdraw_To_Address_Zero();
10 | error CollateralManagement_Cannot_Withdraw_To_Self();
11 |
12 | /**
13 | * @title Enables deposits and withdrawals.
14 | */
15 | abstract contract CollateralManagement is AdminRole {
16 | using AddressUpgradeable for address payable;
17 |
18 | /**
19 | * @notice Emitted when funds are withdrawn from this contract.
20 | * @param to The address which received the ETH withdrawn.
21 | * @param amount The amount of ETH which was withdrawn.
22 | */
23 | event FundsWithdrawn(address indexed to, uint256 amount);
24 |
25 | /**
26 | * @notice Accept native currency payments (i.e. fees)
27 | */
28 | // solhint-disable-next-line no-empty-blocks
29 | receive() external payable {}
30 |
31 | /**
32 | * @notice Allows an admin to withdraw funds.
33 | * @param to Address to receive the withdrawn funds
34 | * @param amount Amount to withdrawal or 0 to withdraw all available funds
35 | */
36 | function withdrawFunds(address payable to, uint256 amount) external onlyAdmin {
37 | if (amount == 0) {
38 | amount = address(this).balance;
39 | }
40 | if (to == address(0)) {
41 | revert CollateralManagement_Cannot_Withdraw_To_Address_Zero();
42 | } else if (to == address(this)) {
43 | revert CollateralManagement_Cannot_Withdraw_To_Self();
44 | }
45 | to.sendValue(amount);
46 |
47 | emit FundsWithdrawn(to, amount);
48 | }
49 |
50 | /**
51 | * @notice This empty reserved space is put in place to allow future versions to add new
52 | * variables without shifting down storage in the inheritance chain.
53 | * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
54 | */
55 | uint256[1000] private __gap;
56 | }
57 |
--------------------------------------------------------------------------------
/contracts/mixins/treasury/OperatorRole.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6 |
7 | import "./OZAccessControlUpgradeable.sol";
8 |
9 | /**
10 | * @title Defines a role for Foundation operator accounts.
11 | * @dev Wraps a role from OpenZeppelin's AccessControl for easy integration.
12 | */
13 | abstract contract OperatorRole is Initializable, OZAccessControlUpgradeable {
14 | bytes32 private constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
15 |
16 | /**
17 | * @notice Adds the account to the list of approved operators.
18 | * @dev Only callable by admins as enforced by `grantRole`.
19 | * @param account The address to be approved.
20 | */
21 | function grantOperator(address account) external {
22 | grantRole(OPERATOR_ROLE, account);
23 | }
24 |
25 | /**
26 | * @notice Removes the account from the list of approved operators.
27 | * @dev Only callable by admins as enforced by `revokeRole`.
28 | * @param account The address to be removed from the approved list.
29 | */
30 | function revokeOperator(address account) external {
31 | revokeRole(OPERATOR_ROLE, account);
32 | }
33 |
34 | /**
35 | * @notice Returns one of the operator by index.
36 | * @param index The index of the operator to return from 0 to getOperatorMemberCount() - 1.
37 | * @return account The address of the operator.
38 | */
39 | function getOperatorMember(uint256 index) external view returns (address account) {
40 | account = getRoleMember(OPERATOR_ROLE, index);
41 | }
42 |
43 | /**
44 | * @notice Checks how many accounts have been granted operator access.
45 | * @return count The number of accounts with operator access.
46 | */
47 | function getOperatorMemberCount() external view returns (uint256 count) {
48 | count = getRoleMemberCount(OPERATOR_ROLE);
49 | }
50 |
51 | /**
52 | * @notice Checks if the account provided is an operator.
53 | * @param account The address to check.
54 | * @return approved True if the account is an operator.
55 | */
56 | function isOperator(address account) external view returns (bool approved) {
57 | approved = hasRole(OPERATOR_ROLE, account);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/contracts/mocks/BasicERC721WithAccessControlMock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts/access/AccessControl.sol";
6 | import "./BasicERC721.sol";
7 |
8 | contract BasicERC721WithAccessControlMock is BasicERC721, AccessControl {
9 | constructor() {
10 | _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
11 | }
12 |
13 | function supportsInterface(bytes4 interfaceId)
14 | public
15 | view
16 | virtual
17 | override(BasicERC721, AccessControl)
18 | returns (bool)
19 | {
20 | return super.supportsInterface(interfaceId);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/contracts/mocks/BasicERC721WithoutOwnerOfRevert.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
6 |
7 | import "./BasicERC721WithAccessControlMock.sol";
8 | import "../interfaces/INFTDropCollectionMint.sol";
9 |
10 | /**
11 | * @notice A basic ERC721 which has ownerOf return address(0) instead of reverting when token does not exist.
12 | */
13 | contract BasicERC721WithoutOwnerOfRevert is
14 | INFTDropCollectionMint,
15 | ERC165Upgradeable,
16 | BasicERC721WithAccessControlMock
17 | {
18 | uint256 private id = 0;
19 |
20 | function ownerOf(uint256 tokenId) public view virtual override returns (address owner) {
21 | return _owners[tokenId];
22 | }
23 |
24 | function supportsInterface(bytes4 interfaceId)
25 | public
26 | view
27 | override(ERC165Upgradeable, BasicERC721WithAccessControlMock)
28 | returns (bool)
29 | {
30 | if (type(INFTDropCollectionMint).interfaceId == interfaceId) {
31 | return true;
32 | }
33 | return super.supportsInterface(interfaceId);
34 | }
35 |
36 | function mintCountTo(uint16 count, address to) external returns (uint256 firstTokenId) {
37 | firstTokenId = id + 1;
38 | id += count;
39 | _owners[firstTokenId] = to;
40 | _balances[to] += count;
41 | }
42 |
43 | function numberOfTokensAvailableToMint() external view override returns (uint256 count) {
44 | return 100 - id;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/contracts/mocks/EmptyMockContract.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | contract EmptyMockContract {
6 | // Something must be included in order to generate the typechain file
7 | event DummyEvent();
8 | }
9 |
--------------------------------------------------------------------------------
/contracts/mocks/FETHMarketMock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "../interfaces/IFethMarket.sol";
6 |
7 | contract FETHMarketMock {
8 | IFethMarket public feth;
9 |
10 | receive() external payable {
11 | require(msg.sender == address(feth), "Only receive from FETH");
12 | }
13 |
14 | function setFeth(address _feth) public {
15 | feth = IFethMarket(_feth);
16 | }
17 |
18 | function marketLockupFor(address account, uint256 amount) public payable {
19 | feth.marketLockupFor{ value: msg.value }(account, amount);
20 | }
21 |
22 | function marketWithdrawLocked(
23 | address account,
24 | uint256 expiration,
25 | uint256 amount
26 | ) public {
27 | feth.marketWithdrawLocked(account, expiration, amount);
28 | }
29 |
30 | function marketWithdrawFrom(address account, uint256 amount) public {
31 | feth.marketWithdrawFrom(account, amount);
32 | }
33 |
34 | function marketUnlockFor(
35 | address account,
36 | uint256 expiration,
37 | uint256 amount
38 | ) public {
39 | feth.marketUnlockFor(account, expiration, amount);
40 | }
41 |
42 | function marketChangeLockup(
43 | address unlockFrom,
44 | uint256 unlockExpiration,
45 | uint256 unlockAmount,
46 | address depositFor,
47 | uint256 depositAmount
48 | ) external payable {
49 | feth.marketChangeLockup{ value: msg.value }(unlockFrom, unlockExpiration, unlockAmount, depositFor, depositAmount);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/contracts/mocks/MockNFT.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts/access/Ownable.sol";
6 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
7 |
8 | contract MockNFT is Ownable, ERC721 {
9 | uint256 private nextTokenId;
10 |
11 | constructor()
12 | ERC721("MockNFT", "mNFT") // solhint-disable-next-line no-empty-blocks
13 | {}
14 |
15 | function mint() external onlyOwner {
16 | _mint(msg.sender, ++nextTokenId);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/contracts/mocks/MockTreasury.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | contract MockTreasury {
6 | // solhint-disable no-empty-blocks
7 | receive() external payable {}
8 |
9 | function isAdmin(
10 | address /*account*/
11 | ) external pure returns (bool) {
12 | return true;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/contracts/mocks/NFTDropCollectionUnknownCreatorMock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts/access/AccessControl.sol";
6 | import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
7 |
8 | import "../interfaces/INFTDropCollectionMint.sol";
9 |
10 | contract NFTDropCollectionUnknownCreatorMock is INFTDropCollectionMint, ERC165, AccessControl {
11 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
12 | bool private sold;
13 |
14 | constructor(address approvedMinter) {
15 | _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
16 | _grantRole(MINTER_ROLE, msg.sender);
17 | if (approvedMinter != address(0)) {
18 | _grantRole(MINTER_ROLE, approvedMinter);
19 | }
20 | }
21 |
22 | function mintCountTo(
23 | uint16, /* count */
24 | address /* to */
25 | ) external returns (uint256 firstTokenId) {
26 | sold = true;
27 | return 1;
28 | }
29 |
30 | function numberOfTokensAvailableToMint() external view returns (uint256 count) {
31 | return sold ? 97 : 100;
32 | }
33 |
34 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, AccessControl) returns (bool) {
35 | if (interfaceId == type(INFTDropCollectionMint).interfaceId) {
36 | return true;
37 | }
38 | return super.supportsInterface(interfaceId);
39 | }
40 |
41 | function balanceOf(
42 | address /*account*/
43 | ) external view returns (uint256) {
44 | return sold ? 3 : 0;
45 | }
46 |
47 | function ownerOf(
48 | uint256 /* tokenId */
49 | ) external view returns (address) {
50 | if (sold) {
51 | return address(0x1);
52 | }
53 | return address(0);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/contracts/mocks/NFTDropCollectionWithoutAccessControl.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
6 |
7 | import "../interfaces/INFTDropCollectionMint.sol";
8 |
9 | contract NFTDropCollectionWithoutAccessControl is INFTDropCollectionMint, ERC165 {
10 | bool private sold;
11 |
12 | function mintCountTo(
13 | uint16, /* count */
14 | address /* to */
15 | ) external returns (uint256 firstTokenId) {
16 | sold = true;
17 | return 1;
18 | }
19 |
20 | function numberOfTokensAvailableToMint() external pure returns (uint256 count) {
21 | return 100;
22 | }
23 |
24 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
25 | if (interfaceId == type(INFTDropCollectionMint).interfaceId) {
26 | return true;
27 | }
28 | return super.supportsInterface(interfaceId);
29 | }
30 |
31 | function balanceOf(
32 | address /*account*/
33 | ) external pure returns (uint256) {
34 | return 0;
35 | }
36 |
37 | function ownerOf(
38 | uint256 /* tokenId */
39 | ) external view returns (address) {
40 | if (sold) {
41 | return address(0x1);
42 | }
43 | return address(0);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/contracts/mocks/NonReceivableMock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
6 |
7 | contract NonReceivableMock {
8 | using AddressUpgradeable for address;
9 |
10 | function callContract(address _contract, bytes memory _callData) public payable {
11 | _contract.functionCallWithValue(_callData, msg.value);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/contracts/mocks/ReceivableMock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | contract ReceivableMock {
6 | // solhint-disable no-empty-blocks
7 | receive() external payable {}
8 | }
9 |
--------------------------------------------------------------------------------
/contracts/mocks/RoyaltyRegistry/MockRoyaltyRegistry.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@manifoldxyz/royalty-registry-solidity/contracts/IRoyaltyRegistry.sol";
6 | import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
7 |
8 | contract MockRoyaltyRegistry is IRoyaltyRegistry, ERC165 {
9 | function setRoyaltyLookupAddress(address tokenAddress, address royaltyAddress)
10 | external
11 | virtual
12 | // solhint-disable-next-line no-empty-blocks
13 | {
14 |
15 | }
16 |
17 | function getRoyaltyLookupAddress(address tokenAddress) external view virtual returns (address) {
18 | return tokenAddress;
19 | }
20 |
21 | function overrideAllowed(
22 | address /* tokenAddress */
23 | ) external view virtual returns (bool) {
24 | return true;
25 | }
26 |
27 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
28 | return interfaceId == type(IRoyaltyRegistry).interfaceId || super.supportsInterface(interfaceId);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/contracts/mocks/WETH9.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GNU-3.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | contract WETH9 {
6 | string public name = "Wrapped Ether";
7 | string public symbol = "WETH";
8 | uint8 public decimals = 18;
9 |
10 | event Approval(address indexed src, address indexed guy, uint256 wad);
11 | event Transfer(address indexed src, address indexed dst, uint256 wad);
12 | event Deposit(address indexed dst, uint256 wad);
13 | event Withdrawal(address indexed src, uint256 wad);
14 |
15 | mapping(address => uint256) public balanceOf;
16 | mapping(address => mapping(address => uint256)) public allowance;
17 |
18 | receive() external payable {
19 | deposit();
20 | }
21 |
22 | function deposit() public payable {
23 | balanceOf[msg.sender] += msg.value;
24 | emit Deposit(msg.sender, msg.value);
25 | }
26 |
27 | function withdraw(uint256 wad) public {
28 | // solhint-disable-next-line reason-string
29 | require(balanceOf[msg.sender] >= wad);
30 | balanceOf[msg.sender] -= wad;
31 | payable(msg.sender).transfer(wad);
32 | emit Withdrawal(msg.sender, wad);
33 | }
34 |
35 | function totalSupply() public view returns (uint256) {
36 | return address(this).balance;
37 | }
38 |
39 | function approve(address guy, uint256 wad) public returns (bool) {
40 | allowance[msg.sender][guy] = wad;
41 | emit Approval(msg.sender, guy, wad);
42 | return true;
43 | }
44 |
45 | function transfer(address dst, uint256 wad) public returns (bool) {
46 | return transferFrom(msg.sender, dst, wad);
47 | }
48 |
49 | function transferFrom(
50 | address src,
51 | address dst,
52 | uint256 wad
53 | ) public returns (bool) {
54 | // solhint-disable-next-line reason-string
55 | require(balanceOf[src] >= wad);
56 |
57 | if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {
58 | // solhint-disable-next-line reason-string
59 | require(allowance[src][msg.sender] >= wad);
60 | allowance[src][msg.sender] -= wad;
61 | }
62 |
63 | balanceOf[src] -= wad;
64 | balanceOf[dst] += wad;
65 |
66 | emit Transfer(src, dst, wad);
67 |
68 | return true;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/contracts/mocks/collections/SequentialMintCollectionMock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6 |
7 | import "../../mixins/collections/SequentialMintCollection.sol";
8 |
9 | contract SequentialMintCollectionMock is Initializable, SequentialMintCollection {
10 | function initializeWithModifier(address payable _creator, uint32 _maxTokenId) external initializer {
11 | SequentialMintCollection._initializeSequentialMintCollection(_creator, _maxTokenId);
12 | }
13 |
14 | function initializeWithoutModifier(address payable _creator, uint32 _maxTokenId) external {
15 | SequentialMintCollection._initializeSequentialMintCollection(_creator, _maxTokenId);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/0266e5039778e2135b8ce9b7c0243400-A3510.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/0266e5039778e2135b8ce9b7c0243400-A3510.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/123c800a205b56354c1bd121a4b1b969-621EA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/123c800a205b56354c1bd121a4b1b969-621EA.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f355-981A7.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f374-505CF.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f389-5C738.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f3c1-445DC.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f3c6-621A1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f43a-EB486.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f440-6C64D.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f44b-8A059.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f44c-59547.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f44d-27259.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f44f-3D381.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f49c-71A75.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f4af-4CFF5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f4b0-53FFF.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f4c6-44E30.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f4db-4C577.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f4dc-AC641.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f506-55112.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f50d-195C0.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f525-8FE4F.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f554-A741F.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f602-168C5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f604-BF863.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f605-42B43.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f606-BE94E.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f609-9EC67.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f60b-B5303.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f629-B734A.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f62d-02603.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f642-83E8A.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f64f-22B8D.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f680-A35CE.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f6a8-A8AB3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f7e5-2145C.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f911-F346C.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f914-15707.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f919-1f3fb-2BA09.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f923-5854E.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f926-E188B.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f929-12865.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f972-F415D.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f973-88B39.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f97a-1F57B.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/1f9d1-5BC80.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/2696-15F4A.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/2705-0589F.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/2764-A3D25.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/2764-fe0f-200d-1f525-0B2B8.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/2a9faff195fe333526cfe6ae6fce1420-927E9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/2a9faff195fe333526cfe6ae6fce1420-927E9.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/32-20e3-EF1FD.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/39a730e4124c3b42df900de7d9d32973-2B76A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/39a730e4124c3b42df900de7d9d32973-2B76A.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/3b01c38b7c5b905fd8e8a1d72f7d7492-6DE9E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/3b01c38b7c5b905fd8e8a1d72f7d7492-6DE9E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/4c405c8d602d7b3489d628bf20bfb843-60774.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/4c405c8d602d7b3489d628bf20bfb843-60774.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/4d708938ebe45c5d7a5f46ecbd3b144e-C87A6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/4d708938ebe45c5d7a5f46ecbd3b144e-C87A6.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/5b069e074423293b86222b4350052da5-77740.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/5b069e074423293b86222b4350052da5-77740.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/5f978694dec96afc1640b5426003aef1-3E74E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/5f978694dec96afc1640b5426003aef1-3E74E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/62208180fa28d8609e6f66d9ad7fca37-00B6C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/62208180fa28d8609e6f66d9ad7fca37-00B6C.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/67594ee4b4d1fc03bca468327a0d145b-0C614.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/67594ee4b4d1fc03bca468327a0d145b-0C614.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/7295c56e5b235d31dcba650a186322b0-1BF1F.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/7295c56e5b235d31dcba650a186322b0-1BF1F.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/754850443909267567-E2D08.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/754850443909267567-E2D08.gif
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/773490663245348864-8086D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/773490663245348864-8086D.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/809922172625748019-3756A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/809922172625748019-3756A.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/851893157188599838-C23B5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/851893157188599838-C23B5.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/851893827027075142-F23DF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/851893827027075142-F23DF.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/851893827089727568-5FD38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/851893827089727568-5FD38.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/851893827315826708-F59C0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/851893827315826708-F59C0.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/910676187288846397-518CD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/910676187288846397-518CD.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/918262047433691247-911FE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/918262047433691247-911FE.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/927703596944998430-0A6FB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/927703596944998430-0A6FB.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/9503f567dabd2781b3d25827ceb83075-313E1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/9503f567dabd2781b3d25827ceb83075-313E1.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/970d2e2f00cd7ef2134a1a3f21326349-B9EEF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/970d2e2f00cd7ef2134a1a3f21326349-B9EEF.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/977133670429261884-CA8EA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/977133670429261884-CA8EA.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/9bf2181404e658cab4039c07df56213f-EC9DE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/9bf2181404e658cab4039c07df56213f-EC9DE.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/9dae367c2914db90ec5f86da55c97b23-D9765.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/9dae367c2914db90ec5f86da55c97b23-D9765.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/C4-banner-7C19B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/C4-banner-7C19B.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/PolygonScan-logo-circle-6FDB5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/PolygonScan-logo-circle-6FDB5.jpg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/a36cb7b26a8fd70f4f296a94c3cf5a9d-2B57B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/a36cb7b26a8fd70f4f296a94c3cf5a9d-2B57B.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/aa8a8cf504d883f45a141e445c26ec7b-0AED8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/aa8a8cf504d883f45a141e445c26ec7b-0AED8.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/aba7f6b23cd80ec9e8655016ce6ef443-929E9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/aba7f6b23cd80ec9e8655016ce6ef443-929E9.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ae70f07a06c1c7e983291bb14a1bed7f-98458.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ae70f07a06c1c7e983291bb14a1bed7f-98458.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/b07292365785e0b7bd0dab7dd7d9e09f-62888.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/b07292365785e0b7bd0dab7dd7d9e09f-62888.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/bugs-bunny-looney-tunes-BD3E0.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/bugs-bunny-looney-tunes-BD3E0.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/c47e074275f9b2c05d095d0847bc18dd-CF0A6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/c47e074275f9b2c05d095d0847bc18dd-CF0A6.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/c9cb30134c634c9e02d0c64df4922803-1AAF2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/c9cb30134c634c9e02d0c64df4922803-1AAF2.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/cb23e87e4eb33d228ed3294f90188951-D6A20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/cb23e87e4eb33d228ed3294f90188951-D6A20.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/d38725fdb3f22651bef3993cf902a61d-F5783.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/d38725fdb3f22651bef3993cf902a61d-F5783.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/d6f1f56d219902e83e7f37cc225ffd0f-D4278.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/d6f1f56d219902e83e7f37cc225ffd0f-D4278.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/d7e057b14c0a958df354613bb7522913-1FD52.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/d7e057b14c0a958df354613bb7522913-1FD52.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/d7e24e0994d277993ed07845cac9ca8b-E4084.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/d7e24e0994d277993ed07845cac9ca8b-E4084.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/dfacc77d68962a991f5a4c3b7859b4c2-0A44D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/dfacc77d68962a991f5a4c3b7859b4c2-0A44D.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/e8e42b0753ed4170607ecff76b81d17e-80898.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/e8e42b0753ed4170607ecff76b81d17e-80898.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-400-E988B.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-400-E988B.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-500-0777F.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-500-0777F.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-600-CB411.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-600-CB411.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-700-891AC.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-700-891AC.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-800-D36B0.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-italic-800-D36B0.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-400-1456D.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-400-1456D.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-500-89CE5.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-500-89CE5.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-600-C1EA8.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-600-C1EA8.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-700-1949A.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-700-1949A.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-800-58487.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/ggsans-normal-800-58487.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/michael-scott-wink-82DFE.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/michael-scott-wink-82DFE.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/oh-hey-ryan-reynolds-13ABB.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/oh-hey-ryan-reynolds-13ABB.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/solarized-dark.min-BA98F.css:
--------------------------------------------------------------------------------
1 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#002b36;color:#839496}.hljs-comment,.hljs-quote{color:#586e75}.hljs-keyword,.hljs-selector-tag,.hljs-addition{color:#859900}.hljs-number,.hljs-string,.hljs-meta .hljs-meta-string,.hljs-literal,.hljs-doctag,.hljs-regexp{color:#2aa198}.hljs-title,.hljs-section,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#268bd2}.hljs-attribute,.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-class .hljs-title,.hljs-type{color:#b58900}.hljs-symbol,.hljs-bullet,.hljs-subst,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-link{color:#cb4b16}.hljs-built_in,.hljs-deletion{color:#dc322f}.hljs-formula{background:#073642}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/spongebob-sad-96BA8.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/spongebob-sad-96BA8.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/unknown-086F2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/unknown-086F2.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/unknown-CB0DC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/unknown-CB0DC.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/will-ferrell-stressed-out-73DD6.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].html_Files/will-ferrell-stressed-out-73DD6.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/C4-banner-7C19B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/C4-banner-7C19B.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/PolygonScan-logo-circle-6FDB5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/PolygonScan-logo-circle-6FDB5.jpg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/bugs-bunny-looney-tunes-6263B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/bugs-bunny-looney-tunes-6263B.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/maxresdefault-7ADB4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/maxresdefault-7ADB4.jpg
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/michael-scott-wink-76E4C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/michael-scott-wink-76E4C.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/oh-hey-ryan-reynolds-80E2E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/oh-hey-ryan-reynolds-80E2E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/spongebob-sad-A44ED.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/spongebob-sad-A44ED.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/unknown-086F2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/unknown-086F2.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/unknown-CB0DC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/unknown-CB0DC.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/will-ferrell-stressed-out-65DCF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2022-08-foundation/4bba2108615701e429bcfa0c5079eb5469811ee6/discord-export/Code4rena - ARCHIVE-Q3-2022 - ☑foundation-aug11 [1006281012931739698].txt_Files/will-ferrell-stressed-out-65DCF.png
--------------------------------------------------------------------------------
/foundry.toml:
--------------------------------------------------------------------------------
1 | [profile.default]
2 | src = 'contracts'
3 | test = 'test/foundry'
4 | libs = ['lib','node_modules']
5 | remappings = ['forge-std/=lib/forge-std/src/','ds-test/=lib/forge-std/lib/ds-test/src/']
6 | optimizer = true
7 | optimizer_runs = 1337
8 |
9 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config
--------------------------------------------------------------------------------
/gas-stories.txt:
--------------------------------------------------------------------------------
1 | Market
2 | =========================================================
3 | NFTDropMarketFixedPriceSale
4 | ···························
5 | [Collector] Mint
6 | 134,500 1st mint
7 | 146,800 1st mint w. buy referrer
8 | 117,400 2nd mint
9 | 129,700 2nd mint w. buy referrer
10 |
11 | [Collector] Mint Batch
12 | 359,100 1st Mint 10
13 | 371,400 1st Mint 10 w. buy referrer
14 | 342,000 2nd Mint 10
15 | 354,300 2nd Mint 10 w. buy referrer
16 |
17 | createFixedPriceSale
18 | 73,400
19 |
20 | NFT
21 | =========================================================
22 | Collection
23 | ···························
24 | Approve
25 | 48,900
26 |
27 | Burn
28 | 56,300 Last NFT
29 | 56,300 with other NFTs
30 |
31 | Mint
32 | 170,500 1st mint
33 | 195,200 1st mint & approve
34 | 153,400 2nd mint
35 | 158,200 2nd mint & approve [approval is redundant]
36 |
37 | Self Destruct
38 | 32,500
39 |
40 | Factory
41 | ···························
42 | Create Collection
43 | 174,600
44 |
45 | Create NFTDropCollection
46 | 319,300
47 | 294,300 w.o Market as Minter
48 |
49 | NFTDropCollection
50 | ···························
51 | [Creator] Mint
52 | 81,700 1st mint
53 | 64,600 2nd mint
54 |
55 | Burn
56 | 39,300 Last NFT
57 | 44,100 with other NFTs
58 |
59 | Self Destruct
60 | 34,500
61 |
62 | Update
63 | 33,200 MaxTokenId
64 | 41,200 PreReveal Content
65 | 34,700 Reveal Collection
66 |
67 |
--------------------------------------------------------------------------------
/hardhat.config.ts:
--------------------------------------------------------------------------------
1 | import "@nomicfoundation/hardhat-chai-matchers";
2 | import { HardhatUserConfig } from "hardhat/types";
3 | import "@typechain/hardhat";
4 | import "solidity-coverage";
5 | import "hardhat-deploy";
6 | import "hardhat-deploy-ethers";
7 | import "@openzeppelin/hardhat-upgrades";
8 | import "@nomiclabs/hardhat-etherscan";
9 | import "hardhat-preprocessor";
10 | import "hardhat-tracer";
11 | import "hardhat-storage-layout";
12 | import "hardhat-gas-reporter";
13 |
14 | const config: HardhatUserConfig = {
15 | solidity: {
16 | version: "0.8.16",
17 | settings: {
18 | optimizer: {
19 | enabled: true,
20 | runs: 1337,
21 | },
22 | },
23 | },
24 | typechain: {
25 | outDir: "src/typechain",
26 | target: "ethers-v5",
27 | externalArtifacts: ["node_modules/@manifoldxyz/royalty-registry-solidity/build/contracts/*.json"],
28 | },
29 | gasReporter: {
30 | currency: "USD",
31 | gasPrice: 100,
32 | excludeContracts: ["mocks/"],
33 | },
34 | };
35 |
36 | export default config;
37 |
--------------------------------------------------------------------------------
/hardhat.gas-stories.config.ts:
--------------------------------------------------------------------------------
1 | import { removeConsoleLog } from "hardhat-preprocessor";
2 | import config from "./hardhat.config";
3 |
4 | export default {
5 | ...config,
6 | preprocess: {
7 | eachLine: removeConsoleLog(() => true),
8 | },
9 | paths: {
10 | ...config.paths,
11 | tests: "./test-gas-stories",
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/helpers/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./utils";
2 |
--------------------------------------------------------------------------------
/helpers/utils.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber, providers } from "ethers";
2 | import { ethers } from "hardhat";
3 | import { ONE_HOUR } from "../test/helpers/constants";
4 |
5 | export async function sleep(ms: number): Promise {
6 | return new Promise(resolve => setTimeout(resolve, ms));
7 | }
8 |
9 | export async function sleepUntil(check: () => Promise): Promise {
10 | for (let i = 1; ; i++) {
11 | if (await check()) return;
12 | console.log("Waiting for tx to mine...");
13 | await sleep(1000 * i);
14 | }
15 | }
16 |
17 | // As of 0.5.9 the OZ test helper for time does not work with Hardhat
18 | export async function increaseTime(seconds: number): Promise {
19 | const provider: providers.JsonRpcProvider = ethers.provider;
20 | await provider.send("evm_increaseTime", [seconds]);
21 | await advanceBlock();
22 | }
23 |
24 | export async function increaseTimeTo(timestamp: number | BigNumber, shouldAdvanceBlock = true): Promise {
25 | timestamp = BigNumber.isBigNumber(timestamp) ? timestamp.toNumber() : timestamp;
26 | const provider: providers.JsonRpcProvider = ethers.provider;
27 | await provider.send("evm_setNextBlockTimestamp", [timestamp]);
28 | if (shouldAdvanceBlock) {
29 | await advanceBlock();
30 | }
31 | }
32 |
33 | export async function increaseTimeToNextHour(): Promise {
34 | const time = await getBlockTime();
35 | const timeToMoveTo = Math.ceil((time + 1) / ONE_HOUR) * ONE_HOUR;
36 | await increaseTimeTo(timeToMoveTo);
37 | }
38 |
39 | export async function getBlockTime(block: string | number = "latest"): Promise {
40 | const provider: providers.JsonRpcProvider = ethers.provider;
41 | return (await provider.getBlock(block)).timestamp;
42 | }
43 |
44 | export async function advanceBlock() {
45 | const provider: providers.JsonRpcProvider = ethers.provider;
46 | await provider.send("evm_mine", []);
47 | }
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "build": "hardhat typechain",
4 | "coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"./test/**/*.ts\"",
5 | "lint": "yarn prettier && yarn lint:sol && yarn lint:ts",
6 | "lint:sol": "solhint --config ./.solhint.json --fix \"contracts/**/*.sol\"",
7 | "lint:ts": "eslint --config ./.eslintrc.yaml --fix",
8 | "prettier": "prettier --config .prettierrc --write --list-different \"**/*.{js,json,md,sol,ts}\"",
9 | "test": "hardhat test",
10 | "test-gas-stories": "hardhat --config hardhat.gas-stories.config.ts test"
11 | },
12 | "devDependencies": {
13 | "@ensdomains/eth-ens-namehash": "2.0.15",
14 | "@ethersproject/abstract-signer": "5.6.2",
15 | "@ethersproject/bignumber": "5.6.2",
16 | "@manifoldxyz/royalty-registry-solidity": "1.0.9",
17 | "@nomicfoundation/hardhat-chai-matchers": "1.0.2",
18 | "@nomicfoundation/hardhat-toolbox": "1.0.2",
19 | "@nomiclabs/hardhat-ethers": "2.1.0",
20 | "@nomiclabs/hardhat-etherscan": "3.1.0",
21 | "@openzeppelin/contracts": "4.7.2",
22 | "@openzeppelin/contracts-upgradeable": "4.7.2",
23 | "@openzeppelin/hardhat-upgrades": "1.19.1",
24 | "@openzeppelin/test-helpers": "0.5.15",
25 | "@primitivefi/hardhat-dodoc": "0.2.3",
26 | "@typechain/ethers-v5": "9.0.0",
27 | "@typechain/hardhat": "6.1.2",
28 | "@types/chai": "4.3.3",
29 | "@types/fs-extra": "9.0.13",
30 | "@types/mocha": "9.1.1",
31 | "@types/node": "18.6.4",
32 | "@typescript-eslint/eslint-plugin": "5.32.0",
33 | "@typescript-eslint/parser": "5.32.0",
34 | "chai": "4.3.6",
35 | "chalk": "4.1.2",
36 | "coveralls": "3.1.1",
37 | "defender-admin-client": "1.25.0",
38 | "dotenv": "16.0.1",
39 | "eslint": "8.21.0",
40 | "eslint-config-prettier": "8.5.0",
41 | "eslint-plugin-no-only-tests": "^3.0.0",
42 | "ethereum-waffle": "3.4.4",
43 | "ethereumjs-util": "7.1.5",
44 | "ethers": "5.6.9",
45 | "fs-extra": "10.1.0",
46 | "hardhat": "2.10.1",
47 | "hardhat-contract-sizer": "2.6.1",
48 | "hardhat-deploy": "0.11.12",
49 | "hardhat-deploy-ethers": "0.3.0-beta.13",
50 | "hardhat-gas-reporter": "1.0.8",
51 | "hardhat-output-validator": "0.1.19",
52 | "hardhat-preprocessor": "0.1.4",
53 | "hardhat-storage-layout": "0.1.6",
54 | "hardhat-tracer": "1.1.0-rc.6",
55 | "mocha": "10.0.0",
56 | "npm-package-json-lint": "6.3.0",
57 | "npm-package-json-lint-config-default": "5.0.0",
58 | "prettier": "2.7.1",
59 | "prettier-plugin-solidity": "1.0.0-dev.23",
60 | "shelljs": "0.8.5",
61 | "solhint": "3.3.7",
62 | "solhint-plugin-prettier": "0.0.5",
63 | "solidity-coverage": "0.8.0-rc.1",
64 | "ts-node": "10.9.1",
65 | "typechain": "8.1.0",
66 | "typescript": "4.6.4"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/slither.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "filter_paths": "(mocks/|test/|node_modules/)"
3 | }
4 |
--------------------------------------------------------------------------------
/slither/NFTCollection.md:
--------------------------------------------------------------------------------
1 | Summary
2 |
3 | - [unprotected-upgrade](#unprotected-upgrade) (1 results) (High)
4 | - [dead-code](#dead-code) (1 results) (Informational)
5 | - [solc-version](#solc-version) (14 results) (Informational)
6 | - [naming-convention](#naming-convention) (4 results) (Informational)
7 |
8 | ## unprotected-upgrade
9 |
10 | Impact: High
11 | Confidence: High
12 |
13 | - [ ] ID-0
14 | [NFTCollection](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTCollection.sol#L28-L338) is an upgradeable contract that does not protect its initiliaze functions: [INFTCollectionInitializer.initialize(address,string,string)](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/interfaces/INFTCollectionInitializer.sol#L6-L10)[NFTCollection.initialize(address,string,string)](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTCollection.sol#L105-L112). Anyone can delete the contract with: [NFTCollection.selfDestruct()](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTCollection.sol#L230-L232)
15 | https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTCollection.sol#L28-L338
16 |
17 | > The template is initialized by the factory when `adminUpdateNFTCollectionImplementation` is called and instances are initialized by the factory when `createNFTCollection` is used to create them. So we are not vulnerable here.
18 |
19 | ## dead-code
20 |
21 | Impact: Informational
22 | Confidence: Medium
23 |
24 | - [ ] ID-1
25 | [AddressLibrary.callAndReturnContractAddress(CallWithoutValue)](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/libraries/AddressLibrary.sol#L34-L39) is never used and should be removed
26 |
27 | https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/libraries/AddressLibrary.sol#L34-L39
28 |
29 | > Invalid. This is just a result of how I updated the repo in order to generate these results.
30 |
31 | ## solc-version
32 |
33 | Impact: Informational
34 | Confidence: High
35 |
36 | > We have opted into using the most recent version of Solidity.
37 |
38 | ## naming-convention
39 |
40 | Impact: Informational
41 | Confidence: High
42 |
43 | > With the exception of leading underscores used on some param names, our variables should consistently be using camel case format.
44 |
--------------------------------------------------------------------------------
/slither/NFTDropCollection.md:
--------------------------------------------------------------------------------
1 | Summary
2 |
3 | - [unprotected-upgrade](#unprotected-upgrade) (1 results) (High)
4 | - [events-maths](#events-maths) (1 results) (Low)
5 | - [solc-version](#solc-version) (16 results) (Informational)
6 | - [naming-convention](#naming-convention) (13 results) (Informational)
7 |
8 | ## unprotected-upgrade
9 |
10 | Impact: High
11 | Confidence: High
12 |
13 | - [ ] ID-0
14 | [NFTDropCollection](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTDropCollection.sol#L28-L305) is an upgradeable contract that does not protect its initiliaze functions: [INFTDropCollectionInitializer.initialize(address,string,string,string,bytes32,uint32,address,address)](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/interfaces/INFTDropCollectionInitializer.sol#L6-L15)[NFTDropCollection.initialize(address,string,string,string,bytes32,uint32,address,address)](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTDropCollection.sol#L120-L152). Anyone can delete the contract with: [NFTDropCollection.selfDestruct()](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTDropCollection.sol#L209-L211)
15 | https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTDropCollection.sol#L28-L305
16 |
17 | > The template is initialized by the factory when `adminUpdateNFTDropCollectionImplementation` is called and instances are initialized by the factory when `createNFTDropCollection` is used to create them. So we are not vulnerable here.
18 |
19 | ## events-maths
20 |
21 | Impact: Low
22 | Confidence: Medium
23 |
24 | - [ ] ID-1
25 | [NFTDropCollection.mintCountTo(uint16,address)](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTDropCollection.sol#L171-L187) should emit an event for: - [latestTokenId = latestTokenId + count](https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTDropCollection.sol#L178)
26 |
27 | https://github.com/code-423n4/2022-08-foundation/blob/main/contracts/NFTDropCollection.sol#L171-L187
28 |
29 | > This information is implicitly available from the most recent mint event (`Transfer` where from is address(0)).
30 |
31 | ## solc-version
32 |
33 | Impact: Informational
34 | Confidence: High
35 |
36 | > We have opted into using the most recent version of Solidity.
37 |
38 | ## naming-convention
39 |
40 | Impact: Informational
41 | Confidence: High
42 |
43 | > With the exception of leading underscores used on some param names, our variables should consistently be using camel case format.
44 |
--------------------------------------------------------------------------------
/src/helpers/bytes.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 |
3 | /**
4 | * Returns the byte position of the account's address within an array of bytes.
5 | */
6 | export function positionOfAddress(bytes: string, account: SignerWithAddress | string): number {
7 | let address: string;
8 | if (typeof account === "string") {
9 | address = account;
10 | } else {
11 | address = account.address;
12 | }
13 | return bytes.indexOf(address.substr(2).toLowerCase()) / 2 - 1;
14 | }
15 |
--------------------------------------------------------------------------------
/src/helpers/process.ts:
--------------------------------------------------------------------------------
1 | import { execSync, spawn } from "child_process";
2 |
3 | export async function sleep(ms: number): Promise {
4 | return new Promise(resolve => setTimeout(resolve, ms));
5 | }
6 |
7 | let processCounter = 0;
8 | export function start(command: string): void {
9 | const processId = processCounter++;
10 | console.log(`Starting ${processId}: ${command}`);
11 | const process = spawn("/bin/sh", ["-c", `${command}`]);
12 |
13 | process.stdout.on("data", data => {
14 | console.log(`> ${processId}: ${data}`);
15 | });
16 |
17 | process.stderr.on("data", data => {
18 | console.error(`> ${processId}: ${data}`);
19 | });
20 |
21 | process.on("close", code => {
22 | console.log(`> ${processId} process exited with code ${code}`);
23 | });
24 |
25 | process.on("error", error => {
26 | console.error(`> ${processId} failed to start: ${error}`);
27 | });
28 | }
29 |
30 | export function run(command: string): void {
31 | console.log(`Running: ${command}`);
32 | execSync(command);
33 | }
34 |
--------------------------------------------------------------------------------
/src/helpers/providerHelpers.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 | import { BigNumber } from "ethers";
3 | import { ethers, network } from "hardhat";
4 |
5 | export async function setAccountETHBalance(
6 | address: SignerWithAddress,
7 | newBalance: BigNumber = ethers.utils.parseEther("1000"),
8 | ) {
9 | const balance = ethers.utils.hexStripZeros(newBalance.toHexString());
10 | await network.provider.send("hardhat_setBalance", [address.address, balance]);
11 | }
12 |
--------------------------------------------------------------------------------
/src/testBehaviors/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./supportsInterface.behavior";
2 |
--------------------------------------------------------------------------------
/src/types/openzeppelin__test-helpers.d.ts:
--------------------------------------------------------------------------------
1 | declare module "@openzeppelin/test-helpers";
2 |
--------------------------------------------------------------------------------
/test-gas-stories/collections.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 | import { ContractTransaction } from "ethers";
3 | import { ethers } from "hardhat";
4 | import { NFTCollectionFactory } from "../src/typechain";
5 | import { getNFTCollection } from "../test/helpers/collectionContract";
6 | import { TestContracts } from "../test/helpers/deploy";
7 | import { testIpfsPath } from "../test/helpers/testData";
8 | import { story } from "./gas-stories";
9 |
10 | describe("Collections", () => {
11 | let creator: SignerWithAddress;
12 | let mockMarket: SignerWithAddress;
13 | let contracts: TestContracts;
14 | let nftCollectionFactoryV2: NFTCollectionFactory;
15 | let tx: ContractTransaction;
16 |
17 | beforeEach(async function () {
18 | [, creator, mockMarket] = await ethers.getSigners();
19 | contracts = this.contracts;
20 | nftCollectionFactoryV2 = contracts.nftCollectionFactoryV2!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
21 | });
22 |
23 | it("Create & mint", async () => {
24 | tx = await nftCollectionFactoryV2.connect(creator).createNFTCollection("TEST", "TEST", 42);
25 | await story("NFT", "Factory", "Create Collection", "", tx);
26 | const collection = await getNFTCollection(tx, creator);
27 |
28 | tx = await collection.connect(creator).mint(testIpfsPath[1]);
29 | await story("NFT", "Collection", "Mint", "1st mint", tx);
30 |
31 | tx = await collection.connect(creator).mint(testIpfsPath[2]);
32 | await story("NFT", "Collection", "Mint", "2nd mint", tx);
33 |
34 | tx = await collection.connect(creator).setApprovalForAll(mockMarket.address, true);
35 | await story("NFT", "Collection", "Approve", "", tx);
36 |
37 | tx = await collection.connect(creator).burn(2);
38 | await story("NFT", "Collection", "Burn", "with other NFTs", tx);
39 | tx = await collection.connect(creator).burn(1);
40 | await story("NFT", "Collection", "Burn", "Last NFT", tx);
41 |
42 | tx = await collection.connect(creator).selfDestruct();
43 | await story("NFT", "Collection", "Self Destruct", "", tx);
44 | });
45 |
46 | it("Mint & approve", async () => {
47 | tx = await nftCollectionFactoryV2.connect(creator).createNFTCollection("TEST", "TEST", 42);
48 | const collection = await getNFTCollection(tx, creator);
49 |
50 | tx = await collection.connect(creator).mintAndApprove(testIpfsPath[1], mockMarket.address);
51 | await story("NFT", "Collection", "Mint", "1st mint & approve", tx);
52 |
53 | tx = await collection.connect(creator).mintAndApprove(testIpfsPath[2], mockMarket.address);
54 | await story("NFT", "Collection", "Mint", "2nd mint & approve", tx, "approval is redundant");
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/test/NFTDropMarket/fixedPrice/mintFromFixedPriceSale.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 | import { ContractTransaction } from "ethers";
3 | import { ethers } from "hardhat";
4 | import { deployAll, TestContracts } from "../../helpers/deploy";
5 | import { assertAllLogs } from "../../helpers/logs";
6 | import { snapshotEach } from "../../helpers/snapshot";
7 |
8 | describe("NFTDropMarket / FixedPrice / mintFromFixedPriceSale", () => {
9 | const price = ethers.utils.parseEther("0.42");
10 | const limitPerAccount = 3;
11 |
12 | let contracts: TestContracts;
13 | let deployer: SignerWithAddress;
14 | let creator: SignerWithAddress;
15 | let collector: SignerWithAddress;
16 | let tx: ContractTransaction;
17 |
18 | snapshotEach(async () => {
19 | [deployer, creator, collector] = await ethers.getSigners();
20 | contracts = await deployAll(deployer, creator);
21 | await contracts.nftDropMarket
22 | .connect(creator)
23 | .createFixedPriceSale(contracts.nftDropCollection.address, price, limitPerAccount);
24 | });
25 |
26 | describe("Mint 3", () => {
27 | const tokenId = 1; // first token
28 | const count = 3;
29 |
30 | beforeEach(async () => {
31 | tx = await contracts.nftDropMarket
32 | .connect(collector)
33 | .mintFromFixedPriceSale(contracts.nftDropCollection.address, count, ethers.constants.AddressZero, {
34 | value: price.mul(count),
35 | });
36 | });
37 |
38 | it("Emits events", async () => {
39 | await assertAllLogs(tx, [
40 | // Mint tokens
41 | {
42 | contract: contracts.nftDropCollection,
43 | eventName: "Transfer",
44 | args: [ethers.constants.AddressZero, collector.address, tokenId],
45 | },
46 | {
47 | contract: contracts.nftDropCollection,
48 | eventName: "Transfer",
49 | args: [ethers.constants.AddressZero, collector.address, tokenId + 1],
50 | },
51 | {
52 | contract: contracts.nftDropCollection,
53 | eventName: "Transfer",
54 | args: [ethers.constants.AddressZero, collector.address, tokenId + 2],
55 | },
56 | // Record sale
57 | {
58 | contract: contracts.nftDropMarket,
59 | eventName: "MintFromFixedPriceDrop",
60 | args: [
61 | // Collection
62 | contracts.nftDropCollection.address,
63 | // Buyer
64 | collector.address,
65 | // First tokenId
66 | tokenId,
67 | // Count
68 | count,
69 | // Total fees 5%
70 | price.mul(count).mul(5).div(100),
71 | // Creator rev 95%
72 | price.mul(count).mul(95).div(100),
73 | ],
74 | },
75 | ]);
76 | });
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/test/NFTDropMarket/initializer.ts:
--------------------------------------------------------------------------------
1 | import { expect } from "chai";
2 | import { deployAll, TestContracts } from "../helpers/deploy";
3 | import { snapshotEach } from "../helpers/snapshot";
4 |
5 | describe("NFTDropMarket / initializer", () => {
6 | let contracts: TestContracts;
7 |
8 | snapshotEach(async () => {
9 | contracts = await deployAll();
10 | });
11 |
12 | it("Cannot initialize again", async () => {
13 | await expect(contracts.nftDropMarket.initialize()).to.be.revertedWith(
14 | "Initializable: contract is already initialized",
15 | );
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/test/collections/NFTCollection/mint.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 | import { expect } from "chai";
3 | import { ethers } from "hardhat";
4 | import { deployAll, TestContracts } from "../../helpers/deploy";
5 | import { snapshotEach } from "../../helpers/snapshot";
6 | import { testIpfsPath } from "../../helpers/testData";
7 |
8 | describe("NFTCollection / mint", () => {
9 | let contracts: TestContracts;
10 | let deployer, creator, rando: SignerWithAddress;
11 |
12 | snapshotEach(async () => {
13 | [deployer, creator, rando] = await ethers.getSigners();
14 | contracts = await deployAll(deployer, creator);
15 | });
16 |
17 | it("Rando cannot mint from the collection", async () => {
18 | await expect(contracts.collection.connect(rando).mint(testIpfsPath[0])).to.be.revertedWith(
19 | "SequentialMintCollection: Caller is not creator",
20 | );
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/collections/NFTCollection/paymentFactory/DrainTokens.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "hardhat";
2 | import { NFTCollectionFactory, NFTCollection, NFTCollection__factory, WETH9 } from "../../../../src/typechain";
3 | import { expect } from "chai";
4 | import { deployAll, deployWETH9 } from "../../../helpers/deploy";
5 | import { testIpfsPath } from "../../../helpers/testData";
6 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address";
7 | import { snapshotEach } from "../../../helpers/snapshot";
8 |
9 | /**
10 | * Confirms that a specific attack to drain tokens stored by the NFT contract is not possible.
11 | */
12 | describe("NFTCollection / paymentFactory / drainTokens", () => {
13 | let deployer: SignerWithAddress;
14 | let creator: SignerWithAddress;
15 | let rando: SignerWithAddress;
16 | let nft: NFTCollection;
17 | let weth9: WETH9;
18 | let nftCollectionFactoryV2: NFTCollectionFactory;
19 | const nftWethBalanceBefore = ethers.utils.parseEther("1");
20 |
21 | snapshotEach(async () => {
22 | [deployer, creator, rando] = await ethers.getSigners();
23 | ({ nftCollectionFactoryV2 } = await deployAll(deployer, creator));
24 | weth9 = await deployWETH9(deployer);
25 | const NAME = "NAME";
26 | const SYMBOL = "SYM";
27 | const NONCE = 0;
28 | await nftCollectionFactoryV2.connect(creator).createNFTCollection(NAME, SYMBOL, NONCE);
29 | const nftAddress = await nftCollectionFactoryV2.predictNFTCollectionAddress(creator.address, NONCE);
30 | nft = NFTCollection__factory.connect(nftAddress, creator);
31 | await weth9.connect(rando).deposit({ value: nftWethBalanceBefore.mul(2) });
32 | await weth9.connect(rando).transfer(nft.address, nftWethBalanceBefore);
33 | });
34 |
35 | it("WETH balance before", async () => {
36 | const balance7 = await weth9.balanceOf(nft.address);
37 | expect(balance7).to.eq(nftWethBalanceBefore);
38 | });
39 |
40 | it("Funds cannot be drained with the factory", async () => {
41 | // Attempt to transfer 1 wei
42 | const callData = weth9.interface.encodeFunctionData("transfer", [rando.address, 1]);
43 | await expect(nft.mintWithCreatorPaymentFactory(testIpfsPath[0], weth9.address, callData)).to.be.revertedWith(
44 | "InternalProxyCall: did not return a contract",
45 | );
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/collections/NFTCollection/royaltyInfo.ts:
--------------------------------------------------------------------------------
1 | import { snapshotEach } from "../../helpers/snapshot";
2 | import { ethers } from "hardhat";
3 | import { FoundationTreasury, NFTCollection, NFTCollectionFactory } from "../../../src/typechain";
4 | import { expect } from "chai";
5 | import { deployAll, deployCollectionImplementationsAndFactory } from "../../helpers/deploy";
6 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
7 | import { ContractTransaction } from "ethers";
8 | import { getNFTCollection } from "../../helpers/collectionContract";
9 | import { shouldSupportInterfaces } from "../../../src/testBehaviors";
10 |
11 | describe("NFTCollection / royaltyInfo", () => {
12 | let deployer: SignerWithAddress;
13 | let creator: SignerWithAddress;
14 | let nft: NFTCollection;
15 | let tx: ContractTransaction;
16 | let nftCollectionFactoryV2: NFTCollectionFactory;
17 | let treasury: FoundationTreasury;
18 | const NONCE = 42;
19 |
20 | snapshotEach(async function () {
21 | [deployer, creator] = await ethers.getSigners();
22 | ({ treasury } = await deployAll(deployer, creator));
23 | ({ nftCollectionFactoryV2 } = await deployCollectionImplementationsAndFactory(deployer, treasury));
24 | tx = await nftCollectionFactoryV2.connect(creator).createNFTCollection("NAME", "SYMBOL", NONCE);
25 | nft = await getNFTCollection(tx, creator);
26 | this.nft = nft;
27 | });
28 |
29 | shouldSupportInterfaces(["ERC2981"]);
30 |
31 | it("Sold for nothing.", async () => {
32 | const fees = await nft.royaltyInfo(1, 0);
33 | expect(fees.receiver).to.eq(creator.address);
34 | expect(fees.royaltyAmount).to.eq(0);
35 | });
36 |
37 | it("Sold for small number.", async () => {
38 | const fees = await nft.royaltyInfo(1, 1);
39 | expect(fees.receiver).to.eq(creator.address);
40 | expect(fees.royaltyAmount).to.eq(0);
41 | });
42 |
43 | it("Sold for 999.", async () => {
44 | const fees = await nft.royaltyInfo(1, 999);
45 | expect(fees.receiver).to.eq(creator.address);
46 | expect(fees.royaltyAmount).to.eq(99);
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/collections/NFTCollectionFactory/collectionFactory.ts:
--------------------------------------------------------------------------------
1 | import { expect } from "chai";
2 | import { deployAll, TestContracts } from "../../helpers/deploy";
3 | import { snapshotEach } from "../../helpers/snapshot";
4 |
5 | describe("NFTcollectionFactory / collectionFactory", () => {
6 | let contracts: TestContracts;
7 |
8 | snapshotEach(async () => {
9 | contracts = await deployAll();
10 | });
11 |
12 | it("NFTCollection exposes factory address", async () => {
13 | expect(await contracts.collection.contractFactory()).to.eq(contracts.nftCollectionFactoryV2.address);
14 | });
15 |
16 | it("NFTDropCollection exposes factory", async () => {
17 | expect(await contracts.nftDropCollection.contractFactory()).to.eq(contracts.nftCollectionFactoryV2.address);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/collections/NFTCollectionFactory/initializer.ts:
--------------------------------------------------------------------------------
1 | import { expect } from "chai";
2 | import { deployAll, TestContracts } from "../../helpers/deploy";
3 | import { snapshotEach } from "../../helpers/snapshot";
4 |
5 | describe("NFTCollectionFactory / initializer", () => {
6 | let contracts: TestContracts;
7 |
8 | snapshotEach(async () => {
9 | contracts = await deployAll();
10 | });
11 |
12 | it("Cannot initialize again", async () => {
13 | await expect(contracts.nftCollectionFactoryV2.initialize(100)).to.be.revertedWith(
14 | "Initializable: contract is already initialized",
15 | );
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/test/collections/NFTCollectionFactory/predictNFTDropCollectionAddress.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 | import { expect } from "chai";
3 | import { ContractTransaction } from "ethers";
4 | import { ethers } from "hardhat";
5 | import { getArgsCreateNFTDropCollection } from "../../fixtures/nftCollectionFactory";
6 | import { getNFTDropCollection } from "../../helpers/collectionContract";
7 | import { deployAll, TestContracts } from "../../helpers/deploy";
8 | import { snapshotEach } from "../../helpers/snapshot";
9 |
10 | describe("NFTDropCollection / predictNFTDropCollectionAddress", () => {
11 | let deployer: SignerWithAddress;
12 | let creator: SignerWithAddress;
13 | let creator2: SignerWithAddress;
14 | let contracts: TestContracts;
15 | let tx: ContractTransaction;
16 |
17 | snapshotEach(async () => {
18 | [deployer, creator, creator2] = await ethers.getSigners();
19 | contracts = await deployAll(deployer, creator);
20 | });
21 |
22 | describe("Create with creator / nonce 0", () => {
23 | const nonce = 0;
24 |
25 | beforeEach(async () => {
26 | tx = await contracts.nftCollectionFactoryV2
27 | .connect(creator)
28 | .createNFTDropCollection(...getArgsCreateNFTDropCollection(contracts, { nonce }));
29 | });
30 |
31 | it("Address matches predicted", async () => {
32 | const predicted = await contracts.nftCollectionFactoryV2.predictNFTDropCollectionAddress(creator.address, nonce);
33 | const actual = await getNFTDropCollection(tx);
34 | expect(predicted).to.eq(actual.address);
35 | });
36 | });
37 |
38 | describe("Create with creator / nonce 42", () => {
39 | const nonce = 42;
40 |
41 | beforeEach(async () => {
42 | tx = await contracts.nftCollectionFactoryV2
43 | .connect(creator)
44 | .createNFTDropCollection(...getArgsCreateNFTDropCollection(contracts, { nonce }));
45 | });
46 |
47 | it("Address matches predicted", async () => {
48 | const predicted = await contracts.nftCollectionFactoryV2.predictNFTDropCollectionAddress(creator.address, nonce);
49 | const actual = await getNFTDropCollection(tx);
50 | expect(predicted).to.eq(actual.address);
51 | });
52 | });
53 |
54 | describe("Create with creator2 / nonce max", () => {
55 | const nonce = ethers.constants.MaxUint256;
56 |
57 | beforeEach(async () => {
58 | tx = await contracts.nftCollectionFactoryV2
59 | .connect(creator2)
60 | .createNFTDropCollection(...getArgsCreateNFTDropCollection(contracts, { nonce }));
61 | });
62 |
63 | it("Address matches predicted", async () => {
64 | const predicted = await contracts.nftCollectionFactoryV2.predictNFTDropCollectionAddress(creator2.address, nonce);
65 | const actual = await getNFTDropCollection(tx);
66 | expect(predicted).to.eq(actual.address);
67 | });
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/test/collections/NFTDropCollection/supportsInterfaces.ts:
--------------------------------------------------------------------------------
1 | import { shouldSupportInterfaces } from "../../../src/testBehaviors";
2 | import { deployAll } from "../../helpers/deploy";
3 | import { snapshotEach } from "../../helpers/snapshot";
4 |
5 | describe("NFTDropCollection / supportsInterfaces", () => {
6 | snapshotEach(async function () {
7 | const contracts = await deployAll();
8 | this.nft = contracts.nftDropCollection;
9 | });
10 |
11 | shouldSupportInterfaces(["GetRoyalties", "TokenCreator", "WithSecondarySales", "ERC2981", "NFTDropCollectionMint"]);
12 | });
13 |
--------------------------------------------------------------------------------
/test/collections/mixins/SequentialMintCollectionInitialize.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 | import { expect } from "chai";
3 | import { ethers } from "hardhat";
4 | import { SequentialMintCollectionMock, SequentialMintCollectionMock__factory } from "../../../src/typechain";
5 | import { snapshotEach } from "../../helpers/snapshot";
6 |
7 | describe("Collections / Mixins / SequentialMintCollectionInitialize", () => {
8 | const maxTokenId = 1;
9 |
10 | let mock: SequentialMintCollectionMock;
11 | let deployer: SignerWithAddress;
12 | let creator: SignerWithAddress;
13 |
14 | snapshotEach(async () => {
15 | [deployer, creator] = await ethers.getSigners();
16 | mock = await new SequentialMintCollectionMock__factory(deployer).deploy();
17 | });
18 |
19 | describe("After init", () => {
20 | beforeEach(async () => {
21 | await mock.initializeWithModifier(creator.address, maxTokenId);
22 | });
23 |
24 | it("Cannot init again", async () => {
25 | await expect(mock.initializeWithModifier(creator.address, maxTokenId)).to.be.revertedWith(
26 | "Initializable: contract is already initialized",
27 | );
28 | });
29 | });
30 |
31 | it("Cannot init mixin without top-level modifier", async () => {
32 | await expect(mock.initializeWithoutModifier(creator.address, maxTokenId)).to.be.revertedWith(
33 | "Initializable: contract is not initializing",
34 | );
35 | });
36 |
37 | it("Cannot init with an empty creator", async () => {
38 | await expect(mock.initializeWithModifier(ethers.constants.AddressZero, maxTokenId)).to.be.revertedWith(
39 | "SequentialMintCollection: Creator cannot be the zero address",
40 | );
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/test/feth/marketInteractions.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 | import { expect } from "chai";
3 | import { ContractTransaction } from "ethers";
4 | import { ethers } from "hardhat";
5 | import { FETH, FETHMarketMock, FETHMarketMock__factory } from "../../src/typechain";
6 | import { deployFETH } from "../helpers/deploy";
7 | import { getFethExpectedExpiration } from "../helpers/feth";
8 | import { snapshotEach } from "../helpers/snapshot";
9 |
10 | describe("FETH / marketInteractions", function () {
11 | let feth: FETH;
12 | let mockMarket: FETHMarketMock;
13 | let deployer: SignerWithAddress;
14 | let user: SignerWithAddress;
15 | let rando: SignerWithAddress;
16 | let tx: ContractTransaction;
17 | const amount = ethers.utils.parseEther("1");
18 | let expiry: number;
19 |
20 | snapshotEach(async () => {
21 | [deployer, user, rando] = await ethers.getSigners();
22 | const MarketMock = new FETHMarketMock__factory(deployer);
23 | mockMarket = await MarketMock.deploy();
24 | feth = await deployFETH({ deployer, marketAddress: mockMarket.address, dropMarketAddress: mockMarket.address });
25 | await mockMarket.setFeth(feth.address);
26 | });
27 |
28 | // This demonstrates how you can use the mock to test FETH directly.
29 | // The other calls are available in a similar fashion.
30 | describe("`marketLockupFor`", () => {
31 | beforeEach(async () => {
32 | tx = await mockMarket.connect(user).marketLockupFor(user.address, amount, { value: amount });
33 | expiry = await getFethExpectedExpiration(tx);
34 | });
35 |
36 | it("Emits BalanceLocked", async () => {
37 | await expect(tx).to.emit(feth, "BalanceLocked").withArgs(user.address, expiry, amount, amount);
38 | });
39 |
40 | it("Has no available balance", async () => {
41 | const balance = await feth.balanceOf(user.address);
42 | expect(balance).to.eq(0);
43 | });
44 |
45 | it("Has total balance", async () => {
46 | const balance = await feth.totalBalanceOf(user.address);
47 | expect(balance).to.eq(amount);
48 | });
49 |
50 | it("Has lockup", async () => {
51 | const lockups = await feth.getLockups(user.address);
52 | expect(lockups.amounts[0]).to.eq(amount);
53 | expect(lockups.expiries[0]).to.eq(expiry);
54 | });
55 |
56 | it("Transfers ETH", async () => {
57 | await expect(tx).to.changeEtherBalances([feth, user], [amount, amount.mul(-1)]);
58 | });
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/test/feth/withdraw.ts:
--------------------------------------------------------------------------------
1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2 | import { expect } from "chai";
3 | import { ContractTransaction } from "ethers";
4 | import { ethers } from "hardhat";
5 | import { FETH } from "../../src/typechain";
6 | import { deployAll } from "../helpers/deploy";
7 | import { snapshotEach } from "../helpers/snapshot";
8 |
9 | describe("FETH / withdraw", function () {
10 | let feth: FETH;
11 | let deployer: SignerWithAddress;
12 | let user: SignerWithAddress;
13 | let operator: SignerWithAddress;
14 | let tx: ContractTransaction;
15 | const balance = ethers.utils.parseEther("1");
16 |
17 | snapshotEach(async () => {
18 | [deployer, user, operator] = await ethers.getSigners();
19 | ({ feth } = await deployAll(deployer));
20 | tx = await feth.connect(user).deposit({ value: balance });
21 | });
22 |
23 | describe("`withdrawAvailableBalance`", () => {
24 | beforeEach(async () => {
25 | tx = await feth.connect(user).withdrawAvailableBalance();
26 | });
27 |
28 | it("Emits ETHWithdrawn", async () => {
29 | await expect(tx).to.emit(feth, "ETHWithdrawn").withArgs(user.address, user.address, balance);
30 | });
31 |
32 | it("Transfers ETH", async () => {
33 | await expect(tx).to.changeEtherBalances([feth, user], [balance.mul(-1), balance]);
34 | });
35 |
36 | it("Has no FETH remaining", async () => {
37 | const balanceOf = await feth.balanceOf(user.address);
38 | expect(balanceOf).to.eq(0);
39 | });
40 |
41 | it("Cannot withdraw again", async () => {
42 | await expect(feth.connect(user).withdrawAvailableBalance()).to.be.revertedWithCustomError(
43 | feth,
44 | "FETH_No_Funds_To_Withdraw",
45 | );
46 | });
47 | });
48 |
49 | describe("`withdrawFrom`", () => {
50 | const amount = ethers.utils.parseEther(".25");
51 |
52 | beforeEach(async () => {
53 | // The operator requires approval
54 | await feth.connect(user).approve(operator.address, amount);
55 | tx = await feth.connect(operator).withdrawFrom(user.address, operator.address, amount);
56 | });
57 |
58 | it("Emits ETHWithdrawn", async () => {
59 | await expect(tx).to.emit(feth, "ETHWithdrawn").withArgs(user.address, operator.address, amount);
60 | });
61 |
62 | it("Transfers ETH", async () => {
63 | await expect(tx).to.changeEtherBalances([feth, operator], [amount.mul(-1), amount]);
64 | });
65 |
66 | it("Has less FETH available", async () => {
67 | const balanceOf = await feth.balanceOf(user.address);
68 | expect(balanceOf).to.eq(balance.sub(amount));
69 | });
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/test/foundry/BytesLibrary.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "../../contracts/libraries/BytesLibrary.sol";
6 |
7 | contract TestBytesLibrary {
8 | using BytesLibrary for bytes;
9 |
10 | function testStartsWithPrefix() public pure {
11 | bytes memory value = "test1234";
12 |
13 | assert(value.startsWith("test"));
14 |
15 | assert(!value.startsWith(" tes"));
16 | assert(!value.startsWith("1234"));
17 |
18 | // Checks if starts with bytes4
19 | assert(!value.startsWith(""));
20 | assert(!value.startsWith("t"));
21 | }
22 |
23 | function testStartsWithFullString() public pure {
24 | bytes memory value = "test";
25 |
26 | assert(value.startsWith("test"));
27 |
28 | assert(!value.startsWith(" tes"));
29 | assert(!value.startsWith("1234"));
30 |
31 | // Checks if starts with bytes4
32 | assert(!value.startsWith(""));
33 | assert(!value.startsWith("t"));
34 | }
35 |
36 | function testStartsWithTooShort() public pure {
37 | bytes memory value = "tes";
38 |
39 | assert(!value.startsWith("tes"));
40 | assert(!value.startsWith(" tes"));
41 | assert(!value.startsWith(""));
42 | assert(!value.startsWith("t"));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test/foundry/FixedPriceDrop.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | import "forge-std/Test.sol";
6 |
7 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
8 |
9 | import "../../contracts/NFTDropMarket.sol";
10 | import "../../contracts/FETH.sol";
11 | import "../../contracts/NFTDropCollection.sol";
12 | import "../../contracts/NFTCollectionFactory.sol";
13 | import "../../contracts/mocks/MockTreasury.sol";
14 | import "../../contracts/mocks/RoyaltyRegistry/MockRoyaltyRegistry.sol";
15 |
16 | contract TestFixedPriceDrop is Test {
17 | address admin = address(99);
18 | address creator = address(1);
19 | address collector = address(2);
20 |
21 | MockTreasury treasury;
22 | NFTDropMarket nftDropMarket;
23 | FETH feth;
24 | MockRoyaltyRegistry royaltyRegistry;
25 | NFTCollectionFactory nftCollectionFactory;
26 |
27 | function setUp() public {
28 | /** Pre-reqs **/
29 | treasury = new MockTreasury();
30 |
31 | /** Deploy Collection Factory **/
32 | nftCollectionFactory = new NFTCollectionFactory(address(treasury));
33 | nftCollectionFactory.initialize(2);
34 | NFTDropCollection nftDropCollectionTemplate = new NFTDropCollection(address(nftCollectionFactory));
35 | nftCollectionFactory.adminUpdateNFTDropCollectionImplementation(address(nftDropCollectionTemplate));
36 |
37 | /** Deploy Market **/
38 | // Deploy the proxy with a placeholder implementation.
39 | TransparentUpgradeableProxy dropMarketProxy = new TransparentUpgradeableProxy(address(treasury), admin, "");
40 | feth = new FETH(payable(dropMarketProxy), payable(dropMarketProxy), 24 hours);
41 | royaltyRegistry = new MockRoyaltyRegistry();
42 |
43 | NFTDropMarket dropMarketImplementation = new NFTDropMarket(
44 | payable(treasury),
45 | address(feth),
46 | address(royaltyRegistry)
47 | );
48 | vm.prank(admin);
49 | dropMarketProxy.upgradeTo(address(dropMarketImplementation));
50 | nftDropMarket = NFTDropMarket(payable(dropMarketProxy));
51 | nftDropMarket.initialize();
52 | }
53 |
54 | function testHappyCase() public {
55 | /** Create drop collection **/
56 | uint256 nonce = 42;
57 | uint32 maxTokenId = 100;
58 | vm.prank(creator);
59 | nftCollectionFactory.createNFTDropCollection(
60 | "Name",
61 | "SYM",
62 | "ipfs://sample",
63 | 0x0,
64 | maxTokenId,
65 | address(nftDropMarket),
66 | nonce
67 | );
68 | NFTDropCollection nftDropCollection = NFTDropCollection(
69 | nftCollectionFactory.predictNFTDropCollectionAddress(creator, nonce)
70 | );
71 |
72 | /** List for sale **/
73 | uint80 price = 0.5 ether;
74 | uint16 limitPerAccount = 10;
75 | vm.prank(creator);
76 | nftDropMarket.createFixedPriceSale(address(nftDropCollection), price, limitPerAccount);
77 |
78 | /** Mint from sale **/
79 | uint16 count = 3;
80 | vm.deal(collector, 999 ether);
81 | vm.prank(collector);
82 | nftDropMarket.mintFromFixedPriceSale{ value: price * count }(address(nftDropCollection), count, payable(0));
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/test/helpers/collectionContract.ts:
--------------------------------------------------------------------------------
1 | import { Provider } from "@ethersproject/abstract-provider";
2 | import { ContractTransaction, Signer } from "ethers";
3 | import { ethers } from "hardhat";
4 | import {
5 | NFTCollection,
6 | NFTCollection__factory,
7 | NFTCollectionFactory__factory,
8 | NFTDropCollection,
9 | NFTDropCollection__factory,
10 | } from "../../src/typechain";
11 | import { NFTDropCollectionCreatedEvent } from "../../src/typechain/NFTCollectionFactory";
12 |
13 | export async function getNFTCollection(
14 | tx: ContractTransaction,
15 | signerOrProvider?: Signer | Provider,
16 | ): Promise {
17 | const receipt = await tx.wait();
18 | const factoryInterface = NFTCollectionFactory__factory.createInterface();
19 | const log = receipt.logs.find(
20 | log =>
21 | log.topics[0] ==
22 | factoryInterface.getEventTopic(
23 | factoryInterface.events["NFTCollectionCreated(address,address,uint256,string,string,uint256)"],
24 | ),
25 | );
26 | if (!log) {
27 | throw new Error("No CollectionCreated event found");
28 | }
29 | const event = factoryInterface.parseLog(log);
30 | const address = event.args.collection;
31 | return NFTCollection__factory.connect(address, signerOrProvider ?? ethers.provider);
32 | }
33 |
34 | export async function getNFTDropCollection(
35 | tx: ContractTransaction,
36 | signerOrProvider?: Signer | Provider,
37 | ): Promise {
38 | const receipt = await tx.wait();
39 | const event = receipt.events?.find(e => e.event === "NFTDropCollectionCreated") as NFTDropCollectionCreatedEvent;
40 | const address = event.args.collection;
41 | return NFTDropCollection__factory.connect(address, signerOrProvider ?? ethers.provider);
42 | }
43 |
--------------------------------------------------------------------------------
/test/helpers/constants.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber, ethers } from "ethers";
2 |
3 | export const BASIS_POINTS = BigNumber.from(10000);
4 | export const ONE_HOUR = 60 * 60;
5 | export const ONE_DAY = 24 * ONE_HOUR;
6 | export const ONE_ETH = ethers.utils.parseEther("1");
7 |
--------------------------------------------------------------------------------
/test/helpers/feth.ts:
--------------------------------------------------------------------------------
1 | import { ContractTransaction } from "ethers";
2 | import { ONE_DAY, ONE_HOUR } from "./constants";
3 | import { getBlockTime } from "./time";
4 |
5 | export async function getFethExpectedExpiration(tx: ContractTransaction): Promise {
6 | const receipt = await tx.wait();
7 | const timestamp = await getBlockTime(receipt.blockNumber);
8 | return getFethExpirationFromSeconds(timestamp);
9 | }
10 |
11 | export function getFethExpirationFromSeconds(timestampInSeconds: number): number {
12 | return Math.ceil(timestampInSeconds / ONE_HOUR) * ONE_HOUR + ONE_DAY;
13 | }
14 |
--------------------------------------------------------------------------------
/test/helpers/logs.ts:
--------------------------------------------------------------------------------
1 | import { expect } from "chai";
2 | import { Contract, ContractTransaction } from "ethers";
3 |
4 | export type EventLog = {
5 | contract: Contract;
6 | eventName: string;
7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
8 | args: any[];
9 | };
10 |
11 | /**
12 | * Checks the tx for the exact logs specified including order, count, and args.
13 | */
14 | export async function assertAllLogs(
15 | tx: ContractTransaction | Promise,
16 | logs: EventLog[],
17 | ): Promise {
18 | if (tx instanceof Promise) {
19 | tx = await tx;
20 | }
21 | const receipt = await tx.wait();
22 | expect(receipt.logs.length).to.eq(
23 | logs.length,
24 | `Log count mismatch. Expected ${logs.length} logs, got ${receipt.logs.length}: ${JSON.stringify(receipt.logs)}`,
25 | );
26 | for (let i = 0; i < logs.length; i++) {
27 | const emittedLog = receipt.logs[i];
28 | const expectedLog = logs[i];
29 | expect(emittedLog.address).to.eq(
30 | expectedLog.contract.address,
31 | `Log address mismatch at index ${i}; expected ${expectedLog.contract.address}, got ${emittedLog.address}`,
32 | );
33 | // Throws if the event is not found
34 | const fragment = expectedLog.contract.interface.getEvent(expectedLog.eventName);
35 | const topic0 = expectedLog.contract.interface.getEventTopic(fragment);
36 | // Confirms order by event name
37 | expect(emittedLog.topics[0]).to.eq(
38 | topic0,
39 | `Topic 0 mismatch at index ${i}. Expected ${expectedLog.eventName} (${topic0}), got ${emittedLog.topics[0]}`,
40 | );
41 |
42 | // For simplicity, check for args with the chai matcher
43 | // -- this means we do not validate the order args emit when the same event name is emitted multiple times
44 | await expect(tx)
45 | .to.emit(expectedLog.contract, expectedLog.eventName)
46 | .withArgs(...expectedLog.args);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/test/helpers/mint.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber, ContractTransaction } from "ethers";
2 |
3 | export async function getMintedTokenId(transaction: ContractTransaction): Promise {
4 | const receipt = await transaction.wait();
5 | const log = receipt.events?.find(e => e.event === "Minted");
6 | if (!log?.args) {
7 | throw new Error("No `Minted` event detected");
8 | }
9 | return log.args[1];
10 | }
11 |
--------------------------------------------------------------------------------
/test/helpers/snapshot.ts:
--------------------------------------------------------------------------------
1 | import { providers } from "ethers";
2 | import { ethers } from "hardhat";
3 | import { Context } from "mocha";
4 |
5 | export function snapshotEach(funcBeforeSnapshot: (this: Context) => Promise): void {
6 | const provider: providers.JsonRpcProvider = ethers.provider;
7 | let snapshotId: string;
8 |
9 | before(async function () {
10 | await funcBeforeSnapshot.call(this);
11 | snapshotId = await provider.send("evm_snapshot", []);
12 | });
13 |
14 | beforeEach(async function () {
15 | await provider.send("evm_revert", [snapshotId]);
16 | snapshotId = await provider.send("evm_snapshot", []);
17 | });
18 |
19 | after(async function () {
20 | // Clean up state when tests finish
21 | await provider.send("evm_revert", [snapshotId]);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/test/helpers/splits.ts:
--------------------------------------------------------------------------------
1 | import { BigNumberish, ContractTransaction, Signer } from "ethers";
2 | import { PercentSplitETH, PercentSplitETH__factory } from "../../src/typechain";
3 | import { Provider } from "@ethersproject/providers";
4 | import { ethers } from "hardhat";
5 |
6 | export type Royalty = { recipient: string; percentInBasisPoints: BigNumberish };
7 |
8 | export async function getSplitInstance(
9 | transaction: ContractTransaction,
10 | signerOrProvider: Signer | Provider,
11 | royalties?: Royalty[],
12 | ): Promise {
13 | const receipt = await transaction.wait();
14 | const log = receipt.events?.find(e => e.event === "PercentSplitCreated");
15 | let splitAddress = log?.args?.contractAddress;
16 | if (!splitAddress) {
17 | if (transaction.to && royalties) {
18 | const splitFactory = PercentSplitETH__factory.connect(transaction.to, signerOrProvider);
19 | splitAddress = await splitFactory.getPredictedSplitAddress(royalties);
20 | if (splitAddress) {
21 | return PercentSplitETH__factory.connect(splitAddress, signerOrProvider);
22 | }
23 | }
24 | throw new Error("Split address not found");
25 | }
26 | return PercentSplitETH__factory.connect(splitAddress, signerOrProvider);
27 | }
28 |
29 | export async function getExpectedPercentSplit(
30 | contracts: { percentSplitFactory: PercentSplitETH },
31 | shares: Royalty[],
32 | signerOrProvider?: Signer | Provider,
33 | ): Promise {
34 | const expectedAddress = await contracts.percentSplitFactory.getPredictedSplitAddress(shares);
35 | return PercentSplitETH__factory.connect(expectedAddress, signerOrProvider ?? ethers.provider);
36 | }
37 |
38 | export async function getSplitShares(address: string): Promise {
39 | const split = PercentSplitETH__factory.connect(address, ethers.provider);
40 | try {
41 | const shares = await split.getShares();
42 | if (shares.length === 0) {
43 | return undefined;
44 | }
45 | return shares.map(s => ({ recipient: s.recipient, percentInBasisPoints: s.percentInBasisPoints }));
46 | } catch {
47 | // Not a valid split contract
48 | return undefined;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/test/helpers/time.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber, providers } from "ethers";
2 | import { ethers } from "hardhat";
3 | import { ONE_HOUR } from "./constants";
4 |
5 | // As of 0.5.9 the OZ test helper for time does not work with Hardhat
6 | export async function increaseTime(seconds: number): Promise {
7 | const provider: providers.JsonRpcProvider = ethers.provider;
8 | await provider.send("evm_increaseTime", [seconds]);
9 | await advanceBlock();
10 | }
11 |
12 | export async function increaseTimeTo(timestamp: number | BigNumber, shouldAdvanceBlock = true): Promise {
13 | timestamp = timestamp instanceof BigNumber ? timestamp.toNumber() : timestamp;
14 | const provider: providers.JsonRpcProvider = ethers.provider;
15 | await provider.send("evm_setNextBlockTimestamp", [timestamp]);
16 | if (shouldAdvanceBlock) {
17 | await advanceBlock();
18 | }
19 | }
20 |
21 | export async function increaseTimeToNextHour(): Promise {
22 | const time = await getBlockTime();
23 | const timeToMoveTo = Math.ceil(time / ONE_HOUR) * ONE_HOUR;
24 | await increaseTimeTo(timeToMoveTo);
25 | }
26 |
27 | export async function getBlockTime(block: string | number = "latest"): Promise {
28 | const provider: providers.JsonRpcProvider = ethers.provider;
29 | return (await provider.getBlock(block)).timestamp;
30 | }
31 |
32 | export async function advanceBlock() {
33 | const provider: providers.JsonRpcProvider = ethers.provider;
34 | await provider.send("evm_mine", []);
35 | }
36 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true,
4 | "forceConsistentCasingInFileNames": true,
5 | "lib": ["es5", "es6"],
6 | "jsx": "react",
7 | "module": "commonjs",
8 | "moduleResolution": "node",
9 | "noImplicitAny": true,
10 | "outDir": "dist",
11 | "resolveJsonModule": true,
12 | "sourceMap": true,
13 | "strict": true,
14 | "target": "es5"
15 | },
16 | "exclude": ["artifacts/**/*", "node_modules"],
17 | "include": ["hardhat*.config.ts", "test/**/*", "src/**/*", "test-gas-stories/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------