├── .prettierignore ├── .gitignore ├── contracts ├── IOperatorFilter.sol ├── ITokenUriDelegate.sol ├── test │ ├── Nonpayable.sol │ ├── Clock.sol │ ├── TransferProxy.sol │ ├── TestSplitClaim.sol │ ├── InefficientSmartWallet.sol │ ├── TestTokenUriDelegate.sol │ ├── TestWeth.sol │ └── TestERC20.sol ├── IWeth.sol ├── IManifold.sol ├── TokenUriDelegate.sol ├── ERC721TokenUriDelegate.sol ├── ShardwalletFactory.sol ├── ERC721OperatorFilter.sol ├── BlacklistOperatorFilter.sol ├── SeedMarket.sol ├── QQL.sol ├── Shardwallet.sol └── MintPass.sol ├── scripts ├── deployShardwalletFactory.js ├── deployMintPass.js ├── sample-script.js ├── diff-gas ├── deploy.js └── gas.js ├── SECURITY.md ├── package.json ├── test ├── TokenUriDelegate.test.js ├── tokenUriDelegate.js ├── ShardwalletFactory.test.js ├── BlacklistOperatorFilter.test.js ├── operatorFilter.js ├── QQL.test.js ├── SeedMarket.test.js └── Shardwallet.test.js ├── README.md ├── hardhat.config.js └── LICENSE /.prettierignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | 3 | # Hardhat files 4 | /cache/ 5 | /artifacts/ 6 | /coverage/ 7 | /coverage.json 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | -------------------------------------------------------------------------------- /contracts/IOperatorFilter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.8; 3 | 4 | interface IOperatorFilter { 5 | function mayTransfer(address operator) external view returns (bool); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/ITokenUriDelegate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.8; 3 | 4 | interface ITokenUriDelegate { 5 | function tokenURI(uint256 tokenId) external view returns (string memory); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/test/Nonpayable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract Nonpayable { 5 | receive() external payable { 6 | revert("Nonpayable: revert!"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /contracts/test/Clock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | pragma solidity ^0.8.0; 3 | 4 | contract Clock { 5 | function timestamp() public view returns (uint256) { 6 | return block.timestamp; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scripts/deployShardwalletFactory.js: -------------------------------------------------------------------------------- 1 | const deploy = require("./deploy"); 2 | 3 | async function main() { 4 | deploy("ShardwalletFactory"); 5 | } 6 | 7 | main().catch((e) => { 8 | console.error(e); 9 | process.exitCode = 1; 10 | }); 11 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | Found a security issue with these contracts? 2 | Please email us: . 3 | We'll get back to you as soon as we can: at time of writing, our median time-to-response is 13 minutes during business hours, defined as "when I am awake". 4 | -------------------------------------------------------------------------------- /contracts/IWeth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IWeth is IERC20 { 7 | function deposit() external payable; 8 | 9 | function withdraw(uint256) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/test/TransferProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.8; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 | 6 | contract TransferProxy { 7 | function transferFrom( 8 | IERC721 tokenContract, 9 | address from, 10 | address to, 11 | uint256 tokenId 12 | ) external { 13 | if (from != msg.sender) revert("TransferProxy: unauthorized"); 14 | tokenContract.transferFrom(from, to, tokenId); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scripts/deployMintPass.js: -------------------------------------------------------------------------------- 1 | const deploy = require("./deploy"); 2 | 3 | async function main() { 4 | const args = process.argv.slice(2); 5 | if (args.length !== 1) { 6 | throw new Error("usage: deployMintPass "); 7 | } 8 | const [rawMaxCreated] = args; 9 | const maxCreated = Number.parseInt(rawMaxCreated, 10); 10 | if (String(maxCreated) !== rawMaxCreated) { 11 | throw new Error("bad maxCreated: " + rawMaxCreated); 12 | } 13 | await deploy("MintPass", maxCreated); 14 | } 15 | 16 | main().catch((e) => { 17 | console.error(e); 18 | process.exitCode = 1; 19 | }); 20 | -------------------------------------------------------------------------------- /contracts/test/TestSplitClaim.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.8; 3 | 4 | import "../Shardwallet.sol"; 5 | 6 | contract TestSplitClaim is Shardwallet { 7 | function splitClaimBatch(uint256 amount, uint24[] memory shareMicros) 8 | external 9 | pure 10 | returns (uint256[] memory) 11 | { 12 | uint256[] memory result = new uint256[](shareMicros.length); 13 | for (uint256 i = 0; i < shareMicros.length; i++) { 14 | result[i] = splitClaim(amount, shareMicros, i); 15 | } 16 | return result; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/IManifold.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @author: manifold.xyz 6 | 7 | /** 8 | * @dev Royalty interface for creator core classes 9 | */ 10 | interface IManifold { 11 | /** 12 | * @dev Get royalites of a token. Returns list of receivers and basisPoints 13 | * 14 | * bytes4(keccak256('getRoyalties(uint256)')) == 0xbb3bafd6 15 | * 16 | * => 0xbb3bafd6 = 0xbb3bafd6 17 | */ 18 | function getRoyalties(uint256 tokenId) 19 | external 20 | view 21 | returns (address payable[] memory recipients, uint256[] memory bps); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/TokenUriDelegate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/utils/Strings.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | import "./ITokenUriDelegate.sol"; 8 | 9 | contract TokenUriDelegate is ITokenUriDelegate, Ownable { 10 | string baseURI_; 11 | 12 | function setBaseURI(string memory baseURI) external onlyOwner { 13 | baseURI_ = baseURI; 14 | } 15 | 16 | function tokenURI(uint256 tokenId) external view returns (string memory) { 17 | return string(abi.encodePacked(baseURI_, Strings.toString(tokenId))); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/test/InefficientSmartWallet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract InefficientSmartWallet { 5 | address owner_; 6 | uint256 deposits_; 7 | 8 | constructor() { 9 | owner_ = msg.sender; 10 | } 11 | 12 | receive() external payable { 13 | deposits_++; 14 | for (uint256 i = 0; i < 256; i++) {} // burn a bit more gas, for fun 15 | } 16 | 17 | function withdraw() external payable { 18 | if (msg.sender != owner_) 19 | revert("InefficientSmartWallet: unauthorized"); 20 | payable(msg.sender).transfer(address(this).balance); 21 | } 22 | 23 | function deposits() external view returns (uint256) { 24 | return deposits_; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/test/TestTokenUriDelegate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/utils/Strings.sol"; 5 | 6 | import "../ITokenUriDelegate.sol"; 7 | 8 | contract TestTokenUriDelegate is ITokenUriDelegate { 9 | function tokenURI(uint256 tokenId) external view returns (string memory) { 10 | string memory sTokenContract = Strings.toHexString( 11 | uint256(uint160(msg.sender)), 12 | 20 13 | ); 14 | string memory sTokenId = Strings.toString(tokenId); 15 | return 16 | string( 17 | bytes.concat( 18 | "data:text/plain,", 19 | bytes(sTokenContract), // includes "0x" prefix 20 | "%20%23", // space, number sign 21 | bytes(sTokenId) 22 | ) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qql-contracts", 3 | "scripts": { 4 | "test": "hardhat test && node ./scripts/gas.js", 5 | "lint": "npm run prettier:check", 6 | "fix": "npm run prettier:write", 7 | "prettier": "prettier '**/*.json' '**/*.js' '**/*.sol'", 8 | "prettier:check": "npm run prettier -- --check", 9 | "prettier:write": "npm run prettier -- --write" 10 | }, 11 | "devDependencies": { 12 | "@nomiclabs/hardhat-ethers": "^2.0.5", 13 | "@nomiclabs/hardhat-etherscan": "^3.1.0", 14 | "@nomiclabs/hardhat-waffle": "^2.0.3", 15 | "chai": "^4.3.6", 16 | "dotenv": "^16.0.1", 17 | "ethereum-waffle": "^3.4.4", 18 | "ethers": "^5.6.5", 19 | "hardhat": "^2.10.1", 20 | "prettier": "^2.6.2", 21 | "prettier-plugin-solidity": "^1.0.0-beta.19", 22 | "solhint": "^3.3.7", 23 | "solhint-plugin-prettier": "^0.0.5", 24 | "solidity-coverage": "^0.7.21" 25 | }, 26 | "dependencies": { 27 | "@openzeppelin/contracts": "^4.6.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/TokenUriDelegate.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | 3 | describe("TokenUriDelegate", () => { 4 | before(async () => { 5 | TokenUriDelegate = await ethers.getContractFactory("TokenUriDelegate"); 6 | }); 7 | 8 | it("gives a coherent response when unset", async () => { 9 | const tud = await TokenUriDelegate.deploy(); 10 | expect(await tud.tokenURI(1)).to.equal("1"); 11 | }); 12 | 13 | it("allows setting the baseURI", async () => { 14 | const tud = await TokenUriDelegate.deploy(); 15 | await tud.setBaseURI("https://token.qql.art/mintpass/"); 16 | expect(await tud.tokenURI(1)).to.equal("https://token.qql.art/mintpass/1"); 17 | }); 18 | 19 | it("only owner can set the baseURI", async () => { 20 | const signers = await ethers.getSigners(); 21 | const tud = await TokenUriDelegate.deploy(); 22 | const fail = tud.connect(signers[1]).setBaseURI("1800-scams-dot-com"); 23 | await expect(fail).to.be.revertedWith("not the owner"); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /contracts/ERC721TokenUriDelegate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 6 | 7 | import "./ITokenUriDelegate.sol"; 8 | 9 | abstract contract ERC721TokenUriDelegate is ERC721, Ownable { 10 | ITokenUriDelegate private tokenUriDelegate_; 11 | 12 | function setTokenUriDelegate(ITokenUriDelegate delegate) public onlyOwner { 13 | tokenUriDelegate_ = delegate; 14 | } 15 | 16 | function tokenUriDelegate() public view returns (ITokenUriDelegate) { 17 | return tokenUriDelegate_; 18 | } 19 | 20 | function tokenURI(uint256 tokenId) 21 | public 22 | view 23 | virtual 24 | override 25 | returns (string memory) 26 | { 27 | if (!_exists(tokenId)) revert("ERC721: invalid token ID"); 28 | ITokenUriDelegate delegate = tokenUriDelegate_; 29 | if (address(delegate) == address(0)) return ""; 30 | return delegate.tokenURI(tokenId); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QQL Contracts 2 | 3 | Contracts for QQL. 4 | For testing: run `npm install` then `npm t`. 5 | 6 | Some of these contracts, but not all of them, are released under the 7 | MIT License. See `LICENSE` for details. 8 | 9 | ## Deployed contracts 10 | 11 | The following key contracts have been deployed to mainnet: 12 | 13 | - `QQL`: [0x845dD2a7eE2a92A0518AB2135365Ed63fdbA0C88][es-qql] (`qql.eth`) from commit 4d6957d4a6e2 14 | - `MintPass`: [0xc73B17179Bf0C59cD5860Bb25247D1D1092c1088][es-mp] (`mintpass.qql.eth`) from commit 4d6957d4a6e2 15 | - `ShardwalletFactory`: [0x4e8cA75b218Ebe20Bf499a1FCEcfb444f22Db518][es-swf] from commit 28904747a644 16 | 17 | [es-qql]: https://etherscan.io/address/0x845dD2a7eE2a92A0518AB2135365Ed63fdbA0C88#code 18 | [es-mp]: https://etherscan.io/address/0xc73B17179Bf0C59cD5860Bb25247D1D1092c1088#code 19 | [es-swf]: https://etherscan.io/address/0x4e8cA75b218Ebe20Bf499a1FCEcfb444f22Db518#code 20 | 21 | ## Security 22 | 23 | Found a security issue with these contracts? 24 | Please email us: . 25 | We'll get back to you as soon as we can: at time of writing, our median time-to-response is 13 minutes during business hours, defined as "when I am awake". 26 | -------------------------------------------------------------------------------- /scripts/sample-script.js: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node