├── merkle-distribution ├── example.json ├── generate-merkle-root.ts ├── balance-tree.ts ├── parse-balance-map.ts └── merkle-tree.ts ├── .gitignore ├── tsconfig.json ├── networks └── rinkeby.json ├── test ├── config │ └── constants.ts ├── MirrorENSRegistrar.spec.ts ├── setup │ └── index.ts ├── MirrorBatchRegistration.spec.ts ├── PublicationRoles.spec.ts ├── WriteDistributionHelperV1.spec.ts └── MirrorWriteToken.spec.ts ├── contracts ├── ens │ ├── interfaces │ │ ├── IMirrorENSRegistrar.sol │ │ ├── IENSResolver.sol │ │ └── IENS.sol │ ├── MirrorENSResolver.sol │ ├── MirrorENSRegistrar.sol │ └── lib │ │ └── ENSRegistry.sol ├── lib │ └── SafeMath.sol ├── interfaces │ └── IMirrorWriteToken.sol ├── helpers │ ├── MirrorBatchRegistration.sol │ └── WriteDistributionHelperV1.sol └── PublicationRoles.sol ├── README.md ├── ts-types └── contracts │ ├── factories │ ├── IENS__factory.ts │ ├── Resolver__factory.ts │ ├── IMirrorPublicationFactoryV1__factory.ts │ ├── Ownable__factory.ts │ ├── IMirrorENSRegistrar__factory.ts │ ├── IENSReverseRegistrar__factory.ts │ ├── IENSResolver__factory.ts │ ├── IMirrorPublicationV1__factory.ts │ ├── IERC20__factory.ts │ ├── MirrorBatchRegistration__factory.ts │ ├── ERC20Burnable__factory.ts │ ├── IMirrorWriteToken__factory.ts │ ├── ENS__factory.ts │ ├── PublicationRoles__factory.ts │ ├── MirrorENSReverseRegistrar__factory.ts │ └── MirrorENSResolver__factory.ts │ ├── index.ts │ ├── IENS.d.ts │ ├── Resolver.d.ts │ ├── MirrorBatchRegistration.d.ts │ ├── IMirrorPublicationFactoryV1.d.ts │ ├── Ownable.d.ts │ ├── IENSReverseRegistrar.d.ts │ ├── IENSResolver.d.ts │ ├── IMirrorENSRegistrar.d.ts │ └── MirrorENSReverseRegistrar.d.ts ├── hardhat.config.ts ├── package.json ├── flattened └── PublicationRoles.sol └── scripts ├── deploy-registrar.ts └── deploy.ts /merkle-distribution/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "0x90F79bf6EB2c4f870365E785982E1f101E93b906": "100" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | outputs/ 3 | Reports/ 4 | coverage/ 5 | cache/ 6 | artifacts/ 7 | build/ 8 | .env 9 | *.DS_Store 10 | env* -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "strict": false, 6 | "esModuleInterop": true, 7 | "resolveJsonModule": true 8 | } 9 | } -------------------------------------------------------------------------------- /networks/rinkeby.json: -------------------------------------------------------------------------------- 1 | { 2 | "Contracts": { 3 | "MirrorWriteToken": "0x2B50248F70bfad9E2b50113dE48eFaae18048F32", 4 | "MirrorENSResolver": "0x03dF40c273B74e837eE56275d3bcC9E6bD4b79BA", 5 | "MirrorENSRegistrar": "0x2B117eFF13A159e059E206cE144F9E01F46a54CD" 6 | } 7 | } -------------------------------------------------------------------------------- /test/config/constants.ts: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | 3 | export const ZERO_BYTES32 = ethers.constants.HashZero; 4 | export const ROOT = "xyz"; 5 | export const subnameWallet = "mirror"; 6 | export const ENS_REGISTRY_ADDRESS = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"; 7 | export const ROOT_NAME = 'mirror.xyz'; 8 | export const ROOT_NODE = ethers.utils.namehash(ROOT_NAME); 9 | -------------------------------------------------------------------------------- /contracts/ens/interfaces/IMirrorENSRegistrar.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | 4 | interface IMirrorENSRegistrar { 5 | function changeRootNodeOwner(address newOwner_) external; 6 | 7 | function register(string calldata label_, address owner_) external; 8 | 9 | function labelOwner(string calldata label) external view returns (address); 10 | 11 | function changeLabelOwner(string calldata label_, address newOwner_) 12 | external; 13 | } 14 | -------------------------------------------------------------------------------- /contracts/ens/interfaces/IENSResolver.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | 4 | interface IENSResolver { 5 | event AddrChanged(bytes32 indexed _node, address _addr); 6 | event NameChanged(bytes32 indexed _node, string _name); 7 | 8 | function addr(bytes32 _node) external view returns (address); 9 | function setAddr(bytes32 _node, address _addr) external; 10 | function name(bytes32 _node) external view returns (string memory); 11 | function setName(bytes32 _node, string calldata _name) external; 12 | } 13 | -------------------------------------------------------------------------------- /merkle-distribution/generate-merkle-root.ts: -------------------------------------------------------------------------------- 1 | import { program } from 'commander' 2 | import fs from 'fs' 3 | import { parseBalanceMap } from './parse-balance-map' 4 | 5 | program 6 | .version('0.0.0') 7 | .option( 8 | '-i, --input ', 9 | 'input JSON file location containing a map of account addresses to string balances' 10 | ) 11 | 12 | program.parse(process.argv) 13 | 14 | const json = JSON.parse(fs.readFileSync(program.opts().input, { encoding: 'utf8' })) 15 | 16 | if (typeof json !== 'object') throw new Error('Invalid JSON') 17 | 18 | console.log(JSON.stringify(parseBalanceMap(json))) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mirror $WRITE Token 2 | 3 | > Mirror is the next big change in the long history of symbolic communication. Through a decentralized, user-owned, crypto-based network, Mirror’s publishing platform revolutionizes the way we express, share and monetize our thoughts. 4 | 5 | 6 | ## Summary 7 | 8 | Includes contracts pertaining to Mirror onboarding. 9 | 10 | ## Contract Deployment Addresses and Verified Source Code 11 | 12 | 13 | * [Write Token ERC20 Contract](https://etherscan.io/address/0x622236bb180256b6ae1a935dae08dc0356141632#code) 14 | 15 | * [Mirror ENS Registrar](https://etherscan.io/address/0x0b53D523912593C18dD7C22AFd2c448BB48e1bf8#code) 16 | -------------------------------------------------------------------------------- /contracts/lib/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.8; 2 | 3 | // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math) 4 | 5 | library SafeMath { 6 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 7 | require((z = x + y) >= x, "ds-math-add-overflow"); 8 | } 9 | 10 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 11 | require((z = x - y) <= x, "ds-math-sub-underflow"); 12 | } 13 | 14 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 15 | require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/IENS__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { IENS } from "../IENS"; 9 | 10 | export class IENS__factory { 11 | static connect(address: string, signerOrProvider: Signer | Provider): IENS { 12 | return new Contract(address, _abi, signerOrProvider) as IENS; 13 | } 14 | } 15 | 16 | const _abi = [ 17 | { 18 | inputs: [ 19 | { 20 | internalType: "bytes32", 21 | name: "_node", 22 | type: "bytes32", 23 | }, 24 | ], 25 | name: "owner", 26 | outputs: [ 27 | { 28 | internalType: "address", 29 | name: "", 30 | type: "address", 31 | }, 32 | ], 33 | stateMutability: "view", 34 | type: "function", 35 | }, 36 | ]; 37 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/Resolver__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { Resolver } from "../Resolver"; 9 | 10 | export class Resolver__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): Resolver { 15 | return new Contract(address, _abi, signerOrProvider) as Resolver; 16 | } 17 | } 18 | 19 | const _abi = [ 20 | { 21 | inputs: [ 22 | { 23 | internalType: "bytes32", 24 | name: "node", 25 | type: "bytes32", 26 | }, 27 | { 28 | internalType: "string", 29 | name: "name", 30 | type: "string", 31 | }, 32 | ], 33 | name: "setName", 34 | outputs: [], 35 | stateMutability: "nonpayable", 36 | type: "function", 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from 'hardhat/config'; 2 | import '@nomiclabs/hardhat-ethers'; 3 | import '@nomiclabs/hardhat-waffle'; 4 | import '@nomiclabs/hardhat-solhint'; 5 | import 'hardhat-typechain'; 6 | 7 | const { alchemyAPIKey, deployerPrivateKey } = require('./env.json'); 8 | 9 | 10 | const config: HardhatUserConfig = { 11 | defaultNetwork: 'hardhat', 12 | solidity: { 13 | version: '0.6.8', 14 | settings: { 15 | optimizer: { 16 | enabled: true, 17 | runs: 2000 18 | } 19 | } 20 | }, 21 | typechain: { 22 | outDir: 'ts-types/contracts', 23 | target: 'ethers-v5' 24 | }, 25 | networks: { 26 | rinkeby: { 27 | url: `http://eth-rinkeby.alchemyapi.io/v2/${alchemyAPIKey}`, 28 | accounts: [deployerPrivateKey], 29 | }, 30 | mainnet: { 31 | url: `https://eth-mainnet.alchemyapi.io/v2/${alchemyAPIKey}`, 32 | accounts: [deployerPrivateKey], 33 | }, 34 | } 35 | }; 36 | 37 | export default config; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mirror-protocol-v1", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": "git@github.com:mirror-xyz/ethereum-protocol.git", 6 | "author": "", 7 | "license": "MIT", 8 | "scripts": { 9 | "test": "hardhat test", 10 | "flatten": "npx hardhat flatten contracts/MirrorWriteToken.sol > flattened/MirrorWriteToken.sol", 11 | "fix:flatten": "sed -i '' '///SPDX/,+1 d' flattened/MirrorWriteToken.sol", 12 | "deploy:default": "hardhat run scripts/deploy.ts", 13 | "deploy:rinkeby": "hardhat run --network rinkeby scripts/deploy.ts", 14 | "deploy:mainnet": "hardhat run --network mainnet scripts/deploy.ts", 15 | "deploy:registrar:default": "hardhat run scripts/deploy-registrar.ts", 16 | "deploy:registrar:rinkeby": "hardhat run --network rinkeby scripts/deploy-registrar.ts" 17 | }, 18 | "dependencies": { 19 | "@nomiclabs/hardhat-solhint": "^2.0.0", 20 | "@openzeppelin/contracts": "^3.3.0", 21 | "@typechain/ethers-v5": "^5.0.0", 22 | "@types/mocha": "^8.2.0", 23 | "hardhat": "^2.0.7", 24 | "hardhat-typechain": "^0.3.4", 25 | "ts-generator": "^0.1.1", 26 | "ts-node": "^9.1.1", 27 | "typechain": "^4.0.1", 28 | "typescript": "^4.1.3" 29 | }, 30 | "devDependencies": { 31 | "@nomiclabs/hardhat-ethers": "^2.0.0", 32 | "@nomiclabs/hardhat-waffle": "^2.0.0", 33 | "chai": "^4.2.0", 34 | "ethereum-waffle": "^3.0.0", 35 | "ethers": "^5.0.25" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /merkle-distribution/balance-tree.ts: -------------------------------------------------------------------------------- 1 | import MerkleTree from './merkle-tree' 2 | import { BigNumber, utils } from 'ethers' 3 | 4 | export default class BalanceTree { 5 | private readonly tree: MerkleTree 6 | constructor(balances: { account: string; amount: BigNumber }[]) { 7 | this.tree = new MerkleTree( 8 | balances.map(({ account, amount }, index) => { 9 | return BalanceTree.toNode(index, account, amount) 10 | }) 11 | ) 12 | } 13 | 14 | public static verifyProof( 15 | index: number | BigNumber, 16 | account: string, 17 | amount: BigNumber, 18 | proof: Buffer[], 19 | root: Buffer 20 | ): boolean { 21 | let pair = BalanceTree.toNode(index, account, amount) 22 | for (const item of proof) { 23 | pair = MerkleTree.combinedHash(pair, item) 24 | } 25 | 26 | return pair.equals(root) 27 | } 28 | 29 | // keccak256(abi.encode(index, account, amount)) 30 | public static toNode(index: number | BigNumber, account: string, amount: BigNumber): Buffer { 31 | return Buffer.from( 32 | utils.solidityKeccak256(['uint256', 'address', 'uint256'], [index, account, amount]).substr(2), 33 | 'hex' 34 | ) 35 | } 36 | 37 | public getHexRoot(): string { 38 | return this.tree.getHexRoot() 39 | } 40 | 41 | // returns the hex bytes32 values of the proof 42 | public getProof(index: number | BigNumber, account: string, amount: BigNumber): string[] { 43 | return this.tree.getHexProof(BalanceTree.toNode(index, account, amount)) 44 | } 45 | } -------------------------------------------------------------------------------- /ts-types/contracts/factories/IMirrorPublicationFactoryV1__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { IMirrorPublicationFactoryV1 } from "../IMirrorPublicationFactoryV1"; 9 | 10 | export class IMirrorPublicationFactoryV1__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): IMirrorPublicationFactoryV1 { 15 | return new Contract( 16 | address, 17 | _abi, 18 | signerOrProvider 19 | ) as IMirrorPublicationFactoryV1; 20 | } 21 | } 22 | 23 | const _abi = [ 24 | { 25 | inputs: [ 26 | { 27 | internalType: "address", 28 | name: "creator", 29 | type: "address", 30 | }, 31 | { 32 | internalType: "string", 33 | name: "label", 34 | type: "string", 35 | }, 36 | { 37 | internalType: "string", 38 | name: "tokenName", 39 | type: "string", 40 | }, 41 | { 42 | internalType: "string", 43 | name: "tokenSymbol", 44 | type: "string", 45 | }, 46 | { 47 | internalType: "uint8", 48 | name: "tokenDecimals", 49 | type: "uint8", 50 | }, 51 | ], 52 | name: "createPublication", 53 | outputs: [ 54 | { 55 | internalType: "address", 56 | name: "publication", 57 | type: "address", 58 | }, 59 | ], 60 | stateMutability: "nonpayable", 61 | type: "function", 62 | }, 63 | ]; 64 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/Ownable__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { Ownable } from "../Ownable"; 9 | 10 | export class Ownable__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): Ownable { 15 | return new Contract(address, _abi, signerOrProvider) as Ownable; 16 | } 17 | } 18 | 19 | const _abi = [ 20 | { 21 | anonymous: false, 22 | inputs: [ 23 | { 24 | indexed: true, 25 | internalType: "address", 26 | name: "previousOwner", 27 | type: "address", 28 | }, 29 | { 30 | indexed: true, 31 | internalType: "address", 32 | name: "newOwner", 33 | type: "address", 34 | }, 35 | ], 36 | name: "OwnershipTransferred", 37 | type: "event", 38 | }, 39 | { 40 | inputs: [], 41 | name: "owner", 42 | outputs: [ 43 | { 44 | internalType: "address", 45 | name: "", 46 | type: "address", 47 | }, 48 | ], 49 | stateMutability: "view", 50 | type: "function", 51 | }, 52 | { 53 | inputs: [], 54 | name: "renounceOwnership", 55 | outputs: [], 56 | stateMutability: "nonpayable", 57 | type: "function", 58 | }, 59 | { 60 | inputs: [ 61 | { 62 | internalType: "address", 63 | name: "newOwner", 64 | type: "address", 65 | }, 66 | ], 67 | name: "transferOwnership", 68 | outputs: [], 69 | stateMutability: "nonpayable", 70 | type: "function", 71 | }, 72 | ]; 73 | -------------------------------------------------------------------------------- /contracts/interfaces/IMirrorWriteToken.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | 4 | interface IMirrorWriteToken { 5 | function register(string calldata label, address owner) external; 6 | 7 | function registrationCost() external view returns (uint256); 8 | 9 | // ============ ERC20 Interface ============ 10 | 11 | event Approval( 12 | address indexed owner, 13 | address indexed spender, 14 | uint256 value 15 | ); 16 | event Transfer(address indexed from, address indexed to, uint256 value); 17 | 18 | function name() external view returns (string memory); 19 | 20 | function symbol() external view returns (string memory); 21 | 22 | function decimals() external view returns (uint8); 23 | 24 | function totalSupply() external view returns (uint256); 25 | 26 | function balanceOf(address owner) external view returns (uint256); 27 | 28 | function allowance(address owner, address spender) 29 | external 30 | view 31 | returns (uint256); 32 | 33 | function approve(address spender, uint256 value) external returns (bool); 34 | 35 | function transfer(address to, uint256 value) external returns (bool); 36 | 37 | function transferFrom( 38 | address from, 39 | address to, 40 | uint256 value 41 | ) external returns (bool); 42 | 43 | function permit( 44 | address owner, 45 | address spender, 46 | uint256 value, 47 | uint256 deadline, 48 | uint8 v, 49 | bytes32 r, 50 | bytes32 s 51 | ) external; 52 | 53 | function nonces(address owner) external view returns (uint256); 54 | 55 | function DOMAIN_SEPARATOR() external view returns (bytes32); 56 | } 57 | -------------------------------------------------------------------------------- /contracts/ens/interfaces/IENS.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | 4 | interface IENS { 5 | // Logged when the owner of a node assigns a new owner to a subnode. 6 | event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); 7 | 8 | // Logged when the owner of a node transfers ownership to a new account. 9 | event Transfer(bytes32 indexed node, address owner); 10 | 11 | // Logged when the resolver for a node changes. 12 | event NewResolver(bytes32 indexed node, address resolver); 13 | 14 | // Logged when the TTL of a node changes 15 | event NewTTL(bytes32 indexed node, uint64 ttl); 16 | 17 | // Logged when an operator is added or removed. 18 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 19 | 20 | function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external; 21 | function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external; 22 | function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32); 23 | function setResolver(bytes32 node, address resolver) external; 24 | function setOwner(bytes32 node, address owner) external; 25 | function setTTL(bytes32 node, uint64 ttl) external; 26 | function setApprovalForAll(address operator, bool approved) external; 27 | function owner(bytes32 node) external view returns (address); 28 | function resolver(bytes32 node) external view returns (address); 29 | function ttl(bytes32 node) external view returns (uint64); 30 | function recordExists(bytes32 node) external view returns (bool); 31 | function isApprovedForAll(address owner, address operator) external view returns (bool); 32 | } 33 | -------------------------------------------------------------------------------- /test/MirrorENSRegistrar.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | 4 | import setup from "./setup"; 5 | 6 | describe("MirrorENSRegistrar", () => { 7 | // Contracts 8 | let mirrorENSRegistrar, mirrorWriteToken; 9 | 10 | // Accounts 11 | let owner; 12 | let account1; 13 | 14 | beforeEach(async () => { 15 | ({ mirrorWriteToken, mirrorENSRegistrar } = await setup()); 16 | 17 | [owner, account1] = await ethers.getSigners(); 18 | }); 19 | 20 | describe("#register", () => { 21 | describe("when called by an address that is not the Write token", () => { 22 | it("reverts the transaction", async () => { 23 | const transaction = mirrorENSRegistrar 24 | .connect(owner) 25 | .register("test", account1.address); 26 | await expect(transaction).to.be.revertedWith( 27 | "MirrorENSRegistrar: caller is not the Mirror Write Token" 28 | ); 29 | }); 30 | }); 31 | 32 | // The rest of register is captured by the MirrorWriteToken spec. 33 | 34 | describe("#labelOwner", () => { 35 | describe("when given an unregistered node", () => { 36 | it("returns the null address", async () => { 37 | const result = await mirrorENSRegistrar.labelOwner("test"); 38 | 39 | expect(result).to.eq("0x0000000000000000000000000000000000000000"); 40 | }); 41 | }); 42 | }); 43 | }); 44 | 45 | describe("#changeRootNodeOwner", () => { 46 | describe("when called by an address that is not the owner token", () => { 47 | it("reverts the transaction", async () => { 48 | const transaction = mirrorENSRegistrar 49 | .connect(account1) 50 | .changeRootNodeOwner(account1.address); 51 | await expect(transaction).to.be.revertedWith( 52 | "Ownable: caller is not the owner" 53 | ); 54 | }); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /contracts/helpers/MirrorBatchRegistration.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import {IMirrorWriteToken} from "../interfaces/IMirrorWriteToken.sol"; 6 | import {SafeMath} from "../lib/SafeMath.sol"; 7 | 8 | /** 9 | * @title MirrorBatchRegistration 10 | * @author MirrorXYZ 11 | * 12 | * A helper contract for registering a batch of users. 13 | */ 14 | contract MirrorBatchRegistration { 15 | using SafeMath for uint256; 16 | 17 | IMirrorWriteToken token; 18 | 19 | constructor(address token_) public { 20 | token = IMirrorWriteToken(token_); 21 | } 22 | 23 | /** 24 | * Given an array of labels and owners, we register each label to each owner 25 | * via 1:1 mapping through index. 26 | * 27 | * Preconditions: 28 | * - labels and owners arrays should correspond exactly. 29 | * - sender needs to grant token allowance to this contract. 30 | * Postconditions: MirrorWriteToken will burn the same number of tokens 31 | * as there are labels to register. 32 | * 33 | * @param labels The list of ENS labels to register. 34 | * @param owners The list of addresses that should own the labels. 35 | */ 36 | function registerBatch(string[] calldata labels, address[] calldata owners) 37 | external 38 | { 39 | uint256 len = labels.length; 40 | uint256 requiredTokens = token.registrationCost().mul(len); 41 | 42 | require( 43 | token.allowance(msg.sender, address(this)) >= requiredTokens, 44 | "MirrorBatchRegistration: need to grant token allowance" 45 | ); 46 | 47 | // Pull the required number of tokens from the sender. 48 | token.transferFrom(msg.sender, address(this), requiredTokens); 49 | 50 | for (uint256 i = 0; i < len; i++) { 51 | token.register(labels[i], owners[i]); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ts-types/contracts/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { Ownable } from "./Ownable"; 5 | export type { IENS } from "./IENS"; 6 | export type { IENSResolver } from "./IENSResolver"; 7 | export type { IMirrorENSRegistrar } from "./IMirrorENSRegistrar"; 8 | export type { ENSRegistry } from "./ENSRegistry"; 9 | export type { MirrorENSRegistrar } from "./MirrorENSRegistrar"; 10 | export type { MirrorENSResolver } from "./MirrorENSResolver"; 11 | export type { MirrorBatchRegistration } from "./MirrorBatchRegistration"; 12 | export type { WriteDistributionHelperV1 } from "./WriteDistributionHelperV1"; 13 | export type { IMirrorWriteToken } from "./IMirrorWriteToken"; 14 | export type { MirrorWriteToken } from "./MirrorWriteToken"; 15 | export type { IENS } from "./IENS"; 16 | export type { PublicationRoles } from "./PublicationRoles"; 17 | 18 | export { Ownable__factory } from "./factories/Ownable__factory"; 19 | export { IENS__factory } from "./factories/IENS__factory"; 20 | export { IENSResolver__factory } from "./factories/IENSResolver__factory"; 21 | export { IMirrorENSRegistrar__factory } from "./factories/IMirrorENSRegistrar__factory"; 22 | export { ENSRegistry__factory } from "./factories/ENSRegistry__factory"; 23 | export { MirrorENSRegistrar__factory } from "./factories/MirrorENSRegistrar__factory"; 24 | export { MirrorENSResolver__factory } from "./factories/MirrorENSResolver__factory"; 25 | export { MirrorBatchRegistration__factory } from "./factories/MirrorBatchRegistration__factory"; 26 | export { WriteDistributionHelperV1__factory } from "./factories/WriteDistributionHelperV1__factory"; 27 | export { IMirrorWriteToken__factory } from "./factories/IMirrorWriteToken__factory"; 28 | export { MirrorWriteToken__factory } from "./factories/MirrorWriteToken__factory"; 29 | export { IENS__factory } from "./factories/IENS__factory"; 30 | export { PublicationRoles__factory } from "./factories/PublicationRoles__factory"; 31 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/IMirrorENSRegistrar__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { IMirrorENSRegistrar } from "../IMirrorENSRegistrar"; 9 | 10 | export class IMirrorENSRegistrar__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): IMirrorENSRegistrar { 15 | return new Contract(address, _abi, signerOrProvider) as IMirrorENSRegistrar; 16 | } 17 | } 18 | 19 | const _abi = [ 20 | { 21 | inputs: [ 22 | { 23 | internalType: "string", 24 | name: "label_", 25 | type: "string", 26 | }, 27 | { 28 | internalType: "address", 29 | name: "newOwner_", 30 | type: "address", 31 | }, 32 | ], 33 | name: "changeLabelOwner", 34 | outputs: [], 35 | stateMutability: "nonpayable", 36 | type: "function", 37 | }, 38 | { 39 | inputs: [ 40 | { 41 | internalType: "address", 42 | name: "newOwner_", 43 | type: "address", 44 | }, 45 | ], 46 | name: "changeRootNodeOwner", 47 | outputs: [], 48 | stateMutability: "nonpayable", 49 | type: "function", 50 | }, 51 | { 52 | inputs: [ 53 | { 54 | internalType: "string", 55 | name: "label", 56 | type: "string", 57 | }, 58 | ], 59 | name: "labelOwner", 60 | outputs: [ 61 | { 62 | internalType: "address", 63 | name: "", 64 | type: "address", 65 | }, 66 | ], 67 | stateMutability: "view", 68 | type: "function", 69 | }, 70 | { 71 | inputs: [ 72 | { 73 | internalType: "string", 74 | name: "label_", 75 | type: "string", 76 | }, 77 | { 78 | internalType: "address", 79 | name: "owner_", 80 | type: "address", 81 | }, 82 | ], 83 | name: "register", 84 | outputs: [], 85 | stateMutability: "nonpayable", 86 | type: "function", 87 | }, 88 | ]; 89 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/IENSReverseRegistrar__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { IENSReverseRegistrar } from "../IENSReverseRegistrar"; 9 | 10 | export class IENSReverseRegistrar__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): IENSReverseRegistrar { 15 | return new Contract( 16 | address, 17 | _abi, 18 | signerOrProvider 19 | ) as IENSReverseRegistrar; 20 | } 21 | } 22 | 23 | const _abi = [ 24 | { 25 | inputs: [ 26 | { 27 | internalType: "address", 28 | name: "_owner", 29 | type: "address", 30 | }, 31 | ], 32 | name: "claim", 33 | outputs: [ 34 | { 35 | internalType: "bytes32", 36 | name: "", 37 | type: "bytes32", 38 | }, 39 | ], 40 | stateMutability: "nonpayable", 41 | type: "function", 42 | }, 43 | { 44 | inputs: [ 45 | { 46 | internalType: "address", 47 | name: "_owner", 48 | type: "address", 49 | }, 50 | { 51 | internalType: "address", 52 | name: "_resolver", 53 | type: "address", 54 | }, 55 | ], 56 | name: "claimWithResolver", 57 | outputs: [ 58 | { 59 | internalType: "bytes32", 60 | name: "", 61 | type: "bytes32", 62 | }, 63 | ], 64 | stateMutability: "nonpayable", 65 | type: "function", 66 | }, 67 | { 68 | inputs: [ 69 | { 70 | internalType: "address", 71 | name: "_addr", 72 | type: "address", 73 | }, 74 | ], 75 | name: "node", 76 | outputs: [ 77 | { 78 | internalType: "bytes32", 79 | name: "", 80 | type: "bytes32", 81 | }, 82 | ], 83 | stateMutability: "pure", 84 | type: "function", 85 | }, 86 | { 87 | inputs: [ 88 | { 89 | internalType: "string", 90 | name: "_name", 91 | type: "string", 92 | }, 93 | ], 94 | name: "setName", 95 | outputs: [ 96 | { 97 | internalType: "bytes32", 98 | name: "", 99 | type: "bytes32", 100 | }, 101 | ], 102 | stateMutability: "nonpayable", 103 | type: "function", 104 | }, 105 | ]; 106 | -------------------------------------------------------------------------------- /test/setup/index.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import { ROOT_NAME, ROOT_NODE, ZERO_BYTES32 } from "../config/constants"; 3 | 4 | async function setup() { 5 | const [owner] = await ethers.getSigners(); 6 | 7 | const ENSRegistry = await ethers.getContractFactory("ENSRegistry"); 8 | const ensRegistry = await ENSRegistry.deploy(); 9 | await ensRegistry.deployed(); 10 | 11 | const PublicationRoles = await ethers.getContractFactory( 12 | "PublicationRoles" 13 | ); 14 | const publicationRoles = await PublicationRoles.deploy(ensRegistry.address); 15 | await publicationRoles.deployed(); 16 | 17 | const MirrorWriteToken = await ethers.getContractFactory("MirrorWriteToken"); 18 | const mirrorWriteToken = await MirrorWriteToken.deploy(); 19 | await mirrorWriteToken.deployed(); 20 | 21 | const WriteDistributionHelper = await ethers.getContractFactory( 22 | "WriteDistributionHelperV1" 23 | ); 24 | const writeDistributionHelper = await WriteDistributionHelper.deploy( 25 | mirrorWriteToken.address 26 | ); 27 | await writeDistributionHelper.deployed(); 28 | 29 | const MirrorBatchRegistration = await ethers.getContractFactory( 30 | "MirrorBatchRegistration" 31 | ); 32 | const mirrorBatchRegistration = await MirrorBatchRegistration.deploy( 33 | mirrorWriteToken.address 34 | ); 35 | await mirrorBatchRegistration.deployed(); 36 | 37 | const MirrorENSResolver = await ethers.getContractFactory( 38 | "MirrorENSResolver" 39 | ); 40 | const mirrorENSResolver = await MirrorENSResolver.deploy(); 41 | await mirrorENSResolver.deployed(); 42 | 43 | const MirrorENSRegistrar = await ethers.getContractFactory( 44 | "MirrorENSRegistrar" 45 | ); 46 | const mirrorENSRegistrar = await MirrorENSRegistrar.deploy( 47 | ROOT_NAME, 48 | ROOT_NODE, 49 | ensRegistry.address, 50 | mirrorENSResolver.address, 51 | mirrorWriteToken.address 52 | ); 53 | await mirrorENSRegistrar.deployed(); 54 | 55 | await mirrorWriteToken.setENSRegistrar(mirrorENSRegistrar.address); 56 | await mirrorENSResolver.transferOwnership(mirrorENSRegistrar.address); 57 | 58 | // Setup root. 59 | await ensRegistry.setSubnodeOwner( 60 | ZERO_BYTES32, 61 | ethers.utils.keccak256(ethers.utils.toUtf8Bytes("xyz")), 62 | owner.address 63 | ); 64 | await ensRegistry.setSubnodeOwner( 65 | ethers.utils.namehash("xyz"), 66 | ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mirror")), 67 | mirrorENSRegistrar.address 68 | ); 69 | 70 | return { 71 | mirrorWriteToken, 72 | mirrorENSRegistrar, 73 | ensRegistry, 74 | mirrorENSResolver, 75 | mirrorBatchRegistration, 76 | writeDistributionHelper, 77 | publicationRoles 78 | }; 79 | } 80 | 81 | export default setup; 82 | -------------------------------------------------------------------------------- /ts-types/contracts/IENS.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | CallOverrides, 17 | } from "@ethersproject/contracts"; 18 | import { BytesLike } from "@ethersproject/bytes"; 19 | import { Listener, Provider } from "@ethersproject/providers"; 20 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 21 | 22 | interface IENSInterface extends ethers.utils.Interface { 23 | functions: { 24 | "owner(bytes32)": FunctionFragment; 25 | }; 26 | 27 | encodeFunctionData(functionFragment: "owner", values: [BytesLike]): string; 28 | 29 | decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result; 30 | 31 | events: {}; 32 | } 33 | 34 | export class IENS extends Contract { 35 | connect(signerOrProvider: Signer | Provider | string): this; 36 | attach(addressOrName: string): this; 37 | deployed(): Promise; 38 | 39 | on(event: EventFilter | string, listener: Listener): this; 40 | once(event: EventFilter | string, listener: Listener): this; 41 | addListener(eventName: EventFilter | string, listener: Listener): this; 42 | removeAllListeners(eventName: EventFilter | string): this; 43 | removeListener(eventName: any, listener: Listener): this; 44 | 45 | interface: IENSInterface; 46 | 47 | functions: { 48 | owner(_node: BytesLike, overrides?: CallOverrides): Promise<[string]>; 49 | 50 | "owner(bytes32)"( 51 | _node: BytesLike, 52 | overrides?: CallOverrides 53 | ): Promise<[string]>; 54 | }; 55 | 56 | owner(_node: BytesLike, overrides?: CallOverrides): Promise; 57 | 58 | "owner(bytes32)"( 59 | _node: BytesLike, 60 | overrides?: CallOverrides 61 | ): Promise; 62 | 63 | callStatic: { 64 | owner(_node: BytesLike, overrides?: CallOverrides): Promise; 65 | 66 | "owner(bytes32)"( 67 | _node: BytesLike, 68 | overrides?: CallOverrides 69 | ): Promise; 70 | }; 71 | 72 | filters: {}; 73 | 74 | estimateGas: { 75 | owner(_node: BytesLike, overrides?: CallOverrides): Promise; 76 | 77 | "owner(bytes32)"( 78 | _node: BytesLike, 79 | overrides?: CallOverrides 80 | ): Promise; 81 | }; 82 | 83 | populateTransaction: { 84 | owner( 85 | _node: BytesLike, 86 | overrides?: CallOverrides 87 | ): Promise; 88 | 89 | "owner(bytes32)"( 90 | _node: BytesLike, 91 | overrides?: CallOverrides 92 | ): Promise; 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /contracts/PublicationRoles.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | 4 | interface IENS { 5 | function owner(bytes32 _node) external view returns (address); 6 | } 7 | 8 | /** 9 | * @title PublicationRoles 10 | * @author MirrorXYZ 11 | */ 12 | contract PublicationRoles { 13 | // Immutable data 14 | address public immutable ens; 15 | 16 | // Mutable data 17 | 18 | // A flat mapping of the hash of the ENS node with the contributor 19 | // address to the hash of the role. 20 | mapping(bytes32 => bytes32) public roles; 21 | 22 | // Modifiers 23 | 24 | modifier onlyPublicationOwner(bytes32 publicationNode) { 25 | require( 26 | ownsPublication(publicationNode, msg.sender), 27 | "Sender must be publication owner" 28 | ); 29 | _; 30 | } 31 | 32 | // Events 33 | event ModifiedRole( 34 | bytes32 indexed publicationNode, 35 | address indexed contributor, 36 | string roleName 37 | ); 38 | 39 | // Constructor 40 | 41 | constructor(address ens_) public { 42 | ens = ens_; 43 | } 44 | 45 | // Modifies data. 46 | 47 | function modifyRole( 48 | address contributor, 49 | // sha256(dev.mirror.xyz) 50 | bytes32 publicationNode, 51 | string calldata roleName 52 | ) external onlyPublicationOwner(publicationNode) { 53 | bytes32 role = encodeRole(roleName); 54 | roles[getContributorId(contributor, publicationNode)] = role; 55 | 56 | emit ModifiedRole(publicationNode, contributor, roleName); 57 | } 58 | 59 | function getContributorId( 60 | address contributor, 61 | // sha256(dev.mirror.xyz) 62 | bytes32 publicationNode 63 | ) public pure returns (bytes32) { 64 | return keccak256(abi.encodePacked(contributor, publicationNode)); 65 | } 66 | 67 | function getRole(address contributor, bytes32 publicationNode) 68 | external 69 | view 70 | returns (bytes32) 71 | { 72 | return roles[getContributorId(contributor, publicationNode)]; 73 | } 74 | 75 | // Convenient for encoding roles consistently. 76 | function encodeRole(string memory roleName) public pure returns (bytes32) { 77 | return keccak256(abi.encodePacked(roleName)); 78 | } 79 | 80 | function ownsPublication(bytes32 publicationNode, address account) 81 | public 82 | view 83 | returns (bool) 84 | { 85 | return publicationOwner(publicationNode) == account; 86 | } 87 | 88 | function publicationOwner(bytes32 publicationNode) 89 | public 90 | view 91 | returns (address) 92 | { 93 | return IENS(ens).owner(publicationNode); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /flattened/PublicationRoles.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | 4 | interface IENS { 5 | function owner(bytes32 _node) external view returns (address); 6 | } 7 | 8 | /** 9 | * @title PublicationRoles 10 | * @author MirrorXYZ 11 | */ 12 | contract PublicationRoles { 13 | // Immutable data 14 | address public immutable ens; 15 | 16 | // Mutable data 17 | 18 | // A flat mapping of the hash of the ENS node with the contributor 19 | // address to the hash of the role. 20 | mapping(bytes32 => bytes32) public roles; 21 | 22 | // Modifiers 23 | 24 | modifier onlyPublicationOwner(bytes32 publicationNode) { 25 | require( 26 | ownsPublication(publicationNode, msg.sender), 27 | "Sender must be publication owner" 28 | ); 29 | _; 30 | } 31 | 32 | // Events 33 | event ModifiedRole( 34 | bytes32 indexed publicationNode, 35 | address indexed contributor, 36 | string roleName 37 | ); 38 | 39 | // Constructor 40 | 41 | constructor(address ens_) public { 42 | ens = ens_; 43 | } 44 | 45 | // Modifies data. 46 | 47 | function modifyRole( 48 | address contributor, 49 | // sha256(dev.mirror.xyz) 50 | bytes32 publicationNode, 51 | string calldata roleName 52 | ) external onlyPublicationOwner(publicationNode) { 53 | bytes32 role = encodeRole(roleName); 54 | roles[getContributorId(contributor, publicationNode)] = role; 55 | 56 | emit ModifiedRole(publicationNode, contributor, roleName); 57 | } 58 | 59 | function getContributorId( 60 | address contributor, 61 | // sha256(dev.mirror.xyz) 62 | bytes32 publicationNode 63 | ) public pure returns (bytes32) { 64 | return keccak256(abi.encodePacked(contributor, publicationNode)); 65 | } 66 | 67 | function getRole(address contributor, bytes32 publicationNode) 68 | external 69 | view 70 | returns (bytes32) 71 | { 72 | return roles[getContributorId(contributor, publicationNode)]; 73 | } 74 | 75 | // Convenient for encoding roles consistently. 76 | function encodeRole(string memory roleName) public pure returns (bytes32) { 77 | return keccak256(abi.encodePacked(roleName)); 78 | } 79 | 80 | function ownsPublication(bytes32 publicationNode, address account) 81 | public 82 | view 83 | returns (bool) 84 | { 85 | return publicationOwner(publicationNode) == account; 86 | } 87 | 88 | function publicationOwner(bytes32 publicationNode) 89 | public 90 | view 91 | returns (address) 92 | { 93 | return IENS(ens).owner(publicationNode); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /contracts/ens/MirrorENSResolver.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity >=0.5.4; 3 | 4 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 5 | import {IENSResolver} from "./interfaces/IENSResolver.sol"; 6 | 7 | contract MirrorENSResolver is Ownable, IENSResolver { 8 | // ============ Constants ============ 9 | 10 | bytes4 constant SUPPORT_INTERFACE_ID = 0x01ffc9a7; 11 | bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de; 12 | bytes4 constant NAME_INTERFACE_ID = 0x691f3431; 13 | 14 | // ============ Structs ============ 15 | 16 | struct Record { 17 | address addr; 18 | string name; 19 | } 20 | 21 | // ============ Mappings ============ 22 | 23 | // mapping between namehash and resolved records 24 | mapping(bytes32 => Record) records; 25 | 26 | // ============ Public Functions ============ 27 | 28 | /** 29 | * @notice Lets the manager set the address associated with an ENS node. 30 | * @param _node The node to update. 31 | * @param _addr The address to set. 32 | */ 33 | function setAddr(bytes32 _node, address _addr) public override onlyOwner { 34 | records[_node].addr = _addr; 35 | emit AddrChanged(_node, _addr); 36 | } 37 | 38 | /** 39 | * @notice Lets the manager set the name associated with an ENS node. 40 | * @param _node The node to update. 41 | * @param _name The name to set. 42 | */ 43 | function setName(bytes32 _node, string memory _name) 44 | public 45 | override 46 | onlyOwner 47 | { 48 | records[_node].name = _name; 49 | emit NameChanged(_node, _name); 50 | } 51 | 52 | /** 53 | * @notice Gets the address associated to an ENS node. 54 | * @param _node The target node. 55 | * @return the address of the target node. 56 | */ 57 | function addr(bytes32 _node) public view override returns (address) { 58 | return records[_node].addr; 59 | } 60 | 61 | /** 62 | * @notice Gets the name associated to an ENS node. 63 | * @param _node The target ENS node. 64 | * @return the name of the target ENS node. 65 | */ 66 | function name(bytes32 _node) public view override returns (string memory) { 67 | return records[_node].name; 68 | } 69 | 70 | /** 71 | * @notice Returns true if the resolver implements the interface specified by the provided hash. 72 | * @param _interfaceID The ID of the interface to check for. 73 | * @return True if the contract implements the requested interface. 74 | */ 75 | function supportsInterface(bytes4 _interfaceID) public pure returns (bool) { 76 | return 77 | _interfaceID == SUPPORT_INTERFACE_ID || 78 | _interfaceID == ADDR_INTERFACE_ID || 79 | _interfaceID == NAME_INTERFACE_ID; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/MirrorBatchRegistration.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | 4 | import setup from "./setup"; 5 | 6 | describe("MirrorBatchRegistration", () => { 7 | // Contracts 8 | let token; 9 | let mirrorBatchRegistration; 10 | let ensRegistry; 11 | let mirrorENSResolver; 12 | 13 | // Accounts 14 | let owner; 15 | let account1; 16 | let account2; 17 | let account3; 18 | 19 | let registrationCost; 20 | 21 | beforeEach(async () => { 22 | ({ 23 | mirrorWriteToken: token, 24 | mirrorBatchRegistration, 25 | mirrorENSResolver, 26 | ensRegistry, 27 | } = await setup()); 28 | 29 | [owner, account1, account2, account3] = await ethers.getSigners(); 30 | 31 | registrationCost = await token.registrationCost(); 32 | }); 33 | 34 | describe("#registerBatch", () => { 35 | describe("when the contract does not have an invite token", () => { 36 | it("reverts the transaction", async () => { 37 | const transaction = mirrorBatchRegistration 38 | .connect(account1) 39 | .registerBatch(["test"], [account1.address]); 40 | await expect(transaction).to.be.revertedWith( 41 | "MirrorBatchRegistration: need to grant token allowance" 42 | ); 43 | }); 44 | }); 45 | 46 | describe("when the account has an invite token", () => { 47 | let transaction; 48 | let receipt; 49 | const initialTokens = 3; 50 | 51 | let labels; 52 | let owners; 53 | 54 | beforeEach(async () => { 55 | const numTokens = registrationCost.mul(initialTokens); 56 | await token.connect(owner).mint(account1.address, numTokens); 57 | // Set allowance 58 | await token 59 | .connect(account1) 60 | .approve(mirrorBatchRegistration.address, numTokens); 61 | 62 | labels = ["label1", "label2", "label3"]; 63 | owners = [account1.address, account2.address, account3.address]; 64 | transaction = await mirrorBatchRegistration 65 | .connect(account1) 66 | .registerBatch(labels, owners); 67 | receipt = await transaction.wait(); 68 | }); 69 | 70 | it("burns the user's token", async () => { 71 | const accountBalance = await token.balanceOf(account1.address); 72 | expect(accountBalance.toString()).to.equal( 73 | (initialTokens - 3).toString() 74 | ); 75 | }); 76 | 77 | it("registers the requested ENS label and assigns ownership to the owner", async () => { 78 | for (let i = 0; i < labels.length; i++) { 79 | const subdomainOwner = await ensRegistry.owner( 80 | ethers.utils.namehash(`${labels[i]}.mirror.xyz`) 81 | ); 82 | expect(subdomainOwner).to.eq(owners[i]); 83 | } 84 | }); 85 | 86 | it("uses 401541 gas", () => { 87 | const { gasUsed } = receipt; 88 | expect(gasUsed).to.eq(401541); 89 | }); 90 | }); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/IENSResolver__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { IENSResolver } from "../IENSResolver"; 9 | 10 | export class IENSResolver__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): IENSResolver { 15 | return new Contract(address, _abi, signerOrProvider) as IENSResolver; 16 | } 17 | } 18 | 19 | const _abi = [ 20 | { 21 | anonymous: false, 22 | inputs: [ 23 | { 24 | indexed: true, 25 | internalType: "bytes32", 26 | name: "_node", 27 | type: "bytes32", 28 | }, 29 | { 30 | indexed: false, 31 | internalType: "address", 32 | name: "_addr", 33 | type: "address", 34 | }, 35 | ], 36 | name: "AddrChanged", 37 | type: "event", 38 | }, 39 | { 40 | anonymous: false, 41 | inputs: [ 42 | { 43 | indexed: true, 44 | internalType: "bytes32", 45 | name: "_node", 46 | type: "bytes32", 47 | }, 48 | { 49 | indexed: false, 50 | internalType: "string", 51 | name: "_name", 52 | type: "string", 53 | }, 54 | ], 55 | name: "NameChanged", 56 | type: "event", 57 | }, 58 | { 59 | inputs: [ 60 | { 61 | internalType: "bytes32", 62 | name: "_node", 63 | type: "bytes32", 64 | }, 65 | ], 66 | name: "addr", 67 | outputs: [ 68 | { 69 | internalType: "address", 70 | name: "", 71 | type: "address", 72 | }, 73 | ], 74 | stateMutability: "view", 75 | type: "function", 76 | }, 77 | { 78 | inputs: [ 79 | { 80 | internalType: "bytes32", 81 | name: "_node", 82 | type: "bytes32", 83 | }, 84 | ], 85 | name: "name", 86 | outputs: [ 87 | { 88 | internalType: "string", 89 | name: "", 90 | type: "string", 91 | }, 92 | ], 93 | stateMutability: "view", 94 | type: "function", 95 | }, 96 | { 97 | inputs: [ 98 | { 99 | internalType: "bytes32", 100 | name: "_node", 101 | type: "bytes32", 102 | }, 103 | { 104 | internalType: "address", 105 | name: "_addr", 106 | type: "address", 107 | }, 108 | ], 109 | name: "setAddr", 110 | outputs: [], 111 | stateMutability: "nonpayable", 112 | type: "function", 113 | }, 114 | { 115 | inputs: [ 116 | { 117 | internalType: "bytes32", 118 | name: "_node", 119 | type: "bytes32", 120 | }, 121 | { 122 | internalType: "string", 123 | name: "_name", 124 | type: "string", 125 | }, 126 | ], 127 | name: "setName", 128 | outputs: [], 129 | stateMutability: "nonpayable", 130 | type: "function", 131 | }, 132 | ]; 133 | -------------------------------------------------------------------------------- /merkle-distribution/parse-balance-map.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber, utils } from 'ethers' 2 | import BalanceTree from './balance-tree' 3 | 4 | const { isAddress, getAddress } = utils 5 | 6 | // This is the blob that gets distributed and pinned to IPFS. 7 | // It is completely sufficient for recreating the entire merkle tree. 8 | // Anyone can verify that all air drops are included in the tree, 9 | // and the tree has no additional distributions. 10 | interface MerkleDistributorInfo { 11 | merkleRoot: string 12 | tokenTotal: string 13 | claims: { 14 | [account: string]: { 15 | index: number 16 | amount: string 17 | proof: string[] 18 | flags?: { 19 | [flag: string]: boolean 20 | } 21 | } 22 | } 23 | } 24 | 25 | type OldFormat = { [account: string]: number | string } 26 | type NewFormat = { address: string; earnings: string; reasons: string } 27 | 28 | export function parseBalanceMap(balances: OldFormat | NewFormat[]): MerkleDistributorInfo { 29 | // if balances are in an old format, process them 30 | const balancesInNewFormat: NewFormat[] = Array.isArray(balances) 31 | ? balances 32 | : Object.keys(balances).map( 33 | (account): NewFormat => ({ 34 | address: account, 35 | earnings: `0x${balances[account].toString(16)}`, 36 | reasons: '', 37 | }) 38 | ) 39 | 40 | const dataByAddress = balancesInNewFormat.reduce<{ 41 | [address: string]: { amount: BigNumber; flags?: { [flag: string]: boolean } } 42 | }>((memo, { address: account, earnings, reasons }) => { 43 | if (!isAddress(account)) { 44 | throw new Error(`Found invalid address: ${account}`) 45 | } 46 | const parsed = getAddress(account) 47 | if (memo[parsed]) throw new Error(`Duplicate address: ${parsed}`) 48 | const parsedNum = BigNumber.from(earnings) 49 | if (parsedNum.lte(0)) throw new Error(`Invalid amount for account: ${account}`) 50 | 51 | const flags = { 52 | isSOCKS: reasons.includes('socks'), 53 | isLP: reasons.includes('lp'), 54 | isUser: reasons.includes('user'), 55 | } 56 | 57 | memo[parsed] = { amount: parsedNum, ...(reasons === '' ? {} : { flags }) } 58 | return memo 59 | }, {}) 60 | 61 | const sortedAddresses = Object.keys(dataByAddress).sort() 62 | 63 | // construct a tree 64 | const tree = new BalanceTree( 65 | sortedAddresses.map((address) => ({ account: address, amount: dataByAddress[address].amount })) 66 | ) 67 | 68 | // generate claims 69 | const claims = sortedAddresses.reduce<{ 70 | [address: string]: { amount: string; index: number; proof: string[]; flags?: { [flag: string]: boolean } } 71 | }>((memo, address, index) => { 72 | const { amount, flags } = dataByAddress[address] 73 | memo[address] = { 74 | index, 75 | amount: amount.toHexString(), 76 | proof: tree.getProof(index, address, amount), 77 | ...(flags ? { flags } : {}), 78 | } 79 | return memo 80 | }, {}) 81 | 82 | const tokenTotal: BigNumber = sortedAddresses.reduce( 83 | (memo, key) => memo.add(dataByAddress[key].amount), 84 | BigNumber.from(0) 85 | ) 86 | 87 | return { 88 | merkleRoot: tree.getHexRoot(), 89 | tokenTotal: tokenTotal.toHexString(), 90 | claims, 91 | } 92 | } -------------------------------------------------------------------------------- /ts-types/contracts/Resolver.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | Overrides, 17 | CallOverrides, 18 | } from "@ethersproject/contracts"; 19 | import { BytesLike } from "@ethersproject/bytes"; 20 | import { Listener, Provider } from "@ethersproject/providers"; 21 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 22 | 23 | interface ResolverInterface extends ethers.utils.Interface { 24 | functions: { 25 | "setName(bytes32,string)": FunctionFragment; 26 | }; 27 | 28 | encodeFunctionData( 29 | functionFragment: "setName", 30 | values: [BytesLike, string] 31 | ): string; 32 | 33 | decodeFunctionResult(functionFragment: "setName", data: BytesLike): Result; 34 | 35 | events: {}; 36 | } 37 | 38 | export class Resolver extends Contract { 39 | connect(signerOrProvider: Signer | Provider | string): this; 40 | attach(addressOrName: string): this; 41 | deployed(): Promise; 42 | 43 | on(event: EventFilter | string, listener: Listener): this; 44 | once(event: EventFilter | string, listener: Listener): this; 45 | addListener(eventName: EventFilter | string, listener: Listener): this; 46 | removeAllListeners(eventName: EventFilter | string): this; 47 | removeListener(eventName: any, listener: Listener): this; 48 | 49 | interface: ResolverInterface; 50 | 51 | functions: { 52 | setName( 53 | node: BytesLike, 54 | name: string, 55 | overrides?: Overrides 56 | ): Promise; 57 | 58 | "setName(bytes32,string)"( 59 | node: BytesLike, 60 | name: string, 61 | overrides?: Overrides 62 | ): Promise; 63 | }; 64 | 65 | setName( 66 | node: BytesLike, 67 | name: string, 68 | overrides?: Overrides 69 | ): Promise; 70 | 71 | "setName(bytes32,string)"( 72 | node: BytesLike, 73 | name: string, 74 | overrides?: Overrides 75 | ): Promise; 76 | 77 | callStatic: { 78 | setName( 79 | node: BytesLike, 80 | name: string, 81 | overrides?: CallOverrides 82 | ): Promise; 83 | 84 | "setName(bytes32,string)"( 85 | node: BytesLike, 86 | name: string, 87 | overrides?: CallOverrides 88 | ): Promise; 89 | }; 90 | 91 | filters: {}; 92 | 93 | estimateGas: { 94 | setName( 95 | node: BytesLike, 96 | name: string, 97 | overrides?: Overrides 98 | ): Promise; 99 | 100 | "setName(bytes32,string)"( 101 | node: BytesLike, 102 | name: string, 103 | overrides?: Overrides 104 | ): Promise; 105 | }; 106 | 107 | populateTransaction: { 108 | setName( 109 | node: BytesLike, 110 | name: string, 111 | overrides?: Overrides 112 | ): Promise; 113 | 114 | "setName(bytes32,string)"( 115 | node: BytesLike, 116 | name: string, 117 | overrides?: Overrides 118 | ): Promise; 119 | }; 120 | } 121 | -------------------------------------------------------------------------------- /ts-types/contracts/MirrorBatchRegistration.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | Overrides, 17 | CallOverrides, 18 | } from "@ethersproject/contracts"; 19 | import { BytesLike } from "@ethersproject/bytes"; 20 | import { Listener, Provider } from "@ethersproject/providers"; 21 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 22 | 23 | interface MirrorBatchRegistrationInterface extends ethers.utils.Interface { 24 | functions: { 25 | "registerBatch(string[],address[])": FunctionFragment; 26 | }; 27 | 28 | encodeFunctionData( 29 | functionFragment: "registerBatch", 30 | values: [string[], string[]] 31 | ): string; 32 | 33 | decodeFunctionResult( 34 | functionFragment: "registerBatch", 35 | data: BytesLike 36 | ): Result; 37 | 38 | events: {}; 39 | } 40 | 41 | export class MirrorBatchRegistration extends Contract { 42 | connect(signerOrProvider: Signer | Provider | string): this; 43 | attach(addressOrName: string): this; 44 | deployed(): Promise; 45 | 46 | on(event: EventFilter | string, listener: Listener): this; 47 | once(event: EventFilter | string, listener: Listener): this; 48 | addListener(eventName: EventFilter | string, listener: Listener): this; 49 | removeAllListeners(eventName: EventFilter | string): this; 50 | removeListener(eventName: any, listener: Listener): this; 51 | 52 | interface: MirrorBatchRegistrationInterface; 53 | 54 | functions: { 55 | registerBatch( 56 | labels: string[], 57 | owners: string[], 58 | overrides?: Overrides 59 | ): Promise; 60 | 61 | "registerBatch(string[],address[])"( 62 | labels: string[], 63 | owners: string[], 64 | overrides?: Overrides 65 | ): Promise; 66 | }; 67 | 68 | registerBatch( 69 | labels: string[], 70 | owners: string[], 71 | overrides?: Overrides 72 | ): Promise; 73 | 74 | "registerBatch(string[],address[])"( 75 | labels: string[], 76 | owners: string[], 77 | overrides?: Overrides 78 | ): Promise; 79 | 80 | callStatic: { 81 | registerBatch( 82 | labels: string[], 83 | owners: string[], 84 | overrides?: CallOverrides 85 | ): Promise; 86 | 87 | "registerBatch(string[],address[])"( 88 | labels: string[], 89 | owners: string[], 90 | overrides?: CallOverrides 91 | ): Promise; 92 | }; 93 | 94 | filters: {}; 95 | 96 | estimateGas: { 97 | registerBatch( 98 | labels: string[], 99 | owners: string[], 100 | overrides?: Overrides 101 | ): Promise; 102 | 103 | "registerBatch(string[],address[])"( 104 | labels: string[], 105 | owners: string[], 106 | overrides?: Overrides 107 | ): Promise; 108 | }; 109 | 110 | populateTransaction: { 111 | registerBatch( 112 | labels: string[], 113 | owners: string[], 114 | overrides?: Overrides 115 | ): Promise; 116 | 117 | "registerBatch(string[],address[])"( 118 | labels: string[], 119 | owners: string[], 120 | overrides?: Overrides 121 | ): Promise; 122 | }; 123 | } 124 | -------------------------------------------------------------------------------- /merkle-distribution/merkle-tree.ts: -------------------------------------------------------------------------------- 1 | import { bufferToHex, keccak256 } from 'ethereumjs-util' 2 | 3 | export default class MerkleTree { 4 | private readonly elements: Buffer[] 5 | private readonly bufferElementPositionIndex: { [hexElement: string]: number } 6 | private readonly layers: Buffer[][] 7 | 8 | constructor(elements: Buffer[]) { 9 | this.elements = [...elements] 10 | // Sort elements 11 | this.elements.sort(Buffer.compare) 12 | // Deduplicate elements 13 | this.elements = MerkleTree.bufDedup(this.elements) 14 | 15 | this.bufferElementPositionIndex = this.elements.reduce<{ [hexElement: string]: number }>((memo, el, index) => { 16 | memo[bufferToHex(el)] = index 17 | return memo 18 | }, {}) 19 | 20 | // Create layers 21 | this.layers = this.getLayers(this.elements) 22 | } 23 | 24 | getLayers(elements: Buffer[]): Buffer[][] { 25 | if (elements.length === 0) { 26 | throw new Error('empty tree') 27 | } 28 | 29 | const layers = [] 30 | layers.push(elements) 31 | 32 | // Get next layer until we reach the root 33 | while (layers[layers.length - 1].length > 1) { 34 | layers.push(this.getNextLayer(layers[layers.length - 1])) 35 | } 36 | 37 | return layers 38 | } 39 | 40 | getNextLayer(elements: Buffer[]): Buffer[] { 41 | return elements.reduce((layer, el, idx, arr) => { 42 | if (idx % 2 === 0) { 43 | // Hash the current element with its pair element 44 | layer.push(MerkleTree.combinedHash(el, arr[idx + 1])) 45 | } 46 | 47 | return layer 48 | }, []) 49 | } 50 | 51 | static combinedHash(first: Buffer, second: Buffer): Buffer { 52 | if (!first) { 53 | return second 54 | } 55 | if (!second) { 56 | return first 57 | } 58 | 59 | return keccak256(MerkleTree.sortAndConcat(first, second)) 60 | } 61 | 62 | getRoot(): Buffer { 63 | return this.layers[this.layers.length - 1][0] 64 | } 65 | 66 | getHexRoot(): string { 67 | return bufferToHex(this.getRoot()) 68 | } 69 | 70 | getProof(el: Buffer) { 71 | let idx = this.bufferElementPositionIndex[bufferToHex(el)] 72 | 73 | if (typeof idx !== 'number') { 74 | throw new Error('Element does not exist in Merkle tree') 75 | } 76 | 77 | return this.layers.reduce((proof, layer) => { 78 | const pairElement = MerkleTree.getPairElement(idx, layer) 79 | 80 | if (pairElement) { 81 | proof.push(pairElement) 82 | } 83 | 84 | idx = Math.floor(idx / 2) 85 | 86 | return proof 87 | }, []) 88 | } 89 | 90 | getHexProof(el: Buffer): string[] { 91 | const proof = this.getProof(el) 92 | 93 | return MerkleTree.bufArrToHexArr(proof) 94 | } 95 | 96 | private static getPairElement(idx: number, layer: Buffer[]): Buffer | null { 97 | const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1 98 | 99 | if (pairIdx < layer.length) { 100 | return layer[pairIdx] 101 | } else { 102 | return null 103 | } 104 | } 105 | 106 | private static bufDedup(elements: Buffer[]): Buffer[] { 107 | return elements.filter((el, idx) => { 108 | return idx === 0 || !elements[idx - 1].equals(el) 109 | }) 110 | } 111 | 112 | private static bufArrToHexArr(arr: Buffer[]): string[] { 113 | if (arr.some((el) => !Buffer.isBuffer(el))) { 114 | throw new Error('Array is not an array of buffers') 115 | } 116 | 117 | return arr.map((el) => '0x' + el.toString('hex')) 118 | } 119 | 120 | private static sortAndConcat(...args: Buffer[]): Buffer { 121 | return Buffer.concat([...args].sort(Buffer.compare)) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/IMirrorPublicationV1__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { IMirrorPublicationV1 } from "../IMirrorPublicationV1"; 9 | 10 | export class IMirrorPublicationV1__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): IMirrorPublicationV1 { 15 | return new Contract( 16 | address, 17 | _abi, 18 | signerOrProvider 19 | ) as IMirrorPublicationV1; 20 | } 21 | } 22 | 23 | const _abi = [ 24 | { 25 | inputs: [ 26 | { 27 | internalType: "address", 28 | name: "account", 29 | type: "address", 30 | }, 31 | ], 32 | name: "addContributor", 33 | outputs: [], 34 | stateMutability: "nonpayable", 35 | type: "function", 36 | }, 37 | { 38 | inputs: [], 39 | name: "decimals", 40 | outputs: [ 41 | { 42 | internalType: "uint8", 43 | name: "", 44 | type: "uint8", 45 | }, 46 | ], 47 | stateMutability: "view", 48 | type: "function", 49 | }, 50 | { 51 | inputs: [ 52 | { 53 | internalType: "address", 54 | name: "account", 55 | type: "address", 56 | }, 57 | ], 58 | name: "disableContributor", 59 | outputs: [], 60 | stateMutability: "nonpayable", 61 | type: "function", 62 | }, 63 | { 64 | inputs: [], 65 | name: "factory", 66 | outputs: [ 67 | { 68 | internalType: "address", 69 | name: "", 70 | type: "address", 71 | }, 72 | ], 73 | stateMutability: "view", 74 | type: "function", 75 | }, 76 | { 77 | inputs: [ 78 | { 79 | internalType: "address", 80 | name: "operator", 81 | type: "address", 82 | }, 83 | { 84 | internalType: "string", 85 | name: "tokenName", 86 | type: "string", 87 | }, 88 | { 89 | internalType: "string", 90 | name: "tokenSymbol", 91 | type: "string", 92 | }, 93 | { 94 | internalType: "uint8", 95 | name: "tokenDecimals", 96 | type: "uint8", 97 | }, 98 | ], 99 | name: "initialize", 100 | outputs: [], 101 | stateMutability: "nonpayable", 102 | type: "function", 103 | }, 104 | { 105 | inputs: [ 106 | { 107 | internalType: "address", 108 | name: "to", 109 | type: "address", 110 | }, 111 | { 112 | internalType: "uint256", 113 | name: "value", 114 | type: "uint256", 115 | }, 116 | ], 117 | name: "mint", 118 | outputs: [ 119 | { 120 | internalType: "bool", 121 | name: "", 122 | type: "bool", 123 | }, 124 | ], 125 | stateMutability: "nonpayable", 126 | type: "function", 127 | }, 128 | { 129 | inputs: [], 130 | name: "name", 131 | outputs: [ 132 | { 133 | internalType: "string", 134 | name: "", 135 | type: "string", 136 | }, 137 | ], 138 | stateMutability: "view", 139 | type: "function", 140 | }, 141 | { 142 | inputs: [], 143 | name: "owner", 144 | outputs: [ 145 | { 146 | internalType: "address", 147 | name: "", 148 | type: "address", 149 | }, 150 | ], 151 | stateMutability: "view", 152 | type: "function", 153 | }, 154 | { 155 | inputs: [], 156 | name: "renounceOwnership", 157 | outputs: [], 158 | stateMutability: "nonpayable", 159 | type: "function", 160 | }, 161 | { 162 | inputs: [], 163 | name: "symbol", 164 | outputs: [ 165 | { 166 | internalType: "string", 167 | name: "", 168 | type: "string", 169 | }, 170 | ], 171 | stateMutability: "view", 172 | type: "function", 173 | }, 174 | { 175 | inputs: [ 176 | { 177 | internalType: "address", 178 | name: "newOwner", 179 | type: "address", 180 | }, 181 | ], 182 | name: "transferOwnership", 183 | outputs: [], 184 | stateMutability: "nonpayable", 185 | type: "function", 186 | }, 187 | ]; 188 | -------------------------------------------------------------------------------- /test/PublicationRoles.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { BigNumber } from "ethers"; 3 | import { keccak256, namehash, toUtf8Bytes } from "ethers/lib/utils"; 4 | import { ethers } from "hardhat"; 5 | 6 | import setup from "./setup"; 7 | 8 | const REGISTRATION_COST = "1000000000000000000"; 9 | 10 | describe("PublicationRoles", () => { 11 | // Contracts 12 | let token; 13 | let mirrorENSRegistrar; 14 | let ensRegistry; 15 | let mirrorENSResolver; 16 | let publicationRoles; 17 | 18 | // Accounts 19 | let owner; 20 | let account1; 21 | let account2; 22 | let account3; 23 | 24 | beforeEach(async () => { 25 | ({ 26 | mirrorWriteToken: token, 27 | mirrorENSRegistrar, 28 | ensRegistry, 29 | mirrorENSResolver, 30 | publicationRoles, 31 | } = await setup()); 32 | 33 | [owner, account1, account2, account3] = await ethers.getSigners(); 34 | }); 35 | 36 | describe("deployment", () => { 37 | it("has the correct ENS address", async () => { 38 | expect(await publicationRoles.ens()).to.eq(ensRegistry.address); 39 | }); 40 | }); 41 | 42 | describe("when a publication has been registered", () => { 43 | const label = "test"; 44 | const publicationNode = ethers.utils.namehash(`${label}.mirror.xyz`); 45 | 46 | beforeEach(async () => { 47 | const numTokens = BigNumber.from(REGISTRATION_COST).mul(1); 48 | await token.connect(owner).mint(account1.address, numTokens); 49 | await token.connect(account1).register(label, account2.address); 50 | }); 51 | 52 | describe("#getContributorId", () => { 53 | it("returns a bytes32 string for a contirbutor and publication combo", async () => { 54 | const node = await publicationRoles.getContributorId( 55 | account2.address, 56 | publicationNode 57 | ); 58 | 59 | expect(node).to.eq( 60 | "0xa176eb88a0306e8ddc619ee57c12aeebf21b8762c9a90d257f896c042183643f" 61 | ); 62 | }); 63 | }); 64 | 65 | describe("#publicationOwner", () => { 66 | describe("when called with the publication that was registered", () => { 67 | it("returns the publication owner address", async () => { 68 | expect( 69 | await publicationRoles.publicationOwner(publicationNode) 70 | ).to.eq(account2.address); 71 | }); 72 | }); 73 | 74 | describe("when called with the publication that was not registered", () => { 75 | it("returns the null address", async () => { 76 | const node = ethers.utils.namehash("fake.mirror.xyz"); 77 | expect(await publicationRoles.publicationOwner(node)).to.eq( 78 | "0x0000000000000000000000000000000000000000" 79 | ); 80 | }); 81 | }); 82 | }); 83 | 84 | describe("#modifyRole", () => { 85 | const roleName = "WRITER"; 86 | 87 | describe("when called by the publication owner", () => { 88 | let receipt; 89 | 90 | it("creates a new role", async () => { 91 | const role = await publicationRoles.encodeRole(roleName); 92 | 93 | const tx = await publicationRoles 94 | .connect(account2) 95 | .modifyRole(account2.address, publicationNode, roleName); 96 | 97 | receipt = await tx.wait(); 98 | 99 | const stored = await publicationRoles.getRole( 100 | account2.address, 101 | publicationNode 102 | ); 103 | 104 | expect(stored).to.eq(role); 105 | }); 106 | 107 | it("emits an event with the role name", async () => { 108 | const { args } = publicationRoles.interface.parseLog( 109 | receipt.events[0] 110 | ); 111 | 112 | expect(args.publicationNode).to.eq(publicationNode); 113 | expect(args.contributor).to.eq(account2.address); 114 | expect(args.roleName).to.eq(roleName); 115 | }); 116 | }); 117 | 118 | describe("when called by someone not the publication owner", () => { 119 | it("reverts the transaction", async () => { 120 | const role = await publicationRoles.encodeRole("WRITER"); 121 | 122 | const tx = publicationRoles 123 | .connect(account1) 124 | .modifyRole(account2.address, publicationNode, role); 125 | 126 | await expect(tx).to.be.revertedWith( 127 | "Sender must be publication owner" 128 | ); 129 | }); 130 | }); 131 | }); 132 | }); 133 | }); 134 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/IERC20__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { IERC20 } from "../IERC20"; 9 | 10 | export class IERC20__factory { 11 | static connect(address: string, signerOrProvider: Signer | Provider): IERC20 { 12 | return new Contract(address, _abi, signerOrProvider) as IERC20; 13 | } 14 | } 15 | 16 | const _abi = [ 17 | { 18 | anonymous: false, 19 | inputs: [ 20 | { 21 | indexed: true, 22 | internalType: "address", 23 | name: "owner", 24 | type: "address", 25 | }, 26 | { 27 | indexed: true, 28 | internalType: "address", 29 | name: "spender", 30 | type: "address", 31 | }, 32 | { 33 | indexed: false, 34 | internalType: "uint256", 35 | name: "value", 36 | type: "uint256", 37 | }, 38 | ], 39 | name: "Approval", 40 | type: "event", 41 | }, 42 | { 43 | anonymous: false, 44 | inputs: [ 45 | { 46 | indexed: true, 47 | internalType: "address", 48 | name: "from", 49 | type: "address", 50 | }, 51 | { 52 | indexed: true, 53 | internalType: "address", 54 | name: "to", 55 | type: "address", 56 | }, 57 | { 58 | indexed: false, 59 | internalType: "uint256", 60 | name: "value", 61 | type: "uint256", 62 | }, 63 | ], 64 | name: "Transfer", 65 | type: "event", 66 | }, 67 | { 68 | inputs: [ 69 | { 70 | internalType: "address", 71 | name: "owner", 72 | type: "address", 73 | }, 74 | { 75 | internalType: "address", 76 | name: "spender", 77 | type: "address", 78 | }, 79 | ], 80 | name: "allowance", 81 | outputs: [ 82 | { 83 | internalType: "uint256", 84 | name: "", 85 | type: "uint256", 86 | }, 87 | ], 88 | stateMutability: "view", 89 | type: "function", 90 | }, 91 | { 92 | inputs: [ 93 | { 94 | internalType: "address", 95 | name: "spender", 96 | type: "address", 97 | }, 98 | { 99 | internalType: "uint256", 100 | name: "amount", 101 | type: "uint256", 102 | }, 103 | ], 104 | name: "approve", 105 | outputs: [ 106 | { 107 | internalType: "bool", 108 | name: "", 109 | type: "bool", 110 | }, 111 | ], 112 | stateMutability: "nonpayable", 113 | type: "function", 114 | }, 115 | { 116 | inputs: [ 117 | { 118 | internalType: "address", 119 | name: "account", 120 | type: "address", 121 | }, 122 | ], 123 | name: "balanceOf", 124 | outputs: [ 125 | { 126 | internalType: "uint256", 127 | name: "", 128 | type: "uint256", 129 | }, 130 | ], 131 | stateMutability: "view", 132 | type: "function", 133 | }, 134 | { 135 | inputs: [], 136 | name: "totalSupply", 137 | outputs: [ 138 | { 139 | internalType: "uint256", 140 | name: "", 141 | type: "uint256", 142 | }, 143 | ], 144 | stateMutability: "view", 145 | type: "function", 146 | }, 147 | { 148 | inputs: [ 149 | { 150 | internalType: "address", 151 | name: "recipient", 152 | type: "address", 153 | }, 154 | { 155 | internalType: "uint256", 156 | name: "amount", 157 | type: "uint256", 158 | }, 159 | ], 160 | name: "transfer", 161 | outputs: [ 162 | { 163 | internalType: "bool", 164 | name: "", 165 | type: "bool", 166 | }, 167 | ], 168 | stateMutability: "nonpayable", 169 | type: "function", 170 | }, 171 | { 172 | inputs: [ 173 | { 174 | internalType: "address", 175 | name: "sender", 176 | type: "address", 177 | }, 178 | { 179 | internalType: "address", 180 | name: "recipient", 181 | type: "address", 182 | }, 183 | { 184 | internalType: "uint256", 185 | name: "amount", 186 | type: "uint256", 187 | }, 188 | ], 189 | name: "transferFrom", 190 | outputs: [ 191 | { 192 | internalType: "bool", 193 | name: "", 194 | type: "bool", 195 | }, 196 | ], 197 | stateMutability: "nonpayable", 198 | type: "function", 199 | }, 200 | ]; 201 | -------------------------------------------------------------------------------- /ts-types/contracts/IMirrorPublicationFactoryV1.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | Overrides, 17 | CallOverrides, 18 | } from "@ethersproject/contracts"; 19 | import { BytesLike } from "@ethersproject/bytes"; 20 | import { Listener, Provider } from "@ethersproject/providers"; 21 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 22 | 23 | interface IMirrorPublicationFactoryV1Interface extends ethers.utils.Interface { 24 | functions: { 25 | "createPublication(address,string,string,string,uint8)": FunctionFragment; 26 | }; 27 | 28 | encodeFunctionData( 29 | functionFragment: "createPublication", 30 | values: [string, string, string, string, BigNumberish] 31 | ): string; 32 | 33 | decodeFunctionResult( 34 | functionFragment: "createPublication", 35 | data: BytesLike 36 | ): Result; 37 | 38 | events: {}; 39 | } 40 | 41 | export class IMirrorPublicationFactoryV1 extends Contract { 42 | connect(signerOrProvider: Signer | Provider | string): this; 43 | attach(addressOrName: string): this; 44 | deployed(): Promise; 45 | 46 | on(event: EventFilter | string, listener: Listener): this; 47 | once(event: EventFilter | string, listener: Listener): this; 48 | addListener(eventName: EventFilter | string, listener: Listener): this; 49 | removeAllListeners(eventName: EventFilter | string): this; 50 | removeListener(eventName: any, listener: Listener): this; 51 | 52 | interface: IMirrorPublicationFactoryV1Interface; 53 | 54 | functions: { 55 | createPublication( 56 | creator: string, 57 | label: string, 58 | tokenName: string, 59 | tokenSymbol: string, 60 | tokenDecimals: BigNumberish, 61 | overrides?: Overrides 62 | ): Promise; 63 | 64 | "createPublication(address,string,string,string,uint8)"( 65 | creator: string, 66 | label: string, 67 | tokenName: string, 68 | tokenSymbol: string, 69 | tokenDecimals: BigNumberish, 70 | overrides?: Overrides 71 | ): Promise; 72 | }; 73 | 74 | createPublication( 75 | creator: string, 76 | label: string, 77 | tokenName: string, 78 | tokenSymbol: string, 79 | tokenDecimals: BigNumberish, 80 | overrides?: Overrides 81 | ): Promise; 82 | 83 | "createPublication(address,string,string,string,uint8)"( 84 | creator: string, 85 | label: string, 86 | tokenName: string, 87 | tokenSymbol: string, 88 | tokenDecimals: BigNumberish, 89 | overrides?: Overrides 90 | ): Promise; 91 | 92 | callStatic: { 93 | createPublication( 94 | creator: string, 95 | label: string, 96 | tokenName: string, 97 | tokenSymbol: string, 98 | tokenDecimals: BigNumberish, 99 | overrides?: CallOverrides 100 | ): Promise; 101 | 102 | "createPublication(address,string,string,string,uint8)"( 103 | creator: string, 104 | label: string, 105 | tokenName: string, 106 | tokenSymbol: string, 107 | tokenDecimals: BigNumberish, 108 | overrides?: CallOverrides 109 | ): Promise; 110 | }; 111 | 112 | filters: {}; 113 | 114 | estimateGas: { 115 | createPublication( 116 | creator: string, 117 | label: string, 118 | tokenName: string, 119 | tokenSymbol: string, 120 | tokenDecimals: BigNumberish, 121 | overrides?: Overrides 122 | ): Promise; 123 | 124 | "createPublication(address,string,string,string,uint8)"( 125 | creator: string, 126 | label: string, 127 | tokenName: string, 128 | tokenSymbol: string, 129 | tokenDecimals: BigNumberish, 130 | overrides?: Overrides 131 | ): Promise; 132 | }; 133 | 134 | populateTransaction: { 135 | createPublication( 136 | creator: string, 137 | label: string, 138 | tokenName: string, 139 | tokenSymbol: string, 140 | tokenDecimals: BigNumberish, 141 | overrides?: Overrides 142 | ): Promise; 143 | 144 | "createPublication(address,string,string,string,uint8)"( 145 | creator: string, 146 | label: string, 147 | tokenName: string, 148 | tokenSymbol: string, 149 | tokenDecimals: BigNumberish, 150 | overrides?: Overrides 151 | ): Promise; 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /scripts/deploy-registrar.ts: -------------------------------------------------------------------------------- 1 | import { ethers, waffle } from "hardhat"; 2 | import fs from "fs"; 3 | 4 | export const ZERO_BYTES32 = ethers.constants.HashZero; 5 | export const ROOT = "xyz"; 6 | export const subnameWallet = "mirror"; 7 | 8 | const config = { 9 | production: { 10 | ENS_REGISTRY_ADDRESS: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", 11 | ROOT_NAME: "mirror.xyz", 12 | ROOT_NODE: ethers.utils.namehash("mirror.xyz"), 13 | WRITE_TOKEN_ADDRESS: "0x622236bb180256b6ae1a935dae08dc0356141632", 14 | MIRROR_RESOLVER_ADDRESS: "0xc11796439c3202f4EF836EB126CC67eB378D52c8", 15 | }, 16 | test: { 17 | ENS_REGISTRY_ADDRESS: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", 18 | ROOT_NAME: "mirror.test", 19 | ROOT_NODE: ethers.utils.namehash("mirror.test"), 20 | WRITE_TOKEN_ADDRESS: "0xE951e74eFd142a970abb78d7F704C9e8446519b3", 21 | MIRROR_RESOLVER_ADDRESS: "0x9fc0471Ac1E4679C8a711d11B1c8B11F47221db5", 22 | }, 23 | }; 24 | 25 | const NETWORK_MAP = { 26 | "0": "mainnet", 27 | "3": "ropsten", 28 | "4": "rinkeby", 29 | "1337": "hardhat", 30 | "31337": "hardhat", 31 | }; 32 | 33 | let isLocal = false; 34 | 35 | async function main() { 36 | const chainId = (await waffle.provider.getNetwork()).chainId; 37 | const networkName = NETWORK_MAP[chainId]; 38 | 39 | console.log(`Deploying to ${networkName}`); 40 | 41 | isLocal = networkName === "hardhat"; 42 | 43 | let owner; 44 | let ensAddress; 45 | let ensRegistry; 46 | let ENS_REGISTRY_ADDRESS; 47 | let ROOT_NAME; 48 | let ROOT_NODE; 49 | let WRITE_TOKEN_ADDRESS; 50 | let MIRROR_RESOLVER_ADDRESS; 51 | 52 | if (networkName === "mainnet") { 53 | ({ 54 | ENS_REGISTRY_ADDRESS, 55 | ROOT_NAME, 56 | ROOT_NODE, 57 | WRITE_TOKEN_ADDRESS, 58 | MIRROR_RESOLVER_ADDRESS, 59 | } = config.production); 60 | } else { 61 | ({ 62 | ENS_REGISTRY_ADDRESS, 63 | ROOT_NAME, 64 | ROOT_NODE, 65 | WRITE_TOKEN_ADDRESS, 66 | MIRROR_RESOLVER_ADDRESS, 67 | } = config.test); 68 | } 69 | 70 | if (isLocal) { 71 | console.log("deploying ENS registry"); 72 | const ENSRegistry = await ethers.getContractFactory("ENSRegistry"); 73 | ensRegistry = await ENSRegistry.deploy(); 74 | await ensRegistry.deployed(); 75 | 76 | ensAddress = ensRegistry.address; 77 | } else { 78 | ensAddress = ENS_REGISTRY_ADDRESS; 79 | } 80 | 81 | console.log("$WRITE:", WRITE_TOKEN_ADDRESS); 82 | console.log("Mirror ENS Resolver:", MIRROR_RESOLVER_ADDRESS); 83 | 84 | console.log("Deploying ENS Registrar"); 85 | const MirrorENSRegistrar = await ethers.getContractFactory( 86 | "MirrorENSRegistrar" 87 | ); 88 | const mirrorENSRegistrar = await MirrorENSRegistrar.deploy( 89 | ROOT_NAME, 90 | ROOT_NODE, 91 | ensAddress, 92 | MIRROR_RESOLVER_ADDRESS, 93 | WRITE_TOKEN_ADDRESS 94 | ); 95 | await mirrorENSRegistrar.deployed(); 96 | 97 | console.log("is local", isLocal); 98 | if (isLocal) { 99 | const accounts = await ethers.getSigners(); 100 | owner = accounts[0].address; 101 | owner = accounts[0]; 102 | 103 | // Setup root. 104 | await ensRegistry.setSubnodeOwner( 105 | ZERO_BYTES32, 106 | ethers.utils.keccak256(ethers.utils.toUtf8Bytes("xyz")), 107 | owner.address 108 | ); 109 | await ensRegistry.setSubnodeOwner( 110 | ethers.utils.namehash("xyz"), 111 | ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mirror")), 112 | mirrorENSRegistrar.address 113 | ); 114 | } 115 | 116 | const mirrorWriteToken = await ( 117 | await ethers.getContractAt("MirrorWriteToken", WRITE_TOKEN_ADDRESS) 118 | ).deployed(); 119 | 120 | const mirrorENSResolver = await ( 121 | await ethers.getContractAt("MirrorENSResolver", MIRROR_RESOLVER_ADDRESS) 122 | ).deployed(); 123 | 124 | console.log("Setting registrar on $WRITE"); 125 | await mirrorWriteToken.setENSRegistrar(mirrorENSRegistrar.address); 126 | console.log("Transferring ownership of Resolver to Registrar"); 127 | await mirrorENSResolver.transferOwnership(mirrorENSRegistrar.address); 128 | 129 | const info = { 130 | Contracts: { 131 | MirrorWriteToken: WRITE_TOKEN_ADDRESS, 132 | MirrorENSResolver: ENS_REGISTRY_ADDRESS, 133 | MirrorENSRegistrar: mirrorENSRegistrar.address, 134 | }, 135 | }; 136 | 137 | console.log(info); 138 | 139 | if (!isLocal) { 140 | fs.writeFileSync( 141 | `${__dirname}/../networks/${networkName}.json`, 142 | JSON.stringify(info, null, 2) 143 | ); 144 | } 145 | } 146 | 147 | main() 148 | .then(() => process.exit(0)) 149 | .catch((error) => { 150 | console.error(error); 151 | process.exit(1); 152 | }); 153 | -------------------------------------------------------------------------------- /ts-types/contracts/Ownable.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | Overrides, 17 | CallOverrides, 18 | } from "@ethersproject/contracts"; 19 | import { BytesLike } from "@ethersproject/bytes"; 20 | import { Listener, Provider } from "@ethersproject/providers"; 21 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 22 | 23 | interface OwnableInterface extends ethers.utils.Interface { 24 | functions: { 25 | "owner()": FunctionFragment; 26 | "renounceOwnership()": FunctionFragment; 27 | "transferOwnership(address)": FunctionFragment; 28 | }; 29 | 30 | encodeFunctionData(functionFragment: "owner", values?: undefined): string; 31 | encodeFunctionData( 32 | functionFragment: "renounceOwnership", 33 | values?: undefined 34 | ): string; 35 | encodeFunctionData( 36 | functionFragment: "transferOwnership", 37 | values: [string] 38 | ): string; 39 | 40 | decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result; 41 | decodeFunctionResult( 42 | functionFragment: "renounceOwnership", 43 | data: BytesLike 44 | ): Result; 45 | decodeFunctionResult( 46 | functionFragment: "transferOwnership", 47 | data: BytesLike 48 | ): Result; 49 | 50 | events: { 51 | "OwnershipTransferred(address,address)": EventFragment; 52 | }; 53 | 54 | getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment; 55 | } 56 | 57 | export class Ownable extends Contract { 58 | connect(signerOrProvider: Signer | Provider | string): this; 59 | attach(addressOrName: string): this; 60 | deployed(): Promise; 61 | 62 | on(event: EventFilter | string, listener: Listener): this; 63 | once(event: EventFilter | string, listener: Listener): this; 64 | addListener(eventName: EventFilter | string, listener: Listener): this; 65 | removeAllListeners(eventName: EventFilter | string): this; 66 | removeListener(eventName: any, listener: Listener): this; 67 | 68 | interface: OwnableInterface; 69 | 70 | functions: { 71 | owner(overrides?: CallOverrides): Promise<[string]>; 72 | 73 | "owner()"(overrides?: CallOverrides): Promise<[string]>; 74 | 75 | renounceOwnership(overrides?: Overrides): Promise; 76 | 77 | "renounceOwnership()"(overrides?: Overrides): Promise; 78 | 79 | transferOwnership( 80 | newOwner: string, 81 | overrides?: Overrides 82 | ): Promise; 83 | 84 | "transferOwnership(address)"( 85 | newOwner: string, 86 | overrides?: Overrides 87 | ): Promise; 88 | }; 89 | 90 | owner(overrides?: CallOverrides): Promise; 91 | 92 | "owner()"(overrides?: CallOverrides): Promise; 93 | 94 | renounceOwnership(overrides?: Overrides): Promise; 95 | 96 | "renounceOwnership()"(overrides?: Overrides): Promise; 97 | 98 | transferOwnership( 99 | newOwner: string, 100 | overrides?: Overrides 101 | ): Promise; 102 | 103 | "transferOwnership(address)"( 104 | newOwner: string, 105 | overrides?: Overrides 106 | ): Promise; 107 | 108 | callStatic: { 109 | owner(overrides?: CallOverrides): Promise; 110 | 111 | "owner()"(overrides?: CallOverrides): Promise; 112 | 113 | renounceOwnership(overrides?: CallOverrides): Promise; 114 | 115 | "renounceOwnership()"(overrides?: CallOverrides): Promise; 116 | 117 | transferOwnership( 118 | newOwner: string, 119 | overrides?: CallOverrides 120 | ): Promise; 121 | 122 | "transferOwnership(address)"( 123 | newOwner: string, 124 | overrides?: CallOverrides 125 | ): Promise; 126 | }; 127 | 128 | filters: { 129 | OwnershipTransferred( 130 | previousOwner: string | null, 131 | newOwner: string | null 132 | ): EventFilter; 133 | }; 134 | 135 | estimateGas: { 136 | owner(overrides?: CallOverrides): Promise; 137 | 138 | "owner()"(overrides?: CallOverrides): Promise; 139 | 140 | renounceOwnership(overrides?: Overrides): Promise; 141 | 142 | "renounceOwnership()"(overrides?: Overrides): Promise; 143 | 144 | transferOwnership( 145 | newOwner: string, 146 | overrides?: Overrides 147 | ): Promise; 148 | 149 | "transferOwnership(address)"( 150 | newOwner: string, 151 | overrides?: Overrides 152 | ): Promise; 153 | }; 154 | 155 | populateTransaction: { 156 | owner(overrides?: CallOverrides): Promise; 157 | 158 | "owner()"(overrides?: CallOverrides): Promise; 159 | 160 | renounceOwnership(overrides?: Overrides): Promise; 161 | 162 | "renounceOwnership()"(overrides?: Overrides): Promise; 163 | 164 | transferOwnership( 165 | newOwner: string, 166 | overrides?: Overrides 167 | ): Promise; 168 | 169 | "transferOwnership(address)"( 170 | newOwner: string, 171 | overrides?: Overrides 172 | ): Promise; 173 | }; 174 | } 175 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/MirrorBatchRegistration__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Signer } from "ethers"; 6 | import { Provider, TransactionRequest } from "@ethersproject/providers"; 7 | import { Contract, ContractFactory, Overrides } from "@ethersproject/contracts"; 8 | 9 | import type { MirrorBatchRegistration } from "../MirrorBatchRegistration"; 10 | 11 | export class MirrorBatchRegistration__factory extends ContractFactory { 12 | constructor(signer?: Signer) { 13 | super(_abi, _bytecode, signer); 14 | } 15 | 16 | deploy( 17 | token_: string, 18 | overrides?: Overrides 19 | ): Promise { 20 | return super.deploy( 21 | token_, 22 | overrides || {} 23 | ) as Promise; 24 | } 25 | getDeployTransaction( 26 | token_: string, 27 | overrides?: Overrides 28 | ): TransactionRequest { 29 | return super.getDeployTransaction(token_, overrides || {}); 30 | } 31 | attach(address: string): MirrorBatchRegistration { 32 | return super.attach(address) as MirrorBatchRegistration; 33 | } 34 | connect(signer: Signer): MirrorBatchRegistration__factory { 35 | return super.connect(signer) as MirrorBatchRegistration__factory; 36 | } 37 | static connect( 38 | address: string, 39 | signerOrProvider: Signer | Provider 40 | ): MirrorBatchRegistration { 41 | return new Contract( 42 | address, 43 | _abi, 44 | signerOrProvider 45 | ) as MirrorBatchRegistration; 46 | } 47 | } 48 | 49 | const _abi = [ 50 | { 51 | inputs: [ 52 | { 53 | internalType: "address", 54 | name: "token_", 55 | type: "address", 56 | }, 57 | ], 58 | stateMutability: "nonpayable", 59 | type: "constructor", 60 | }, 61 | { 62 | inputs: [ 63 | { 64 | internalType: "string[]", 65 | name: "labels", 66 | type: "string[]", 67 | }, 68 | { 69 | internalType: "address[]", 70 | name: "owners", 71 | type: "address[]", 72 | }, 73 | ], 74 | name: "registerBatch", 75 | outputs: [], 76 | stateMutability: "nonpayable", 77 | type: "function", 78 | }, 79 | ]; 80 | 81 | const _bytecode = 82 | "0x608060405234801561001057600080fd5b5060405161076638038061076683398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610082565b600060208284031215610065578081fd5b81516001600160a01b038116811461007b578182fd5b9392505050565b6106d5806100916000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80633c1b1c6e14610030575b600080fd5b61004361003e366004610441565b610045565b005b60008054604080517fdc4a7f7f00000000000000000000000000000000000000000000000000000000815290518693926100fe92859273ffffffffffffffffffffffffffffffffffffffff9092169163dc4a7f7f91600480820192602092909190829003018186803b1580156100ba57600080fd5b505afa1580156100ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f291906104ca565b9063ffffffff61036616565b6000546040517fdd62ed3e000000000000000000000000000000000000000000000000000000008152919250829173ffffffffffffffffffffffffffffffffffffffff9091169063dd62ed3e9061015b90339030906004016104e2565b60206040518083038186803b15801561017357600080fd5b505afa158015610187573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101ab91906104ca565b10156101ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101e3906105dd565b60405180910390fd5b6000546040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd9061024690339030908690600401610509565b602060405180830381600087803b15801561026057600080fd5b505af1158015610274573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029891906104aa565b5060005b8281101561035d5760005473ffffffffffffffffffffffffffffffffffffffff16631e59c5298888848181106102ce57fe5b90506020028101906102e0919061063a565b8888868181106102ec57fe5b90506020020160208101906103019190610406565b6040518463ffffffff1660e01b815260040161031f9392919061053a565b600060405180830381600087803b15801561033957600080fd5b505af115801561034d573d6000803e3d6000fd5b50506001909201915061029c9050565b50505050505050565b60008115806103815750508082028282828161037e57fe5b04145b6103b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101e3906105a6565b92915050565b60008083601f8401126103ce578182fd5b50813567ffffffffffffffff8111156103e5578182fd5b60208301915083602080830285010111156103ff57600080fd5b9250929050565b600060208284031215610417578081fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461043a578182fd5b9392505050565b60008060008060408587031215610456578283fd5b843567ffffffffffffffff8082111561046d578485fd5b610479888389016103bd565b90965094506020870135915080821115610491578384fd5b5061049e878288016103bd565b95989497509550505050565b6000602082840312156104bb578081fd5b8151801515811461043a578182fd5b6000602082840312156104db578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b60006040825283604083015283856060840137806060858401015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116830101905073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b60208082526014908201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604082015260600190565b60208082526036908201527f4d6972726f724261746368526567697374726174696f6e3a206e65656420746f60408201527f206772616e7420746f6b656e20616c6c6f77616e636500000000000000000000606082015260800190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261066e578283fd5b8084018035925067ffffffffffffffff831115610689578384fd5b602001925050368190038213156103ff57600080fdfea2646970667358221220bacec3447820a061cf41ca1ecbd2da0d1613aa01417fde3d40162bbadaf9cb8864736f6c63430006080033"; 83 | -------------------------------------------------------------------------------- /scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers, waffle } from "hardhat"; 2 | import fs from "fs"; 3 | 4 | export const ZERO_BYTES32 = ethers.constants.HashZero; 5 | export const ROOT = "xyz"; 6 | export const subnameWallet = "mirror"; 7 | 8 | const config = { 9 | production: { 10 | ENS_REGISTRY_ADDRESS: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", 11 | ROOT_NAME: "mirror.xyz", 12 | ROOT_NODE: ethers.utils.namehash("mirror.xyz"), 13 | }, 14 | test: { 15 | ENS_REGISTRY_ADDRESS: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", 16 | ROOT_NAME: "mirror.test", 17 | ROOT_NODE: ethers.utils.namehash("mirror.test"), 18 | }, 19 | }; 20 | 21 | const NETWORK_MAP = { 22 | "1": "mainnet", 23 | "3": "ropsten", 24 | "4": "rinkeby", 25 | "1337": "hardhat", 26 | "31337": "hardhat", 27 | }; 28 | 29 | let isLocal = false; 30 | 31 | async function main() { 32 | const chainId = (await waffle.provider.getNetwork()).chainId; 33 | const networkName = NETWORK_MAP[chainId]; 34 | 35 | console.log(`Deploying to ${networkName}`); 36 | 37 | isLocal = networkName === "hardhat"; 38 | 39 | let owner; 40 | let ensAddress; 41 | let ensRegistry; 42 | let ENS_REGISTRY_ADDRESS; 43 | let ROOT_NAME; 44 | let ROOT_NODE; 45 | 46 | if (networkName === "mainnet") { 47 | ({ ENS_REGISTRY_ADDRESS, ROOT_NAME, ROOT_NODE } = config.production); 48 | } else { 49 | ({ ENS_REGISTRY_ADDRESS, ROOT_NAME, ROOT_NODE } = config.test); 50 | } 51 | 52 | if (isLocal) { 53 | console.log("deploying ENS registry"); 54 | const ENSRegistry = await ethers.getContractFactory("ENSRegistry"); 55 | ensRegistry = await ENSRegistry.deploy(); 56 | await ensRegistry.deployed(); 57 | 58 | ensAddress = ensRegistry.address; 59 | } else { 60 | ensAddress = ENS_REGISTRY_ADDRESS; 61 | } 62 | 63 | // console.log("Deploying $WRITE"); 64 | // const MirrorWriteToken = await ethers.getContractFactory("MirrorWriteToken"); 65 | // const mirrorWriteToken = await MirrorWriteToken.deploy(); 66 | // await mirrorWriteToken.deployed(); 67 | 68 | // console.log("Deploying Batch Registration"); 69 | // const MirrorBatchRegistration = await ethers.getContractFactory( 70 | // "MirrorBatchRegistration" 71 | // ); 72 | // const mirrorBatchRegistration = await MirrorBatchRegistration.deploy( 73 | // mirrorWriteToken.address 74 | // ); 75 | // await mirrorBatchRegistration.deployed(); 76 | 77 | // console.log("Deploying ENS Resolver"); 78 | // const MirrorENSResolver = await ethers.getContractFactory( 79 | // "MirrorENSResolver" 80 | // ); 81 | // const mirrorENSResolver = await MirrorENSResolver.deploy(); 82 | // await mirrorENSResolver.deployed(); 83 | 84 | // console.log("Deploying ENS Registrar"); 85 | // const MirrorENSRegistrar = await ethers.getContractFactory( 86 | // "MirrorENSRegistrar" 87 | // ); 88 | // const mirrorENSRegistrar = await MirrorENSRegistrar.deploy( 89 | // ROOT_NAME, 90 | // ROOT_NODE, 91 | // ensAddress, 92 | // mirrorENSResolver.address, 93 | // mirrorWriteToken.address 94 | // ); 95 | // await mirrorENSRegistrar.deployed(); 96 | 97 | console.log("Deploying PublicationRoles"); 98 | const PublicationRoles = await ethers.getContractFactory("PublicationRoles"); 99 | const publicationRoles = await PublicationRoles.deploy(ensAddress); 100 | await publicationRoles.deployed(); 101 | 102 | console.log(publicationRoles.address); 103 | 104 | // console.log("is local", isLocal); 105 | // if (isLocal) { 106 | // const accounts = await ethers.getSigners(); 107 | // owner = accounts[0].address; 108 | // owner = accounts[0]; 109 | 110 | // const ReverseRegistrar = await ethers.getContractFactory( 111 | // "MirrorENSReverseRegistrar" 112 | // ); 113 | // const reverseRegistrar = await ReverseRegistrar.deploy( 114 | // ensRegistry.address, 115 | // mirrorENSRegistrar.address 116 | // ); 117 | // await reverseRegistrar.deployed(); 118 | 119 | // // Setup root. 120 | // await ensRegistry.setSubnodeOwner( 121 | // ZERO_BYTES32, 122 | // ethers.utils.keccak256(ethers.utils.toUtf8Bytes("xyz")), 123 | // owner.address 124 | // ); 125 | // await ensRegistry.setSubnodeOwner( 126 | // ethers.utils.namehash("xyz"), 127 | // ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mirror")), 128 | // mirrorENSRegistrar.address 129 | // ); 130 | // await ensRegistry.setSubnodeOwner( 131 | // ZERO_BYTES32, 132 | // ethers.utils.keccak256(ethers.utils.toUtf8Bytes("reverse")), 133 | // owner.address 134 | // ); 135 | // await ensRegistry.setSubnodeOwner( 136 | // ethers.utils.namehash("reverse"), 137 | // ethers.utils.keccak256(ethers.utils.toUtf8Bytes("addr")), 138 | // reverseRegistrar.address 139 | // ); 140 | // } 141 | 142 | // console.log("Setting registrar on $WRITE"); 143 | // await mirrorWriteToken.setENSRegistrar(mirrorENSRegistrar.address); 144 | // console.log("Transferring ownership of Resolver to Registrar"); 145 | // await mirrorENSResolver.transferOwnership(mirrorENSRegistrar.address); 146 | // console.log("Updating ENS Reverse Registrar"); 147 | // await mirrorENSRegistrar.updateENSReverseRegistrar(); 148 | 149 | // const info = { 150 | // Contracts: { 151 | // MirrorWriteToken: mirrorWriteToken.address, 152 | // MirrorENSResolver: mirrorENSResolver.address, 153 | // MirrorENSRegistrar: mirrorENSRegistrar.address, 154 | // MirrorBatchRegistration: mirrorBatchRegistration.address, 155 | // }, 156 | // }; 157 | 158 | // console.log(info); 159 | 160 | // if (!isLocal) { 161 | // fs.writeFileSync( 162 | // `${__dirname}/../networks/${networkName}.json`, 163 | // JSON.stringify(info, null, 2) 164 | // ); 165 | // } 166 | } 167 | 168 | main() 169 | .then(() => process.exit(0)) 170 | .catch((error) => { 171 | console.error(error); 172 | process.exit(1); 173 | }); 174 | -------------------------------------------------------------------------------- /contracts/ens/MirrorENSRegistrar.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | 4 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 5 | import {IENS} from "./interfaces/IENS.sol"; 6 | import {IENSResolver} from "./interfaces/IENSResolver.sol"; 7 | import {IMirrorENSRegistrar} from "./interfaces/IMirrorENSRegistrar.sol"; 8 | 9 | contract MirrorENSRegistrar is IMirrorENSRegistrar, Ownable { 10 | // ============ Immutable Storage ============ 11 | 12 | /** 13 | * The name of the ENS root, e.g. "mirror.xyz". 14 | * @dev dependency injectable for testnet. 15 | */ 16 | string public rootName; 17 | 18 | /** 19 | * The node of the root name (e.g. namehash(mirror.xyz)) 20 | */ 21 | bytes32 public immutable rootNode; 22 | 23 | /** 24 | * The address of the public ENS registry. 25 | * @dev Dependency-injectable for testing purposes, but otherwise this is the 26 | * canonical ENS registry at 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e. 27 | */ 28 | IENS public immutable ensRegistry; 29 | 30 | /** 31 | * The address of the MirrorWriteToken that gates access to this namespace. 32 | */ 33 | address public immutable writeToken; 34 | 35 | /** 36 | * The address of the MirrorENSResolver. 37 | */ 38 | IENSResolver public immutable ensResolver; 39 | 40 | // ============ Events ============ 41 | 42 | event RootNodeOwnerChange(bytes32 indexed node, address indexed owner); 43 | event RegisteredENS(address indexed _owner, string _ens); 44 | event UpdatedENS(address indexed _owner, string _ens); 45 | 46 | // ============ Modifiers ============ 47 | 48 | /** 49 | * @dev Modifier to check whether the `msg.sender` is the MirrorWriteToken. 50 | * If it is, it will run the function. Otherwise, it will revert. 51 | */ 52 | modifier onlyWriteToken() { 53 | require( 54 | msg.sender == writeToken, 55 | "MirrorENSRegistrar: caller is not the Mirror Write Token" 56 | ); 57 | _; 58 | } 59 | 60 | // ============ Constructor ============ 61 | 62 | /** 63 | * @notice Constructor that sets the ENS root name and root node to manage. 64 | * @param rootName_ The root name (e.g. mirror.xyz). 65 | * @param rootNode_ The node of the root name (e.g. namehash(mirror.xyz)). 66 | * @param ensRegistry_ The address of the ENS registry 67 | * @param ensResolver_ The address of the ENS resolver 68 | * @param writeToken_ The address of the Mirror Write Token 69 | */ 70 | constructor( 71 | string memory rootName_, 72 | bytes32 rootNode_, 73 | address ensRegistry_, 74 | address ensResolver_, 75 | address writeToken_ 76 | ) public { 77 | rootName = rootName_; 78 | rootNode = rootNode_; 79 | 80 | writeToken = writeToken_; 81 | 82 | // Registrations are cheaper if these are instantiated. 83 | ensRegistry = IENS(ensRegistry_); 84 | ensResolver = IENSResolver(ensResolver_); 85 | } 86 | 87 | // ============ Registration ============ 88 | 89 | /** 90 | * @notice Assigns an ENS subdomain of the root node to a target address. 91 | * Registers both the forward. Can only be called by writeToken. 92 | * @param label_ The subdomain label. 93 | * @param owner_ The owner of the subdomain. 94 | */ 95 | function register(string calldata label_, address owner_) 96 | external 97 | override 98 | onlyWriteToken 99 | { 100 | bytes32 labelNode = keccak256(abi.encodePacked(label_)); 101 | bytes32 node = keccak256(abi.encodePacked(rootNode, labelNode)); 102 | 103 | require( 104 | ensRegistry.owner(node) == address(0), 105 | "MirrorENSManager: label is already owned" 106 | ); 107 | 108 | // Forward ENS 109 | ensRegistry.setSubnodeRecord( 110 | rootNode, 111 | labelNode, 112 | owner_, 113 | address(ensResolver), 114 | 0 115 | ); 116 | ensResolver.setAddr(node, owner_); 117 | 118 | emit RegisteredENS(owner_, label_); 119 | } 120 | 121 | // ============ ENS Management ============ 122 | 123 | /** 124 | * @notice This function must be called when the ENSRegistrar contract is replaced 125 | * and the address of the new ENSRegistrar should be provided. 126 | * @param _newOwner The address of the new ENS Registrar that will manage the root node. 127 | */ 128 | function changeRootNodeOwner(address _newOwner) 129 | external 130 | override 131 | onlyOwner 132 | { 133 | ensRegistry.setOwner(rootNode, _newOwner); 134 | emit RootNodeOwnerChange(rootNode, _newOwner); 135 | } 136 | 137 | // ============ ENS Subnode Management ============ 138 | 139 | function labelOwner(string calldata label) 140 | external 141 | view 142 | override 143 | returns (address) 144 | { 145 | bytes32 labelNode = keccak256(abi.encodePacked(label)); 146 | bytes32 node = keccak256(abi.encodePacked(rootNode, labelNode)); 147 | 148 | return ensRegistry.owner(node); 149 | } 150 | 151 | function changeLabelOwner(string calldata label_, address newOwner_) 152 | external 153 | override 154 | { 155 | bytes32 labelNode = keccak256(abi.encodePacked(label_)); 156 | bytes32 node = keccak256(abi.encodePacked(rootNode, labelNode)); 157 | 158 | require( 159 | ensRegistry.owner(node) == msg.sender, 160 | "MirrorENSManager: sender does not own label" 161 | ); 162 | 163 | // Forward ENS 164 | ensRegistry.setSubnodeRecord( 165 | rootNode, 166 | labelNode, 167 | newOwner_, 168 | address(ensResolver), 169 | 0 170 | ); 171 | ensResolver.setAddr(node, newOwner_); 172 | 173 | emit UpdatedENS( 174 | newOwner_, 175 | string(abi.encodePacked(label_, ".", rootName)) 176 | ); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /test/WriteDistributionHelperV1.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { BigNumber } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import BalanceTree from "../merkle-distribution/balance-tree"; 5 | 6 | import setup from "./setup"; 7 | 8 | const oneToken = "1000000000000000000"; 9 | const twoTokens = "2000000000000000000"; 10 | const twentyOneTokens = "21000000000000000000"; 11 | 12 | const overrides = { 13 | gasLimit: 9500000, 14 | }; 15 | 16 | describe("WriteDistributionHelper", () => { 17 | // Contracts 18 | let token; 19 | let writeDistributionHelper; 20 | 21 | // Accounts 22 | let owner; 23 | let account1; 24 | let account2; 25 | let account3; 26 | 27 | beforeEach(async () => { 28 | ({ mirrorWriteToken: token, writeDistributionHelper } = await setup()); 29 | 30 | [owner, account1, account2, account3] = await ethers.getSigners(); 31 | }); 32 | 33 | describe("#distributeTo", () => { 34 | describe("when the account has an invite token", () => { 35 | let transaction; 36 | let receipt; 37 | 38 | beforeEach(async () => { 39 | await token 40 | .connect(owner) 41 | .mint(writeDistributionHelper.address, twoTokens); 42 | 43 | transaction = await writeDistributionHelper 44 | .connect(owner) 45 | .distributeTo([account1.address, account2.address]); 46 | 47 | receipt = await transaction.wait(); 48 | }); 49 | 50 | it("sends the user a token", async () => { 51 | expect((await token.balanceOf(account1.address)).toString()).to.equal( 52 | oneToken 53 | ); 54 | expect((await token.balanceOf(account2.address)).toString()).to.equal( 55 | oneToken 56 | ); 57 | expect((await token.balanceOf(account1.address)).toString()).to.equal( 58 | oneToken 59 | ); 60 | }); 61 | 62 | it("uses 70068 gas", () => { 63 | const { gasUsed } = receipt; 64 | expect(gasUsed).to.eq(70068); 65 | }); 66 | }); 67 | 68 | describe("when called by the non-owner", () => { 69 | let transaction; 70 | 71 | beforeEach(async () => { 72 | await token 73 | .connect(owner) 74 | .mint(writeDistributionHelper.address, twoTokens); 75 | }); 76 | 77 | it("reverts", async () => { 78 | transaction = writeDistributionHelper 79 | .connect(account1) 80 | .distributeTo([account1.address, account2.address]); 81 | await expect(transaction).to.be.revertedWith( 82 | "WriteDistributionV1: caller is not the owner" 83 | ); 84 | }); 85 | }); 86 | }); 87 | 88 | describe("#setMerkleRoot", () => { 89 | it("sets the merkle root when called by owner", async () => { 90 | const tree = new BalanceTree([ 91 | { account: account2.address, amount: BigNumber.from(10) }, 92 | { account: account3.address, amount: BigNumber.from(11) }, 93 | ]); 94 | 95 | await writeDistributionHelper 96 | .connect(owner) 97 | .setMerkleRoot(tree.getHexRoot()); 98 | 99 | const result = await writeDistributionHelper.merkleRoot(); 100 | expect(result).to.eq(tree.getHexRoot()); 101 | }); 102 | }); 103 | 104 | describe("#claim", () => { 105 | describe("two account tree", () => { 106 | let tree: BalanceTree; 107 | 108 | const amounts = [10, 11]; 109 | 110 | beforeEach("set merkle root", async () => { 111 | tree = new BalanceTree([ 112 | { account: account2.address, amount: BigNumber.from(amounts[0]) }, 113 | { account: account3.address, amount: BigNumber.from(amounts[1]) }, 114 | ]); 115 | 116 | await token 117 | .connect(owner) 118 | .mint(writeDistributionHelper.address, twentyOneTokens); 119 | 120 | await writeDistributionHelper 121 | .connect(owner) 122 | .setMerkleRoot(tree.getHexRoot()); 123 | }); 124 | 125 | it("first successful claim", async () => { 126 | const proof0 = tree.getProof( 127 | 0, 128 | account2.address, 129 | BigNumber.from(amounts[0]) 130 | ); 131 | 132 | await expect( 133 | writeDistributionHelper.claim( 134 | 0, 135 | account2.address, 136 | amounts[0], 137 | proof0, 138 | overrides 139 | ) 140 | ) 141 | .to.emit(writeDistributionHelper, "Claimed") 142 | .withArgs(0, account2.address, amounts[0]); 143 | }); 144 | 145 | it("second successful claim", async () => { 146 | const proof0 = tree.getProof( 147 | 1, 148 | account3.address, 149 | BigNumber.from(amounts[1]) 150 | ); 151 | 152 | await expect( 153 | writeDistributionHelper.claim( 154 | 1, 155 | account3.address, 156 | amounts[1], 157 | proof0, 158 | overrides 159 | ) 160 | ) 161 | .to.emit(writeDistributionHelper, "Claimed") 162 | .withArgs(1, account3.address, amounts[1]); 163 | }); 164 | 165 | it("transfers the token", async () => { 166 | const proof0 = tree.getProof( 167 | 0, 168 | account2.address, 169 | BigNumber.from(amounts[0]) 170 | ); 171 | expect(await token.balanceOf(account2.address)).to.eq(0); 172 | 173 | await writeDistributionHelper.claim( 174 | 0, 175 | account2.address, 176 | amounts[0], 177 | proof0, 178 | overrides 179 | ); 180 | expect(await token.balanceOf(account2.address)).to.eq(amounts[0]); 181 | 182 | const proof1 = tree.getProof( 183 | 1, 184 | account3.address, 185 | BigNumber.from(amounts[1]) 186 | ); 187 | expect(await token.balanceOf(account3.address)).to.eq(0); 188 | 189 | await writeDistributionHelper.claim( 190 | 1, 191 | account3.address, 192 | amounts[1], 193 | proof1, 194 | overrides 195 | ); 196 | expect(await token.balanceOf(account3.address)).to.eq(amounts[1]); 197 | }); 198 | }); 199 | }); 200 | }); 201 | -------------------------------------------------------------------------------- /contracts/helpers/WriteDistributionHelperV1.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-or-later 2 | pragma solidity 0.6.8; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import { 6 | MerkleProof 7 | } from "@openzeppelin/contracts/cryptography/MerkleProof.sol"; 8 | import {IMirrorWriteToken} from "../interfaces/IMirrorWriteToken.sol"; 9 | import {SafeMath} from "../lib/SafeMath.sol"; 10 | 11 | /** 12 | * @title WriteDistributionHelperV1 13 | * @author MirrorXYZ 14 | * 15 | * A helper contract for distributing $WRITE token. 16 | */ 17 | contract WriteDistributionHelperV1 { 18 | // ============ Constants ============ 19 | 20 | uint64 constant units = 1e18; 21 | 22 | // ============ Immutable Storage ============ 23 | 24 | address public immutable token; 25 | 26 | // ============ Mutable Storage ============ 27 | 28 | address private _owner; 29 | /** 30 | * @dev Allows for two-step ownership transfer, whereby the next owner 31 | * needs to accept the ownership transfer explicitly. 32 | */ 33 | address private _nextOwner; 34 | bytes32 public merkleRoot; 35 | mapping(uint256 => uint256) private claimedBitMap; 36 | 37 | // ============ Events ============ 38 | 39 | event Distributed(address account); 40 | event RootUpdated(bytes32 oldRoot, bytes32 newRoot); 41 | event Claimed(uint256 index, address account, uint256 amount); 42 | event Transfer(address indexed from, address indexed to, uint256 value); 43 | event OwnershipTransferred( 44 | address indexed previousOwner, 45 | address indexed newOwner 46 | ); 47 | 48 | // ============ Modifiers ============ 49 | 50 | modifier onlyOwner() { 51 | require(isOwner(), "WriteDistributionV1: caller is not the owner."); 52 | _; 53 | } 54 | 55 | modifier onlyNextOwner() { 56 | require( 57 | isNextOwner(), 58 | "WriteDistributionV1: current owner must set caller as next owner." 59 | ); 60 | _; 61 | } 62 | 63 | // ============ Constructor ============ 64 | 65 | constructor(address token_) public { 66 | token = token_; 67 | 68 | _owner = tx.origin; 69 | emit OwnershipTransferred(address(0), _owner); 70 | } 71 | 72 | // ============ Ownership ============ 73 | 74 | /** 75 | * @dev Returns true if the caller is the current owner. 76 | */ 77 | function isOwner() public view returns (bool) { 78 | return msg.sender == _owner; 79 | } 80 | 81 | /** 82 | * @dev Returns true if the caller is the next owner. 83 | */ 84 | function isNextOwner() public view returns (bool) { 85 | return msg.sender == _nextOwner; 86 | } 87 | 88 | /** 89 | * @dev Allows a new account (`newOwner`) to accept ownership. 90 | * Can only be called by the current owner. 91 | */ 92 | function transferOwnership(address nextOwner_) external onlyOwner { 93 | require( 94 | nextOwner_ != address(0), 95 | "WriteDistributionV1: next owner is the zero address." 96 | ); 97 | 98 | _nextOwner = nextOwner_; 99 | } 100 | 101 | /** 102 | * @dev Cancel a transfer of ownership to a new account. 103 | * Can only be called by the current owner. 104 | */ 105 | function cancelOwnershipTransfer() external onlyOwner { 106 | delete _nextOwner; 107 | } 108 | 109 | /** 110 | * @dev Transfers ownership of the contract to the caller. 111 | * Can only be called by a new potential owner set by the current owner. 112 | */ 113 | function acceptOwnership() external onlyNextOwner { 114 | delete _nextOwner; 115 | 116 | emit OwnershipTransferred(_owner, msg.sender); 117 | 118 | _owner = msg.sender; 119 | } 120 | 121 | /** 122 | * @dev Leaves the contract without owner. It will not be possible to call 123 | * `onlyOwner` functions anymore. Can only be called by the current owner. 124 | * 125 | * NOTE: Renouncing ownership will leave the contract without an owner, 126 | * thereby removing any functionality that is only available to the owner. 127 | */ 128 | function renounceOwnership() external onlyOwner { 129 | emit OwnershipTransferred(_owner, address(0)); 130 | _owner = address(0); 131 | } 132 | 133 | // ============ Distribution ============ 134 | 135 | function distributeTo(address[] memory addresses) public onlyOwner returns (bool ok) { 136 | IMirrorWriteToken tokenContract = IMirrorWriteToken(token); 137 | 138 | for (uint256 i = 0; i < addresses.length; i++) { 139 | tokenContract.transfer(addresses[i], units); 140 | emit Distributed(addresses[i]); 141 | } 142 | 143 | return true; 144 | } 145 | 146 | // ============ Merkle-Tree Token Claim ============ 147 | 148 | function setMerkleRoot(bytes32 merkleRoot_) external onlyOwner { 149 | emit RootUpdated(merkleRoot, merkleRoot_); 150 | merkleRoot = merkleRoot_; 151 | } 152 | 153 | function isClaimed(uint256 index) public view returns (bool) { 154 | uint256 claimedWordIndex = index / 256; 155 | uint256 claimedBitIndex = index % 256; 156 | uint256 claimedWord = claimedBitMap[claimedWordIndex]; 157 | uint256 mask = (1 << claimedBitIndex); 158 | return claimedWord & mask == mask; 159 | } 160 | 161 | function _setClaimed(uint256 index) private { 162 | uint256 claimedWordIndex = index / 256; 163 | uint256 claimedBitIndex = index % 256; 164 | claimedBitMap[claimedWordIndex] = 165 | claimedBitMap[claimedWordIndex] | 166 | (1 << claimedBitIndex); 167 | } 168 | 169 | function claim( 170 | uint256 index, 171 | address account, 172 | uint256 amount, 173 | bytes32[] calldata merkleProof 174 | ) external { 175 | require(!isClaimed(index), "WriteDistributionV1: already claimed."); 176 | 177 | // Verify the merkle proof. 178 | bytes32 node = keccak256(abi.encodePacked(index, account, amount)); 179 | require( 180 | MerkleProof.verify(merkleProof, merkleRoot, node), 181 | "WriteDistributionV1: Invalid proof." 182 | ); 183 | 184 | // Mark it claimed and send the token. 185 | _setClaimed(index); 186 | require( 187 | IMirrorWriteToken(token).transfer(account, amount), 188 | "WriteDistributionV1: Transfer failed." 189 | ); 190 | 191 | emit Claimed(index, account, amount); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /ts-types/contracts/IENSReverseRegistrar.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | Overrides, 17 | CallOverrides, 18 | } from "@ethersproject/contracts"; 19 | import { BytesLike } from "@ethersproject/bytes"; 20 | import { Listener, Provider } from "@ethersproject/providers"; 21 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 22 | 23 | interface IENSReverseRegistrarInterface extends ethers.utils.Interface { 24 | functions: { 25 | "claim(address)": FunctionFragment; 26 | "claimWithResolver(address,address)": FunctionFragment; 27 | "node(address)": FunctionFragment; 28 | "setName(string)": FunctionFragment; 29 | }; 30 | 31 | encodeFunctionData(functionFragment: "claim", values: [string]): string; 32 | encodeFunctionData( 33 | functionFragment: "claimWithResolver", 34 | values: [string, string] 35 | ): string; 36 | encodeFunctionData(functionFragment: "node", values: [string]): string; 37 | encodeFunctionData(functionFragment: "setName", values: [string]): string; 38 | 39 | decodeFunctionResult(functionFragment: "claim", data: BytesLike): Result; 40 | decodeFunctionResult( 41 | functionFragment: "claimWithResolver", 42 | data: BytesLike 43 | ): Result; 44 | decodeFunctionResult(functionFragment: "node", data: BytesLike): Result; 45 | decodeFunctionResult(functionFragment: "setName", data: BytesLike): Result; 46 | 47 | events: {}; 48 | } 49 | 50 | export class IENSReverseRegistrar extends Contract { 51 | connect(signerOrProvider: Signer | Provider | string): this; 52 | attach(addressOrName: string): this; 53 | deployed(): Promise; 54 | 55 | on(event: EventFilter | string, listener: Listener): this; 56 | once(event: EventFilter | string, listener: Listener): this; 57 | addListener(eventName: EventFilter | string, listener: Listener): this; 58 | removeAllListeners(eventName: EventFilter | string): this; 59 | removeListener(eventName: any, listener: Listener): this; 60 | 61 | interface: IENSReverseRegistrarInterface; 62 | 63 | functions: { 64 | claim(_owner: string, overrides?: Overrides): Promise; 65 | 66 | "claim(address)"( 67 | _owner: string, 68 | overrides?: Overrides 69 | ): Promise; 70 | 71 | claimWithResolver( 72 | _owner: string, 73 | _resolver: string, 74 | overrides?: Overrides 75 | ): Promise; 76 | 77 | "claimWithResolver(address,address)"( 78 | _owner: string, 79 | _resolver: string, 80 | overrides?: Overrides 81 | ): Promise; 82 | 83 | node(_addr: string, overrides?: CallOverrides): Promise<[string]>; 84 | 85 | "node(address)"( 86 | _addr: string, 87 | overrides?: CallOverrides 88 | ): Promise<[string]>; 89 | 90 | setName(_name: string, overrides?: Overrides): Promise; 91 | 92 | "setName(string)"( 93 | _name: string, 94 | overrides?: Overrides 95 | ): Promise; 96 | }; 97 | 98 | claim(_owner: string, overrides?: Overrides): Promise; 99 | 100 | "claim(address)"( 101 | _owner: string, 102 | overrides?: Overrides 103 | ): Promise; 104 | 105 | claimWithResolver( 106 | _owner: string, 107 | _resolver: string, 108 | overrides?: Overrides 109 | ): Promise; 110 | 111 | "claimWithResolver(address,address)"( 112 | _owner: string, 113 | _resolver: string, 114 | overrides?: Overrides 115 | ): Promise; 116 | 117 | node(_addr: string, overrides?: CallOverrides): Promise; 118 | 119 | "node(address)"(_addr: string, overrides?: CallOverrides): Promise; 120 | 121 | setName(_name: string, overrides?: Overrides): Promise; 122 | 123 | "setName(string)"( 124 | _name: string, 125 | overrides?: Overrides 126 | ): Promise; 127 | 128 | callStatic: { 129 | claim(_owner: string, overrides?: CallOverrides): Promise; 130 | 131 | "claim(address)"( 132 | _owner: string, 133 | overrides?: CallOverrides 134 | ): Promise; 135 | 136 | claimWithResolver( 137 | _owner: string, 138 | _resolver: string, 139 | overrides?: CallOverrides 140 | ): Promise; 141 | 142 | "claimWithResolver(address,address)"( 143 | _owner: string, 144 | _resolver: string, 145 | overrides?: CallOverrides 146 | ): Promise; 147 | 148 | node(_addr: string, overrides?: CallOverrides): Promise; 149 | 150 | "node(address)"(_addr: string, overrides?: CallOverrides): Promise; 151 | 152 | setName(_name: string, overrides?: CallOverrides): Promise; 153 | 154 | "setName(string)"( 155 | _name: string, 156 | overrides?: CallOverrides 157 | ): Promise; 158 | }; 159 | 160 | filters: {}; 161 | 162 | estimateGas: { 163 | claim(_owner: string, overrides?: Overrides): Promise; 164 | 165 | "claim(address)"(_owner: string, overrides?: Overrides): Promise; 166 | 167 | claimWithResolver( 168 | _owner: string, 169 | _resolver: string, 170 | overrides?: Overrides 171 | ): Promise; 172 | 173 | "claimWithResolver(address,address)"( 174 | _owner: string, 175 | _resolver: string, 176 | overrides?: Overrides 177 | ): Promise; 178 | 179 | node(_addr: string, overrides?: CallOverrides): Promise; 180 | 181 | "node(address)"( 182 | _addr: string, 183 | overrides?: CallOverrides 184 | ): Promise; 185 | 186 | setName(_name: string, overrides?: Overrides): Promise; 187 | 188 | "setName(string)"(_name: string, overrides?: Overrides): Promise; 189 | }; 190 | 191 | populateTransaction: { 192 | claim(_owner: string, overrides?: Overrides): Promise; 193 | 194 | "claim(address)"( 195 | _owner: string, 196 | overrides?: Overrides 197 | ): Promise; 198 | 199 | claimWithResolver( 200 | _owner: string, 201 | _resolver: string, 202 | overrides?: Overrides 203 | ): Promise; 204 | 205 | "claimWithResolver(address,address)"( 206 | _owner: string, 207 | _resolver: string, 208 | overrides?: Overrides 209 | ): Promise; 210 | 211 | node( 212 | _addr: string, 213 | overrides?: CallOverrides 214 | ): Promise; 215 | 216 | "node(address)"( 217 | _addr: string, 218 | overrides?: CallOverrides 219 | ): Promise; 220 | 221 | setName( 222 | _name: string, 223 | overrides?: Overrides 224 | ): Promise; 225 | 226 | "setName(string)"( 227 | _name: string, 228 | overrides?: Overrides 229 | ): Promise; 230 | }; 231 | } 232 | -------------------------------------------------------------------------------- /contracts/ens/lib/ENSRegistry.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: BSD-2-Clause 2 | pragma solidity 0.6.8; 3 | 4 | import {IENS} from "../interfaces/IENS.sol"; 5 | 6 | /** 7 | * The ENS registry contract. 8 | */ 9 | contract ENSRegistry is IENS { 10 | struct Record { 11 | address owner; 12 | address resolver; 13 | uint64 ttl; 14 | } 15 | 16 | mapping(bytes32 => Record) records; 17 | mapping(address => mapping(address => bool)) operators; 18 | 19 | // Permits modifications only by the owner of the specified node. 20 | modifier authorised(bytes32 node) { 21 | address owner = records[node].owner; 22 | require(owner == msg.sender || operators[owner][msg.sender]); 23 | _; 24 | } 25 | 26 | /** 27 | * @dev Constructs a new ENS registrar. 28 | */ 29 | constructor() public { 30 | records[0x0].owner = msg.sender; 31 | } 32 | 33 | /** 34 | * @dev Sets the record for a node. 35 | * @param node The node to update. 36 | * @param owner The address of the new owner. 37 | * @param resolver The address of the resolver. 38 | * @param ttl The TTL in seconds. 39 | */ 40 | function setRecord( 41 | bytes32 node, 42 | address owner, 43 | address resolver, 44 | uint64 ttl 45 | ) external override { 46 | setOwner(node, owner); 47 | _setResolverAndTTL(node, resolver, ttl); 48 | } 49 | 50 | /** 51 | * @dev Sets the record for a subnode. 52 | * @param node The parent node. 53 | * @param label The hash of the label specifying the subnode. 54 | * @param owner The address of the new owner. 55 | * @param resolver The address of the resolver. 56 | * @param ttl The TTL in seconds. 57 | */ 58 | function setSubnodeRecord( 59 | bytes32 node, 60 | bytes32 label, 61 | address owner, 62 | address resolver, 63 | uint64 ttl 64 | ) external override { 65 | bytes32 subnode = setSubnodeOwner(node, label, owner); 66 | _setResolverAndTTL(subnode, resolver, ttl); 67 | } 68 | 69 | /** 70 | * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node. 71 | * @param node The node to transfer ownership of. 72 | * @param owner The address of the new owner. 73 | */ 74 | function setOwner(bytes32 node, address owner) 75 | public 76 | override 77 | authorised(node) 78 | { 79 | _setOwner(node, owner); 80 | emit Transfer(node, owner); 81 | } 82 | 83 | /** 84 | * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node. 85 | * @param node The parent node. 86 | * @param label The hash of the label specifying the subnode. 87 | * @param owner The address of the new owner. 88 | */ 89 | function setSubnodeOwner( 90 | bytes32 node, 91 | bytes32 label, 92 | address owner 93 | ) public override authorised(node) returns (bytes32) { 94 | bytes32 subnode = keccak256(abi.encodePacked(node, label)); 95 | _setOwner(subnode, owner); 96 | emit NewOwner(node, label, owner); 97 | return subnode; 98 | } 99 | 100 | /** 101 | * @dev Sets the resolver address for the specified node. 102 | * @param node The node to update. 103 | * @param resolver The address of the resolver. 104 | */ 105 | function setResolver(bytes32 node, address resolver) 106 | public 107 | override 108 | authorised(node) 109 | { 110 | emit NewResolver(node, resolver); 111 | records[node].resolver = resolver; 112 | } 113 | 114 | /** 115 | * @dev Sets the TTL for the specified node. 116 | * @param node The node to update. 117 | * @param ttl The TTL in seconds. 118 | */ 119 | function setTTL(bytes32 node, uint64 ttl) public override authorised(node) { 120 | emit NewTTL(node, ttl); 121 | records[node].ttl = ttl; 122 | } 123 | 124 | /** 125 | * @dev Enable or disable approval for a third party ("operator") to manage 126 | * all of `msg.sender`'s ENS records. Emits the ApprovalForAll event. 127 | * @param operator Address to add to the set of authorized operators. 128 | * @param approved True if the operator is approved, false to revoke approval. 129 | */ 130 | function setApprovalForAll(address operator, bool approved) 131 | external 132 | override 133 | { 134 | operators[msg.sender][operator] = approved; 135 | emit ApprovalForAll(msg.sender, operator, approved); 136 | } 137 | 138 | /** 139 | * @dev Returns the address that owns the specified node. 140 | * @param node The specified node. 141 | * @return address of the owner. 142 | */ 143 | function owner(bytes32 node) public view override returns (address) { 144 | address addr = records[node].owner; 145 | if (addr == address(this)) { 146 | return address(0x0); 147 | } 148 | 149 | return addr; 150 | } 151 | 152 | /** 153 | * @dev Returns the address of the resolver for the specified node. 154 | * @param node The specified node. 155 | * @return address of the resolver. 156 | */ 157 | function resolver(bytes32 node) public view override returns (address) { 158 | return records[node].resolver; 159 | } 160 | 161 | /** 162 | * @dev Returns the TTL of a node, and any records associated with it. 163 | * @param node The specified node. 164 | * @return ttl of the node. 165 | */ 166 | function ttl(bytes32 node) public view override returns (uint64) { 167 | return records[node].ttl; 168 | } 169 | 170 | /** 171 | * @dev Returns whether a record has been imported to the registry. 172 | * @param node The specified node. 173 | * @return Bool if record exists 174 | */ 175 | function recordExists(bytes32 node) public view override returns (bool) { 176 | return records[node].owner != address(0x0); 177 | } 178 | 179 | /** 180 | * @dev Query if an address is an authorized operator for another address. 181 | * @param owner The address that owns the records. 182 | * @param operator The address that acts on behalf of the owner. 183 | * @return True if `operator` is an approved operator for `owner`, false otherwise. 184 | */ 185 | function isApprovedForAll(address owner, address operator) 186 | external 187 | view 188 | override 189 | returns (bool) 190 | { 191 | return operators[owner][operator]; 192 | } 193 | 194 | function _setOwner(bytes32 node, address owner) internal { 195 | records[node].owner = owner; 196 | } 197 | 198 | function _setResolverAndTTL( 199 | bytes32 node, 200 | address resolver, 201 | uint64 ttl 202 | ) internal { 203 | if (resolver != records[node].resolver) { 204 | records[node].resolver = resolver; 205 | emit NewResolver(node, resolver); 206 | } 207 | 208 | if (ttl != records[node].ttl) { 209 | records[node].ttl = ttl; 210 | emit NewTTL(node, ttl); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/ERC20Burnable__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { ERC20Burnable } from "../ERC20Burnable"; 9 | 10 | export class ERC20Burnable__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): ERC20Burnable { 15 | return new Contract(address, _abi, signerOrProvider) as ERC20Burnable; 16 | } 17 | } 18 | 19 | const _abi = [ 20 | { 21 | anonymous: false, 22 | inputs: [ 23 | { 24 | indexed: true, 25 | internalType: "address", 26 | name: "owner", 27 | type: "address", 28 | }, 29 | { 30 | indexed: true, 31 | internalType: "address", 32 | name: "spender", 33 | type: "address", 34 | }, 35 | { 36 | indexed: false, 37 | internalType: "uint256", 38 | name: "value", 39 | type: "uint256", 40 | }, 41 | ], 42 | name: "Approval", 43 | type: "event", 44 | }, 45 | { 46 | anonymous: false, 47 | inputs: [ 48 | { 49 | indexed: true, 50 | internalType: "address", 51 | name: "from", 52 | type: "address", 53 | }, 54 | { 55 | indexed: true, 56 | internalType: "address", 57 | name: "to", 58 | type: "address", 59 | }, 60 | { 61 | indexed: false, 62 | internalType: "uint256", 63 | name: "value", 64 | type: "uint256", 65 | }, 66 | ], 67 | name: "Transfer", 68 | type: "event", 69 | }, 70 | { 71 | inputs: [ 72 | { 73 | internalType: "address", 74 | name: "owner", 75 | type: "address", 76 | }, 77 | { 78 | internalType: "address", 79 | name: "spender", 80 | type: "address", 81 | }, 82 | ], 83 | name: "allowance", 84 | outputs: [ 85 | { 86 | internalType: "uint256", 87 | name: "", 88 | type: "uint256", 89 | }, 90 | ], 91 | stateMutability: "view", 92 | type: "function", 93 | }, 94 | { 95 | inputs: [ 96 | { 97 | internalType: "address", 98 | name: "spender", 99 | type: "address", 100 | }, 101 | { 102 | internalType: "uint256", 103 | name: "amount", 104 | type: "uint256", 105 | }, 106 | ], 107 | name: "approve", 108 | outputs: [ 109 | { 110 | internalType: "bool", 111 | name: "", 112 | type: "bool", 113 | }, 114 | ], 115 | stateMutability: "nonpayable", 116 | type: "function", 117 | }, 118 | { 119 | inputs: [ 120 | { 121 | internalType: "address", 122 | name: "account", 123 | type: "address", 124 | }, 125 | ], 126 | name: "balanceOf", 127 | outputs: [ 128 | { 129 | internalType: "uint256", 130 | name: "", 131 | type: "uint256", 132 | }, 133 | ], 134 | stateMutability: "view", 135 | type: "function", 136 | }, 137 | { 138 | inputs: [ 139 | { 140 | internalType: "uint256", 141 | name: "amount", 142 | type: "uint256", 143 | }, 144 | ], 145 | name: "burn", 146 | outputs: [], 147 | stateMutability: "nonpayable", 148 | type: "function", 149 | }, 150 | { 151 | inputs: [ 152 | { 153 | internalType: "address", 154 | name: "account", 155 | type: "address", 156 | }, 157 | { 158 | internalType: "uint256", 159 | name: "amount", 160 | type: "uint256", 161 | }, 162 | ], 163 | name: "burnFrom", 164 | outputs: [], 165 | stateMutability: "nonpayable", 166 | type: "function", 167 | }, 168 | { 169 | inputs: [], 170 | name: "decimals", 171 | outputs: [ 172 | { 173 | internalType: "uint8", 174 | name: "", 175 | type: "uint8", 176 | }, 177 | ], 178 | stateMutability: "view", 179 | type: "function", 180 | }, 181 | { 182 | inputs: [ 183 | { 184 | internalType: "address", 185 | name: "spender", 186 | type: "address", 187 | }, 188 | { 189 | internalType: "uint256", 190 | name: "subtractedValue", 191 | type: "uint256", 192 | }, 193 | ], 194 | name: "decreaseAllowance", 195 | outputs: [ 196 | { 197 | internalType: "bool", 198 | name: "", 199 | type: "bool", 200 | }, 201 | ], 202 | stateMutability: "nonpayable", 203 | type: "function", 204 | }, 205 | { 206 | inputs: [ 207 | { 208 | internalType: "address", 209 | name: "spender", 210 | type: "address", 211 | }, 212 | { 213 | internalType: "uint256", 214 | name: "addedValue", 215 | type: "uint256", 216 | }, 217 | ], 218 | name: "increaseAllowance", 219 | outputs: [ 220 | { 221 | internalType: "bool", 222 | name: "", 223 | type: "bool", 224 | }, 225 | ], 226 | stateMutability: "nonpayable", 227 | type: "function", 228 | }, 229 | { 230 | inputs: [], 231 | name: "name", 232 | outputs: [ 233 | { 234 | internalType: "string", 235 | name: "", 236 | type: "string", 237 | }, 238 | ], 239 | stateMutability: "view", 240 | type: "function", 241 | }, 242 | { 243 | inputs: [], 244 | name: "symbol", 245 | outputs: [ 246 | { 247 | internalType: "string", 248 | name: "", 249 | type: "string", 250 | }, 251 | ], 252 | stateMutability: "view", 253 | type: "function", 254 | }, 255 | { 256 | inputs: [], 257 | name: "totalSupply", 258 | outputs: [ 259 | { 260 | internalType: "uint256", 261 | name: "", 262 | type: "uint256", 263 | }, 264 | ], 265 | stateMutability: "view", 266 | type: "function", 267 | }, 268 | { 269 | inputs: [ 270 | { 271 | internalType: "address", 272 | name: "recipient", 273 | type: "address", 274 | }, 275 | { 276 | internalType: "uint256", 277 | name: "amount", 278 | type: "uint256", 279 | }, 280 | ], 281 | name: "transfer", 282 | outputs: [ 283 | { 284 | internalType: "bool", 285 | name: "", 286 | type: "bool", 287 | }, 288 | ], 289 | stateMutability: "nonpayable", 290 | type: "function", 291 | }, 292 | { 293 | inputs: [ 294 | { 295 | internalType: "address", 296 | name: "sender", 297 | type: "address", 298 | }, 299 | { 300 | internalType: "address", 301 | name: "recipient", 302 | type: "address", 303 | }, 304 | { 305 | internalType: "uint256", 306 | name: "amount", 307 | type: "uint256", 308 | }, 309 | ], 310 | name: "transferFrom", 311 | outputs: [ 312 | { 313 | internalType: "bool", 314 | name: "", 315 | type: "bool", 316 | }, 317 | ], 318 | stateMutability: "nonpayable", 319 | type: "function", 320 | }, 321 | ]; 322 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/IMirrorWriteToken__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { IMirrorWriteToken } from "../IMirrorWriteToken"; 9 | 10 | export class IMirrorWriteToken__factory { 11 | static connect( 12 | address: string, 13 | signerOrProvider: Signer | Provider 14 | ): IMirrorWriteToken { 15 | return new Contract(address, _abi, signerOrProvider) as IMirrorWriteToken; 16 | } 17 | } 18 | 19 | const _abi = [ 20 | { 21 | anonymous: false, 22 | inputs: [ 23 | { 24 | indexed: true, 25 | internalType: "address", 26 | name: "owner", 27 | type: "address", 28 | }, 29 | { 30 | indexed: true, 31 | internalType: "address", 32 | name: "spender", 33 | type: "address", 34 | }, 35 | { 36 | indexed: false, 37 | internalType: "uint256", 38 | name: "value", 39 | type: "uint256", 40 | }, 41 | ], 42 | name: "Approval", 43 | type: "event", 44 | }, 45 | { 46 | anonymous: false, 47 | inputs: [ 48 | { 49 | indexed: true, 50 | internalType: "address", 51 | name: "from", 52 | type: "address", 53 | }, 54 | { 55 | indexed: true, 56 | internalType: "address", 57 | name: "to", 58 | type: "address", 59 | }, 60 | { 61 | indexed: false, 62 | internalType: "uint256", 63 | name: "value", 64 | type: "uint256", 65 | }, 66 | ], 67 | name: "Transfer", 68 | type: "event", 69 | }, 70 | { 71 | inputs: [], 72 | name: "DOMAIN_SEPARATOR", 73 | outputs: [ 74 | { 75 | internalType: "bytes32", 76 | name: "", 77 | type: "bytes32", 78 | }, 79 | ], 80 | stateMutability: "view", 81 | type: "function", 82 | }, 83 | { 84 | inputs: [ 85 | { 86 | internalType: "address", 87 | name: "owner", 88 | type: "address", 89 | }, 90 | { 91 | internalType: "address", 92 | name: "spender", 93 | type: "address", 94 | }, 95 | ], 96 | name: "allowance", 97 | outputs: [ 98 | { 99 | internalType: "uint256", 100 | name: "", 101 | type: "uint256", 102 | }, 103 | ], 104 | stateMutability: "view", 105 | type: "function", 106 | }, 107 | { 108 | inputs: [ 109 | { 110 | internalType: "address", 111 | name: "spender", 112 | type: "address", 113 | }, 114 | { 115 | internalType: "uint256", 116 | name: "value", 117 | type: "uint256", 118 | }, 119 | ], 120 | name: "approve", 121 | outputs: [ 122 | { 123 | internalType: "bool", 124 | name: "", 125 | type: "bool", 126 | }, 127 | ], 128 | stateMutability: "nonpayable", 129 | type: "function", 130 | }, 131 | { 132 | inputs: [ 133 | { 134 | internalType: "address", 135 | name: "owner", 136 | type: "address", 137 | }, 138 | ], 139 | name: "balanceOf", 140 | outputs: [ 141 | { 142 | internalType: "uint256", 143 | name: "", 144 | type: "uint256", 145 | }, 146 | ], 147 | stateMutability: "view", 148 | type: "function", 149 | }, 150 | { 151 | inputs: [], 152 | name: "decimals", 153 | outputs: [ 154 | { 155 | internalType: "uint8", 156 | name: "", 157 | type: "uint8", 158 | }, 159 | ], 160 | stateMutability: "view", 161 | type: "function", 162 | }, 163 | { 164 | inputs: [], 165 | name: "name", 166 | outputs: [ 167 | { 168 | internalType: "string", 169 | name: "", 170 | type: "string", 171 | }, 172 | ], 173 | stateMutability: "view", 174 | type: "function", 175 | }, 176 | { 177 | inputs: [ 178 | { 179 | internalType: "address", 180 | name: "owner", 181 | type: "address", 182 | }, 183 | ], 184 | name: "nonces", 185 | outputs: [ 186 | { 187 | internalType: "uint256", 188 | name: "", 189 | type: "uint256", 190 | }, 191 | ], 192 | stateMutability: "view", 193 | type: "function", 194 | }, 195 | { 196 | inputs: [ 197 | { 198 | internalType: "address", 199 | name: "owner", 200 | type: "address", 201 | }, 202 | { 203 | internalType: "address", 204 | name: "spender", 205 | type: "address", 206 | }, 207 | { 208 | internalType: "uint256", 209 | name: "value", 210 | type: "uint256", 211 | }, 212 | { 213 | internalType: "uint256", 214 | name: "deadline", 215 | type: "uint256", 216 | }, 217 | { 218 | internalType: "uint8", 219 | name: "v", 220 | type: "uint8", 221 | }, 222 | { 223 | internalType: "bytes32", 224 | name: "r", 225 | type: "bytes32", 226 | }, 227 | { 228 | internalType: "bytes32", 229 | name: "s", 230 | type: "bytes32", 231 | }, 232 | ], 233 | name: "permit", 234 | outputs: [], 235 | stateMutability: "nonpayable", 236 | type: "function", 237 | }, 238 | { 239 | inputs: [ 240 | { 241 | internalType: "string", 242 | name: "label", 243 | type: "string", 244 | }, 245 | { 246 | internalType: "address", 247 | name: "owner", 248 | type: "address", 249 | }, 250 | ], 251 | name: "register", 252 | outputs: [], 253 | stateMutability: "nonpayable", 254 | type: "function", 255 | }, 256 | { 257 | inputs: [], 258 | name: "registrationCost", 259 | outputs: [ 260 | { 261 | internalType: "uint256", 262 | name: "", 263 | type: "uint256", 264 | }, 265 | ], 266 | stateMutability: "view", 267 | type: "function", 268 | }, 269 | { 270 | inputs: [], 271 | name: "symbol", 272 | outputs: [ 273 | { 274 | internalType: "string", 275 | name: "", 276 | type: "string", 277 | }, 278 | ], 279 | stateMutability: "view", 280 | type: "function", 281 | }, 282 | { 283 | inputs: [], 284 | name: "totalSupply", 285 | outputs: [ 286 | { 287 | internalType: "uint256", 288 | name: "", 289 | type: "uint256", 290 | }, 291 | ], 292 | stateMutability: "view", 293 | type: "function", 294 | }, 295 | { 296 | inputs: [ 297 | { 298 | internalType: "address", 299 | name: "to", 300 | type: "address", 301 | }, 302 | { 303 | internalType: "uint256", 304 | name: "value", 305 | type: "uint256", 306 | }, 307 | ], 308 | name: "transfer", 309 | outputs: [ 310 | { 311 | internalType: "bool", 312 | name: "", 313 | type: "bool", 314 | }, 315 | ], 316 | stateMutability: "nonpayable", 317 | type: "function", 318 | }, 319 | { 320 | inputs: [ 321 | { 322 | internalType: "address", 323 | name: "from", 324 | type: "address", 325 | }, 326 | { 327 | internalType: "address", 328 | name: "to", 329 | type: "address", 330 | }, 331 | { 332 | internalType: "uint256", 333 | name: "value", 334 | type: "uint256", 335 | }, 336 | ], 337 | name: "transferFrom", 338 | outputs: [ 339 | { 340 | internalType: "bool", 341 | name: "", 342 | type: "bool", 343 | }, 344 | ], 345 | stateMutability: "nonpayable", 346 | type: "function", 347 | }, 348 | ]; 349 | -------------------------------------------------------------------------------- /ts-types/contracts/IENSResolver.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | Overrides, 17 | CallOverrides, 18 | } from "@ethersproject/contracts"; 19 | import { BytesLike } from "@ethersproject/bytes"; 20 | import { Listener, Provider } from "@ethersproject/providers"; 21 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 22 | 23 | interface IENSResolverInterface extends ethers.utils.Interface { 24 | functions: { 25 | "addr(bytes32)": FunctionFragment; 26 | "name(bytes32)": FunctionFragment; 27 | "setAddr(bytes32,address)": FunctionFragment; 28 | "setName(bytes32,string)": FunctionFragment; 29 | }; 30 | 31 | encodeFunctionData(functionFragment: "addr", values: [BytesLike]): string; 32 | encodeFunctionData(functionFragment: "name", values: [BytesLike]): string; 33 | encodeFunctionData( 34 | functionFragment: "setAddr", 35 | values: [BytesLike, string] 36 | ): string; 37 | encodeFunctionData( 38 | functionFragment: "setName", 39 | values: [BytesLike, string] 40 | ): string; 41 | 42 | decodeFunctionResult(functionFragment: "addr", data: BytesLike): Result; 43 | decodeFunctionResult(functionFragment: "name", data: BytesLike): Result; 44 | decodeFunctionResult(functionFragment: "setAddr", data: BytesLike): Result; 45 | decodeFunctionResult(functionFragment: "setName", data: BytesLike): Result; 46 | 47 | events: { 48 | "AddrChanged(bytes32,address)": EventFragment; 49 | "NameChanged(bytes32,string)": EventFragment; 50 | }; 51 | 52 | getEvent(nameOrSignatureOrTopic: "AddrChanged"): EventFragment; 53 | getEvent(nameOrSignatureOrTopic: "NameChanged"): EventFragment; 54 | } 55 | 56 | export class IENSResolver extends Contract { 57 | connect(signerOrProvider: Signer | Provider | string): this; 58 | attach(addressOrName: string): this; 59 | deployed(): Promise; 60 | 61 | on(event: EventFilter | string, listener: Listener): this; 62 | once(event: EventFilter | string, listener: Listener): this; 63 | addListener(eventName: EventFilter | string, listener: Listener): this; 64 | removeAllListeners(eventName: EventFilter | string): this; 65 | removeListener(eventName: any, listener: Listener): this; 66 | 67 | interface: IENSResolverInterface; 68 | 69 | functions: { 70 | addr(_node: BytesLike, overrides?: CallOverrides): Promise<[string]>; 71 | 72 | "addr(bytes32)"( 73 | _node: BytesLike, 74 | overrides?: CallOverrides 75 | ): Promise<[string]>; 76 | 77 | name(_node: BytesLike, overrides?: CallOverrides): Promise<[string]>; 78 | 79 | "name(bytes32)"( 80 | _node: BytesLike, 81 | overrides?: CallOverrides 82 | ): Promise<[string]>; 83 | 84 | setAddr( 85 | _node: BytesLike, 86 | _addr: string, 87 | overrides?: Overrides 88 | ): Promise; 89 | 90 | "setAddr(bytes32,address)"( 91 | _node: BytesLike, 92 | _addr: string, 93 | overrides?: Overrides 94 | ): Promise; 95 | 96 | setName( 97 | _node: BytesLike, 98 | _name: string, 99 | overrides?: Overrides 100 | ): Promise; 101 | 102 | "setName(bytes32,string)"( 103 | _node: BytesLike, 104 | _name: string, 105 | overrides?: Overrides 106 | ): Promise; 107 | }; 108 | 109 | addr(_node: BytesLike, overrides?: CallOverrides): Promise; 110 | 111 | "addr(bytes32)"(_node: BytesLike, overrides?: CallOverrides): Promise; 112 | 113 | name(_node: BytesLike, overrides?: CallOverrides): Promise; 114 | 115 | "name(bytes32)"(_node: BytesLike, overrides?: CallOverrides): Promise; 116 | 117 | setAddr( 118 | _node: BytesLike, 119 | _addr: string, 120 | overrides?: Overrides 121 | ): Promise; 122 | 123 | "setAddr(bytes32,address)"( 124 | _node: BytesLike, 125 | _addr: string, 126 | overrides?: Overrides 127 | ): Promise; 128 | 129 | setName( 130 | _node: BytesLike, 131 | _name: string, 132 | overrides?: Overrides 133 | ): Promise; 134 | 135 | "setName(bytes32,string)"( 136 | _node: BytesLike, 137 | _name: string, 138 | overrides?: Overrides 139 | ): Promise; 140 | 141 | callStatic: { 142 | addr(_node: BytesLike, overrides?: CallOverrides): Promise; 143 | 144 | "addr(bytes32)"( 145 | _node: BytesLike, 146 | overrides?: CallOverrides 147 | ): Promise; 148 | 149 | name(_node: BytesLike, overrides?: CallOverrides): Promise; 150 | 151 | "name(bytes32)"( 152 | _node: BytesLike, 153 | overrides?: CallOverrides 154 | ): Promise; 155 | 156 | setAddr( 157 | _node: BytesLike, 158 | _addr: string, 159 | overrides?: CallOverrides 160 | ): Promise; 161 | 162 | "setAddr(bytes32,address)"( 163 | _node: BytesLike, 164 | _addr: string, 165 | overrides?: CallOverrides 166 | ): Promise; 167 | 168 | setName( 169 | _node: BytesLike, 170 | _name: string, 171 | overrides?: CallOverrides 172 | ): Promise; 173 | 174 | "setName(bytes32,string)"( 175 | _node: BytesLike, 176 | _name: string, 177 | overrides?: CallOverrides 178 | ): Promise; 179 | }; 180 | 181 | filters: { 182 | AddrChanged(_node: BytesLike | null, _addr: null): EventFilter; 183 | 184 | NameChanged(_node: BytesLike | null, _name: null): EventFilter; 185 | }; 186 | 187 | estimateGas: { 188 | addr(_node: BytesLike, overrides?: CallOverrides): Promise; 189 | 190 | "addr(bytes32)"( 191 | _node: BytesLike, 192 | overrides?: CallOverrides 193 | ): Promise; 194 | 195 | name(_node: BytesLike, overrides?: CallOverrides): Promise; 196 | 197 | "name(bytes32)"( 198 | _node: BytesLike, 199 | overrides?: CallOverrides 200 | ): Promise; 201 | 202 | setAddr( 203 | _node: BytesLike, 204 | _addr: string, 205 | overrides?: Overrides 206 | ): Promise; 207 | 208 | "setAddr(bytes32,address)"( 209 | _node: BytesLike, 210 | _addr: string, 211 | overrides?: Overrides 212 | ): Promise; 213 | 214 | setName( 215 | _node: BytesLike, 216 | _name: string, 217 | overrides?: Overrides 218 | ): Promise; 219 | 220 | "setName(bytes32,string)"( 221 | _node: BytesLike, 222 | _name: string, 223 | overrides?: Overrides 224 | ): Promise; 225 | }; 226 | 227 | populateTransaction: { 228 | addr( 229 | _node: BytesLike, 230 | overrides?: CallOverrides 231 | ): Promise; 232 | 233 | "addr(bytes32)"( 234 | _node: BytesLike, 235 | overrides?: CallOverrides 236 | ): Promise; 237 | 238 | name( 239 | _node: BytesLike, 240 | overrides?: CallOverrides 241 | ): Promise; 242 | 243 | "name(bytes32)"( 244 | _node: BytesLike, 245 | overrides?: CallOverrides 246 | ): Promise; 247 | 248 | setAddr( 249 | _node: BytesLike, 250 | _addr: string, 251 | overrides?: Overrides 252 | ): Promise; 253 | 254 | "setAddr(bytes32,address)"( 255 | _node: BytesLike, 256 | _addr: string, 257 | overrides?: Overrides 258 | ): Promise; 259 | 260 | setName( 261 | _node: BytesLike, 262 | _name: string, 263 | overrides?: Overrides 264 | ): Promise; 265 | 266 | "setName(bytes32,string)"( 267 | _node: BytesLike, 268 | _name: string, 269 | overrides?: Overrides 270 | ): Promise; 271 | }; 272 | } 273 | -------------------------------------------------------------------------------- /ts-types/contracts/IMirrorENSRegistrar.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | Overrides, 17 | CallOverrides, 18 | } from "@ethersproject/contracts"; 19 | import { BytesLike } from "@ethersproject/bytes"; 20 | import { Listener, Provider } from "@ethersproject/providers"; 21 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 22 | 23 | interface IMirrorENSRegistrarInterface extends ethers.utils.Interface { 24 | functions: { 25 | "changeLabelOwner(string,address)": FunctionFragment; 26 | "changeRootNodeOwner(address)": FunctionFragment; 27 | "labelOwner(string)": FunctionFragment; 28 | "register(string,address)": FunctionFragment; 29 | }; 30 | 31 | encodeFunctionData( 32 | functionFragment: "changeLabelOwner", 33 | values: [string, string] 34 | ): string; 35 | encodeFunctionData( 36 | functionFragment: "changeRootNodeOwner", 37 | values: [string] 38 | ): string; 39 | encodeFunctionData(functionFragment: "labelOwner", values: [string]): string; 40 | encodeFunctionData( 41 | functionFragment: "register", 42 | values: [string, string] 43 | ): string; 44 | 45 | decodeFunctionResult( 46 | functionFragment: "changeLabelOwner", 47 | data: BytesLike 48 | ): Result; 49 | decodeFunctionResult( 50 | functionFragment: "changeRootNodeOwner", 51 | data: BytesLike 52 | ): Result; 53 | decodeFunctionResult(functionFragment: "labelOwner", data: BytesLike): Result; 54 | decodeFunctionResult(functionFragment: "register", data: BytesLike): Result; 55 | 56 | events: {}; 57 | } 58 | 59 | export class IMirrorENSRegistrar extends Contract { 60 | connect(signerOrProvider: Signer | Provider | string): this; 61 | attach(addressOrName: string): this; 62 | deployed(): Promise; 63 | 64 | on(event: EventFilter | string, listener: Listener): this; 65 | once(event: EventFilter | string, listener: Listener): this; 66 | addListener(eventName: EventFilter | string, listener: Listener): this; 67 | removeAllListeners(eventName: EventFilter | string): this; 68 | removeListener(eventName: any, listener: Listener): this; 69 | 70 | interface: IMirrorENSRegistrarInterface; 71 | 72 | functions: { 73 | changeLabelOwner( 74 | label_: string, 75 | newOwner_: string, 76 | overrides?: Overrides 77 | ): Promise; 78 | 79 | "changeLabelOwner(string,address)"( 80 | label_: string, 81 | newOwner_: string, 82 | overrides?: Overrides 83 | ): Promise; 84 | 85 | changeRootNodeOwner( 86 | newOwner_: string, 87 | overrides?: Overrides 88 | ): Promise; 89 | 90 | "changeRootNodeOwner(address)"( 91 | newOwner_: string, 92 | overrides?: Overrides 93 | ): Promise; 94 | 95 | labelOwner(label: string, overrides?: CallOverrides): Promise<[string]>; 96 | 97 | "labelOwner(string)"( 98 | label: string, 99 | overrides?: CallOverrides 100 | ): Promise<[string]>; 101 | 102 | register( 103 | label_: string, 104 | owner_: string, 105 | overrides?: Overrides 106 | ): Promise; 107 | 108 | "register(string,address)"( 109 | label_: string, 110 | owner_: string, 111 | overrides?: Overrides 112 | ): Promise; 113 | }; 114 | 115 | changeLabelOwner( 116 | label_: string, 117 | newOwner_: string, 118 | overrides?: Overrides 119 | ): Promise; 120 | 121 | "changeLabelOwner(string,address)"( 122 | label_: string, 123 | newOwner_: string, 124 | overrides?: Overrides 125 | ): Promise; 126 | 127 | changeRootNodeOwner( 128 | newOwner_: string, 129 | overrides?: Overrides 130 | ): Promise; 131 | 132 | "changeRootNodeOwner(address)"( 133 | newOwner_: string, 134 | overrides?: Overrides 135 | ): Promise; 136 | 137 | labelOwner(label: string, overrides?: CallOverrides): Promise; 138 | 139 | "labelOwner(string)"( 140 | label: string, 141 | overrides?: CallOverrides 142 | ): Promise; 143 | 144 | register( 145 | label_: string, 146 | owner_: string, 147 | overrides?: Overrides 148 | ): Promise; 149 | 150 | "register(string,address)"( 151 | label_: string, 152 | owner_: string, 153 | overrides?: Overrides 154 | ): Promise; 155 | 156 | callStatic: { 157 | changeLabelOwner( 158 | label_: string, 159 | newOwner_: string, 160 | overrides?: CallOverrides 161 | ): Promise; 162 | 163 | "changeLabelOwner(string,address)"( 164 | label_: string, 165 | newOwner_: string, 166 | overrides?: CallOverrides 167 | ): Promise; 168 | 169 | changeRootNodeOwner( 170 | newOwner_: string, 171 | overrides?: CallOverrides 172 | ): Promise; 173 | 174 | "changeRootNodeOwner(address)"( 175 | newOwner_: string, 176 | overrides?: CallOverrides 177 | ): Promise; 178 | 179 | labelOwner(label: string, overrides?: CallOverrides): Promise; 180 | 181 | "labelOwner(string)"( 182 | label: string, 183 | overrides?: CallOverrides 184 | ): Promise; 185 | 186 | register( 187 | label_: string, 188 | owner_: string, 189 | overrides?: CallOverrides 190 | ): Promise; 191 | 192 | "register(string,address)"( 193 | label_: string, 194 | owner_: string, 195 | overrides?: CallOverrides 196 | ): Promise; 197 | }; 198 | 199 | filters: {}; 200 | 201 | estimateGas: { 202 | changeLabelOwner( 203 | label_: string, 204 | newOwner_: string, 205 | overrides?: Overrides 206 | ): Promise; 207 | 208 | "changeLabelOwner(string,address)"( 209 | label_: string, 210 | newOwner_: string, 211 | overrides?: Overrides 212 | ): Promise; 213 | 214 | changeRootNodeOwner( 215 | newOwner_: string, 216 | overrides?: Overrides 217 | ): Promise; 218 | 219 | "changeRootNodeOwner(address)"( 220 | newOwner_: string, 221 | overrides?: Overrides 222 | ): Promise; 223 | 224 | labelOwner(label: string, overrides?: CallOverrides): Promise; 225 | 226 | "labelOwner(string)"( 227 | label: string, 228 | overrides?: CallOverrides 229 | ): Promise; 230 | 231 | register( 232 | label_: string, 233 | owner_: string, 234 | overrides?: Overrides 235 | ): Promise; 236 | 237 | "register(string,address)"( 238 | label_: string, 239 | owner_: string, 240 | overrides?: Overrides 241 | ): Promise; 242 | }; 243 | 244 | populateTransaction: { 245 | changeLabelOwner( 246 | label_: string, 247 | newOwner_: string, 248 | overrides?: Overrides 249 | ): Promise; 250 | 251 | "changeLabelOwner(string,address)"( 252 | label_: string, 253 | newOwner_: string, 254 | overrides?: Overrides 255 | ): Promise; 256 | 257 | changeRootNodeOwner( 258 | newOwner_: string, 259 | overrides?: Overrides 260 | ): Promise; 261 | 262 | "changeRootNodeOwner(address)"( 263 | newOwner_: string, 264 | overrides?: Overrides 265 | ): Promise; 266 | 267 | labelOwner( 268 | label: string, 269 | overrides?: CallOverrides 270 | ): Promise; 271 | 272 | "labelOwner(string)"( 273 | label: string, 274 | overrides?: CallOverrides 275 | ): Promise; 276 | 277 | register( 278 | label_: string, 279 | owner_: string, 280 | overrides?: Overrides 281 | ): Promise; 282 | 283 | "register(string,address)"( 284 | label_: string, 285 | owner_: string, 286 | overrides?: Overrides 287 | ): Promise; 288 | }; 289 | } 290 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/ENS__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer } from "ethers"; 6 | import { Provider } from "@ethersproject/providers"; 7 | 8 | import type { ENS } from "../ENS"; 9 | 10 | export class ENS__factory { 11 | static connect(address: string, signerOrProvider: Signer | Provider): ENS { 12 | return new Contract(address, _abi, signerOrProvider) as ENS; 13 | } 14 | } 15 | 16 | const _abi = [ 17 | { 18 | anonymous: false, 19 | inputs: [ 20 | { 21 | indexed: true, 22 | internalType: "address", 23 | name: "owner", 24 | type: "address", 25 | }, 26 | { 27 | indexed: true, 28 | internalType: "address", 29 | name: "operator", 30 | type: "address", 31 | }, 32 | { 33 | indexed: false, 34 | internalType: "bool", 35 | name: "approved", 36 | type: "bool", 37 | }, 38 | ], 39 | name: "ApprovalForAll", 40 | type: "event", 41 | }, 42 | { 43 | anonymous: false, 44 | inputs: [ 45 | { 46 | indexed: true, 47 | internalType: "bytes32", 48 | name: "node", 49 | type: "bytes32", 50 | }, 51 | { 52 | indexed: true, 53 | internalType: "bytes32", 54 | name: "label", 55 | type: "bytes32", 56 | }, 57 | { 58 | indexed: false, 59 | internalType: "address", 60 | name: "owner", 61 | type: "address", 62 | }, 63 | ], 64 | name: "NewOwner", 65 | type: "event", 66 | }, 67 | { 68 | anonymous: false, 69 | inputs: [ 70 | { 71 | indexed: true, 72 | internalType: "bytes32", 73 | name: "node", 74 | type: "bytes32", 75 | }, 76 | { 77 | indexed: false, 78 | internalType: "address", 79 | name: "resolver", 80 | type: "address", 81 | }, 82 | ], 83 | name: "NewResolver", 84 | type: "event", 85 | }, 86 | { 87 | anonymous: false, 88 | inputs: [ 89 | { 90 | indexed: true, 91 | internalType: "bytes32", 92 | name: "node", 93 | type: "bytes32", 94 | }, 95 | { 96 | indexed: false, 97 | internalType: "uint64", 98 | name: "ttl", 99 | type: "uint64", 100 | }, 101 | ], 102 | name: "NewTTL", 103 | type: "event", 104 | }, 105 | { 106 | anonymous: false, 107 | inputs: [ 108 | { 109 | indexed: true, 110 | internalType: "bytes32", 111 | name: "node", 112 | type: "bytes32", 113 | }, 114 | { 115 | indexed: false, 116 | internalType: "address", 117 | name: "owner", 118 | type: "address", 119 | }, 120 | ], 121 | name: "Transfer", 122 | type: "event", 123 | }, 124 | { 125 | inputs: [ 126 | { 127 | internalType: "address", 128 | name: "owner", 129 | type: "address", 130 | }, 131 | { 132 | internalType: "address", 133 | name: "operator", 134 | type: "address", 135 | }, 136 | ], 137 | name: "isApprovedForAll", 138 | outputs: [ 139 | { 140 | internalType: "bool", 141 | name: "", 142 | type: "bool", 143 | }, 144 | ], 145 | stateMutability: "view", 146 | type: "function", 147 | }, 148 | { 149 | inputs: [ 150 | { 151 | internalType: "bytes32", 152 | name: "node", 153 | type: "bytes32", 154 | }, 155 | ], 156 | name: "owner", 157 | outputs: [ 158 | { 159 | internalType: "address", 160 | name: "", 161 | type: "address", 162 | }, 163 | ], 164 | stateMutability: "view", 165 | type: "function", 166 | }, 167 | { 168 | inputs: [ 169 | { 170 | internalType: "bytes32", 171 | name: "node", 172 | type: "bytes32", 173 | }, 174 | ], 175 | name: "recordExists", 176 | outputs: [ 177 | { 178 | internalType: "bool", 179 | name: "", 180 | type: "bool", 181 | }, 182 | ], 183 | stateMutability: "view", 184 | type: "function", 185 | }, 186 | { 187 | inputs: [ 188 | { 189 | internalType: "bytes32", 190 | name: "node", 191 | type: "bytes32", 192 | }, 193 | ], 194 | name: "resolver", 195 | outputs: [ 196 | { 197 | internalType: "address", 198 | name: "", 199 | type: "address", 200 | }, 201 | ], 202 | stateMutability: "view", 203 | type: "function", 204 | }, 205 | { 206 | inputs: [ 207 | { 208 | internalType: "address", 209 | name: "operator", 210 | type: "address", 211 | }, 212 | { 213 | internalType: "bool", 214 | name: "approved", 215 | type: "bool", 216 | }, 217 | ], 218 | name: "setApprovalForAll", 219 | outputs: [], 220 | stateMutability: "nonpayable", 221 | type: "function", 222 | }, 223 | { 224 | inputs: [ 225 | { 226 | internalType: "bytes32", 227 | name: "node", 228 | type: "bytes32", 229 | }, 230 | { 231 | internalType: "address", 232 | name: "owner", 233 | type: "address", 234 | }, 235 | ], 236 | name: "setOwner", 237 | outputs: [], 238 | stateMutability: "nonpayable", 239 | type: "function", 240 | }, 241 | { 242 | inputs: [ 243 | { 244 | internalType: "bytes32", 245 | name: "node", 246 | type: "bytes32", 247 | }, 248 | { 249 | internalType: "address", 250 | name: "owner", 251 | type: "address", 252 | }, 253 | { 254 | internalType: "address", 255 | name: "resolver", 256 | type: "address", 257 | }, 258 | { 259 | internalType: "uint64", 260 | name: "ttl", 261 | type: "uint64", 262 | }, 263 | ], 264 | name: "setRecord", 265 | outputs: [], 266 | stateMutability: "nonpayable", 267 | type: "function", 268 | }, 269 | { 270 | inputs: [ 271 | { 272 | internalType: "bytes32", 273 | name: "node", 274 | type: "bytes32", 275 | }, 276 | { 277 | internalType: "address", 278 | name: "resolver", 279 | type: "address", 280 | }, 281 | ], 282 | name: "setResolver", 283 | outputs: [], 284 | stateMutability: "nonpayable", 285 | type: "function", 286 | }, 287 | { 288 | inputs: [ 289 | { 290 | internalType: "bytes32", 291 | name: "node", 292 | type: "bytes32", 293 | }, 294 | { 295 | internalType: "bytes32", 296 | name: "label", 297 | type: "bytes32", 298 | }, 299 | { 300 | internalType: "address", 301 | name: "owner", 302 | type: "address", 303 | }, 304 | ], 305 | name: "setSubnodeOwner", 306 | outputs: [ 307 | { 308 | internalType: "bytes32", 309 | name: "", 310 | type: "bytes32", 311 | }, 312 | ], 313 | stateMutability: "nonpayable", 314 | type: "function", 315 | }, 316 | { 317 | inputs: [ 318 | { 319 | internalType: "bytes32", 320 | name: "node", 321 | type: "bytes32", 322 | }, 323 | { 324 | internalType: "bytes32", 325 | name: "label", 326 | type: "bytes32", 327 | }, 328 | { 329 | internalType: "address", 330 | name: "owner", 331 | type: "address", 332 | }, 333 | { 334 | internalType: "address", 335 | name: "resolver", 336 | type: "address", 337 | }, 338 | { 339 | internalType: "uint64", 340 | name: "ttl", 341 | type: "uint64", 342 | }, 343 | ], 344 | name: "setSubnodeRecord", 345 | outputs: [], 346 | stateMutability: "nonpayable", 347 | type: "function", 348 | }, 349 | { 350 | inputs: [ 351 | { 352 | internalType: "bytes32", 353 | name: "node", 354 | type: "bytes32", 355 | }, 356 | { 357 | internalType: "uint64", 358 | name: "ttl", 359 | type: "uint64", 360 | }, 361 | ], 362 | name: "setTTL", 363 | outputs: [], 364 | stateMutability: "nonpayable", 365 | type: "function", 366 | }, 367 | { 368 | inputs: [ 369 | { 370 | internalType: "bytes32", 371 | name: "node", 372 | type: "bytes32", 373 | }, 374 | ], 375 | name: "ttl", 376 | outputs: [ 377 | { 378 | internalType: "uint64", 379 | name: "", 380 | type: "uint64", 381 | }, 382 | ], 383 | stateMutability: "view", 384 | type: "function", 385 | }, 386 | ]; 387 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/PublicationRoles__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Signer } from "ethers"; 6 | import { Provider, TransactionRequest } from "@ethersproject/providers"; 7 | import { Contract, ContractFactory, Overrides } from "@ethersproject/contracts"; 8 | 9 | import type { PublicationRoles } from "../PublicationRoles"; 10 | 11 | export class PublicationRoles__factory extends ContractFactory { 12 | constructor(signer?: Signer) { 13 | super(_abi, _bytecode, signer); 14 | } 15 | 16 | deploy(ens_: string, overrides?: Overrides): Promise { 17 | return super.deploy(ens_, overrides || {}) as Promise; 18 | } 19 | getDeployTransaction( 20 | ens_: string, 21 | overrides?: Overrides 22 | ): TransactionRequest { 23 | return super.getDeployTransaction(ens_, overrides || {}); 24 | } 25 | attach(address: string): PublicationRoles { 26 | return super.attach(address) as PublicationRoles; 27 | } 28 | connect(signer: Signer): PublicationRoles__factory { 29 | return super.connect(signer) as PublicationRoles__factory; 30 | } 31 | static connect( 32 | address: string, 33 | signerOrProvider: Signer | Provider 34 | ): PublicationRoles { 35 | return new Contract(address, _abi, signerOrProvider) as PublicationRoles; 36 | } 37 | } 38 | 39 | const _abi = [ 40 | { 41 | inputs: [ 42 | { 43 | internalType: "address", 44 | name: "ens_", 45 | type: "address", 46 | }, 47 | ], 48 | stateMutability: "nonpayable", 49 | type: "constructor", 50 | }, 51 | { 52 | anonymous: false, 53 | inputs: [ 54 | { 55 | indexed: true, 56 | internalType: "bytes32", 57 | name: "publicationNode", 58 | type: "bytes32", 59 | }, 60 | { 61 | indexed: true, 62 | internalType: "address", 63 | name: "contributor", 64 | type: "address", 65 | }, 66 | { 67 | indexed: false, 68 | internalType: "string", 69 | name: "roleName", 70 | type: "string", 71 | }, 72 | ], 73 | name: "ModifiedRole", 74 | type: "event", 75 | }, 76 | { 77 | inputs: [ 78 | { 79 | internalType: "string", 80 | name: "roleName", 81 | type: "string", 82 | }, 83 | ], 84 | name: "encodeRole", 85 | outputs: [ 86 | { 87 | internalType: "bytes32", 88 | name: "", 89 | type: "bytes32", 90 | }, 91 | ], 92 | stateMutability: "pure", 93 | type: "function", 94 | }, 95 | { 96 | inputs: [], 97 | name: "ens", 98 | outputs: [ 99 | { 100 | internalType: "address", 101 | name: "", 102 | type: "address", 103 | }, 104 | ], 105 | stateMutability: "view", 106 | type: "function", 107 | }, 108 | { 109 | inputs: [ 110 | { 111 | internalType: "address", 112 | name: "contributor", 113 | type: "address", 114 | }, 115 | { 116 | internalType: "bytes32", 117 | name: "publicationNode", 118 | type: "bytes32", 119 | }, 120 | ], 121 | name: "getContributorId", 122 | outputs: [ 123 | { 124 | internalType: "bytes32", 125 | name: "", 126 | type: "bytes32", 127 | }, 128 | ], 129 | stateMutability: "pure", 130 | type: "function", 131 | }, 132 | { 133 | inputs: [ 134 | { 135 | internalType: "address", 136 | name: "contributor", 137 | type: "address", 138 | }, 139 | { 140 | internalType: "bytes32", 141 | name: "publicationNode", 142 | type: "bytes32", 143 | }, 144 | ], 145 | name: "getRole", 146 | outputs: [ 147 | { 148 | internalType: "bytes32", 149 | name: "", 150 | type: "bytes32", 151 | }, 152 | ], 153 | stateMutability: "view", 154 | type: "function", 155 | }, 156 | { 157 | inputs: [ 158 | { 159 | internalType: "address", 160 | name: "contributor", 161 | type: "address", 162 | }, 163 | { 164 | internalType: "bytes32", 165 | name: "publicationNode", 166 | type: "bytes32", 167 | }, 168 | { 169 | internalType: "string", 170 | name: "roleName", 171 | type: "string", 172 | }, 173 | ], 174 | name: "modifyRole", 175 | outputs: [], 176 | stateMutability: "nonpayable", 177 | type: "function", 178 | }, 179 | { 180 | inputs: [ 181 | { 182 | internalType: "bytes32", 183 | name: "publicationNode", 184 | type: "bytes32", 185 | }, 186 | { 187 | internalType: "address", 188 | name: "account", 189 | type: "address", 190 | }, 191 | ], 192 | name: "ownsPublication", 193 | outputs: [ 194 | { 195 | internalType: "bool", 196 | name: "", 197 | type: "bool", 198 | }, 199 | ], 200 | stateMutability: "view", 201 | type: "function", 202 | }, 203 | { 204 | inputs: [ 205 | { 206 | internalType: "bytes32", 207 | name: "publicationNode", 208 | type: "bytes32", 209 | }, 210 | ], 211 | name: "publicationOwner", 212 | outputs: [ 213 | { 214 | internalType: "address", 215 | name: "", 216 | type: "address", 217 | }, 218 | ], 219 | stateMutability: "view", 220 | type: "function", 221 | }, 222 | { 223 | inputs: [ 224 | { 225 | internalType: "bytes32", 226 | name: "", 227 | type: "bytes32", 228 | }, 229 | ], 230 | name: "roles", 231 | outputs: [ 232 | { 233 | internalType: "bytes32", 234 | name: "", 235 | type: "bytes32", 236 | }, 237 | ], 238 | stateMutability: "view", 239 | type: "function", 240 | }, 241 | ]; 242 | 243 | const _bytecode = 244 | "0x60a060405234801561001057600080fd5b5060405161070d38038061070d8339818101604052602081101561003357600080fd5b5051606081901b6001600160601b0319166080526001600160a01b03166106a161006c6000398061032b52806104fc52506106a16000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063953566511161005b57806395356651146101ea5780639b51325c14610207578063d7c9d9b914610224578063df4b2b30146102ca57610088565b8063203bac611461008d5780633f15457f146100d85780635ad4b6741461010957806387645d071461019d575b600080fd5b6100c6600480360360408110156100a357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610303565b60408051918252519081900360200190f35b6100e0610329565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61019b6004803603606081101561011f57600080fd5b73ffffffffffffffffffffffffffffffffffffffff8235169160208101359181019060608101604082013564010000000081111561015c57600080fd5b82018360208201111561016e57600080fd5b8035906020019184600183028401116401000000008311171561019057600080fd5b50909250905061034d565b005b6101d6600480360360408110156101b357600080fd5b508035906020013573ffffffffffffffffffffffffffffffffffffffff166104a6565b604080519115158252519081900360200190f35b6100c66004803603602081101561020057600080fd5b50356104e6565b6100e06004803603602081101561021d57600080fd5b50356104f8565b6100c66004803603602081101561023a57600080fd5b81019060208101813564010000000081111561025557600080fd5b82018360208201111561026757600080fd5b8035906020019184600183028401116401000000008311171561028957600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061059d945050505050565b6100c6600480360360408110156102e057600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610614565b60008060006103128585610614565b815260200190815260200160002054905092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b8261035881336104a6565b6103c357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f53656e646572206d757374206265207075626c69636174696f6e206f776e6572604482015290519081900360640190fd5b600061040484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061059d92505050565b9050806000806104148989610614565b8152602001908152602001600020819055508573ffffffffffffffffffffffffffffffffffffffff16857fe879c072038f2b71589e434ffa4c1dc3ce12ddea487e3306092fe46f0467abac868660405180806020018281038252848482818152602001925080828437600083820152604051601f909101601f19169092018290039550909350505050a3505050505050565b60008173ffffffffffffffffffffffffffffffffffffffff166104c8846104f8565b73ffffffffffffffffffffffffffffffffffffffff16149392505050565b60006020819052908152604090205481565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166302571be3836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561056b57600080fd5b505afa15801561057f573d6000803e3d6000fd5b505050506040513d602081101561059557600080fd5b505192915050565b6000816040516020018082805190602001908083835b602083106105d25780518252601f1990920191602091820191016105b3565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051602081830303815290604052805190602001209050919050565b6040805160609390931b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602080850191909152603480850193909352815180850390930183526054909301905280519101209056fea264697066735822122098819d7d044aa1c4f829b120264940ccdfd6e1c79baa404d1af4bbc32e0e7d7b64736f6c63430006080033"; 245 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/MirrorENSReverseRegistrar__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Signer } from "ethers"; 6 | import { Provider, TransactionRequest } from "@ethersproject/providers"; 7 | import { Contract, ContractFactory, Overrides } from "@ethersproject/contracts"; 8 | 9 | import type { MirrorENSReverseRegistrar } from "../MirrorENSReverseRegistrar"; 10 | 11 | export class MirrorENSReverseRegistrar__factory extends ContractFactory { 12 | constructor(signer?: Signer) { 13 | super(_abi, _bytecode, signer); 14 | } 15 | 16 | deploy( 17 | ens_: string, 18 | resolver_: string, 19 | overrides?: Overrides 20 | ): Promise { 21 | return super.deploy( 22 | ens_, 23 | resolver_, 24 | overrides || {} 25 | ) as Promise; 26 | } 27 | getDeployTransaction( 28 | ens_: string, 29 | resolver_: string, 30 | overrides?: Overrides 31 | ): TransactionRequest { 32 | return super.getDeployTransaction(ens_, resolver_, overrides || {}); 33 | } 34 | attach(address: string): MirrorENSReverseRegistrar { 35 | return super.attach(address) as MirrorENSReverseRegistrar; 36 | } 37 | connect(signer: Signer): MirrorENSReverseRegistrar__factory { 38 | return super.connect(signer) as MirrorENSReverseRegistrar__factory; 39 | } 40 | static connect( 41 | address: string, 42 | signerOrProvider: Signer | Provider 43 | ): MirrorENSReverseRegistrar { 44 | return new Contract( 45 | address, 46 | _abi, 47 | signerOrProvider 48 | ) as MirrorENSReverseRegistrar; 49 | } 50 | } 51 | 52 | const _abi = [ 53 | { 54 | inputs: [ 55 | { 56 | internalType: "address", 57 | name: "ens_", 58 | type: "address", 59 | }, 60 | { 61 | internalType: "address", 62 | name: "resolver_", 63 | type: "address", 64 | }, 65 | ], 66 | stateMutability: "nonpayable", 67 | type: "constructor", 68 | }, 69 | { 70 | inputs: [], 71 | name: "ADDR_REVERSE_NODE", 72 | outputs: [ 73 | { 74 | internalType: "bytes32", 75 | name: "", 76 | type: "bytes32", 77 | }, 78 | ], 79 | stateMutability: "view", 80 | type: "function", 81 | }, 82 | { 83 | inputs: [ 84 | { 85 | internalType: "address", 86 | name: "owner", 87 | type: "address", 88 | }, 89 | ], 90 | name: "claim", 91 | outputs: [ 92 | { 93 | internalType: "bytes32", 94 | name: "", 95 | type: "bytes32", 96 | }, 97 | ], 98 | stateMutability: "nonpayable", 99 | type: "function", 100 | }, 101 | { 102 | inputs: [ 103 | { 104 | internalType: "address", 105 | name: "owner", 106 | type: "address", 107 | }, 108 | { 109 | internalType: "address", 110 | name: "resolver", 111 | type: "address", 112 | }, 113 | ], 114 | name: "claimWithResolver", 115 | outputs: [ 116 | { 117 | internalType: "bytes32", 118 | name: "", 119 | type: "bytes32", 120 | }, 121 | ], 122 | stateMutability: "nonpayable", 123 | type: "function", 124 | }, 125 | { 126 | inputs: [], 127 | name: "ens", 128 | outputs: [ 129 | { 130 | internalType: "address", 131 | name: "", 132 | type: "address", 133 | }, 134 | ], 135 | stateMutability: "view", 136 | type: "function", 137 | }, 138 | { 139 | inputs: [ 140 | { 141 | internalType: "address", 142 | name: "addr", 143 | type: "address", 144 | }, 145 | ], 146 | name: "node", 147 | outputs: [ 148 | { 149 | internalType: "bytes32", 150 | name: "", 151 | type: "bytes32", 152 | }, 153 | ], 154 | stateMutability: "pure", 155 | type: "function", 156 | }, 157 | { 158 | inputs: [], 159 | name: "resolver", 160 | outputs: [ 161 | { 162 | internalType: "address", 163 | name: "", 164 | type: "address", 165 | }, 166 | ], 167 | stateMutability: "view", 168 | type: "function", 169 | }, 170 | { 171 | inputs: [ 172 | { 173 | internalType: "string", 174 | name: "name", 175 | type: "string", 176 | }, 177 | ], 178 | name: "setName", 179 | outputs: [ 180 | { 181 | internalType: "bytes32", 182 | name: "", 183 | type: "bytes32", 184 | }, 185 | ], 186 | stateMutability: "nonpayable", 187 | type: "function", 188 | }, 189 | ]; 190 | 191 | const _bytecode = 192 | "0x608060405234801561001057600080fd5b506040516109b93803806109b98339818101604052604081101561003357600080fd5b508051602091820151600080546001600160a01b038085166001600160a01b03199283161780845560018054838716941693909317909255604080516302571be360e01b81527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26004820152905195969495939492909116926302571be392602480840193919291829003018186803b1580156100cf57600080fd5b505afa1580156100e3573d6000803e3d6000fd5b505050506040513d60208110156100f957600080fd5b505190506001600160a01b038116156101815760408051630f41a04d60e11b815233600482015290516001600160a01b03831691631e83409a9160248083019260209291908290030181600087803b15801561015457600080fd5b505af1158015610168573d6000803e3d6000fd5b505050506040513d602081101561017e57600080fd5b50505b505050610826806101936000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c80633f15457f1161005b5780633f15457f1461010c5780637cf8a2eb14610114578063bffbe61c1461011c578063c47f0027146101425761007d565b806304f3bcec146100825780630f5a5466146100a65780631e83409a146100e6575b600080fd5b61008a6101e8565b604080516001600160a01b039092168252519081900360200190f35b6100d4600480360360408110156100bc57600080fd5b506001600160a01b03813581169160200135166101f7565b60408051918252519081900360200190f35b6100d4600480360360208110156100fc57600080fd5b50356001600160a01b03166105d9565b61008a6105ec565b6100d46105fb565b6100d46004803603602081101561013257600080fd5b50356001600160a01b031661061f565b6100d46004803603602081101561015857600080fd5b81019060208101813564010000000081111561017357600080fd5b82018360208201111561018557600080fd5b803590602001918460018302840111640100000000831117156101a757600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061067f945050505050565b6001546001600160a01b031681565b6000806102033361078c565b604080517f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260208083019190915281830184905282518083038401815260608301808552815191830191909120600080547f02571be300000000000000000000000000000000000000000000000000000000909352606485018290529451959650946001600160a01b03909116926302571be3926084808301939192829003018186803b1580156102b357600080fd5b505afa1580156102c7573d6000803e3d6000fd5b505050506040513d60208110156102dd57600080fd5b505190506001600160a01b038516158015906103955750600054604080517f0178b8bf0000000000000000000000000000000000000000000000000000000081526004810185905290516001600160a01b0390921691630178b8bf91602480820192602092909190829003018186803b15801561035957600080fd5b505afa15801561036d573d6000803e3d6000fd5b505050506040513d602081101561038357600080fd5b50516001600160a01b03868116911614155b156104f2576001600160a01b038116301461046e5760008054604080517f06ab59230000000000000000000000000000000000000000000000000000000081527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260048201526024810187905230604482015290516001600160a01b03909216926306ab5923926064808401936020939083900390910190829087803b15801561043e57600080fd5b505af1158015610452573d6000803e3d6000fd5b505050506040513d602081101561046857600080fd5b50309150505b60008054604080517f1896f70a000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03898116602483015291519190921692631896f70a926044808201939182900301818387803b1580156104d957600080fd5b505af11580156104ed573d6000803e3d6000fd5b505050505b856001600160a01b0316816001600160a01b0316146105d05760008054604080517f06ab59230000000000000000000000000000000000000000000000000000000081527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26004820152602481018790526001600160a01b038a81166044830152915191909216926306ab592392606480820193602093909283900390910190829087803b1580156105a357600080fd5b505af11580156105b7573d6000803e3d6000fd5b505050506040513d60208110156105cd57600080fd5b50505b50949350505050565b60006105e68260006101f7565b92915050565b6000546001600160a01b031681565b7f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e281565b60007f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e261064b8361078c565b6040516020018083815260200182815260200192505050604051602081830303815290604052805190602001209050919050565b600154600090819061069b9030906001600160a01b03166101f7565b600154604080517f7737221300000000000000000000000000000000000000000000000000000000815260048101848152602482019283528751604483015287519495506001600160a01b0390931693637737221393869389939192909160640190602085019080838360005b83811015610720578181015183820152602001610708565b50505050905090810190601f16801561074d5780820380516001836020036101000a031916815260200191505b509350505050600060405180830381600087803b15801561076d57600080fd5b505af1158015610781573d6000803e3d6000fd5b509295945050505050565b60007f303132333435363738396162636465660000000000000000000000000000000060285b80156107e35760001901600f841682901a815360109093049260001901600f841682901a81536010840493506107b2565b505060286000209291505056fea26469706673582212209e069f9ffaba8758b4561b63040ad173b625b7ad8cc1ad8ad26efeace79b712164736f6c63430006080033"; 193 | -------------------------------------------------------------------------------- /ts-types/contracts/MirrorENSReverseRegistrar.d.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { 6 | ethers, 7 | EventFilter, 8 | Signer, 9 | BigNumber, 10 | BigNumberish, 11 | PopulatedTransaction, 12 | } from "ethers"; 13 | import { 14 | Contract, 15 | ContractTransaction, 16 | Overrides, 17 | CallOverrides, 18 | } from "@ethersproject/contracts"; 19 | import { BytesLike } from "@ethersproject/bytes"; 20 | import { Listener, Provider } from "@ethersproject/providers"; 21 | import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi"; 22 | 23 | interface MirrorENSReverseRegistrarInterface extends ethers.utils.Interface { 24 | functions: { 25 | "ADDR_REVERSE_NODE()": FunctionFragment; 26 | "claim(address)": FunctionFragment; 27 | "claimWithResolver(address,address)": FunctionFragment; 28 | "ens()": FunctionFragment; 29 | "node(address)": FunctionFragment; 30 | "resolver()": FunctionFragment; 31 | "setName(string)": FunctionFragment; 32 | }; 33 | 34 | encodeFunctionData( 35 | functionFragment: "ADDR_REVERSE_NODE", 36 | values?: undefined 37 | ): string; 38 | encodeFunctionData(functionFragment: "claim", values: [string]): string; 39 | encodeFunctionData( 40 | functionFragment: "claimWithResolver", 41 | values: [string, string] 42 | ): string; 43 | encodeFunctionData(functionFragment: "ens", values?: undefined): string; 44 | encodeFunctionData(functionFragment: "node", values: [string]): string; 45 | encodeFunctionData(functionFragment: "resolver", values?: undefined): string; 46 | encodeFunctionData(functionFragment: "setName", values: [string]): string; 47 | 48 | decodeFunctionResult( 49 | functionFragment: "ADDR_REVERSE_NODE", 50 | data: BytesLike 51 | ): Result; 52 | decodeFunctionResult(functionFragment: "claim", data: BytesLike): Result; 53 | decodeFunctionResult( 54 | functionFragment: "claimWithResolver", 55 | data: BytesLike 56 | ): Result; 57 | decodeFunctionResult(functionFragment: "ens", data: BytesLike): Result; 58 | decodeFunctionResult(functionFragment: "node", data: BytesLike): Result; 59 | decodeFunctionResult(functionFragment: "resolver", data: BytesLike): Result; 60 | decodeFunctionResult(functionFragment: "setName", data: BytesLike): Result; 61 | 62 | events: {}; 63 | } 64 | 65 | export class MirrorENSReverseRegistrar extends Contract { 66 | connect(signerOrProvider: Signer | Provider | string): this; 67 | attach(addressOrName: string): this; 68 | deployed(): Promise; 69 | 70 | on(event: EventFilter | string, listener: Listener): this; 71 | once(event: EventFilter | string, listener: Listener): this; 72 | addListener(eventName: EventFilter | string, listener: Listener): this; 73 | removeAllListeners(eventName: EventFilter | string): this; 74 | removeListener(eventName: any, listener: Listener): this; 75 | 76 | interface: MirrorENSReverseRegistrarInterface; 77 | 78 | functions: { 79 | ADDR_REVERSE_NODE(overrides?: CallOverrides): Promise<[string]>; 80 | 81 | "ADDR_REVERSE_NODE()"(overrides?: CallOverrides): Promise<[string]>; 82 | 83 | claim(owner: string, overrides?: Overrides): Promise; 84 | 85 | "claim(address)"( 86 | owner: string, 87 | overrides?: Overrides 88 | ): Promise; 89 | 90 | claimWithResolver( 91 | owner: string, 92 | resolver: string, 93 | overrides?: Overrides 94 | ): Promise; 95 | 96 | "claimWithResolver(address,address)"( 97 | owner: string, 98 | resolver: string, 99 | overrides?: Overrides 100 | ): Promise; 101 | 102 | ens(overrides?: CallOverrides): Promise<[string]>; 103 | 104 | "ens()"(overrides?: CallOverrides): Promise<[string]>; 105 | 106 | node(addr: string, overrides?: CallOverrides): Promise<[string]>; 107 | 108 | "node(address)"(addr: string, overrides?: CallOverrides): Promise<[string]>; 109 | 110 | resolver(overrides?: CallOverrides): Promise<[string]>; 111 | 112 | "resolver()"(overrides?: CallOverrides): Promise<[string]>; 113 | 114 | setName(name: string, overrides?: Overrides): Promise; 115 | 116 | "setName(string)"( 117 | name: string, 118 | overrides?: Overrides 119 | ): Promise; 120 | }; 121 | 122 | ADDR_REVERSE_NODE(overrides?: CallOverrides): Promise; 123 | 124 | "ADDR_REVERSE_NODE()"(overrides?: CallOverrides): Promise; 125 | 126 | claim(owner: string, overrides?: Overrides): Promise; 127 | 128 | "claim(address)"( 129 | owner: string, 130 | overrides?: Overrides 131 | ): Promise; 132 | 133 | claimWithResolver( 134 | owner: string, 135 | resolver: string, 136 | overrides?: Overrides 137 | ): Promise; 138 | 139 | "claimWithResolver(address,address)"( 140 | owner: string, 141 | resolver: string, 142 | overrides?: Overrides 143 | ): Promise; 144 | 145 | ens(overrides?: CallOverrides): Promise; 146 | 147 | "ens()"(overrides?: CallOverrides): Promise; 148 | 149 | node(addr: string, overrides?: CallOverrides): Promise; 150 | 151 | "node(address)"(addr: string, overrides?: CallOverrides): Promise; 152 | 153 | resolver(overrides?: CallOverrides): Promise; 154 | 155 | "resolver()"(overrides?: CallOverrides): Promise; 156 | 157 | setName(name: string, overrides?: Overrides): Promise; 158 | 159 | "setName(string)"( 160 | name: string, 161 | overrides?: Overrides 162 | ): Promise; 163 | 164 | callStatic: { 165 | ADDR_REVERSE_NODE(overrides?: CallOverrides): Promise; 166 | 167 | "ADDR_REVERSE_NODE()"(overrides?: CallOverrides): Promise; 168 | 169 | claim(owner: string, overrides?: CallOverrides): Promise; 170 | 171 | "claim(address)"(owner: string, overrides?: CallOverrides): Promise; 172 | 173 | claimWithResolver( 174 | owner: string, 175 | resolver: string, 176 | overrides?: CallOverrides 177 | ): Promise; 178 | 179 | "claimWithResolver(address,address)"( 180 | owner: string, 181 | resolver: string, 182 | overrides?: CallOverrides 183 | ): Promise; 184 | 185 | ens(overrides?: CallOverrides): Promise; 186 | 187 | "ens()"(overrides?: CallOverrides): Promise; 188 | 189 | node(addr: string, overrides?: CallOverrides): Promise; 190 | 191 | "node(address)"(addr: string, overrides?: CallOverrides): Promise; 192 | 193 | resolver(overrides?: CallOverrides): Promise; 194 | 195 | "resolver()"(overrides?: CallOverrides): Promise; 196 | 197 | setName(name: string, overrides?: CallOverrides): Promise; 198 | 199 | "setName(string)"(name: string, overrides?: CallOverrides): Promise; 200 | }; 201 | 202 | filters: {}; 203 | 204 | estimateGas: { 205 | ADDR_REVERSE_NODE(overrides?: CallOverrides): Promise; 206 | 207 | "ADDR_REVERSE_NODE()"(overrides?: CallOverrides): Promise; 208 | 209 | claim(owner: string, overrides?: Overrides): Promise; 210 | 211 | "claim(address)"(owner: string, overrides?: Overrides): Promise; 212 | 213 | claimWithResolver( 214 | owner: string, 215 | resolver: string, 216 | overrides?: Overrides 217 | ): Promise; 218 | 219 | "claimWithResolver(address,address)"( 220 | owner: string, 221 | resolver: string, 222 | overrides?: Overrides 223 | ): Promise; 224 | 225 | ens(overrides?: CallOverrides): Promise; 226 | 227 | "ens()"(overrides?: CallOverrides): Promise; 228 | 229 | node(addr: string, overrides?: CallOverrides): Promise; 230 | 231 | "node(address)"( 232 | addr: string, 233 | overrides?: CallOverrides 234 | ): Promise; 235 | 236 | resolver(overrides?: CallOverrides): Promise; 237 | 238 | "resolver()"(overrides?: CallOverrides): Promise; 239 | 240 | setName(name: string, overrides?: Overrides): Promise; 241 | 242 | "setName(string)"(name: string, overrides?: Overrides): Promise; 243 | }; 244 | 245 | populateTransaction: { 246 | ADDR_REVERSE_NODE(overrides?: CallOverrides): Promise; 247 | 248 | "ADDR_REVERSE_NODE()"( 249 | overrides?: CallOverrides 250 | ): Promise; 251 | 252 | claim(owner: string, overrides?: Overrides): Promise; 253 | 254 | "claim(address)"( 255 | owner: string, 256 | overrides?: Overrides 257 | ): Promise; 258 | 259 | claimWithResolver( 260 | owner: string, 261 | resolver: string, 262 | overrides?: Overrides 263 | ): Promise; 264 | 265 | "claimWithResolver(address,address)"( 266 | owner: string, 267 | resolver: string, 268 | overrides?: Overrides 269 | ): Promise; 270 | 271 | ens(overrides?: CallOverrides): Promise; 272 | 273 | "ens()"(overrides?: CallOverrides): Promise; 274 | 275 | node( 276 | addr: string, 277 | overrides?: CallOverrides 278 | ): Promise; 279 | 280 | "node(address)"( 281 | addr: string, 282 | overrides?: CallOverrides 283 | ): Promise; 284 | 285 | resolver(overrides?: CallOverrides): Promise; 286 | 287 | "resolver()"(overrides?: CallOverrides): Promise; 288 | 289 | setName(name: string, overrides?: Overrides): Promise; 290 | 291 | "setName(string)"( 292 | name: string, 293 | overrides?: Overrides 294 | ): Promise; 295 | }; 296 | } 297 | -------------------------------------------------------------------------------- /ts-types/contracts/factories/MirrorENSResolver__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Signer } from "ethers"; 6 | import { Provider, TransactionRequest } from "@ethersproject/providers"; 7 | import { Contract, ContractFactory, Overrides } from "@ethersproject/contracts"; 8 | 9 | import type { MirrorENSResolver } from "../MirrorENSResolver"; 10 | 11 | export class MirrorENSResolver__factory extends ContractFactory { 12 | constructor(signer?: Signer) { 13 | super(_abi, _bytecode, signer); 14 | } 15 | 16 | deploy(overrides?: Overrides): Promise { 17 | return super.deploy(overrides || {}) as Promise; 18 | } 19 | getDeployTransaction(overrides?: Overrides): TransactionRequest { 20 | return super.getDeployTransaction(overrides || {}); 21 | } 22 | attach(address: string): MirrorENSResolver { 23 | return super.attach(address) as MirrorENSResolver; 24 | } 25 | connect(signer: Signer): MirrorENSResolver__factory { 26 | return super.connect(signer) as MirrorENSResolver__factory; 27 | } 28 | static connect( 29 | address: string, 30 | signerOrProvider: Signer | Provider 31 | ): MirrorENSResolver { 32 | return new Contract(address, _abi, signerOrProvider) as MirrorENSResolver; 33 | } 34 | } 35 | 36 | const _abi = [ 37 | { 38 | anonymous: false, 39 | inputs: [ 40 | { 41 | indexed: true, 42 | internalType: "bytes32", 43 | name: "_node", 44 | type: "bytes32", 45 | }, 46 | { 47 | indexed: false, 48 | internalType: "address", 49 | name: "_addr", 50 | type: "address", 51 | }, 52 | ], 53 | name: "AddrChanged", 54 | type: "event", 55 | }, 56 | { 57 | anonymous: false, 58 | inputs: [ 59 | { 60 | indexed: true, 61 | internalType: "bytes32", 62 | name: "_node", 63 | type: "bytes32", 64 | }, 65 | { 66 | indexed: false, 67 | internalType: "string", 68 | name: "_name", 69 | type: "string", 70 | }, 71 | ], 72 | name: "NameChanged", 73 | type: "event", 74 | }, 75 | { 76 | anonymous: false, 77 | inputs: [ 78 | { 79 | indexed: true, 80 | internalType: "address", 81 | name: "previousOwner", 82 | type: "address", 83 | }, 84 | { 85 | indexed: true, 86 | internalType: "address", 87 | name: "newOwner", 88 | type: "address", 89 | }, 90 | ], 91 | name: "OwnershipTransferred", 92 | type: "event", 93 | }, 94 | { 95 | inputs: [ 96 | { 97 | internalType: "bytes32", 98 | name: "_node", 99 | type: "bytes32", 100 | }, 101 | ], 102 | name: "addr", 103 | outputs: [ 104 | { 105 | internalType: "address", 106 | name: "", 107 | type: "address", 108 | }, 109 | ], 110 | stateMutability: "view", 111 | type: "function", 112 | }, 113 | { 114 | inputs: [ 115 | { 116 | internalType: "bytes32", 117 | name: "_node", 118 | type: "bytes32", 119 | }, 120 | ], 121 | name: "name", 122 | outputs: [ 123 | { 124 | internalType: "string", 125 | name: "", 126 | type: "string", 127 | }, 128 | ], 129 | stateMutability: "view", 130 | type: "function", 131 | }, 132 | { 133 | inputs: [], 134 | name: "owner", 135 | outputs: [ 136 | { 137 | internalType: "address", 138 | name: "", 139 | type: "address", 140 | }, 141 | ], 142 | stateMutability: "view", 143 | type: "function", 144 | }, 145 | { 146 | inputs: [], 147 | name: "renounceOwnership", 148 | outputs: [], 149 | stateMutability: "nonpayable", 150 | type: "function", 151 | }, 152 | { 153 | inputs: [ 154 | { 155 | internalType: "bytes32", 156 | name: "_node", 157 | type: "bytes32", 158 | }, 159 | { 160 | internalType: "address", 161 | name: "_addr", 162 | type: "address", 163 | }, 164 | ], 165 | name: "setAddr", 166 | outputs: [], 167 | stateMutability: "nonpayable", 168 | type: "function", 169 | }, 170 | { 171 | inputs: [ 172 | { 173 | internalType: "bytes32", 174 | name: "_node", 175 | type: "bytes32", 176 | }, 177 | { 178 | internalType: "string", 179 | name: "_name", 180 | type: "string", 181 | }, 182 | ], 183 | name: "setName", 184 | outputs: [], 185 | stateMutability: "nonpayable", 186 | type: "function", 187 | }, 188 | { 189 | inputs: [ 190 | { 191 | internalType: "bytes4", 192 | name: "_interfaceID", 193 | type: "bytes4", 194 | }, 195 | ], 196 | name: "supportsInterface", 197 | outputs: [ 198 | { 199 | internalType: "bool", 200 | name: "", 201 | type: "bool", 202 | }, 203 | ], 204 | stateMutability: "pure", 205 | type: "function", 206 | }, 207 | { 208 | inputs: [ 209 | { 210 | internalType: "address", 211 | name: "newOwner", 212 | type: "address", 213 | }, 214 | ], 215 | name: "transferOwnership", 216 | outputs: [], 217 | stateMutability: "nonpayable", 218 | type: "function", 219 | }, 220 | ]; 221 | 222 | const _bytecode = 223 | "0x608060405234801561001057600080fd5b5060006100246001600160e01b0361007316565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350610077565b3390565b610965806100866000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063773722131161005b57806377372213146101b55780638da5cb5b14610262578063d5fa2b001461026a578063f2fde38b1461029657610088565b806301ffc9a71461008d5780633b3b57de146100e0578063691f343114610119578063715018a6146101ab575b600080fd5b6100cc600480360360208110156100a357600080fd5b50357fffffffff00000000000000000000000000000000000000000000000000000000166102bc565b604080519115158252519081900360200190f35b6100fd600480360360208110156100f657600080fd5b50356103a1565b604080516001600160a01b039092168252519081900360200190f35b6101366004803603602081101561012f57600080fd5b50356103bc565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610170578181015183820152602001610158565b50505050905090810190601f16801561019d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101b361047c565b005b6101b3600480360360408110156101cb57600080fd5b813591908101906040810160208201356401000000008111156101ed57600080fd5b8201836020820111156101ff57600080fd5b8035906020019184600183028401116401000000008311171561022157600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061053d945050505050565b6100fd61066b565b6101b36004803603604081101561028057600080fd5b50803590602001356001600160a01b031661067b565b6101b3600480360360208110156102ac57600080fd5b50356001600160a01b0316610756565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000148061034f57507fffffffff0000000000000000000000000000000000000000000000000000000082167f3b3b57de00000000000000000000000000000000000000000000000000000000145b8061039b57507fffffffff0000000000000000000000000000000000000000000000000000000082167f691f343100000000000000000000000000000000000000000000000000000000145b92915050565b6000908152600160205260409020546001600160a01b031690565b600081815260016020818152604092839020820180548451600294821615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190911693909304601f810183900483028401830190945283835260609390918301828280156104705780601f1061044557610100808354040283529160200191610470565b820191906000526020600020905b81548152906001019060200180831161045357829003601f168201915b50505050509050919050565b61048461086d565b6000546001600160a01b039081169116146104e6576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a36000805473ffffffffffffffffffffffffffffffffffffffff19169055565b61054561086d565b6000546001600160a01b039081169116146105a7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600082815260016020818152604090922083516105cc93919092019190840190610871565b50817fb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f7826040518080602001828103825283818151815260200191508051906020019080838360005b8381101561062d578181015183820152602001610615565b50505050905090810190601f16801561065a5780820380516001836020036101000a031916815260200191505b509250505060405180910390a25050565b6000546001600160a01b03165b90565b61068361086d565b6000546001600160a01b039081169116146106e5576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600082815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0385169081179091558251908152915184927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a25050565b61075e61086d565b6000546001600160a01b039081169116146107c0576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166108055760405162461bcd60e51b815260040180806020018281038252602681526020018061090a6026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b3390565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106108b257805160ff19168380011785556108df565b828001600101855582156108df579182015b828111156108df5782518255916020019190600101906108c4565b506108eb9291506108ef565b5090565b61067891905b808211156108eb57600081556001016108f556fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373a2646970667358221220b5ddd9d4abbd6a454e0105da3403269677548064e34f7464e9962828d859944664736f6c63430006080033"; 224 | -------------------------------------------------------------------------------- /test/MirrorWriteToken.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { BigNumber } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | 5 | import setup from "./setup"; 6 | 7 | const REGISTRATION_COST = "1000000000000000000"; 8 | 9 | describe("MirrorWriteToken", () => { 10 | // Contracts 11 | let token; 12 | let mirrorENSRegistrar; 13 | let ensRegistry; 14 | let mirrorENSResolver; 15 | 16 | // Accounts 17 | let owner; 18 | let account1; 19 | let account2; 20 | let account3; 21 | 22 | beforeEach(async () => { 23 | ({ 24 | mirrorWriteToken: token, 25 | mirrorENSRegistrar, 26 | ensRegistry, 27 | mirrorENSResolver, 28 | } = await setup()); 29 | 30 | [owner, account1, account2, account3] = await ethers.getSigners(); 31 | }); 32 | 33 | describe("deployed", () => { 34 | it("has the correct name", async () => { 35 | const name = await token.name(); 36 | expect(name).to.eq("Mirror Write Token"); 37 | }); 38 | 39 | it("has the correct token symbol", async () => { 40 | const symbol = await token.symbol(); 41 | expect(symbol).to.eq("WRITE"); 42 | }); 43 | 44 | it("has the correct number of decimals", async () => { 45 | const decimals = await token.decimals(); 46 | expect(decimals.toString()).to.eq("18"); 47 | }); 48 | 49 | it("has the correct registration cost", async () => { 50 | const cost = await token.registrationCost(); 51 | expect(cost.toString()).to.eq(REGISTRATION_COST); 52 | }); 53 | 54 | it("has the correct registrar", async () => { 55 | const registrar = await token.ensRegistrar(); 56 | expect(registrar).to.eq(mirrorENSRegistrar.address); 57 | }); 58 | }); 59 | 60 | describe("#mint", () => { 61 | describe("when called by the owner to be given to another account", () => { 62 | let accountToReceive; 63 | let accountNotToReceive; 64 | 65 | beforeEach(async () => { 66 | accountToReceive = account1; 67 | accountNotToReceive = account2; 68 | 69 | await token.connect(owner).mint(account1.address, 1); 70 | }); 71 | 72 | it("mints a token for the account", async () => { 73 | const accountBalance = await token.balanceOf(accountToReceive.address); 74 | expect(accountBalance.toString()).to.equal("1"); 75 | }); 76 | 77 | it("other accounts still have 0 balance", async () => { 78 | const accountBalance = await token.balanceOf( 79 | accountNotToReceive.address 80 | ); 81 | expect(accountBalance.toString()).to.equal("0"); 82 | }); 83 | }); 84 | 85 | describe("when called by a non-owner to be given to another account", () => { 86 | let nonOwner; 87 | let accountIntendedToReceive; 88 | let transaction; 89 | 90 | beforeEach(async () => { 91 | nonOwner = account1; 92 | accountIntendedToReceive = account2; 93 | }); 94 | 95 | it("reverts the transaction", async () => { 96 | transaction = token 97 | .connect(nonOwner) 98 | .mint(accountIntendedToReceive.address, 1); 99 | await expect(transaction).to.be.revertedWith( 100 | "MirrorWriteToken: caller is not the owner" 101 | ); 102 | }); 103 | 104 | it("the accounts all still have 0 balance", async () => { 105 | const accountBalance = await token.balanceOf( 106 | accountIntendedToReceive.address 107 | ); 108 | expect(accountBalance.toString()).to.equal("0"); 109 | }); 110 | }); 111 | }); 112 | 113 | describe("#setENSRegistrar", () => { 114 | describe("when called by the owner", () => { 115 | it("updates the registrar appropriately", async () => { 116 | // Set it to a new address, and check that it updated correctly. 117 | const newAddress = "0xC85Ef1106632B9e7F8DE9cE0c0f1de1F70E67694"; 118 | await token.connect(owner).setENSRegistrar(newAddress); 119 | 120 | expect(await token.ensRegistrar()).to.eq(newAddress); 121 | 122 | // Set it back to the actual registrar, and check that it updated correctly again. 123 | await token.connect(owner).setENSRegistrar(mirrorENSRegistrar.address); 124 | expect(await token.ensRegistrar()).to.eq(mirrorENSRegistrar.address); 125 | }); 126 | }); 127 | 128 | describe("when called by a non-owner account", () => { 129 | it("it reverts the transaction", async () => { 130 | // Set it to a new address, and check that it updated correctly. 131 | const newAddress = "0xC85Ef1106632B9e7F8DE9cE0c0f1de1F70E67694"; 132 | const transaction = token.connect(account1).setENSRegistrar(newAddress); 133 | await expect(transaction).to.be.revertedWith( 134 | "MirrorWriteToken: caller is not the owner" 135 | ); 136 | 137 | // Original registrar still set. 138 | expect(await token.ensRegistrar()).to.eq(mirrorENSRegistrar.address); 139 | }); 140 | }); 141 | }); 142 | 143 | describe("#setRegistrable", () => { 144 | describe("when called by an address that is not the token owner", () => { 145 | it("reverts the transaction", async () => { 146 | const transaction = token.connect(account1).setRegistrable(false); 147 | await expect(transaction).to.be.revertedWith( 148 | "MirrorWriteToken: caller is not the owner" 149 | ); 150 | }); 151 | }); 152 | 153 | describe("when called by the token owner", () => { 154 | it("updates the registrable variable appropriately", async () => { 155 | await token.connect(owner).setRegistrable(false); 156 | expect(await token.registrable()).to.eq(false); 157 | 158 | await token.connect(owner).setRegistrable(true); 159 | expect(await token.registrable()).to.eq(true); 160 | }); 161 | }); 162 | }); 163 | 164 | describe("#register", () => { 165 | describe("when the account does not have an invite token", () => { 166 | it("reverts the transaction", async () => { 167 | const transaction = token 168 | .connect(account1) 169 | .register("test", account1.address); 170 | await expect(transaction).to.be.reverted; 171 | }); 172 | }); 173 | 174 | describe("when the account has an invite token", () => { 175 | let transaction; 176 | let receipt; 177 | const label = "test"; 178 | const initialTokens = 3; 179 | 180 | beforeEach(async () => { 181 | const numTokens = BigNumber.from(REGISTRATION_COST).mul(initialTokens); 182 | await token.connect(owner).mint(account1.address, numTokens); 183 | // Note: Here we actually register a label for a different account, 184 | // to test that it doesn't have to be the msg.sender's account that's registered. 185 | transaction = await token 186 | .connect(account1) 187 | .register(label, account2.address); 188 | receipt = await transaction.wait(); 189 | }); 190 | 191 | describe("when `registrable` is set to false", () => { 192 | it("reverts the transaction", async () => { 193 | await token.connect(owner).setRegistrable(false); 194 | const transaction = token 195 | .connect(account1) 196 | .register(label, account1.address); 197 | await expect(transaction).to.be.revertedWith( 198 | "MirrorWriteToken: registration is closed" 199 | ); 200 | await token.connect(owner).setRegistrable(false); 201 | }); 202 | }); 203 | 204 | describe("when a label has already been taken", () => { 205 | it("reverts the transaction", async () => { 206 | const transaction = token 207 | .connect(account1) 208 | .register(label, account1.address); 209 | await expect(transaction).to.be.revertedWith( 210 | "MirrorENSManager: label is already owned" 211 | ); 212 | }); 213 | }); 214 | 215 | it("burns the user's token", async () => { 216 | const accountBalance = await token.balanceOf(account1.address); 217 | const expectedBalance = BigNumber.from(REGISTRATION_COST).mul( 218 | initialTokens - 1 219 | ); 220 | expect(accountBalance.toString()).to.equal(expectedBalance.toString()); 221 | }); 222 | 223 | it("registers the requested ENS label and assigns ownership to the owner", async () => { 224 | const subdomainOwner = await ensRegistry.owner( 225 | ethers.utils.namehash(`${label}.mirror.xyz`) 226 | ); 227 | expect(subdomainOwner).to.eq(account2.address); 228 | }); 229 | 230 | it("uses 122814 gas", () => { 231 | const { gasUsed } = receipt; 232 | expect(gasUsed).to.eq(122814); 233 | }); 234 | 235 | describe("changing ENS ownership", () => { 236 | describe("labelOwner", () => { 237 | it("returns the owner of the label", async () => { 238 | const result = await mirrorENSRegistrar.labelOwner(label); 239 | expect(result).to.eq(account2.address); 240 | }); 241 | }); 242 | 243 | describe("changeLabelOwner", () => { 244 | describe("when the owner changes the label", () => { 245 | beforeEach(async () => { 246 | // Change from account2 to account3. 247 | await mirrorENSRegistrar 248 | .connect(account2) 249 | .changeLabelOwner(label, account3.address); 250 | }); 251 | 252 | it("updates ownership", async () => { 253 | const result = await mirrorENSRegistrar.labelOwner(label); 254 | expect(result).to.eq(account3.address); 255 | 256 | const subdomainOwner = await ensRegistry.owner( 257 | ethers.utils.namehash(`${label}.mirror.xyz`) 258 | ); 259 | expect(subdomainOwner).to.eq(account3.address); 260 | }); 261 | 262 | it("updates resolved address", async () => { 263 | const subdomainAddr = await mirrorENSResolver.addr( 264 | ethers.utils.namehash(`${label}.mirror.xyz`) 265 | ); 266 | expect(subdomainAddr).to.eq(account3.address); 267 | }); 268 | 269 | it("prevents another account from changing ownership", async () => { 270 | const transaction = mirrorENSRegistrar 271 | .connect(account2) 272 | .changeLabelOwner(label, account1.address); 273 | await expect(transaction).to.be.revertedWith( 274 | "MirrorENSManager: sender does not own label" 275 | ); 276 | }); 277 | }); 278 | }); 279 | }); 280 | }); 281 | }); 282 | }); 283 | --------------------------------------------------------------------------------