├── index.js ├── .env.template ├── .gitignore ├── .eslintrc.js ├── contracts ├── test │ └── MockProxyRegistry.sol └── RFOXCollection.sol ├── index.d.ts ├── hardhat.config.js ├── .github └── workflows │ └── main.yml ├── package.json ├── scripts ├── mint.js ├── sell.js └── deploy.js ├── test ├── RFOXCollection.int.test.js └── RFOXCollection.test.js ├── README.md └── artifacts └── contracts └── RFOXCollection.sol └── RFOXCollection.json /index.js: -------------------------------------------------------------------------------- 1 | const json = require("./artifacts/contracts/RFOXCollection.sol/RFOXCollection.json"); 2 | 3 | module.exports = { 4 | RFOXCollection: json, 5 | } 6 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | export INFURA_KEY= 2 | export OWNER_ADDRESS= 3 | export MNEMONIC="" 4 | export NFT_CONTRACT_ADDRESS= 5 | 6 | export PINATA_API_KEY= 7 | export PINATA_API_SECRET= 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /artifacts/**/*.json 2 | !/artifacts/contracts/RFOXCollection.sol/RFOXCollection.json 3 | /cache/ 4 | /node_modules/ 5 | /.env 6 | 7 | /coverage/ 8 | /coverage.json 9 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | globals: { 8 | Atomics: "readonly", 9 | SharedArrayBuffer: "readonly", 10 | }, 11 | parserOptions: { 12 | ecmaVersion: 2018, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /contracts/test/MockProxyRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | /** 7 | * @dev A simple mock ProxyRegistry for use in local tests with minimal security 8 | */ 9 | contract MockProxyRegistry is Ownable { 10 | mapping(address => address) public proxies; 11 | 12 | /** 13 | * @notice Allow the owner to set a proxy for testing 14 | * @param _address The address that the proxy will act on behalf of 15 | * @param _proxyForAddress The proxy that will act on behalf of the address 16 | */ 17 | function setProxy(address _address, address _proxyForAddress) 18 | external 19 | onlyOwner 20 | { 21 | proxies[_address] = _proxyForAddress; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | 2 | export type Param = { 3 | internalType: string, 4 | name: string, 5 | type: string, 6 | } 7 | 8 | export type ConstructorType = { 9 | type: 'constructor', 10 | inputs: Param[], 11 | stateMutability: string 12 | } 13 | 14 | export type EventType = { 15 | type: 'event', 16 | name: string, 17 | inputs: Param[], 18 | anonymous: boolean, 19 | } 20 | 21 | export type FunctionType = { 22 | type: 'function', 23 | name: string, 24 | inputs: Param[], 25 | outputs: Param[], 26 | stateMutability: string 27 | } 28 | 29 | export const RFOXCollection: { 30 | contractName: string, 31 | abi: (ConstructorType | EventType | FunctionType)[], 32 | metadata: string, 33 | bytecode: string, 34 | deployedBytecode: string, 35 | linkReferences: any, 36 | deployedLinkReferences: any, 37 | }; 38 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@nomiclabs/hardhat-waffle'); 2 | require('@nomiclabs/hardhat-etherscan'); 3 | require('solidity-coverage'); 4 | 5 | const networks = process.env.INFURA_KEY && process.env.OWNER_ADDRESS && process.env.MNEMONIC ? 6 | { 7 | rinkeby: { 8 | url: 'https://rinkeby.infura.io/v3/' + process.env.INFURA_KEY, 9 | chainId: 4, 10 | from: process.env.OWNER_ADDRESS, 11 | gas: 'auto', 12 | accounts: { 13 | mnemonic: process.env.MNEMONIC 14 | }, 15 | } 16 | } 17 | : 18 | {}; 19 | 20 | const etherscan = process.env.ETHERSCAN_API_KEY ? 21 | { 22 | apiKey: process.env.ETHERSCAN_API_KEY 23 | } 24 | : 25 | {}; 26 | 27 | const defaultNetwork = process.env.INFURA_KEY && process.env.OWNER_ADDRESS && process.env.MNEMONIC ? 28 | "rinkeby" : 29 | "hardhat" 30 | 31 | /** 32 | * @type import('hardhat/config').HardhatUserConfig 33 | */ 34 | module.exports = { 35 | defaultNetwork, 36 | networks, 37 | solidity: "0.8.0", 38 | etherscan, 39 | }; 40 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: NFT Platform Contracts CI 2 | 3 | on: 4 | # We want to trigger the build & test pipelines in every branch. 5 | push: 6 | # But do no run the pipeline when only `md` files are modified. 7 | paths-ignore: 8 | - '**.md' 9 | 10 | jobs: 11 | 12 | check-abi: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - run: yarn install --frozen-lockfile 17 | - run: yarn build 18 | - run: git --no-pager diff --exit-code artifacts/contracts/RFOXCollection.sol/RFOXCollection.json 19 | 20 | test: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - run: yarn install --frozen-lockfile 25 | - run: yarn test 26 | 27 | coverage: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v2 31 | - run: yarn install --frozen-lockfile 32 | - run: yarn coverage 33 | - run: git remote set-url origin https://git:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git 34 | - run: yarn gh-pages --dist coverage/ --user "Github Workflow Bot " 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nft-platform-contracts", 3 | "version": "0.1.0", 4 | "description": "RFOX Labs' Smart Contracts, including ERC721 templates, and factories.", 5 | "private": true, 6 | "main": "index.js", 7 | "types": "index.d.ts", 8 | "scripts": { 9 | "build": "hardhat compile", 10 | "test": "hardhat test test/RFOXCollection.test.js", 11 | "test:int": ". ./.env && hardhat test test/RFOXCollection.int.test.js", 12 | "coverage": "hardhat coverage --testfiles test/RFOXCollection.test.js", 13 | "deploy": ". ./.env && hardhat run --network rinkeby scripts/deploy.js", 14 | "mint": ". ./.env && hardhat run --network rinkeby scripts/mint.js" 15 | }, 16 | "license": "ISC", 17 | "devDependencies": { 18 | "@0x/subproviders": "^6.5.4", 19 | "@nomiclabs/hardhat-ethers": "^2.0.2", 20 | "@nomiclabs/hardhat-etherscan": "^2.1.4", 21 | "@nomiclabs/hardhat-waffle": "^2.0.1", 22 | "@openzeppelin/contracts": "^4.3.2", 23 | "@pinata/sdk": "^1.1.23", 24 | "chai": "^4.3.4", 25 | "eslint": "^7.22.0", 26 | "ethereum-waffle": "^3.0.0", 27 | "ethers": "^5.4.4", 28 | "gh-pages": "^3.2.3", 29 | "hardhat": "^2.6.0", 30 | "opensea-js": "^1.1.11", 31 | "solidity-coverage": "^0.7.17", 32 | "web3": "^1.5.1", 33 | "web3-provider-engine": "^16.0.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /scripts/mint.js: -------------------------------------------------------------------------------- 1 | const OWNER_ADDRESS = process.env.OWNER_ADDRESS; 2 | const NUM_CREATURES = 5; 3 | 4 | const pinata = require('@pinata/sdk')(process.env.PINATA_API_KEY, process.env.PINATA_API_SECRET); 5 | 6 | async function main() { 7 | const RFOXCollection = await ethers.getContractFactory('RFOXCollection'); 8 | const contract = await RFOXCollection.attach(process.env.NFT_CONTRACT_ADDRESS); 9 | 10 | // Creatures issued directly to the owner. 11 | for (var i = 0; i < NUM_CREATURES; i++) { 12 | console.log("minting "); 13 | 14 | const res = await pinata.pinJSONToIPFS({ 15 | name: `Chainlink Elf #${i}`, 16 | description: "Inspiring, Based, Mythical, Oracle loving creature. Leading the new world and helping teach about superior digital agreements. Also is good with a bow!", 17 | image: "https://ipfs.io/ipfs/QmTgqnhFBMkfT9s8PHKcdXBn1f5bG3Q5hmBaR4U6hoTvb1?filename=Chainlink_Elf.png", 18 | attributes: [ 19 | { 20 | trait_type: "Strength", 21 | value: 59 22 | }, 23 | { 24 | trait_type: "Dexterity", 25 | value: 29 26 | }, 27 | ] 28 | }, { 29 | pinataMetadata: { 30 | name: 'MyCustomName', 31 | keyvalues: { 32 | } 33 | }, 34 | pinataOptions: { 35 | cidVersion: 1 36 | } 37 | }); 38 | console.log(res); 39 | 40 | const result = await contract.safeMint(OWNER_ADDRESS, res.IpfsHash); 41 | console.log("Minted creature. Transaction: " + result.transactionHash); 42 | } 43 | } 44 | 45 | main() 46 | .then(() => process.exit(0)) 47 | .catch(err => { 48 | console.error(err); 49 | process.exit(1); 50 | }); 51 | -------------------------------------------------------------------------------- /scripts/sell.js: -------------------------------------------------------------------------------- 1 | const MnemonicWalletSubprovider = require("@0x/subproviders").MnemonicWalletSubprovider; 2 | const RPCSubprovider = require("web3-provider-engine/subproviders/rpc"); 3 | const Web3ProviderEngine = require("web3-provider-engine"); 4 | const opensea = require("opensea-js"); 5 | const OpenSeaPort = opensea.OpenSeaPort; 6 | const Network = opensea.Network; 7 | 8 | const NFT_CONTRACT_ADDRESS = process.env.NFT_CONTRACT_ADDRESS; 9 | const OWNER_ADDRESS = process.env.OWNER_ADDRESS; 10 | 11 | // This example provider won't let you make transactions, only read-only calls: 12 | // const provider = new Web3.providers.HttpProvider('https://rinkeby.infura.io/v3/' + NODE_API_KEY) 13 | const provider = new Web3ProviderEngine(); 14 | provider.addProvider(new MnemonicWalletSubprovider({ 15 | mnemonic: process.env.MNEMONIC, 16 | baseDerivationPath: `44'/60'/0'/0`, 17 | })); 18 | provider.addProvider(new RPCSubprovider({ 19 | rpcUrl: "https://rinkeby.infura.io/v3/" + process.env.INFURA_KEY 20 | })); 21 | provider.start(); 22 | 23 | const seaport = new OpenSeaPort(provider, 24 | { 25 | networkName: Network.Rinkeby 26 | // apiKey: API_KEY, 27 | }, 28 | arg => console.log(arg) 29 | ) 30 | 31 | async function main() { 32 | const asset = await seaport.api.getAsset({ 33 | tokenAddress: NFT_CONTRACT_ADDRESS, 34 | tokenId: 1, 35 | }) 36 | console.log(asset) 37 | 38 | // Example: simple fixed-price sale of an item owned by a user. 39 | console.log("Auctioning an item for a fixed price..."); 40 | const fixedPriceSellOrder = await seaport.createSellOrder({ 41 | asset: { 42 | tokenId: "1", 43 | tokenAddress: NFT_CONTRACT_ADDRESS, 44 | }, 45 | startAmount: 0.05, 46 | expirationTime: 0, 47 | accountAddress: OWNER_ADDRESS, 48 | }); 49 | console.log( 50 | `Successfully created a fixed-price sell order! ${fixedPriceSellOrder.asset.openseaLink}\n` 51 | ); 52 | 53 | } 54 | 55 | main() 56 | .then(() => process.exit(0)) 57 | .catch(err => { 58 | console.error(err); 59 | process.exit(1); 60 | }); 61 | -------------------------------------------------------------------------------- /scripts/deploy.js: -------------------------------------------------------------------------------- 1 | const pinata = require('@pinata/sdk')(process.env.PINATA_API_KEY, process.env.PINATA_API_SECRET); 2 | 3 | function sleep(ms) { 4 | return new Promise((resolve) => { 5 | setTimeout(resolve, ms); 6 | }); 7 | } 8 | 9 | async function main() { 10 | // OpenSea proxy registry addresses for rinkeby and mainnet. 11 | const proxyRegistryAddress = '0xf57b2c51ded3a29e6891aba85459d600256cf317'; 12 | 13 | const name = 'This is a test for RFLabs'; 14 | 15 | const res = await pinata.pinJSONToIPFS({ 16 | name, 17 | "description": "Friendly monsters of the Open sea.", 18 | "external_link": "https://github.com/ProjectOpenSea/opensea-creatures/", 19 | "image": "ipfs://QmQPt2MWQFrmy6HN8GwQXYCTqQexnb51LBsRypM8YEUGc1", 20 | "seller_fee_basis_points": 100, 21 | "fee_recipient": process.env.OWNER_ADDRESS, 22 | // custom 23 | "banner_image": "ipfs://QmQPt2MWQFrmy6HN8GwQXYCTqQexnb51LBsRypM8YEUGc1", 24 | "wiki_url": "https://github.com/wiki-url-test-ProjectOpenSea/opensea-creatures/", 25 | }, { 26 | pinataMetadata: { 27 | name: 'Collection', 28 | keyvalues: { 29 | } 30 | }, 31 | pinataOptions: { 32 | cidVersion: 1 33 | } 34 | }); 35 | console.log(res); 36 | 37 | const RFOXCollection = await ethers.getContractFactory("RFOXCollection"); 38 | const contractURI = `ipfs://${res.IpfsHash}`; 39 | const contractName = 'RFOX:' + name; 40 | const symbol = ''; 41 | const contract = await RFOXCollection.deploy(contractURI, contractName, symbol, proxyRegistryAddress); 42 | 43 | console.log('Deployed to', contract.address, 'in transaction', contract.deployTransaction.hash); 44 | 45 | { 46 | console.log('Waiting to check contract deployment..'); 47 | await sleep(30000); 48 | 49 | const RFOXCollection_ = await ethers.getContractFactory('RFOXCollection'); 50 | const contract_ = await RFOXCollection_.attach(contract.address); 51 | 52 | console.log('Check contract deployment', 53 | await contract_.contractURI(), 54 | await contract_.name(), 55 | await contract_.symbol() 56 | ); 57 | } 58 | 59 | console.log('Verify smart contract in Etherscan ..') 60 | 61 | await hre.run("verify:verify", { 62 | address: contract.address, 63 | constructorArguments: [contractURI, contractName, symbol, proxyRegistryAddress], 64 | }); 65 | 66 | } 67 | 68 | main() 69 | .then(() => process.exit(0)) 70 | .catch(err => { 71 | console.error(err); 72 | process.exit(1); 73 | }); 74 | -------------------------------------------------------------------------------- /test/RFOXCollection.int.test.js: -------------------------------------------------------------------------------- 1 | require('@ethersproject/bignumber'); 2 | require('isomorphic-fetch'); 3 | const { expect } = require('chai'); 4 | const pinata = require('@pinata/sdk')(process.env.PINATA_API_KEY, process.env.PINATA_API_SECRET); 5 | 6 | function sleep(ms) { 7 | return new Promise((resolve) => { 8 | setTimeout(resolve, ms); 9 | }); 10 | } 11 | 12 | describe("RFOXCollection Contract", (done) => { 13 | it('should deploy a new contract & mint a new asset', async () => { 14 | // Arrange 15 | const proxyRegistryAddress = '0xf57b2c51ded3a29e6891aba85459d600256cf317'; 16 | 17 | const name = '[int] This is a test for RFLabs - ' + Date.now(); 18 | 19 | const collection = await pinata.pinJSONToIPFS({ 20 | name, 21 | "description": "[int] Friendly monsters of the Open sea.", 22 | "external_link": "https://github.com/ProjectOpenSea/opensea-creatures/", 23 | "image": "ipfs://QmQPt2MWQFrmy6HN8GwQXYCTqQexnb51LBsRypM8YEUGc1", 24 | "seller_fee_basis_points": 100, 25 | "fee_recipient": process.env.OWNER_ADDRESS, 26 | // custom 27 | "banner_image": "ipfs://QmQPt2MWQFrmy6HN8GwQXYCTqQexnb51LBsRypM8YEUGc1", 28 | "wiki_url": "https://github.com/wiki-url-test-ProjectOpenSea/opensea-creatures/", 29 | }, { 30 | pinataMetadata: { 31 | name: 'Collection', 32 | keyvalues: { 33 | } 34 | }, 35 | pinataOptions: { 36 | cidVersion: 1 37 | } 38 | }); 39 | 40 | const RFOXCollection = await ethers.getContractFactory("RFOXCollection"); 41 | const contractURI = `ipfs://${collection.IpfsHash}`; 42 | const contractName = 'RFOX:' + name; 43 | const symbol = ''; 44 | 45 | const asset = await pinata.pinJSONToIPFS({ 46 | name: '[int] Chainlink Elf - ' + Date.now(), 47 | description: "Inspiring, Based, Mythical, Oracle loving creature. Leading the new world and helping teach about superior digital agreements. Also is good with a bow!", 48 | image: "https://ipfs.io/ipfs/QmTgqnhFBMkfT9s8PHKcdXBn1f5bG3Q5hmBaR4U6hoTvb1?filename=Chainlink_Elf.png", 49 | attributes: [ 50 | { 51 | trait_type: "Strength", 52 | value: 59 53 | }, 54 | { 55 | trait_type: "Dexterity", 56 | value: 29 57 | }, 58 | ] 59 | }, { 60 | pinataMetadata: { 61 | name: 'MyCustomName', 62 | keyvalues: { 63 | } 64 | }, 65 | pinataOptions: { 66 | cidVersion: 1 67 | } 68 | }); 69 | 70 | // Act 71 | const contract = await RFOXCollection.deploy(contractURI, contractName, symbol, proxyRegistryAddress); 72 | await contract.deployed(); 73 | console.log('Deployed to', contract.address, 'in transaction', contract.deployTransaction.hash); 74 | 75 | const result = await contract.safeMint(process.env.OWNER_ADDRESS, asset.IpfsHash); 76 | console.log("Minted creature. Transaction: " + result.hash); 77 | 78 | // Assert 79 | const RFOXCollection_ = await ethers.getContractFactory('RFOXCollection'); 80 | const contract_ = await RFOXCollection_.attach(contract.address); 81 | 82 | expect(await contract_.contractURI()).to.equal(contractURI); 83 | expect(await contract_.name()).to.equal(contractName); 84 | expect(await contract_.symbol()).to.equal(symbol); 85 | }).timeout(100000); 86 | }); 87 | -------------------------------------------------------------------------------- /contracts/RFOXCollection.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; 6 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | import "@openzeppelin/contracts/utils/Counters.sol"; 9 | 10 | contract OwnableDelegateProxy {} 11 | 12 | contract ProxyRegistry { 13 | mapping(address => OwnableDelegateProxy) public proxies; 14 | } 15 | 16 | contract RFOXCollection is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable { 17 | 18 | string private _contractURI; 19 | 20 | address private _proxyRegistryAddress; 21 | 22 | using Counters for Counters.Counter; 23 | 24 | Counters.Counter private _tokenIdCounter; 25 | 26 | constructor(string memory contractURI_, string memory name_, string memory symbol_, address proxyRegistryAddress_) ERC721(name_, symbol_) { 27 | _contractURI = contractURI_; 28 | _proxyRegistryAddress = proxyRegistryAddress_; 29 | } 30 | 31 | function contractURI() public view returns (string memory) { 32 | return _contractURI; 33 | } 34 | 35 | function safeMint(address to_, string memory tokenURI_) public onlyOwner { 36 | require(bytes(tokenURI_).length > 0, "RFOXCOllection: `tokenURI_` is empty"); 37 | 38 | uint256 tokenId = _tokenIdCounter.current(); 39 | _safeMint(to_, tokenId); 40 | _setTokenURI(tokenId, tokenURI_); 41 | _tokenIdCounter.increment(); 42 | } 43 | 44 | function _baseURI() internal pure override returns (string memory) { 45 | return "ipfs://"; 46 | } 47 | 48 | function _beforeTokenTransfer(address from, address to, uint256 tokenId) 49 | internal 50 | override(ERC721, ERC721Enumerable) 51 | { 52 | super._beforeTokenTransfer(from, to, tokenId); 53 | } 54 | 55 | function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { 56 | super._burn(tokenId); 57 | } 58 | 59 | function tokenURI(uint256 tokenId) 60 | public 61 | view 62 | override(ERC721, ERC721URIStorage) 63 | returns (string memory) 64 | { 65 | return super.tokenURI(tokenId); 66 | } 67 | 68 | /** 69 | * 70 | */ 71 | function setTokenURI(uint256 tokenId_, string memory tokenURI_) public { 72 | require(_isApprovedOrOwner(_msgSender(), tokenId_), "RFOXCollection: `setTokenURI` caller is not owner nor approved"); 73 | require(bytes(tokenURI_).length > 0, "RFOXCOllection: `tokenURI_` is empty"); 74 | 75 | _setTokenURI(tokenId_, tokenURI_); 76 | } 77 | 78 | function supportsInterface(bytes4 interfaceId) 79 | public 80 | view 81 | override(ERC721, ERC721Enumerable) 82 | returns (bool) 83 | { 84 | return super.supportsInterface(interfaceId); 85 | } 86 | 87 | /** 88 | * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings. 89 | */ 90 | function isApprovedForAll(address owner, address operator) override public view returns (bool) { 91 | // Whitelist OpenSea proxy contract for easy trading. 92 | ProxyRegistry proxyRegistry = ProxyRegistry(_proxyRegistryAddress); 93 | if (address(proxyRegistry.proxies(owner)) == operator) { 94 | return true; 95 | } 96 | 97 | return super.isApprovedForAll(owner, operator); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /test/RFOXCollection.test.js: -------------------------------------------------------------------------------- 1 | require('@ethersproject/bignumber'); 2 | const { expect } = require('chai'); 3 | 4 | describe("RFOXCollection Contract", () => { 5 | const CONTRACT_URI = 'ipfs://QmYSrTKu4GQoKSH17wn5SsCVEaEQMwxDRsTfwrzD43HWRD'; 6 | 7 | let owner; 8 | let otherSigner; 9 | let receiverSigner; 10 | let proxyForOwner; 11 | 12 | let proxy; 13 | let contract; 14 | 15 | before(async () => { 16 | [owner, otherSigner, receiverSigner, proxyForOwner] = await ethers.getSigners(); 17 | 18 | const MockProxyRegistry = await ethers.getContractFactory("MockProxyRegistry"); 19 | proxy = await MockProxyRegistry.deploy(); 20 | await proxy.setProxy(owner.address, proxyForOwner.address); 21 | 22 | const RFOXCollection = await ethers.getContractFactory("RFOXCollection"); 23 | contract = await RFOXCollection.deploy(CONTRACT_URI, 'RFOX Test Collection', 'TST', proxy.address); 24 | }); 25 | 26 | it('should set the constructor args to the supplied value', async () => { 27 | expect(await contract.contractURI()).to.equal(CONTRACT_URI); 28 | expect(await contract.name()).to.equal('RFOX Test Collection'); 29 | expect(await contract.symbol()).to.equal('TST'); 30 | }); 31 | 32 | it('should get and set URI for minted tokens', async () => { 33 | for (let tokenId = 0; tokenId < 10; tokenId++) { 34 | const hash = `QmYSrTKu4GQoKSH17wn5SsCVEaEQMwxDRsTfwrzD43HWR${tokenId}`; 35 | await contract.safeMint(owner.address, hash); 36 | expect(await contract.tokenURI(tokenId)).to.equal(`ipfs://${hash}`); 37 | 38 | const updatedHash = `QmYSrTKu4GQoKSH17wn5SsCVEaEQMwxDRsTfwrzD43HWR-new${tokenId}`; 39 | await contract.setTokenURI(tokenId, updatedHash); 40 | expect(await contract.tokenURI(tokenId)).to.equal(`ipfs://${updatedHash}`); 41 | } 42 | }); 43 | 44 | it('should fail if token URI is empty when minting', async () => { 45 | await expect( 46 | contract.safeMint('0xB9cD322bbC641B93e9A9F4262283Fd30C44c35d9', '') 47 | ).to.be.revertedWith('RFOXCOllection: `tokenURI_` is empty'); 48 | }); 49 | 50 | it('should fail if someone else than the NFT owner tries to set the token URI', async () => { 51 | const nextTokenId = (await contract.totalSupply()).toNumber(); 52 | await contract.safeMint(owner.address, 'hash-here'); 53 | await expect( 54 | contract.connect(otherSigner).setTokenURI(nextTokenId, 'QmYSrTKu4GQoKSH17wn5SsCVEaEQMwxDRsTfwrzD43HWR') 55 | ).to.be.revertedWith('RFOXCollection: `setTokenURI` caller is not owner nor approved'); 56 | }); 57 | 58 | it('should fail if token URI is empty', async () => { 59 | const nextTokenId = (await contract.totalSupply()).toNumber(); 60 | await contract.safeMint(owner.address, 'hash-here'); 61 | await expect( 62 | contract.setTokenURI(nextTokenId, '') 63 | ).to.be.revertedWith('RFOXCOllection: `tokenURI_` is empty'); 64 | }); 65 | 66 | it('should allow `proxyForOwner` to make transfers', async () => { 67 | const n = (await contract.totalSupply()).toNumber(); 68 | for (let i = 0; i < 5; i++) { 69 | const hash = `QmYSrTKu4GQoKSH17wn5SsCVEaEQMwxDRsTfwrzD43HWR${i}q`; 70 | await contract.connect(owner).safeMint(owner.address, hash); 71 | 72 | const tokenId = n + i; 73 | expect(await contract.tokenURI(tokenId)).to.equal(`ipfs://${hash}`); 74 | 75 | expect(await contract.connect(owner).ownerOf(tokenId)).to.equal(owner.address); 76 | await contract.connect(proxyForOwner).transferFrom(owner.address, receiverSigner.address, tokenId); 77 | expect(await contract.ownerOf(tokenId)).to.equal(receiverSigner.address); 78 | } 79 | }); 80 | 81 | it('should fail if someone else than the owner tries to mint a token', async () => { 82 | await expect( 83 | contract.connect(otherSigner).safeMint('0xB9cD322bbC641B93e9A9F4262283Fd30C44c35d9', 'QmYSrTKu4GQoKSH17wn5SsCVEaEQMwxDRsTfwrzD43HWR') 84 | ).to.be.revertedWith('Ownable: caller is not the owner'); 85 | }); 86 | 87 | }); 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RFOXLabs Collection ERC721 Smart Contracts 2 | 3 | ## What's included 4 | 5 | ### Sample ERC721 Contracts 6 | 7 | This includes a very simple sample ERC721 for the purposes of demonstrating integration with the [OpenSea](https://opensea.io) marketplace. 8 | We include a script for minting the items. 9 | 10 | Additionally, this contract whitelists the proxy accounts of OpenSea users so that they are automatically able to trade the ERC721 item on OpenSea (without having to pay gas for an additional approval). 11 | On OpenSea, each user has a "proxy" account that they control, and is ultimately called by the exchange contracts to trade their items. 12 | (Note that this addition does not mean that OpenSea itself has access to the items, simply that the users can list them more easily if they wish to do so) 13 | 14 | ## Requirements 15 | 16 | ### Node version 17 | 18 | Either make sure you're running a version of node compliant with the `engines` requirement in `package.json`, or install Node Version Manager [`nvm`](https://github.com/creationix/nvm) and run `nvm use` to use the correct version of node. 19 | 20 | ## Installation 21 | 22 | Run 23 | 24 | ```sh 25 | yarn 26 | ``` 27 | 28 | If you run into an error while building the dependencies and you're on a Mac, run the code below, remove your `node_modules` folder, and do a fresh `yarn install`: 29 | 30 | ```sh 31 | xcode-select --install # Install Command Line Tools if you haven't already. 32 | sudo xcode-select --switch /Library/Developer/CommandLineTools # Enable command line tools 33 | sudo npm explore npm -g -- npm install node-gyp@latest # Update node-gyp 34 | ``` 35 | 36 | ## Deploying 37 | 38 | ### Deploying to the *Rinkeby* network 39 | 40 | 1. To access a Rinkeby testnet node, you'll need to sign up for [Alchemy](https://dashboard.alchemyapi.io/signup?referral=affiliate:e535c3c3-9bc4-428f-8e27-4b70aa2e8ca5) and get a free API key. Click "View Key" and then copy the part of the URL after `v2/`. 41 | a. You can use [Infura](https://infura.io) if you want as well. Just change `ALCHEMY_KEY` below to `INFURA_KEY`. 42 | 2. Using your API key and the mnemonic for your Metamask wallet (make sure you're using a Metamask seed phrase that you're comfortable using for testing purposes), run: 43 | 44 | ```sh 45 | export ALCHEMY_KEY="" 46 | export MNEMONIC="" 47 | DEPLOY_CREATURES_SALE=1 48 | yarn hardhat run scripts/deploy.js rinkeby 49 | ``` 50 | 51 | ### Deploying to the mainnet Ethereum network 52 | 53 | Make sure your wallet has at least a few dollars worth of ETH in it. 54 | Then run 55 | 56 | ```sh 57 | yarn hardhat run scripts/deploy.js --network mainnet 58 | ``` 59 | 60 | Look for your newly deployed contract address in the logs! 🥳 61 | 62 | ## Minting tokens 63 | 64 | After deploying to the Rinkeby network, there will be a contract on Rinkeby that will be viewable on [Rinkeby Etherscan](https://rinkeby.etherscan.io). For example, here is a [recently deployed contract](https://rinkeby.etherscan.io/address/0xeba05c5521a3b81e23d15ae9b2d07524bc453561). You should set this contract address and the address of your Metamask account as environment variables when running the minting script. If a [CreatureFactory was deployed](https://github.com/ProjectOpenSea/opensea-creatures/blob/master/migrations/2_deploy_contracts.js#L38), which the sample deploy steps above do, you'll need to specify its address below as it will be the owner on the NFT contract, and only it will have mint permissions. In that case, you won't need NFT_CONTRACT_ADDRESS, as all we need is the contract with mint permissions here. 65 | 66 | ```sh 67 | export OWNER_ADDRESS="" 68 | export NFT_CONTRACT_ADDRESS="" 69 | export FACTORY_CONTRACT_ADDRESS="" 70 | export NETWORK="rinkeby" 71 | node scripts/mint.js 72 | ``` 73 | 74 | ## Diagnosing Common Issues 75 | 76 | If you're running a modified version of `sell.js` and not getting expected behavior, check the following: 77 | 78 | - Is the `expirationTime` in future? If no, change it to a time in the future. 79 | 80 | - Is the `expirationTime` a fractional second? If yes, round the listing time to the nearest second. 81 | 82 | - Are the input addresses all strings? If no, convert them to strings. 83 | 84 | - Are the input addresses checksummed? You might need to use the checksummed version of the address. 85 | 86 | - Is your computer's internal clock accurate? If no, try enabling automatic clock adjustment locally or following [this tutorial](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html) to update an Amazon EC2 instance. 87 | 88 | - Do you have any conflicts that result from globally installed node packages? If yes, try `yarn remove -g hardhat; yarn` 89 | 90 | - Are you running a version of node compliant with the `engines` requirement in `package.json`? If no, try `nvm use; rm -rf node_modules; yarn` 91 | 92 | ## Why are some standard methods overridden? 93 | 94 | This contract overrides the `isApprovedForAll` method in order to whitelist the proxy accounts of OpenSea users. This means that they are automatically able to trade your ERC-1155 items on OpenSea (without having to pay gas for an additional approval). On OpenSea, each user has a "proxy" account that they control, and is ultimately called by the exchange contracts to trade their items. 95 | 96 | Note that this addition does not mean that OpenSea itself has access to the items, simply that the users can list them more easily if they wish to do so! 97 | 98 | ## Viewing your items on OpenSea 99 | 100 | OpenSea will automatically pick up transfers on your contract. You can visit an asset by going to `https://opensea.io/assets/CONTRACT_ADDRESS/TOKEN_ID`. 101 | 102 | To load all your metadata on your items at once, visit [https://opensea.io/get-listed](https://opensea.io/get-listed) and enter your address to load the metadata into OpenSea! You can even do this for the Rinkeby test network if you deployed there, by going to [https://rinkeby.opensea.io/get-listed](https://rinkeby.opensea.io/get-listed). 103 | 104 | ## Troubleshooting 105 | 106 | ### It doesn't compile 107 | 108 | Install hardhat locally: 109 | 110 | ```sh 111 | yarn add --dev hardhat @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai 112 | 113 | ``` 114 | 115 | You can also debug just the compile step by running `yarn hardhat compile`. 116 | 117 | ### It doesn't deploy anything 118 | 119 | This is often due to the hardhat-hdwallet provider not being able to connect. Go to your [Alchemy Dashboard](https://dashboard.alchemyapi.io/signup?referral=affiliate:e535c3c3-9bc4-428f-8e27-4b70aa2e8ca5) (or infura.io) and create a new project. Use your "project ID" as your new `ALCHEMY_KEY` and make sure you export that command-line variable above. 120 | 121 | ## Running Local Tests 122 | Run the following in terminal window: 123 | 124 | ```sh 125 | yarn run test 126 | ``` 127 | ## Running Integration Tests 128 | Run the following in terminal window: 129 | 130 | ```sh 131 | yarn run test:int 132 | ``` 133 | 134 | ## Demo Scripts 135 | Deploy a new contract: 136 | 137 | ```sh 138 | yarn deploy 139 | ``` 140 | 141 | Mint sample assets: 142 | ```sh 143 | yarn mint 144 | ``` 145 | -------------------------------------------------------------------------------- /artifacts/contracts/RFOXCollection.sol/RFOXCollection.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "RFOXCollection", 4 | "sourceName": "contracts/RFOXCollection.sol", 5 | "abi": [ 6 | { 7 | "inputs": [ 8 | { 9 | "internalType": "string", 10 | "name": "contractURI_", 11 | "type": "string" 12 | }, 13 | { 14 | "internalType": "string", 15 | "name": "name_", 16 | "type": "string" 17 | }, 18 | { 19 | "internalType": "string", 20 | "name": "symbol_", 21 | "type": "string" 22 | }, 23 | { 24 | "internalType": "address", 25 | "name": "proxyRegistryAddress_", 26 | "type": "address" 27 | } 28 | ], 29 | "stateMutability": "nonpayable", 30 | "type": "constructor" 31 | }, 32 | { 33 | "anonymous": false, 34 | "inputs": [ 35 | { 36 | "indexed": true, 37 | "internalType": "address", 38 | "name": "owner", 39 | "type": "address" 40 | }, 41 | { 42 | "indexed": true, 43 | "internalType": "address", 44 | "name": "approved", 45 | "type": "address" 46 | }, 47 | { 48 | "indexed": true, 49 | "internalType": "uint256", 50 | "name": "tokenId", 51 | "type": "uint256" 52 | } 53 | ], 54 | "name": "Approval", 55 | "type": "event" 56 | }, 57 | { 58 | "anonymous": false, 59 | "inputs": [ 60 | { 61 | "indexed": true, 62 | "internalType": "address", 63 | "name": "owner", 64 | "type": "address" 65 | }, 66 | { 67 | "indexed": true, 68 | "internalType": "address", 69 | "name": "operator", 70 | "type": "address" 71 | }, 72 | { 73 | "indexed": false, 74 | "internalType": "bool", 75 | "name": "approved", 76 | "type": "bool" 77 | } 78 | ], 79 | "name": "ApprovalForAll", 80 | "type": "event" 81 | }, 82 | { 83 | "anonymous": false, 84 | "inputs": [ 85 | { 86 | "indexed": true, 87 | "internalType": "address", 88 | "name": "previousOwner", 89 | "type": "address" 90 | }, 91 | { 92 | "indexed": true, 93 | "internalType": "address", 94 | "name": "newOwner", 95 | "type": "address" 96 | } 97 | ], 98 | "name": "OwnershipTransferred", 99 | "type": "event" 100 | }, 101 | { 102 | "anonymous": false, 103 | "inputs": [ 104 | { 105 | "indexed": true, 106 | "internalType": "address", 107 | "name": "from", 108 | "type": "address" 109 | }, 110 | { 111 | "indexed": true, 112 | "internalType": "address", 113 | "name": "to", 114 | "type": "address" 115 | }, 116 | { 117 | "indexed": true, 118 | "internalType": "uint256", 119 | "name": "tokenId", 120 | "type": "uint256" 121 | } 122 | ], 123 | "name": "Transfer", 124 | "type": "event" 125 | }, 126 | { 127 | "inputs": [ 128 | { 129 | "internalType": "address", 130 | "name": "to", 131 | "type": "address" 132 | }, 133 | { 134 | "internalType": "uint256", 135 | "name": "tokenId", 136 | "type": "uint256" 137 | } 138 | ], 139 | "name": "approve", 140 | "outputs": [], 141 | "stateMutability": "nonpayable", 142 | "type": "function" 143 | }, 144 | { 145 | "inputs": [ 146 | { 147 | "internalType": "address", 148 | "name": "owner", 149 | "type": "address" 150 | } 151 | ], 152 | "name": "balanceOf", 153 | "outputs": [ 154 | { 155 | "internalType": "uint256", 156 | "name": "", 157 | "type": "uint256" 158 | } 159 | ], 160 | "stateMutability": "view", 161 | "type": "function" 162 | }, 163 | { 164 | "inputs": [], 165 | "name": "contractURI", 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": "uint256", 180 | "name": "tokenId", 181 | "type": "uint256" 182 | } 183 | ], 184 | "name": "getApproved", 185 | "outputs": [ 186 | { 187 | "internalType": "address", 188 | "name": "", 189 | "type": "address" 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": "operator", 205 | "type": "address" 206 | } 207 | ], 208 | "name": "isApprovedForAll", 209 | "outputs": [ 210 | { 211 | "internalType": "bool", 212 | "name": "", 213 | "type": "bool" 214 | } 215 | ], 216 | "stateMutability": "view", 217 | "type": "function" 218 | }, 219 | { 220 | "inputs": [], 221 | "name": "name", 222 | "outputs": [ 223 | { 224 | "internalType": "string", 225 | "name": "", 226 | "type": "string" 227 | } 228 | ], 229 | "stateMutability": "view", 230 | "type": "function" 231 | }, 232 | { 233 | "inputs": [], 234 | "name": "owner", 235 | "outputs": [ 236 | { 237 | "internalType": "address", 238 | "name": "", 239 | "type": "address" 240 | } 241 | ], 242 | "stateMutability": "view", 243 | "type": "function" 244 | }, 245 | { 246 | "inputs": [ 247 | { 248 | "internalType": "uint256", 249 | "name": "tokenId", 250 | "type": "uint256" 251 | } 252 | ], 253 | "name": "ownerOf", 254 | "outputs": [ 255 | { 256 | "internalType": "address", 257 | "name": "", 258 | "type": "address" 259 | } 260 | ], 261 | "stateMutability": "view", 262 | "type": "function" 263 | }, 264 | { 265 | "inputs": [], 266 | "name": "renounceOwnership", 267 | "outputs": [], 268 | "stateMutability": "nonpayable", 269 | "type": "function" 270 | }, 271 | { 272 | "inputs": [ 273 | { 274 | "internalType": "address", 275 | "name": "to_", 276 | "type": "address" 277 | }, 278 | { 279 | "internalType": "string", 280 | "name": "tokenURI_", 281 | "type": "string" 282 | } 283 | ], 284 | "name": "safeMint", 285 | "outputs": [], 286 | "stateMutability": "nonpayable", 287 | "type": "function" 288 | }, 289 | { 290 | "inputs": [ 291 | { 292 | "internalType": "address", 293 | "name": "from", 294 | "type": "address" 295 | }, 296 | { 297 | "internalType": "address", 298 | "name": "to", 299 | "type": "address" 300 | }, 301 | { 302 | "internalType": "uint256", 303 | "name": "tokenId", 304 | "type": "uint256" 305 | } 306 | ], 307 | "name": "safeTransferFrom", 308 | "outputs": [], 309 | "stateMutability": "nonpayable", 310 | "type": "function" 311 | }, 312 | { 313 | "inputs": [ 314 | { 315 | "internalType": "address", 316 | "name": "from", 317 | "type": "address" 318 | }, 319 | { 320 | "internalType": "address", 321 | "name": "to", 322 | "type": "address" 323 | }, 324 | { 325 | "internalType": "uint256", 326 | "name": "tokenId", 327 | "type": "uint256" 328 | }, 329 | { 330 | "internalType": "bytes", 331 | "name": "_data", 332 | "type": "bytes" 333 | } 334 | ], 335 | "name": "safeTransferFrom", 336 | "outputs": [], 337 | "stateMutability": "nonpayable", 338 | "type": "function" 339 | }, 340 | { 341 | "inputs": [ 342 | { 343 | "internalType": "address", 344 | "name": "operator", 345 | "type": "address" 346 | }, 347 | { 348 | "internalType": "bool", 349 | "name": "approved", 350 | "type": "bool" 351 | } 352 | ], 353 | "name": "setApprovalForAll", 354 | "outputs": [], 355 | "stateMutability": "nonpayable", 356 | "type": "function" 357 | }, 358 | { 359 | "inputs": [ 360 | { 361 | "internalType": "uint256", 362 | "name": "tokenId_", 363 | "type": "uint256" 364 | }, 365 | { 366 | "internalType": "string", 367 | "name": "tokenURI_", 368 | "type": "string" 369 | } 370 | ], 371 | "name": "setTokenURI", 372 | "outputs": [], 373 | "stateMutability": "nonpayable", 374 | "type": "function" 375 | }, 376 | { 377 | "inputs": [ 378 | { 379 | "internalType": "bytes4", 380 | "name": "interfaceId", 381 | "type": "bytes4" 382 | } 383 | ], 384 | "name": "supportsInterface", 385 | "outputs": [ 386 | { 387 | "internalType": "bool", 388 | "name": "", 389 | "type": "bool" 390 | } 391 | ], 392 | "stateMutability": "view", 393 | "type": "function" 394 | }, 395 | { 396 | "inputs": [], 397 | "name": "symbol", 398 | "outputs": [ 399 | { 400 | "internalType": "string", 401 | "name": "", 402 | "type": "string" 403 | } 404 | ], 405 | "stateMutability": "view", 406 | "type": "function" 407 | }, 408 | { 409 | "inputs": [ 410 | { 411 | "internalType": "uint256", 412 | "name": "index", 413 | "type": "uint256" 414 | } 415 | ], 416 | "name": "tokenByIndex", 417 | "outputs": [ 418 | { 419 | "internalType": "uint256", 420 | "name": "", 421 | "type": "uint256" 422 | } 423 | ], 424 | "stateMutability": "view", 425 | "type": "function" 426 | }, 427 | { 428 | "inputs": [ 429 | { 430 | "internalType": "address", 431 | "name": "owner", 432 | "type": "address" 433 | }, 434 | { 435 | "internalType": "uint256", 436 | "name": "index", 437 | "type": "uint256" 438 | } 439 | ], 440 | "name": "tokenOfOwnerByIndex", 441 | "outputs": [ 442 | { 443 | "internalType": "uint256", 444 | "name": "", 445 | "type": "uint256" 446 | } 447 | ], 448 | "stateMutability": "view", 449 | "type": "function" 450 | }, 451 | { 452 | "inputs": [ 453 | { 454 | "internalType": "uint256", 455 | "name": "tokenId", 456 | "type": "uint256" 457 | } 458 | ], 459 | "name": "tokenURI", 460 | "outputs": [ 461 | { 462 | "internalType": "string", 463 | "name": "", 464 | "type": "string" 465 | } 466 | ], 467 | "stateMutability": "view", 468 | "type": "function" 469 | }, 470 | { 471 | "inputs": [], 472 | "name": "totalSupply", 473 | "outputs": [ 474 | { 475 | "internalType": "uint256", 476 | "name": "", 477 | "type": "uint256" 478 | } 479 | ], 480 | "stateMutability": "view", 481 | "type": "function" 482 | }, 483 | { 484 | "inputs": [ 485 | { 486 | "internalType": "address", 487 | "name": "from", 488 | "type": "address" 489 | }, 490 | { 491 | "internalType": "address", 492 | "name": "to", 493 | "type": "address" 494 | }, 495 | { 496 | "internalType": "uint256", 497 | "name": "tokenId", 498 | "type": "uint256" 499 | } 500 | ], 501 | "name": "transferFrom", 502 | "outputs": [], 503 | "stateMutability": "nonpayable", 504 | "type": "function" 505 | }, 506 | { 507 | "inputs": [ 508 | { 509 | "internalType": "address", 510 | "name": "newOwner", 511 | "type": "address" 512 | } 513 | ], 514 | "name": "transferOwnership", 515 | "outputs": [], 516 | "stateMutability": "nonpayable", 517 | "type": "function" 518 | } 519 | ], 520 | "bytecode": "0x60806040523480156200001157600080fd5b506040516200413d3803806200413d8339818101604052810190620000379190620002f8565b8282816000908051906020019062000051929190620001bf565b5080600190805190602001906200006a929190620001bf565b5050506200008d62000081620000f160201b60201c565b620000f960201b60201c565b83600c9080519060200190620000a5929190620001bf565b5080600d60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050506200052e565b600033905090565b6000600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b828054620001cd9062000480565b90600052602060002090601f016020900481019282620001f157600085556200023d565b82601f106200020c57805160ff19168380011785556200023d565b828001600101855582156200023d579182015b828111156200023c5782518255916020019190600101906200021f565b5b5090506200024c919062000250565b5090565b5b808211156200026b57600081600090555060010162000251565b5090565b6000620002866200028084620003e3565b620003af565b9050828152602081018484840111156200029f57600080fd5b620002ac8482856200044a565b509392505050565b600081519050620002c58162000514565b92915050565b600082601f830112620002dd57600080fd5b8151620002ef8482602086016200026f565b91505092915050565b600080600080608085870312156200030f57600080fd5b600085015167ffffffffffffffff8111156200032a57600080fd5b6200033887828801620002cb565b945050602085015167ffffffffffffffff8111156200035657600080fd5b6200036487828801620002cb565b935050604085015167ffffffffffffffff8111156200038257600080fd5b6200039087828801620002cb565b9250506060620003a387828801620002b4565b91505092959194509250565b6000604051905081810181811067ffffffffffffffff82111715620003d957620003d8620004e5565b5b8060405250919050565b600067ffffffffffffffff821115620004015762000400620004e5565b5b601f19601f8301169050602081019050919050565b600062000423826200042a565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60005b838110156200046a5780820151818401526020810190506200044d565b838111156200047a576000848401525b50505050565b600060028204905060018216806200049957607f821691505b60208210811415620004b057620004af620004b6565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200051f8162000416565b81146200052b57600080fd5b50565b613bff806200053e6000396000f3fe608060405234801561001057600080fd5b50600436106101425760003560e01c806370a08231116100b8578063b88d4fde1161007c578063b88d4fde14610375578063c87b56dd14610391578063d204c45e146103c1578063e8a3d485146103dd578063e985e9c5146103fb578063f2fde38b1461042b57610142565b806370a08231146102e3578063715018a6146103135780638da5cb5b1461031d57806395d89b411461033b578063a22cb4651461035957610142565b806318160ddd1161010a57806318160ddd146101fd57806323b872dd1461021b5780632f745c591461023757806342842e0e146102675780634f6ccce7146102835780636352211e146102b357610142565b806301ffc9a71461014757806306fdde0314610177578063081812fc14610195578063095ea7b3146101c5578063162094c4146101e1575b600080fd5b610161600480360381019061015c91906129ef565b610447565b60405161016e919061346e565b60405180910390f35b61017f610459565b60405161018c9190613489565b60405180910390f35b6101af60048036038101906101aa9190612a6a565b6104eb565b6040516101bc9190613407565b60405180910390f35b6101df60048036038101906101da91906129b3565b610570565b005b6101fb60048036038101906101f69190612a93565b610688565b005b61020561072a565b604051610212919061376b565b60405180910390f35b61023560048036038101906102309190612859565b610737565b005b610251600480360381019061024c91906129b3565b610797565b60405161025e919061376b565b60405180910390f35b610281600480360381019061027c9190612859565b61083c565b005b61029d60048036038101906102989190612a6a565b61085c565b6040516102aa919061376b565b60405180910390f35b6102cd60048036038101906102c89190612a6a565b6108f3565b6040516102da9190613407565b60405180910390f35b6102fd60048036038101906102f891906127f4565b6109a5565b60405161030a919061376b565b60405180910390f35b61031b610a5d565b005b610325610ae5565b6040516103329190613407565b60405180910390f35b610343610b0f565b6040516103509190613489565b60405180910390f35b610373600480360381019061036e9190612923565b610ba1565b005b61038f600480360381019061038a91906128a8565b610d22565b005b6103ab60048036038101906103a69190612a6a565b610d84565b6040516103b89190613489565b60405180910390f35b6103db60048036038101906103d6919061295f565b610d96565b005b6103e5610e87565b6040516103f29190613489565b60405180910390f35b6104156004803603810190610410919061281d565b610f19565b604051610422919061346e565b60405180910390f35b610445600480360381019061044091906127f4565b61101b565b005b600061045282611113565b9050919050565b606060008054610468906139dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610494906139dd565b80156104e15780601f106104b6576101008083540402835291602001916104e1565b820191906000526020600020905b8154815290600101906020018083116104c457829003601f168201915b5050505050905090565b60006104f68261118d565b610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161052c9061366b565b60405180910390fd5b6004600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b600061057b826108f3565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156105ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e3906136eb565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661060b6111f9565b73ffffffffffffffffffffffffffffffffffffffff16148061063a5750610639816106346111f9565b610f19565b5b610679576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610670906135ab565b60405180910390fd5b6106838383611201565b505050565b6106996106936111f9565b836112ba565b6106d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106cf906134eb565b60405180910390fd5b600081511161071c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107139061370b565b60405180910390fd5b6107268282611398565b5050565b6000600880549050905090565b6107486107426111f9565b826112ba565b610787576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061372b565b60405180910390fd5b61079283838361140c565b505050565b60006107a2836109a5565b82106107e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107da906134ab565b60405180910390fd5b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054905092915050565b61085783838360405180602001604052806000815250610d22565b505050565b600061086661072a565b82106108a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089e9061374b565b60405180910390fd5b600882815481106108e1577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001549050919050565b6000806002600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561099c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610993906135eb565b60405180910390fd5b80915050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610a16576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a0d906135cb565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b610a656111f9565b73ffffffffffffffffffffffffffffffffffffffff16610a83610ae5565b73ffffffffffffffffffffffffffffffffffffffff1614610ad9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ad09061368b565b60405180910390fd5b610ae36000611668565b565b6000600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060018054610b1e906139dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610b4a906139dd565b8015610b975780601f10610b6c57610100808354040283529160200191610b97565b820191906000526020600020905b815481529060010190602001808311610b7a57829003601f168201915b5050505050905090565b610ba96111f9565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610c17576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c0e9061356b565b60405180910390fd5b8060056000610c246111f9565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16610cd16111f9565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610d16919061346e565b60405180910390a35050565b610d33610d2d6111f9565b836112ba565b610d72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d699061372b565b60405180910390fd5b610d7e8484848461172e565b50505050565b6060610d8f8261178a565b9050919050565b610d9e6111f9565b73ffffffffffffffffffffffffffffffffffffffff16610dbc610ae5565b73ffffffffffffffffffffffffffffffffffffffff1614610e12576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e099061368b565b60405180910390fd5b6000815111610e56576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e4d9061370b565b60405180910390fd5b6000610e62600e6118dc565b9050610e6e83826118ea565b610e788183611398565b610e82600e611908565b505050565b6060600c8054610e96906139dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610ec2906139dd565b8015610f0f5780601f10610ee457610100808354040283529160200191610f0f565b820191906000526020600020905b815481529060010190602001808311610ef257829003601f168201915b5050505050905090565b600080600d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1663c4552791866040518263ffffffff1660e01b8152600401610f919190613407565b60206040518083038186803b158015610fa957600080fd5b505afa158015610fbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe19190612a41565b73ffffffffffffffffffffffffffffffffffffffff161415611007576001915050611015565b611011848461191e565b9150505b92915050565b6110236111f9565b73ffffffffffffffffffffffffffffffffffffffff16611041610ae5565b73ffffffffffffffffffffffffffffffffffffffff1614611097576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108e9061368b565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611107576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fe9061350b565b60405180910390fd5b61111081611668565b50565b60007f780e9d63000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806111865750611185826119b2565b5b9050919050565b60008073ffffffffffffffffffffffffffffffffffffffff166002600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614159050919050565b600033905090565b816004600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16611274836108f3565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60006112c58261118d565b611304576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112fb9061358b565b60405180910390fd5b600061130f836108f3565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16148061137e57508373ffffffffffffffffffffffffffffffffffffffff16611366846104eb565b73ffffffffffffffffffffffffffffffffffffffff16145b8061138f575061138e8185610f19565b5b91505092915050565b6113a18261118d565b6113e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113d79061360b565b60405180910390fd5b80600a60008481526020019081526020016000209080519060200190611407929190612603565b505050565b8273ffffffffffffffffffffffffffffffffffffffff1661142c826108f3565b73ffffffffffffffffffffffffffffffffffffffff1614611482576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611479906136ab565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156114f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114e99061354b565b60405180910390fd5b6114fd838383611a94565b611508600082611201565b6001600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461155891906138e1565b925050819055506001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546115af919061385a565b92505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6000600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b61173984848461140c565b61174584848484611aa4565b611784576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161177b906134cb565b60405180910390fd5b50505050565b60606117958261118d565b6117d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117cb9061364b565b60405180910390fd5b6000600a600084815260200190815260200160002080546117f4906139dd565b80601f0160208091040260200160405190810160405280929190818152602001828054611820906139dd565b801561186d5780601f106118425761010080835404028352916020019161186d565b820191906000526020600020905b81548152906001019060200180831161185057829003601f168201915b50505050509050600061187e611c3b565b90506000815114156118945781925050506118d7565b6000825111156118c95780826040516020016118b19291906133e3565b604051602081830303815290604052925050506118d7565b6118d284611c78565b925050505b919050565b600081600001549050919050565b611904828260405180602001604052806000815250611d1f565b5050565b6001816000016000828254019250508190555050565b6000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480611a7d57507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b80611a8d5750611a8c82611d7a565b5b9050919050565b611a9f838383611de4565b505050565b6000611ac58473ffffffffffffffffffffffffffffffffffffffff16611ef8565b15611c2e578373ffffffffffffffffffffffffffffffffffffffff1663150b7a02611aee6111f9565b8786866040518563ffffffff1660e01b8152600401611b109493929190613422565b602060405180830381600087803b158015611b2a57600080fd5b505af1925050508015611b5b57506040513d601f19601f82011682018060405250810190611b589190612a18565b60015b611bde573d8060008114611b8b576040519150601f19603f3d011682016040523d82523d6000602084013e611b90565b606091505b50600081511415611bd6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bcd906134cb565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050611c33565b600190505b949350505050565b60606040518060400160405280600781526020017f697066733a2f2f00000000000000000000000000000000000000000000000000815250905090565b6060611c838261118d565b611cc2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cb9906136cb565b60405180910390fd5b6000611ccc611c3b565b90506000815111611cec5760405180602001604052806000815250611d17565b80611cf684611f0b565b604051602001611d079291906133e3565b6040516020818303038152906040525b915050919050565b611d2983836120b8565b611d366000848484611aa4565b611d75576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d6c906134cb565b60405180910390fd5b505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611def838383612286565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611e3257611e2d8161228b565b611e71565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614611e7057611e6f83826122d4565b5b5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611eb457611eaf81612441565b611ef3565b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614611ef257611ef18282612584565b5b5b505050565b600080823b905060008111915050919050565b60606000821415611f53576040518060400160405280600181526020017f300000000000000000000000000000000000000000000000000000000000000081525090506120b3565b600082905060005b60008214611f85578080611f6e90613a0f565b915050600a82611f7e91906138b0565b9150611f5b565b60008167ffffffffffffffff811115611fc7577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015611ff95781602001600182028036833780820191505090505b5090505b600085146120ac5760018261201291906138e1565b9150600a856120219190613a58565b603061202d919061385a565b60f81b818381518110612069577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a856120a591906138b0565b9450611ffd565b8093505050505b919050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415612128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161211f9061362b565b60405180910390fd5b6121318161118d565b15612171576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121689061352b565b60405180910390fd5b61217d60008383611a94565b6001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546121cd919061385a565b92505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a45050565b505050565b6008805490506009600083815260200190815260200160002081905550600881908060018154018082558091505060019003906000526020600020016000909190919091505550565b600060016122e1846109a5565b6122eb91906138e1565b90506000600760008481526020019081526020016000205490508181146123d0576000600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002054905080600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002081905550816007600083815260200190815260200160002081905550505b6007600084815260200190815260200160002060009055600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000206000905550505050565b6000600160088054905061245591906138e1565b90506000600960008481526020019081526020016000205490506000600883815481106124ab577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154905080600883815481106124f3577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020018190555081600960008381526020019081526020016000208190555060096000858152602001908152602001600020600090556008805480612568577f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6001900381819060005260206000200160009055905550505050565b600061258f836109a5565b905081600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002081905550806007600084815260200190815260200160002081905550505050565b82805461260f906139dd565b90600052602060002090601f0160209004810192826126315760008555612678565b82601f1061264a57805160ff1916838001178555612678565b82800160010185558215612678579182015b8281111561267757825182559160200191906001019061265c565b5b5090506126859190612689565b5090565b5b808211156126a257600081600090555060010161268a565b5090565b60006126b96126b4846137b7565b613786565b9050828152602081018484840111156126d157600080fd5b6126dc84828561399b565b509392505050565b60006126f76126f2846137e7565b613786565b90508281526020810184848401111561270f57600080fd5b61271a84828561399b565b509392505050565b60008135905061273181613b56565b92915050565b60008135905061274681613b6d565b92915050565b60008135905061275b81613b84565b92915050565b60008151905061277081613b84565b92915050565b600082601f83011261278757600080fd5b81356127978482602086016126a6565b91505092915050565b6000815190506127af81613b9b565b92915050565b600082601f8301126127c657600080fd5b81356127d68482602086016126e4565b91505092915050565b6000813590506127ee81613bb2565b92915050565b60006020828403121561280657600080fd5b600061281484828501612722565b91505092915050565b6000806040838503121561283057600080fd5b600061283e85828601612722565b925050602061284f85828601612722565b9150509250929050565b60008060006060848603121561286e57600080fd5b600061287c86828701612722565b935050602061288d86828701612722565b925050604061289e868287016127df565b9150509250925092565b600080600080608085870312156128be57600080fd5b60006128cc87828801612722565b94505060206128dd87828801612722565b93505060406128ee878288016127df565b925050606085013567ffffffffffffffff81111561290b57600080fd5b61291787828801612776565b91505092959194509250565b6000806040838503121561293657600080fd5b600061294485828601612722565b925050602061295585828601612737565b9150509250929050565b6000806040838503121561297257600080fd5b600061298085828601612722565b925050602083013567ffffffffffffffff81111561299d57600080fd5b6129a9858286016127b5565b9150509250929050565b600080604083850312156129c657600080fd5b60006129d485828601612722565b92505060206129e5858286016127df565b9150509250929050565b600060208284031215612a0157600080fd5b6000612a0f8482850161274c565b91505092915050565b600060208284031215612a2a57600080fd5b6000612a3884828501612761565b91505092915050565b600060208284031215612a5357600080fd5b6000612a61848285016127a0565b91505092915050565b600060208284031215612a7c57600080fd5b6000612a8a848285016127df565b91505092915050565b60008060408385031215612aa657600080fd5b6000612ab4858286016127df565b925050602083013567ffffffffffffffff811115612ad157600080fd5b612add858286016127b5565b9150509250929050565b612af081613915565b82525050565b612aff81613927565b82525050565b6000612b1082613817565b612b1a818561382d565b9350612b2a8185602086016139aa565b612b3381613b45565b840191505092915050565b6000612b4982613822565b612b53818561383e565b9350612b638185602086016139aa565b612b6c81613b45565b840191505092915050565b6000612b8282613822565b612b8c818561384f565b9350612b9c8185602086016139aa565b80840191505092915050565b6000612bb5602b8361383e565b91507f455243373231456e756d657261626c653a206f776e657220696e646578206f7560008301527f74206f6620626f756e64730000000000000000000000000000000000000000006020830152604082019050919050565b6000612c1b60328361383e565b91507f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560008301527f63656976657220696d706c656d656e74657200000000000000000000000000006020830152604082019050919050565b6000612c81603e8361383e565b91507f52464f58436f6c6c656374696f6e3a2060736574546f6b656e5552496020636160008301527f6c6c6572206973206e6f74206f776e6572206e6f7220617070726f76656400006020830152604082019050919050565b6000612ce760268361383e565b91507f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008301527f64647265737300000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000612d4d601c8361383e565b91507f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006000830152602082019050919050565b6000612d8d60248361383e565b91507f4552433732313a207472616e7366657220746f20746865207a65726f2061646460008301527f72657373000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000612df360198361383e565b91507f4552433732313a20617070726f766520746f2063616c6c6572000000000000006000830152602082019050919050565b6000612e33602c8361383e565b91507f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860008301527f697374656e7420746f6b656e00000000000000000000000000000000000000006020830152604082019050919050565b6000612e9960388361383e565b91507f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760008301527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006020830152604082019050919050565b6000612eff602a8361383e565b91507f4552433732313a2062616c616e636520717565727920666f7220746865207a6560008301527f726f2061646472657373000000000000000000000000000000000000000000006020830152604082019050919050565b6000612f6560298361383e565b91507f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460008301527f656e7420746f6b656e00000000000000000000000000000000000000000000006020830152604082019050919050565b6000612fcb602e8361383e565b91507f45524337323155524953746f726167653a2055524920736574206f66206e6f6e60008301527f6578697374656e7420746f6b656e0000000000000000000000000000000000006020830152604082019050919050565b600061303160208361383e565b91507f4552433732313a206d696e7420746f20746865207a65726f20616464726573736000830152602082019050919050565b600061307160318361383e565b91507f45524337323155524953746f726167653a2055524920717565727920666f722060008301527f6e6f6e6578697374656e7420746f6b656e0000000000000000000000000000006020830152604082019050919050565b60006130d7602c8361383e565b91507f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860008301527f697374656e7420746f6b656e00000000000000000000000000000000000000006020830152604082019050919050565b600061313d60208361383e565b91507f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726000830152602082019050919050565b600061317d60298361383e565b91507f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960008301527f73206e6f74206f776e00000000000000000000000000000000000000000000006020830152604082019050919050565b60006131e3602f8361383e565b91507f4552433732314d657461646174613a2055524920717565727920666f72206e6f60008301527f6e6578697374656e7420746f6b656e00000000000000000000000000000000006020830152604082019050919050565b600061324960218361383e565b91507f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560008301527f72000000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006132af60248361383e565b91507f52464f58434f6c6c656374696f6e3a2060746f6b656e5552495f60206973206560008301527f6d707479000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061331560318361383e565b91507f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f60008301527f776e6572206e6f7220617070726f7665640000000000000000000000000000006020830152604082019050919050565b600061337b602c8361383e565b91507f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60008301527f7574206f6620626f756e647300000000000000000000000000000000000000006020830152604082019050919050565b6133dd81613991565b82525050565b60006133ef8285612b77565b91506133fb8284612b77565b91508190509392505050565b600060208201905061341c6000830184612ae7565b92915050565b60006080820190506134376000830187612ae7565b6134446020830186612ae7565b61345160408301856133d4565b81810360608301526134638184612b05565b905095945050505050565b60006020820190506134836000830184612af6565b92915050565b600060208201905081810360008301526134a38184612b3e565b905092915050565b600060208201905081810360008301526134c481612ba8565b9050919050565b600060208201905081810360008301526134e481612c0e565b9050919050565b6000602082019050818103600083015261350481612c74565b9050919050565b6000602082019050818103600083015261352481612cda565b9050919050565b6000602082019050818103600083015261354481612d40565b9050919050565b6000602082019050818103600083015261356481612d80565b9050919050565b6000602082019050818103600083015261358481612de6565b9050919050565b600060208201905081810360008301526135a481612e26565b9050919050565b600060208201905081810360008301526135c481612e8c565b9050919050565b600060208201905081810360008301526135e481612ef2565b9050919050565b6000602082019050818103600083015261360481612f58565b9050919050565b6000602082019050818103600083015261362481612fbe565b9050919050565b6000602082019050818103600083015261364481613024565b9050919050565b6000602082019050818103600083015261366481613064565b9050919050565b60006020820190508181036000830152613684816130ca565b9050919050565b600060208201905081810360008301526136a481613130565b9050919050565b600060208201905081810360008301526136c481613170565b9050919050565b600060208201905081810360008301526136e4816131d6565b9050919050565b600060208201905081810360008301526137048161323c565b9050919050565b60006020820190508181036000830152613724816132a2565b9050919050565b6000602082019050818103600083015261374481613308565b9050919050565b600060208201905081810360008301526137648161336e565b9050919050565b600060208201905061378060008301846133d4565b92915050565b6000604051905081810181811067ffffffffffffffff821117156137ad576137ac613b16565b5b8060405250919050565b600067ffffffffffffffff8211156137d2576137d1613b16565b5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff82111561380257613801613b16565b5b601f19601f8301169050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600081905092915050565b600061386582613991565b915061387083613991565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156138a5576138a4613a89565b5b828201905092915050565b60006138bb82613991565b91506138c683613991565b9250826138d6576138d5613ab8565b5b828204905092915050565b60006138ec82613991565b91506138f783613991565b92508282101561390a57613909613a89565b5b828203905092915050565b600061392082613971565b9050919050565b60008115159050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600061396a82613915565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b838110156139c85780820151818401526020810190506139ad565b838111156139d7576000848401525b50505050565b600060028204905060018216806139f557607f821691505b60208210811415613a0957613a08613ae7565b5b50919050565b6000613a1a82613991565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415613a4d57613a4c613a89565b5b600182019050919050565b6000613a6382613991565b9150613a6e83613991565b925082613a7e57613a7d613ab8565b5b828206905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b613b5f81613915565b8114613b6a57600080fd5b50565b613b7681613927565b8114613b8157600080fd5b50565b613b8d81613933565b8114613b9857600080fd5b50565b613ba48161395f565b8114613baf57600080fd5b50565b613bbb81613991565b8114613bc657600080fd5b5056fea26469706673582212200532bdc1aaf605a7cef00f25f2418789d8fe122a91ad854b917156e4e20e872964736f6c63430008000033", 521 | "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101425760003560e01c806370a08231116100b8578063b88d4fde1161007c578063b88d4fde14610375578063c87b56dd14610391578063d204c45e146103c1578063e8a3d485146103dd578063e985e9c5146103fb578063f2fde38b1461042b57610142565b806370a08231146102e3578063715018a6146103135780638da5cb5b1461031d57806395d89b411461033b578063a22cb4651461035957610142565b806318160ddd1161010a57806318160ddd146101fd57806323b872dd1461021b5780632f745c591461023757806342842e0e146102675780634f6ccce7146102835780636352211e146102b357610142565b806301ffc9a71461014757806306fdde0314610177578063081812fc14610195578063095ea7b3146101c5578063162094c4146101e1575b600080fd5b610161600480360381019061015c91906129ef565b610447565b60405161016e919061346e565b60405180910390f35b61017f610459565b60405161018c9190613489565b60405180910390f35b6101af60048036038101906101aa9190612a6a565b6104eb565b6040516101bc9190613407565b60405180910390f35b6101df60048036038101906101da91906129b3565b610570565b005b6101fb60048036038101906101f69190612a93565b610688565b005b61020561072a565b604051610212919061376b565b60405180910390f35b61023560048036038101906102309190612859565b610737565b005b610251600480360381019061024c91906129b3565b610797565b60405161025e919061376b565b60405180910390f35b610281600480360381019061027c9190612859565b61083c565b005b61029d60048036038101906102989190612a6a565b61085c565b6040516102aa919061376b565b60405180910390f35b6102cd60048036038101906102c89190612a6a565b6108f3565b6040516102da9190613407565b60405180910390f35b6102fd60048036038101906102f891906127f4565b6109a5565b60405161030a919061376b565b60405180910390f35b61031b610a5d565b005b610325610ae5565b6040516103329190613407565b60405180910390f35b610343610b0f565b6040516103509190613489565b60405180910390f35b610373600480360381019061036e9190612923565b610ba1565b005b61038f600480360381019061038a91906128a8565b610d22565b005b6103ab60048036038101906103a69190612a6a565b610d84565b6040516103b89190613489565b60405180910390f35b6103db60048036038101906103d6919061295f565b610d96565b005b6103e5610e87565b6040516103f29190613489565b60405180910390f35b6104156004803603810190610410919061281d565b610f19565b604051610422919061346e565b60405180910390f35b610445600480360381019061044091906127f4565b61101b565b005b600061045282611113565b9050919050565b606060008054610468906139dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610494906139dd565b80156104e15780601f106104b6576101008083540402835291602001916104e1565b820191906000526020600020905b8154815290600101906020018083116104c457829003601f168201915b5050505050905090565b60006104f68261118d565b610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161052c9061366b565b60405180910390fd5b6004600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b600061057b826108f3565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156105ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e3906136eb565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661060b6111f9565b73ffffffffffffffffffffffffffffffffffffffff16148061063a5750610639816106346111f9565b610f19565b5b610679576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610670906135ab565b60405180910390fd5b6106838383611201565b505050565b6106996106936111f9565b836112ba565b6106d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106cf906134eb565b60405180910390fd5b600081511161071c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107139061370b565b60405180910390fd5b6107268282611398565b5050565b6000600880549050905090565b6107486107426111f9565b826112ba565b610787576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061372b565b60405180910390fd5b61079283838361140c565b505050565b60006107a2836109a5565b82106107e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107da906134ab565b60405180910390fd5b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054905092915050565b61085783838360405180602001604052806000815250610d22565b505050565b600061086661072a565b82106108a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089e9061374b565b60405180910390fd5b600882815481106108e1577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001549050919050565b6000806002600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561099c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610993906135eb565b60405180910390fd5b80915050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610a16576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a0d906135cb565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b610a656111f9565b73ffffffffffffffffffffffffffffffffffffffff16610a83610ae5565b73ffffffffffffffffffffffffffffffffffffffff1614610ad9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ad09061368b565b60405180910390fd5b610ae36000611668565b565b6000600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060018054610b1e906139dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610b4a906139dd565b8015610b975780601f10610b6c57610100808354040283529160200191610b97565b820191906000526020600020905b815481529060010190602001808311610b7a57829003601f168201915b5050505050905090565b610ba96111f9565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610c17576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c0e9061356b565b60405180910390fd5b8060056000610c246111f9565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16610cd16111f9565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610d16919061346e565b60405180910390a35050565b610d33610d2d6111f9565b836112ba565b610d72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d699061372b565b60405180910390fd5b610d7e8484848461172e565b50505050565b6060610d8f8261178a565b9050919050565b610d9e6111f9565b73ffffffffffffffffffffffffffffffffffffffff16610dbc610ae5565b73ffffffffffffffffffffffffffffffffffffffff1614610e12576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e099061368b565b60405180910390fd5b6000815111610e56576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e4d9061370b565b60405180910390fd5b6000610e62600e6118dc565b9050610e6e83826118ea565b610e788183611398565b610e82600e611908565b505050565b6060600c8054610e96906139dd565b80601f0160208091040260200160405190810160405280929190818152602001828054610ec2906139dd565b8015610f0f5780601f10610ee457610100808354040283529160200191610f0f565b820191906000526020600020905b815481529060010190602001808311610ef257829003601f168201915b5050505050905090565b600080600d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1663c4552791866040518263ffffffff1660e01b8152600401610f919190613407565b60206040518083038186803b158015610fa957600080fd5b505afa158015610fbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe19190612a41565b73ffffffffffffffffffffffffffffffffffffffff161415611007576001915050611015565b611011848461191e565b9150505b92915050565b6110236111f9565b73ffffffffffffffffffffffffffffffffffffffff16611041610ae5565b73ffffffffffffffffffffffffffffffffffffffff1614611097576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108e9061368b565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611107576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fe9061350b565b60405180910390fd5b61111081611668565b50565b60007f780e9d63000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806111865750611185826119b2565b5b9050919050565b60008073ffffffffffffffffffffffffffffffffffffffff166002600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614159050919050565b600033905090565b816004600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16611274836108f3565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60006112c58261118d565b611304576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112fb9061358b565b60405180910390fd5b600061130f836108f3565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16148061137e57508373ffffffffffffffffffffffffffffffffffffffff16611366846104eb565b73ffffffffffffffffffffffffffffffffffffffff16145b8061138f575061138e8185610f19565b5b91505092915050565b6113a18261118d565b6113e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113d79061360b565b60405180910390fd5b80600a60008481526020019081526020016000209080519060200190611407929190612603565b505050565b8273ffffffffffffffffffffffffffffffffffffffff1661142c826108f3565b73ffffffffffffffffffffffffffffffffffffffff1614611482576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611479906136ab565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156114f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114e99061354b565b60405180910390fd5b6114fd838383611a94565b611508600082611201565b6001600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461155891906138e1565b925050819055506001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546115af919061385a565b92505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6000600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b61173984848461140c565b61174584848484611aa4565b611784576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161177b906134cb565b60405180910390fd5b50505050565b60606117958261118d565b6117d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117cb9061364b565b60405180910390fd5b6000600a600084815260200190815260200160002080546117f4906139dd565b80601f0160208091040260200160405190810160405280929190818152602001828054611820906139dd565b801561186d5780601f106118425761010080835404028352916020019161186d565b820191906000526020600020905b81548152906001019060200180831161185057829003601f168201915b50505050509050600061187e611c3b565b90506000815114156118945781925050506118d7565b6000825111156118c95780826040516020016118b19291906133e3565b604051602081830303815290604052925050506118d7565b6118d284611c78565b925050505b919050565b600081600001549050919050565b611904828260405180602001604052806000815250611d1f565b5050565b6001816000016000828254019250508190555050565b6000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480611a7d57507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b80611a8d5750611a8c82611d7a565b5b9050919050565b611a9f838383611de4565b505050565b6000611ac58473ffffffffffffffffffffffffffffffffffffffff16611ef8565b15611c2e578373ffffffffffffffffffffffffffffffffffffffff1663150b7a02611aee6111f9565b8786866040518563ffffffff1660e01b8152600401611b109493929190613422565b602060405180830381600087803b158015611b2a57600080fd5b505af1925050508015611b5b57506040513d601f19601f82011682018060405250810190611b589190612a18565b60015b611bde573d8060008114611b8b576040519150601f19603f3d011682016040523d82523d6000602084013e611b90565b606091505b50600081511415611bd6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611bcd906134cb565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050611c33565b600190505b949350505050565b60606040518060400160405280600781526020017f697066733a2f2f00000000000000000000000000000000000000000000000000815250905090565b6060611c838261118d565b611cc2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cb9906136cb565b60405180910390fd5b6000611ccc611c3b565b90506000815111611cec5760405180602001604052806000815250611d17565b80611cf684611f0b565b604051602001611d079291906133e3565b6040516020818303038152906040525b915050919050565b611d2983836120b8565b611d366000848484611aa4565b611d75576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d6c906134cb565b60405180910390fd5b505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611def838383612286565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611e3257611e2d8161228b565b611e71565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614611e7057611e6f83826122d4565b5b5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611eb457611eaf81612441565b611ef3565b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614611ef257611ef18282612584565b5b5b505050565b600080823b905060008111915050919050565b60606000821415611f53576040518060400160405280600181526020017f300000000000000000000000000000000000000000000000000000000000000081525090506120b3565b600082905060005b60008214611f85578080611f6e90613a0f565b915050600a82611f7e91906138b0565b9150611f5b565b60008167ffffffffffffffff811115611fc7577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015611ff95781602001600182028036833780820191505090505b5090505b600085146120ac5760018261201291906138e1565b9150600a856120219190613a58565b603061202d919061385a565b60f81b818381518110612069577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a856120a591906138b0565b9450611ffd565b8093505050505b919050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415612128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161211f9061362b565b60405180910390fd5b6121318161118d565b15612171576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121689061352b565b60405180910390fd5b61217d60008383611a94565b6001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546121cd919061385a565b92505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a45050565b505050565b6008805490506009600083815260200190815260200160002081905550600881908060018154018082558091505060019003906000526020600020016000909190919091505550565b600060016122e1846109a5565b6122eb91906138e1565b90506000600760008481526020019081526020016000205490508181146123d0576000600660008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002054905080600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002081905550816007600083815260200190815260200160002081905550505b6007600084815260200190815260200160002060009055600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000206000905550505050565b6000600160088054905061245591906138e1565b90506000600960008481526020019081526020016000205490506000600883815481106124ab577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154905080600883815481106124f3577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020018190555081600960008381526020019081526020016000208190555060096000858152602001908152602001600020600090556008805480612568577f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6001900381819060005260206000200160009055905550505050565b600061258f836109a5565b905081600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002081905550806007600084815260200190815260200160002081905550505050565b82805461260f906139dd565b90600052602060002090601f0160209004810192826126315760008555612678565b82601f1061264a57805160ff1916838001178555612678565b82800160010185558215612678579182015b8281111561267757825182559160200191906001019061265c565b5b5090506126859190612689565b5090565b5b808211156126a257600081600090555060010161268a565b5090565b60006126b96126b4846137b7565b613786565b9050828152602081018484840111156126d157600080fd5b6126dc84828561399b565b509392505050565b60006126f76126f2846137e7565b613786565b90508281526020810184848401111561270f57600080fd5b61271a84828561399b565b509392505050565b60008135905061273181613b56565b92915050565b60008135905061274681613b6d565b92915050565b60008135905061275b81613b84565b92915050565b60008151905061277081613b84565b92915050565b600082601f83011261278757600080fd5b81356127978482602086016126a6565b91505092915050565b6000815190506127af81613b9b565b92915050565b600082601f8301126127c657600080fd5b81356127d68482602086016126e4565b91505092915050565b6000813590506127ee81613bb2565b92915050565b60006020828403121561280657600080fd5b600061281484828501612722565b91505092915050565b6000806040838503121561283057600080fd5b600061283e85828601612722565b925050602061284f85828601612722565b9150509250929050565b60008060006060848603121561286e57600080fd5b600061287c86828701612722565b935050602061288d86828701612722565b925050604061289e868287016127df565b9150509250925092565b600080600080608085870312156128be57600080fd5b60006128cc87828801612722565b94505060206128dd87828801612722565b93505060406128ee878288016127df565b925050606085013567ffffffffffffffff81111561290b57600080fd5b61291787828801612776565b91505092959194509250565b6000806040838503121561293657600080fd5b600061294485828601612722565b925050602061295585828601612737565b9150509250929050565b6000806040838503121561297257600080fd5b600061298085828601612722565b925050602083013567ffffffffffffffff81111561299d57600080fd5b6129a9858286016127b5565b9150509250929050565b600080604083850312156129c657600080fd5b60006129d485828601612722565b92505060206129e5858286016127df565b9150509250929050565b600060208284031215612a0157600080fd5b6000612a0f8482850161274c565b91505092915050565b600060208284031215612a2a57600080fd5b6000612a3884828501612761565b91505092915050565b600060208284031215612a5357600080fd5b6000612a61848285016127a0565b91505092915050565b600060208284031215612a7c57600080fd5b6000612a8a848285016127df565b91505092915050565b60008060408385031215612aa657600080fd5b6000612ab4858286016127df565b925050602083013567ffffffffffffffff811115612ad157600080fd5b612add858286016127b5565b9150509250929050565b612af081613915565b82525050565b612aff81613927565b82525050565b6000612b1082613817565b612b1a818561382d565b9350612b2a8185602086016139aa565b612b3381613b45565b840191505092915050565b6000612b4982613822565b612b53818561383e565b9350612b638185602086016139aa565b612b6c81613b45565b840191505092915050565b6000612b8282613822565b612b8c818561384f565b9350612b9c8185602086016139aa565b80840191505092915050565b6000612bb5602b8361383e565b91507f455243373231456e756d657261626c653a206f776e657220696e646578206f7560008301527f74206f6620626f756e64730000000000000000000000000000000000000000006020830152604082019050919050565b6000612c1b60328361383e565b91507f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560008301527f63656976657220696d706c656d656e74657200000000000000000000000000006020830152604082019050919050565b6000612c81603e8361383e565b91507f52464f58436f6c6c656374696f6e3a2060736574546f6b656e5552496020636160008301527f6c6c6572206973206e6f74206f776e6572206e6f7220617070726f76656400006020830152604082019050919050565b6000612ce760268361383e565b91507f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008301527f64647265737300000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000612d4d601c8361383e565b91507f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006000830152602082019050919050565b6000612d8d60248361383e565b91507f4552433732313a207472616e7366657220746f20746865207a65726f2061646460008301527f72657373000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000612df360198361383e565b91507f4552433732313a20617070726f766520746f2063616c6c6572000000000000006000830152602082019050919050565b6000612e33602c8361383e565b91507f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860008301527f697374656e7420746f6b656e00000000000000000000000000000000000000006020830152604082019050919050565b6000612e9960388361383e565b91507f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760008301527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006020830152604082019050919050565b6000612eff602a8361383e565b91507f4552433732313a2062616c616e636520717565727920666f7220746865207a6560008301527f726f2061646472657373000000000000000000000000000000000000000000006020830152604082019050919050565b6000612f6560298361383e565b91507f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460008301527f656e7420746f6b656e00000000000000000000000000000000000000000000006020830152604082019050919050565b6000612fcb602e8361383e565b91507f45524337323155524953746f726167653a2055524920736574206f66206e6f6e60008301527f6578697374656e7420746f6b656e0000000000000000000000000000000000006020830152604082019050919050565b600061303160208361383e565b91507f4552433732313a206d696e7420746f20746865207a65726f20616464726573736000830152602082019050919050565b600061307160318361383e565b91507f45524337323155524953746f726167653a2055524920717565727920666f722060008301527f6e6f6e6578697374656e7420746f6b656e0000000000000000000000000000006020830152604082019050919050565b60006130d7602c8361383e565b91507f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860008301527f697374656e7420746f6b656e00000000000000000000000000000000000000006020830152604082019050919050565b600061313d60208361383e565b91507f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726000830152602082019050919050565b600061317d60298361383e565b91507f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960008301527f73206e6f74206f776e00000000000000000000000000000000000000000000006020830152604082019050919050565b60006131e3602f8361383e565b91507f4552433732314d657461646174613a2055524920717565727920666f72206e6f60008301527f6e6578697374656e7420746f6b656e00000000000000000000000000000000006020830152604082019050919050565b600061324960218361383e565b91507f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560008301527f72000000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006132af60248361383e565b91507f52464f58434f6c6c656374696f6e3a2060746f6b656e5552495f60206973206560008301527f6d707479000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061331560318361383e565b91507f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f60008301527f776e6572206e6f7220617070726f7665640000000000000000000000000000006020830152604082019050919050565b600061337b602c8361383e565b91507f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60008301527f7574206f6620626f756e647300000000000000000000000000000000000000006020830152604082019050919050565b6133dd81613991565b82525050565b60006133ef8285612b77565b91506133fb8284612b77565b91508190509392505050565b600060208201905061341c6000830184612ae7565b92915050565b60006080820190506134376000830187612ae7565b6134446020830186612ae7565b61345160408301856133d4565b81810360608301526134638184612b05565b905095945050505050565b60006020820190506134836000830184612af6565b92915050565b600060208201905081810360008301526134a38184612b3e565b905092915050565b600060208201905081810360008301526134c481612ba8565b9050919050565b600060208201905081810360008301526134e481612c0e565b9050919050565b6000602082019050818103600083015261350481612c74565b9050919050565b6000602082019050818103600083015261352481612cda565b9050919050565b6000602082019050818103600083015261354481612d40565b9050919050565b6000602082019050818103600083015261356481612d80565b9050919050565b6000602082019050818103600083015261358481612de6565b9050919050565b600060208201905081810360008301526135a481612e26565b9050919050565b600060208201905081810360008301526135c481612e8c565b9050919050565b600060208201905081810360008301526135e481612ef2565b9050919050565b6000602082019050818103600083015261360481612f58565b9050919050565b6000602082019050818103600083015261362481612fbe565b9050919050565b6000602082019050818103600083015261364481613024565b9050919050565b6000602082019050818103600083015261366481613064565b9050919050565b60006020820190508181036000830152613684816130ca565b9050919050565b600060208201905081810360008301526136a481613130565b9050919050565b600060208201905081810360008301526136c481613170565b9050919050565b600060208201905081810360008301526136e4816131d6565b9050919050565b600060208201905081810360008301526137048161323c565b9050919050565b60006020820190508181036000830152613724816132a2565b9050919050565b6000602082019050818103600083015261374481613308565b9050919050565b600060208201905081810360008301526137648161336e565b9050919050565b600060208201905061378060008301846133d4565b92915050565b6000604051905081810181811067ffffffffffffffff821117156137ad576137ac613b16565b5b8060405250919050565b600067ffffffffffffffff8211156137d2576137d1613b16565b5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff82111561380257613801613b16565b5b601f19601f8301169050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600081905092915050565b600061386582613991565b915061387083613991565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156138a5576138a4613a89565b5b828201905092915050565b60006138bb82613991565b91506138c683613991565b9250826138d6576138d5613ab8565b5b828204905092915050565b60006138ec82613991565b91506138f783613991565b92508282101561390a57613909613a89565b5b828203905092915050565b600061392082613971565b9050919050565b60008115159050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600061396a82613915565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b838110156139c85780820151818401526020810190506139ad565b838111156139d7576000848401525b50505050565b600060028204905060018216806139f557607f821691505b60208210811415613a0957613a08613ae7565b5b50919050565b6000613a1a82613991565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415613a4d57613a4c613a89565b5b600182019050919050565b6000613a6382613991565b9150613a6e83613991565b925082613a7e57613a7d613ab8565b5b828206905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b613b5f81613915565b8114613b6a57600080fd5b50565b613b7681613927565b8114613b8157600080fd5b50565b613b8d81613933565b8114613b9857600080fd5b50565b613ba48161395f565b8114613baf57600080fd5b50565b613bbb81613991565b8114613bc657600080fd5b5056fea26469706673582212200532bdc1aaf605a7cef00f25f2418789d8fe122a91ad854b917156e4e20e872964736f6c63430008000033", 522 | "linkReferences": {}, 523 | "deployedLinkReferences": {} 524 | } 525 | --------------------------------------------------------------------------------