diff --git a/README.md b/README.md index 5f13ae9..107a194 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,4 @@ Usage: | token/ERC20Token | No | Yes | Yes | | ens/ENSRegistry | Yes | Yes | No | | ens/PublicRegistry | Yes | Yes | No | -| registry/ENSSubdomainRegistry | Yes | Yes | No | \ No newline at end of file +| registry/UsernameRegistrar | Yes | Yes | Yes | \ No newline at end of file diff --git a/app/actions/accounts.js b/app/actions/accounts.js index 0f9e25a..8fd8cd3 100644 --- a/app/actions/accounts.js +++ b/app/actions/accounts.js @@ -1,5 +1,5 @@ import ERC20Token from 'Embark/contracts/ERC20Token' -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry' +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar' import TestToken from 'Embark/contracts/TestToken' import { getDefaultAccount } from '../utils/web3Helpers' @@ -42,7 +42,7 @@ export const checkAndDispatchStatusContactCode = dispatch => { export const fetchAndDispatchSNTAllowance = dispatch => { const { methods: { allowance } } = TestToken; const { receiveSntAllowance } = accountActions; - const spender = ENSSubdomainRegistry._address; + const spender = UsernameRegistrar._address; allowance(getDefaultAccount(), spender) .call() .then(allowance => { diff --git a/app/components/ens/addDomain.js b/app/components/ens/addDomain.js index 294d500..d256b29 100644 --- a/app/components/ens/addDomain.js +++ b/app/components/ens/addDomain.js @@ -1,4 +1,4 @@ -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; import web3 from 'web3'; import ENSRegistry from 'Embark/contracts/ENSRegistry'; import React from 'react'; @@ -11,16 +11,16 @@ import { debounce } from 'lodash/fp'; const { methods: { owner } } = ENSRegistry; const delay = debounce(500); -const getDomain = (hashedDomain, domains) => domains(hashedDomain).call(); -const registryIsOwner = address => address == ENSSubdomainRegistry._address; -const fetchOwner = domainName => owner(hash(domainName)).call(); +const getRegistry = (hashedRegistry, registrys) => registrys(hashedRegistry).call(); +const registryIsOwner = address => address == UsernameRegistrar._address; +const fetchOwner = registryName => owner(hash(registryName)).call(); const debounceFetchOwner = delay(fetchOwner); -const getAndIsOwner = async domainName => { - const address = await debounceFetchOwner(domainName); +const getAndIsOwner = async registryName => { + const address = await debounceFetchOwner(registryName); return registryIsOwner(address); } -const fetchDomain = delay(getDomain); -const setPrice = (domainFn, hashedDomain, price) => domainFn(hashedDomain, price || 0).send(); +const fetchRegistry = delay(getRegistry); +const setPrice = (registryFn, price) => registryFn(price || 0).send(); const InnerForm = ({ values, @@ -33,53 +33,50 @@ const InnerForm = ({ }) => (
) -const AddDomain = withFormik({ - mapPropsToValues: props => ({ domainName: '', domainPrice: '' }), +const AddRegistry = withFormik({ + mapPropsToValues: props => ({ registryName: '', registryPrice: '' }), async validate(values) { - const { domainName } = values; + const { registryName } = values; const errors = {}; - if (!domainName) errors.domainName = 'Required'; - if (domainName && !await getAndIsOwner(domainName)) errors.domainName = 'This domain is not owned by registry'; + if (!registryName) errors.registryName = 'Required'; + if (registryName && !await getAndIsOwner(registryName)) errors.registryName = 'This registry is not owned by registry'; if (Object.keys(errors).length) throw errors; }, async handleSubmit(values, { setSubmitting }) { - const { domainName, domainPrice } = values; - const { methods: { domains, setDomainPrice, updateDomainPrice } } = ENSSubdomainRegistry; - const hashedDomain = hash(domainName); - const { state } = await getDomain(hashedDomain, domains); + const { registryName, registryPrice } = values; + const { methods: { state, activate, updateRegistryPrice } } = UsernameRegistrar; + const { registryState } = await state(); console.log( 'Inputs for setPrice', - Number(state) ? 'updateDomainPrice' : 'setDomainPrice', - hashedDomain, - web3.utils.toWei(domainPrice.toString(), 'ether'), + Number(registryState) ? 'updateRegistryPrice' : 'activate', + web3.utils.toWei(registryPrice.toString(), 'ether'), ); setPrice( - Number(state) ? updateDomainPrice : setDomainPrice, - hashedDomain, - web3.utils.toWei(domainPrice.toString(), 'ether'), + Number(registryState) ? updateRegistryPrice : activate, + web3.utils.toWei(registryPrice.toString(), 'ether'), ) .then(res => { setSubmitting(false); @@ -92,4 +89,4 @@ const AddDomain = withFormik({ } })(InnerForm); -export default AddDomain; +export default AddRegistry; diff --git a/app/components/ens/copy.js b/app/components/ens/copy.js index c0e5d00..bcd5a10 100644 --- a/app/components/ens/copy.js +++ b/app/components/ens/copy.js @@ -2,7 +2,7 @@ export default { release: { title: { sub: 'Done!', - body: 'The released domain will be available to other users' + body: 'The released username will be available to other users' }, subheading: 'Follow the progress in the Transaction History section of your wallet.' }, diff --git a/app/components/ens/moveDomain.js b/app/components/ens/moveDomain.js index 0d35cf4..b6bd7df 100644 --- a/app/components/ens/moveDomain.js +++ b/app/components/ens/moveDomain.js @@ -1,7 +1,6 @@ -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; import web3 from 'web3'; import React from 'react'; -import { hash } from 'eth-ens-namehash'; import { Button } from 'react-bootstrap'; import FieldGroup from '../standard/FieldGroup'; import { withFormik } from 'formik'; @@ -25,16 +24,6 @@ const InnerForm = ({ value={values.newAddress} error={errors.newAddress} /> - ) @@ -49,16 +38,14 @@ const MoveDomain = withFormik({ if (Object.keys(errors).length) throw errors; }, async handleSubmit(values, { setSubmitting }) { - const { newAddress, domainName } = values; - const { methods: { moveDomain } } = ENSSubdomainRegistry; - const hashedDomain = hash(domainName); + const { newAddress } = values; + const { methods: { moveDomain } } = UsernameRegistrar; console.log( - `inputs for moveDomain of domain name: ${domainName}`, - newAddress, - hashedDomain, + `inputs for moveDomain:}`, + newAddress ); - moveDomain(newAddress, hashedDomain) + moveDomain(newAddress) .send() .then((res) => { setSubmitting(false); diff --git a/app/components/ens/nameLookup.js b/app/components/ens/nameLookup.js index d2f325a..031d1b9 100644 --- a/app/components/ens/nameLookup.js +++ b/app/components/ens/nameLookup.js @@ -7,7 +7,7 @@ import { hash } from 'eth-ens-namehash'; import { isNil } from 'lodash'; import Hidden from '@material-ui/core/Hidden'; import Typography from '@material-ui/core/Typography'; -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; import ENSRegistry from 'Embark/contracts/ENSRegistry'; import { Button, Field, TextInput, MobileSearch, MobileButton, Card, Info, Text } from '../../ui/components' import { IconCheck } from '../../ui/icons' @@ -23,7 +23,7 @@ import StatusLogo from '../../ui/icons/components/StatusLogo' import EnsLogo from '../../ui/icons/logos/ens.png'; import { formatPrice } from '../ens/utils'; import CheckCircle from '../../ui/icons/components/baseline_check_circle_outline.png'; -const { getPrice, getExpirationTime, release } = ENSSubdomainRegistry.methods; +const { getPrice, getExpirationTime, release } = UsernameRegistrar.methods; import NotInterested from '@material-ui/icons/NotInterested'; import Face from '@material-ui/icons/Face'; import Copy from './copy'; @@ -37,7 +37,7 @@ const validStatusAddress = address => !address.includes(invalidSuffix); const formatName = domainName => domainName.includes('.') ? normalizer.normalize(domainName) : normalizer.normalize(`${domainName}.stateofus.eth`); const getDomain = fullDomain => formatName(fullDomain).split('.').slice(1).join('.'); const hashedDomain = domainName => hash(getDomain(domainName)); -const registryIsOwner = address => address == ENSSubdomainRegistry._address; +const registryIsOwner = address => address == UsernameRegistrar._address; const { soliditySha3, fromWei } = web3.utils; @@ -123,8 +123,7 @@ class RenderAddresses extends PureComponent { if (!isNil(value)) { this.setState({ submitted: true }) release( - soliditySha3(domainName), - hash('stateofus.eth'), + soliditySha3(domainName) ) .send() } else { @@ -205,7 +204,7 @@ class Register extends PureComponent { componentDidMount() { const { domainName } = this.props; - getPrice(hashedDomain(domainName)) + getPrice() .call() .then((res) => { this.setState({ domainPrice: res })}); } diff --git a/app/components/ens/registerSubDomain.js b/app/components/ens/registerSubDomain.js index c0cc921..754f61a 100644 --- a/app/components/ens/registerSubDomain.js +++ b/app/components/ens/registerSubDomain.js @@ -1,5 +1,6 @@ import web3 from "Embark/web3" -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; +import PublicResolver from 'Embark/contracts/PublicResolver'; import TestToken from 'Embark/contracts/TestToken'; import React from 'react'; import { connect } from 'react-redux'; @@ -60,7 +61,7 @@ const InnerForm = ({ mode="strong" style={{ marginTop: '5px' }} onClick={() => { - ENSSubdomainRegistry.methods.getPrice(hash(values.domainName)) + UsernameRegistrar.methods.getPrice() .call() .then((res) => { setFieldValue('price', fromWei(res)); }); }} @@ -126,7 +127,7 @@ const InnerForm = ({
{!Number(SNTAllowance) || (Number(domainPrice) && !Number(SNTBalance)) ? @@ -151,7 +152,8 @@ const RegisterSubDomain = withFormik({ const { editAccount, preRegisteredCallback } = props; const { address, statusAddress } = values; const { subDomain, domainName, registeredCallbackFn } = props || values; - const { methods: { register } } = ENSSubdomainRegistry; + const { methods: { register } } = UsernameRegistrar; + const { methods: { setAddr, setPubkey } } = PublicResolver; const subdomainHash = soliditySha3(subDomain); const domainNameHash = hash(domainName); const resolveToAddr = address || zeroAddress; @@ -162,7 +164,6 @@ const RegisterSubDomain = withFormik({ const funcsToSend = []; const args = [ subdomainHash, - domainNameHash, resolveToAddr, points ? points.x : zeroBytes32, points ? points.y : zeroBytes32, diff --git a/app/components/ens/setupENS.js b/app/components/ens/setupENS.js index 477f4ca..8c160ca 100644 --- a/app/components/ens/setupENS.js +++ b/app/components/ens/setupENS.js @@ -1,6 +1,6 @@ import React, { Fragment } from 'react'; import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap'; -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; import web3Utils from 'web3-utils' import { hash } from 'eth-ens-namehash' @@ -12,10 +12,10 @@ const dispatchSetup = (ENSRegistry) => { setSubnodeOwner(zeroBytes32, sha3('eth'), getUserAddress(ENSRegistry)) .send() .then(res => { console.log(res) }) - setSubnodeOwner(hash('eth'), sha3('stateofus'), ENSSubdomainRegistry._address) + setSubnodeOwner(hash('eth'), sha3('stateofus'), UsernameRegistrar._address) .send() .then(res => { console.log(res) }) - setSubnodeOwner(hash('eth'), sha3('stateofus'), ENSSubdomainRegistry._address) + setSubnodeOwner(hash('eth'), sha3('stateofus'), UsernameRegistrar._address) .send() .then(res => { console.log(res) }) } diff --git a/app/components/ens/updateController.js b/app/components/ens/updateController.js index 3212d79..8b6b6a5 100644 --- a/app/components/ens/updateController.js +++ b/app/components/ens/updateController.js @@ -1,4 +1,4 @@ -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; import web3 from 'web3'; import React from 'react'; import { Button } from 'react-bootstrap'; @@ -39,7 +39,7 @@ const UpdateController = withFormik({ }, async handleSubmit(values, { setSubmitting }) { const { newAddress } = values; - const { methods: { changeController } } = ENSSubdomainRegistry; + const { methods: { changeController } } = UsernameRegistrar; changeController(newAddress) .send() .then((res) => { diff --git a/app/components/ensSubManagement.js b/app/components/ensSubManagement.js index ef6c3cf..a91b524 100644 --- a/app/components/ensSubManagement.js +++ b/app/components/ensSubManagement.js @@ -1,6 +1,6 @@ import EmbarkJS from 'Embark/EmbarkJS'; import ENSRegistry from 'Embark/contracts/ENSRegistry'; -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; import TestToken from 'Embark/contracts/TestToken'; import React, { Fragment } from 'react'; import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap'; @@ -24,7 +24,7 @@ const ENSSubManagement = props => (

Subdomain Management

Change Registry Controller

-

Add/Update Domain Price

+

Activate Registry/Update Registry Price

Move Domain To Another Registry

@@ -34,7 +34,7 @@ const ENSSubManagement = props => (

diff --git a/app/components/enssubdomainregistry.js b/app/components/enssubdomainregistry.js deleted file mode 100644 index 64a22ea..0000000 --- a/app/components/enssubdomainregistry.js +++ /dev/null @@ -1,127 +0,0 @@ -import EmbarkJS from 'Embark/EmbarkJS'; -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; -import React from 'react'; -import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap'; - -class ENSSubdomainRegistryUI extends React.Component { - - constructor(props) { - super(props); - this.state = { - username : "", - address: "", - pubkey: "", - logs: [] - } - } - - update_username(e){ - this.setState({username: e.target.value}); - //lookup ens - //- if not found enable register and lookup price - // ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call() - // and enable register button - // - //- if found, try to resolve address and pubkey - // - //- lookup subdomain owner and if subdomain owner is defaultAccount - // enable edit of address/pubkey and other buttons (except release domain and update funds owner) - // - //- if ENSSubdomainRegistry.methods.getCreationDate(subdomainNameHash) + - // ENSSubdomainRegistry.methods.releaseDelay().call(); - // is less then current timestamp enable "release domain" button - // - //- if ens.owner(subdomain) is not equal to ENSSubdomainRegistry.getFundsOwner(subdomain) - // and default account is ens.owner(subdomain), enable update funds owner - // - //- if ens.owner(domain) is not equal ENSubdomainRegistry.address - // and default account is ens.owner(subdomain), enable move subdomain - } - - update_address(e){ - this.setState({address: e.target.value}); - } - - update_pubkey(e){ - this.setState({pubkey: e.target.value}); - } - - register() { - /*let result = await ENSSubdomainRegistry.methods.register( - labelHash, - domains.paid.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - */ - } - - updateResolver() { - //should call PublicResolver.methods.setAddr(address) - // + PublicResolver.methods.setPubKey(bytes32,bytes32) - } - - updateFundsOwner() { - /*ENSSubdomainRegistry.methods.updateFundsOwner( - labelHash, - domains.paid.namehash - ).send({from: newOwner});*/ - } - - moveSubdomain () { - //ENSSubdomainRegistry.methods.moveAccount(labelHash, domainHash) - } - - releaseSubdomain() { - /** - * ENSSubdomainRegistry.methods.release( - web3Utils.sha3(subdomain), - domains.free.namehash - ).send({from: registrant}); - */ - } - - _addToLog(txt){ - this.state.logs.push(txt); - this.setState({logs: this.state.logs}); - } - - render(){ - return ( - -

Subdomain management

-
- - this.update_username(e)} /> - this.update_address(e)} /> - this.update_pubkey(e) } /> - - - - - - - -
- - -

Contract Calls

-

Javascript calls being made:

-
- { - this.state.logs.map((item, i) =>

{item}

) - } -
-
- ); - } - } - - export default ENSSubdomainRegistryUI; \ No newline at end of file diff --git a/app/dapp.js b/app/dapp.js index c0b46cd..8819a02 100644 --- a/app/dapp.js +++ b/app/dapp.js @@ -10,7 +10,7 @@ import TestTokenUI from './components/testtoken'; import ERC20TokenUI from './components/erc20token'; import TestToken from 'Embark/contracts/TestToken'; import ENSSubManagement from './components/ensSubManagement'; -import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; +import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'; import NameLookup from './components/ens/nameLookup'; import AdminMode from './components/AdminMode'; import TokenPermissions from './components/standard/TokenPermissionConnect'; @@ -66,14 +66,14 @@ class App extends React.Component {
} {searching && - +

{ this.setState({ admin: !admin })}} /> diff --git a/config/contracts.js b/config/contracts.js index 52e6135..da243a1 100644 --- a/config/contracts.js +++ b/config/contracts.js @@ -27,13 +27,15 @@ module.exports = { "args": ["$ENSRegistry"], "deploy": true }, - "ENSSubdomainRegistry": { + + "UsernameRegistrar": { "args": [ "$TestToken", "$ENSRegistry", "$PublicResolver", - "3", - [merkleRoot], + "0x5f7791d31ca0493e9ca7c9ca16695ecd9d5044768674d14d31ab5d8277518fff", + 3, + merkleTree.getHexRoot(), "0x9e183BC54Bb4f3cCa1A478CA6f2c3EdC37B60478" ] } @@ -56,18 +58,18 @@ module.exports = { "$ENSRegistry" ] }, - "ENSSubdomainRegistry": { + "UsernameRegistrar": { "args": [ "$TestToken", "$ENSRegistry", "$PublicResolver", - "3", - [merkleRoot], + "0x5f7791d31ca0493e9ca7c9ca16695ecd9d5044768674d14d31ab5d8277518fff", + 3, + merkleTree.getHexRoot(), "0x0" ], "onDeploy": [ - "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', ENSSubdomainRegistry.address).send()", - "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x7b4768a525e733422bf968587a91b4036e5176d36f44a9fb5b29d0bca03ab3a3', ENSSubdomainRegistry.address).send()" + "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', UsernameRegistrar.address).send()" ] } } @@ -83,7 +85,7 @@ module.exports = { "TestToken": { "address": "0x744d70fdbe2ba4cf95131626614a1763df805b9e" }, - "ENSSubdomainRegistry": { + "UsernameRegistrar": { "address": "0xDBf9038cf5Aaa030890790dB87E746E00Fc352b3" }, "ERC20Receiver": { "deploy": false }, @@ -93,6 +95,7 @@ module.exports = { "MerkleProofWrapper": { "address": "0x76E55E13C5891a90f7fCA2e1238a6B3463F564e2" }, + "ERC20Receiver": { "deploy": false }, "SafeMath": { "address": "0xA115a57952D3337e2a1aB3Cb82bA376EEcDDc469" } @@ -118,7 +121,7 @@ module.exports = { "MerkleProofWrapper": { "address": "0x58E01078d14142E0370526dFdAE44E4f508c844B" }, - "ENSSubdomainRegistry": { + "UsernameRegistrar": { "address": "0x028F3Df706c5295Ba283c326F4692c375D14cb68" }, "ERC20Receiver": { "deploy": false } diff --git a/config/contracts.json b/config/contracts.json deleted file mode 100644 index 8cfdef0..0000000 --- a/config/contracts.json +++ /dev/null @@ -1,122 +0,0 @@ -module.exports = { - "default": { - "deployment": { - "host": "localhost", - "port": 8545, - "type": "rpc" - }, - "dappConnection": [ - "$WEB3", - "http://localhost:8545" - ], - "gas": "auto", - "contracts": { - "TestToken": { - "args": [ ] - }, - "ENSRegistry": { - "deploy": true - }, - "PublicResolver": { - "args": ["$ENSRegistry"], - "deploy": true - }, - "ENSSubdomainRegistry": { - "args": [ - "$TestToken", - "$ENSRegistry", - "$PublicResolver", - "3", - [merkleRoot], - "0x0" - ] - } - } - }, - "development": { - "contracts": { - "TestToken": { - "deploy": true - }, - "ENSRegistry": { - "deploy": true, - "onDeploy": [ - "ENSRegistry.methods.setSubnodeOwner('0x0000000000000000000000000000000000000000000000000000000000000000', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0', web3.eth.defaultAccount).send()" - ] - }, - "PublicResolver": { - "deploy": true, - "args": [ - "$ENSRegistry" - ] - }, - "ENSSubdomainRegistry": { - "args": [ - "$TestToken", - "$ENSRegistry", - "$PublicResolver", - "3", - [merkleRoot], - "0x0" - ], - "onDeploy": [ - "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', ENSSubdomainRegistry.address).send()", - "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x7b4768a525e733422bf968587a91b4036e5176d36f44a9fb5b29d0bca03ab3a3', ENSSubdomainRegistry.address).send()" - ] - } - } - }, - "livenet":{ - "contracts": { - "ENSRegistry": { - "address": "0x314159265dd8dbb310642f98f50c066173c1259b" - }, - "PublicResolver": { - "address": "0x5FfC014343cd971B7eb70732021E26C35B744cc4" - }, - "TestToken": { - "address": "0x744d70fdbe2ba4cf95131626614a1763df805b9e" - }, - "ENSSubdomainRegistry": { - "address": "0xDBf9038cf5Aaa030890790dB87E746E00Fc352b3" - }, - "ERC20Receiver": { "deploy": false }, - "SafeMath": { - "address": "0xA115a57952D3337e2a1aB3Cb82bA376EEcDDc469" - } - } - }, - "testnet":{ - "contracts": { - "ENSRegistry": { - "address": "0x112234455c3a32fd11230c42e7bccd4a84e02010" - }, - "PublicResolver": { - "address": "0x29754bADB2640b98F6deF0f52D41418b0d2e0C51" - }, - "TestToken": { - "address": "0xc55cF4B03948D7EBc8b9E8BAD92643703811d162" - }, - "SafeMath": { - "address": "0x0F9992f7737f9ba3aceD170D4D1259cb2CEcc050" - }, - "MerkleProof": { - "address": "0x5df00E70AD165D50228DB6d8285fB6EAAc630FD7" - }, - "MerkleProofWrapper": { - "address": "0x58E01078d14142E0370526dFdAE44E4f508c844B" - }, - "ERC20Receiver": { "deploy": false } - } - }, - "rinkeby":{ - "contracts": { - "ENSRegistry": { - "address": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A" - }, - "PublicResolver": { - "address": "0x5d20cf83cb385e06d2f2a892f9322cd4933eacdc" - } - } - } -} diff --git a/config/ens-usernames/reservedNames.js b/config/ens-usernames/reservedNames.js index ca39bb9..6b7fa00 100644 --- a/config/ens-usernames/reservedNames.js +++ b/config/ens-usernames/reservedNames.js @@ -1,6 +1,4 @@ -exports.ReservedUsernames = [ - - +exports.reservedNames = [ //Ethereum reserved words 'eth', 'ether', diff --git a/contracts/registry/ENSSubdomainRegistry.sol b/contracts/registry/ENSSubdomainRegistry.sol deleted file mode 100644 index b15c343..0000000 --- a/contracts/registry/ENSSubdomainRegistry.sol +++ /dev/null @@ -1,464 +0,0 @@ -pragma solidity ^0.4.23; - -import "../common/MerkleProof.sol"; -import "../common/Controlled.sol"; -import "../token/ERC20Token.sol"; -import "../ens/ENS.sol"; -import "../ens/PublicResolver.sol"; - -/** - * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) - * @notice Sell ENS subdomains of owned domains. - */ -contract ENSSubdomainRegistry is Controlled { - - ERC20Token public token; - ENS public ens; - PublicResolver public resolver; - address public parentRegistry; - - uint256 public releaseDelay = 365 days; - mapping (bytes32 => Domain) public domains; - mapping (bytes32 => Account) public accounts; - - //slashing conditions - uint256 public subdomainMinLenght; - bytes32[] public reservedSubdomainsMerkleRoots; - - event DomainPrice(bytes32 indexed namehash, uint256 price); - event DomainMoved(bytes32 indexed namehash, address newRegistry); - event SubdomainOwner(bytes32 subdomainHash, address accountOwner); - - enum NodeState { Free, Owned, Moved } - struct Domain { - NodeState state; - uint256 price; - } - - struct Account { - uint256 tokenBalance; - uint256 creationTime; - address accountOwner; - } - - modifier onlyParentRegistry { - require(msg.sender == parentRegistry, "Migration only."); - _; - } - - /** - * @notice Initializes a UserRegistry contract - * @param _token fee token base - * @param _ens Ethereum Name Service root address - * @param _resolver Default resolver to use in initial settings - * @param _subdomainMinLenght Minimum length of usernames - * @param _reservedSubdomainsMerkleRoots Merkle Roots of reserved subdomains - * @param _parentRegistry Address of old registry (if any) for account migration. - */ - constructor( - ERC20Token _token, - ENS _ens, - PublicResolver _resolver, - uint256 _subdomainMinLenght, - bytes32[] _reservedSubdomainsMerkleRoots, - address _parentRegistry - ) - public - { - token = _token; - ens = _ens; - resolver = _resolver; - subdomainMinLenght = _subdomainMinLenght; - reservedSubdomainsMerkleRoots = _reservedSubdomainsMerkleRoots; - parentRegistry = _parentRegistry; - } - - /** - * @notice Registers `_userHash` subdomain to `_domainHash` setting msg.sender as owner. - * @param _userHash choosen unowned subdomain hash - * @param _domainHash choosen contract owned domain hash - * @param _account optional address to set at public resolver - * @param _pubkeyA optional pubkey part A to set at public resolver - * @param _pubkeyB optional pubkey part B to set at public resolver - */ - function register( - bytes32 _userHash, - bytes32 _domainHash, - address _account, - bytes32 _pubkeyA, - bytes32 _pubkeyB - ) - external - returns(bytes32 subdomainHash) - { - Domain memory domain = domains[_domainHash]; - require(domain.state == NodeState.Owned, "Domain unavailable."); - subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash)); - require(ens.owner(subdomainHash) == address(0), "ENS node already owned."); - require(accounts[subdomainHash].creationTime == 0, "Username already registered."); - accounts[subdomainHash] = Account(domain.price, block.timestamp, msg.sender); - if(domain.price > 0) { - require(token.allowance(msg.sender, address(this)) >= domain.price, "Unallowed to spend."); - require( - token.transferFrom( - address(msg.sender), - address(this), - domain.price - ), - "Transfer failed" - ); - } - - bool resolvePubkey = _pubkeyA != 0 || _pubkeyB != 0; - bool resolveAccount = _account != address(0); - if (resolvePubkey || resolveAccount) { - //set to self the ownship to setup initial resolver - ens.setSubnodeOwner(_domainHash, _userHash, address(this)); - ens.setResolver(subdomainHash, resolver); //default resolver - if (resolveAccount) { - resolver.setAddr(subdomainHash, _account); - } - if (resolvePubkey) { - resolver.setPubkey(subdomainHash, _pubkeyA, _pubkeyB); - } - ens.setOwner(subdomainHash, msg.sender); - }else { - //transfer ownship of subdone directly to registrant - ens.setSubnodeOwner(_domainHash, _userHash, msg.sender); - } - emit SubdomainOwner(subdomainHash, msg.sender); - } - - /** - * @notice release subdomain and retrieve locked fee, needs to be called after `releasePeriod` from creation time. - * @param _userHash `msg.sender` owned subdomain hash - * @param _domainHash choosen contract owned domain hash - */ - function release( - bytes32 _userHash, - bytes32 _domainHash - ) - external - { - bool isDomainController = ens.owner(_domainHash) == address(this); - bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash)); - Account memory account = accounts[subdomainHash]; - require(account.creationTime > 0, "Username not registered."); - if (isDomainController) { - require(msg.sender == ens.owner(subdomainHash), "Not owner of ENS node."); - require(block.timestamp > account.creationTime + releaseDelay, "Release period not reached."); - ens.setSubnodeOwner(_domainHash, _userHash, address(this)); - ens.setResolver(subdomainHash, address(0)); - ens.setOwner(subdomainHash, address(0)); - } else { - require(msg.sender == account.accountOwner, "Not the former account owner."); - } - delete accounts[subdomainHash]; - if (account.tokenBalance > 0) { - require(token.transfer(msg.sender, account.tokenBalance), "Transfer failed"); - } - emit SubdomainOwner(subdomainHash, address(0)); - - } - - /** - * @notice updates funds owner, useful to move subdomain account to new registry. - * @param _userHash `msg.sender` owned subdomain hash - * @param _domainHash choosen contract owned domain hash - **/ - function updateAccountOwner( - bytes32 _userHash, - bytes32 _domainHash - ) - external - { - bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash)); - require(accounts[subdomainHash].creationTime > 0, "Username not registered."); - require(msg.sender == ens.owner(subdomainHash), "Caller not owner of ENS node."); - require(ens.owner(_domainHash) == address(this), "Registry not owner of domain."); - accounts[subdomainHash].accountOwner = msg.sender; - emit SubdomainOwner(subdomainHash, msg.sender); - } - - /** - * @notice slash account due too length restriction - * @param _subdomain raw value of offending subdomain - * @param _domainHash domain hash - */ - function slashSmallSubdomain( - bytes _subdomain, - bytes32 _domainHash - ) - external - { - require(_subdomain.length < subdomainMinLenght, "Not a small subdomain."); - slashSubdomain(_subdomain, _domainHash); - } - - /** - * @notice slash account due look like an address - * @param _subdomain raw value of offending subdomain - * @param _domainHash domain hash - */ - function slashAddressLikeSubdomain( - string _subdomain, - bytes32 _domainHash - ) - external - { - bytes memory subdomain = bytes(_subdomain); - require(subdomain.length > 12, "Too small to look like an address."); - require(subdomain[0] == byte("0"), "First character need to be 0"); - require(subdomain[1] == byte("x"), "Second character need to be x"); - slashSubdomain(subdomain, _domainHash); - } - - /** - * @notice slash account due reserved name - * @param _subdomain raw value of offending subdomain - * @param _domainHash domain hash - */ - function slashReservedSubdomain( - bytes _subdomain, - bytes32 _domainHash, - uint256 _rootPos, - bytes32[] _proof - ) - external - { - require(reservedSubdomainsMerkleRoots.length > _rootPos, "Invalid Merkle Root"); - require( - MerkleProof.verifyProof( - _proof, - reservedSubdomainsMerkleRoots[_rootPos], - keccak256(_subdomain) - ), - "Invalid Proof." - ); - slashSubdomain(_subdomain, _domainHash); - } - - /** - * @notice slash account of invalid subdomain - * @param _subdomain raw value of offending subdomain - * @param _domainHash domain hash - * @param _offendingPos position of invalid character - */ - function slashInvalidSubdomain( - bytes _subdomain, - bytes32 _domainHash, - uint256 _offendingPos - ) - external - { - require(_subdomain.length > _offendingPos, "Invalid position."); - byte b = _subdomain[_offendingPos]; - - require(!((b >= 48 && b <= 57) || (b >= 97 && b <= 122)), "Not invalid character."); - - slashSubdomain(_subdomain, _domainHash); - } - - function slashSubdomain(bytes _subdomain, bytes32 _domainHash) internal { - bytes32 userHash = keccak256(_subdomain); - bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, userHash)); - require(accounts[subdomainHash].creationTime > 0, "Username not registered."); - - ens.setSubnodeOwner(_domainHash, userHash, address(this)); - ens.setResolver(subdomainHash, address(0)); - ens.setOwner(subdomainHash, address(0)); - - uint256 amountToTransfer = accounts[subdomainHash].tokenBalance; - delete accounts[subdomainHash]; - if(amountToTransfer > 0){ - require(token.transfer(msg.sender, amountToTransfer), "Error in transfer."); - } - emit SubdomainOwner(subdomainHash, address(0)); - } - - /** - * @notice Migrate account to new registry - * @param _userHash `msg.sender` owned subdomain hash - * @param _domainHash choosen contract owned domain hash - **/ - function moveAccount( - bytes32 _userHash, - bytes32 _domainHash - ) - external - { - bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash)); - require(msg.sender == accounts[subdomainHash].accountOwner, "Callable only by account owner."); - ENSSubdomainRegistry _newRegistry = ENSSubdomainRegistry(ens.owner(_domainHash)); - Account memory account = accounts[subdomainHash]; - delete accounts[subdomainHash]; - //require(address(this) == _newRegistry.parentRegistry(), "Wrong update."); - token.approve(_newRegistry, account.tokenBalance); - _newRegistry.migrateAccount( - _userHash, - _domainHash, - account.tokenBalance, - account.creationTime, - account.accountOwner - ); - } - - /** - * @dev callabe only by parent registry to continue migration of domain - **/ - function migrateDomain( - bytes32 _domain, - uint256 _price - ) - external - onlyParentRegistry - { - require(ens.owner(_domain) == address(this), "ENS domain owner not transfered."); - assert(domains[_domain].state == NodeState.Free); - domains[_domain] = Domain(NodeState.Owned, _price); - } - - /** - * @dev callable only by parent registry for continue user opt-in migration - * @param _userHash any subdomain hash coming from parent - * @param _domainHash choosen contract owned domain hash - * @param _tokenBalance amount being transferred - * @param _creationTime any value coming from parent - * @param _accountOwner accountOwner for opt-out/release at domain move - **/ - function migrateAccount( - bytes32 _userHash, - bytes32 _domainHash, - uint256 _tokenBalance, - uint256 _creationTime, - address _accountOwner - ) - external - onlyParentRegistry - { - bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash)); - accounts[subdomainHash] = Account(_tokenBalance, _creationTime, _accountOwner); - if (_tokenBalance > 0) { - require( - token.transferFrom( - parentRegistry, - address(this), - _tokenBalance - ), - "Error moving funds from old registar." - ); - } - - } - - /** - * @notice moves a domain to other Registry (will not move subdomains accounts) - * @param _newRegistry new registry hodling this domain - * @param _domain domain being moved - */ - function moveDomain( - ENSSubdomainRegistry _newRegistry, - bytes32 _domain - ) - external - onlyController - { - require(domains[_domain].state == NodeState.Owned, "Wrong domain"); - require(ens.owner(_domain) == address(this), "Domain not owned anymore."); - uint256 price = domains[_domain].price; - domains[_domain].state = NodeState.Moved; - ens.setOwner(_domain, _newRegistry); - _newRegistry.migrateDomain(_domain, price); - emit DomainMoved(_domain, _newRegistry); - } - - /** - * @notice Controller include new domain available to register - * @param _domain domain owned by user registry being activated - * @param _price cost to register subnode from this node - */ - function setDomainPrice( - bytes32 _domain, - uint256 _price - ) - external - onlyController - { - require(domains[_domain].state == NodeState.Free, "Domain state is not free"); - require(ens.owner(_domain) == address(this), "Registry does not own domain"); - domains[_domain] = Domain(NodeState.Owned, _price); - emit DomainPrice(_domain, _price); - } - - /** - * @notice updates domain price - * @param _domain active domain being defined price - * @param _price new price - */ - function updateDomainPrice( - bytes32 _domain, - uint256 _price - ) - external - onlyController - { - Domain storage domain = domains[_domain]; - require(domain.state == NodeState.Owned, "Domain not owned"); - domain.price = _price; - emit DomainPrice(_domain, _price); - } - - /** - * @notice updates default public resolver for newly registred subdomains - * @param _resolver new default resolver - */ - function setResolver( - address _resolver - ) - external - onlyController - { - resolver = PublicResolver(_resolver); - } - - function getPrice(bytes32 _domainHash) - external - view - returns(uint256 subdomainPrice) - { - subdomainPrice = domains[_domainHash].price; - } - - function getAccountBalance(bytes32 _subdomainHash) - external - view - returns(uint256 accountBalance) - { - accountBalance = accounts[_subdomainHash].tokenBalance; - } - - function getAccountOwner(bytes32 _subdomainHash) - external - view - returns(address accountOwner) - { - accountOwner = accounts[_subdomainHash].accountOwner; - } - - function getCreationTime(bytes32 _subdomainHash) - external - view - returns(uint256 creationTime) - { - creationTime = accounts[_subdomainHash].creationTime; - } - - function getExpirationTime(bytes32 _subdomainHash) - external - view - returns(uint256 expirationTime) - { - expirationTime = accounts[_subdomainHash].creationTime + releaseDelay; - } - -} diff --git a/contracts/registry/UsernameRegistrar.sol b/contracts/registry/UsernameRegistrar.sol new file mode 100644 index 0000000..496c194 --- /dev/null +++ b/contracts/registry/UsernameRegistrar.sol @@ -0,0 +1,661 @@ +pragma solidity ^0.4.24; + +import "../common/MerkleProof.sol"; +import "../common/Controlled.sol"; +import "../token/ERC20Token.sol"; +import "../token/ApproveAndCallFallBack.sol"; +import "../ens/ENS.sol"; +import "../ens/PublicResolver.sol"; + +/** + * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) + * @notice Registers usernames as ENS subnodes of the domain `ensNode` + */ +contract UsernameRegistrar is Controlled, ApproveAndCallFallBack { + + ERC20Token public token; + ENS public ensRegistry; + PublicResolver public resolver; + address public parentRegistry; + + uint256 public releaseDelay = 365 days; + mapping (bytes32 => Account) public accounts; + + //slashing conditions + uint256 public usernameMinLenght; + bytes32 public reservedUsernamesMerkleRoot; + + event RegistryPrice(uint256 price); + event RegistryMoved(address newRegistry); + event UsernameOwner(bytes32 indexed nameHash, address owner); + + enum RegistrarState { Unactive, Active, Moved } + bytes32 public ensNode; + uint256 public price; + RegistrarState public state; + uint256 private reserveAmount; + + struct Account { + uint256 balance; + uint256 creationTime; + address owner; + } + + modifier onlyParentRegistry { + require(msg.sender == parentRegistry, "Migration only."); + _; + } + + /** + * @notice Initializes a UserRegistry contract + * @param _token fee token base + * @param _ensRegistry Ethereum Name Service root address + * @param _resolver Default resolver to use in initial settings + * @param _ensNode ENS node (registry) being used for usernames subnodes (subregistry) + * @param _usernameMinLenght Minimum length of usernames + * @param _reservedUsernamesMerkleRoot Merkle Roots of reserved usernames + * @param _parentRegistry Address of old registry (if any) for account migration. + */ + constructor( + ERC20Token _token, + ENS _ensRegistry, + PublicResolver _resolver, + bytes32 _ensNode, + uint256 _usernameMinLenght, + bytes32 _reservedUsernamesMerkleRoot, + address _parentRegistry + ) + public + { + token = _token; + ensRegistry = _ensRegistry; + resolver = _resolver; + ensNode = _ensNode; + usernameMinLenght = _usernameMinLenght; + reservedUsernamesMerkleRoot = _reservedUsernamesMerkleRoot; + parentRegistry = _parentRegistry; + } + + /** + * @notice Registers `_label` username to `ensNode` setting msg.sender as owner. + * Terms of name registration: + * - SNT is deposited, not spent; the amount is locked up for 1 year. + * - After 1 year, the user can release the name and receive their deposit back (at any time). + * - User deposits are completely protected. The contract controller cannot access them. + * - User's address(es) will be publicly associated with the ENS name. + * - User must authorise the contract to transfer `price` `token.name()` on their behalf. + * - Usernames registered with less then `usernameMinLenght` characters can be slashed. + * - Usernames contained in the merkle tree of root `reservedUsernamesMerkleRoot` can be slashed. + * - Usernames starting with `0x` and bigger then 12 characters can be slashed. + * - If terms of the contract change—e.g. Status makes contract upgrades—the user has the right to release the username and get their deposit back. + * @param _label choosen unowned username hash + * @param _account optional address to set at public resolver + * @param _pubkeyA optional pubkey part A to set at public resolver + * @param _pubkeyB optional pubkey part B to set at public resolver + */ + function register( + bytes32 _label, + address _account, + bytes32 _pubkeyA, + bytes32 _pubkeyB + ) + external + returns(bytes32 namehash) + { + return registerUser(msg.sender, _label, _account, _pubkeyA, _pubkeyB); + } + + /** + * @notice release username and retrieve locked fee, needs to be called after `releasePeriod` from creation time. + * @param _label `msg.sender` owned username hash + */ + function release( + bytes32 _label + ) + external + { + bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label)); + Account memory account = accounts[_label]; + require(account.creationTime > 0, "Username not registered."); + if (state == RegistrarState.Active) { + require(msg.sender == ensRegistry.owner(namehash), "Not owner of ENS node."); + require(block.timestamp > account.creationTime + releaseDelay, "Release period not reached."); + ensRegistry.setSubnodeOwner(ensNode, _label, address(this)); + ensRegistry.setResolver(namehash, address(0)); + ensRegistry.setOwner(namehash, address(0)); + } else { + require(msg.sender == account.owner, "Not the former account owner."); + address newOwner = ensRegistry.owner(ensNode); + //low level call, case dropUsername not implemented or failing, proceed release. + newOwner.call( + abi.encodeWithSignature( + "dropUsername(bytes32)", + _label + ) + ); + } + delete accounts[_label]; + if (account.balance > 0) { + reserveAmount -= account.balance; + require(token.transfer(msg.sender, account.balance), "Transfer failed"); + } + emit UsernameOwner(_label, address(0)); + + } + + /** + * @notice updates funds owner, useful to move username account to new registry. + * @param _label `msg.sender` owned username hash + **/ + function updateAccountOwner( + bytes32 _label + ) + external + { + bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label)); + require(msg.sender == ensRegistry.owner(namehash), "Caller not owner of ENS node."); + require(accounts[_label].creationTime > 0, "Username not registered."); + require(ensRegistry.owner(ensNode) == address(this), "Registry not owner of registry."); + accounts[_label].owner = msg.sender; + emit UsernameOwner(namehash, msg.sender); + } + + /** + * @notice slash account due too length restriction + * @param _username raw value of offending username + */ + function slashSmallUsername( + bytes _username + ) + external + { + require(_username.length < usernameMinLenght, "Not a small username."); + slashUsername(_username); + } + + /** + * @notice slash account due look like an address + * @param _username raw value of offending username + */ + function slashAddressLikeUsername( + string _username + ) + external + { + bytes memory username = bytes(_username); + require(username.length > 12, "Too small to look like an address."); + require(username[0] == byte("0"), "First character need to be 0"); + require(username[1] == byte("x"), "Second character need to be x"); + slashUsername(username); + } + + /** + * @notice slash account due reserved name + * @param _username raw value of offending username + */ + function slashReservedUsername( + bytes _username, + bytes32[] _proof + ) + external + { + require( + MerkleProof.verifyProof( + _proof, + reservedUsernamesMerkleRoot, + keccak256(_username) + ), + "Invalid Proof." + ); + slashUsername(_username); + } + + /** + * @notice slash account of invalid username + * @param _username raw value of offending username + * @param _offendingPos position of invalid character + */ + function slashInvalidUsername( + bytes _username, + uint256 _offendingPos + ) + external + { + require(_username.length > _offendingPos, "Invalid position."); + byte b = _username[_offendingPos]; + + require(!((b >= 48 && b <= 57) || (b >= 97 && b <= 122)), "Not invalid character."); + + slashUsername(_username); + } + + /** + * @notice Migrate account to new registry + * @param _label `msg.sender` owned username hash + **/ + function moveAccount( + bytes32 _label + ) + external + { + require(msg.sender == accounts[_label].owner, "Callable only by account owner."); + UsernameRegistrar _newRegistry = UsernameRegistrar(ensRegistry.owner(ensNode)); + Account memory account = accounts[_label]; + delete accounts[_label]; + + token.approve(_newRegistry, account.balance); + _newRegistry.migrateUsername( + _label, + account.balance, + account.creationTime, + account.owner + ); + } + + /** + * @dev callabe only by parent registry to continue migration of ENSSubdomainRegistry + * @param _domainHash needs to be this contract ensNode + **/ + function migrateDomain( + uint256 _price, + bytes32 _domainHash + ) + external + onlyParentRegistry + { + require(_domainHash == ensNode, "Wrong Registry"); + migrateRegistry(_price); + } + + /** + * @notice Activate registry + * @param _price The price of username registry + */ + function activate( + uint256 _price + ) + external + onlyController + { + require(state == RegistrarState.Unactive, "Registry state is not unactive"); + require(ensRegistry.owner(ensNode) == address(this), "Registry does not own registry"); + price = _price; + state = RegistrarState.Active; + emit RegistryPrice(_price); + } + + /** + * @notice updates default public resolver for newly registred usernames + * @param _resolver new default resolver + */ + function setResolver( + address _resolver + ) + external + onlyController + { + resolver = PublicResolver(_resolver); + } + + /** + * @notice updates registry price + * @param _price new price + */ + function updateRegistryPrice( + uint256 _price + ) + external + onlyController + { + require(state == RegistrarState.Active, "Registry not owned"); + price = _price; + emit RegistryPrice(_price); + } + + /** + * @notice moves a registry to other Registry (will not move usernames accounts) + * @param _newRegistry new registry hodling this registry + */ + function moveRegistry( + UsernameRegistrar _newRegistry + ) + external + onlyController + { + require(state == RegistrarState.Active, "Wrong registry"); + require(ensRegistry.owner(ensNode) == address(this), "Registry not owned anymore."); + state = RegistrarState.Moved; + ensRegistry.setOwner(ensNode, _newRegistry); + _newRegistry.migrateRegistry(price); + emit RegistryMoved(_newRegistry); + } + + /** + * @dev callable only by parent registry for continue user opt-in migration + * @param _domainHash needs to be this contract ensNode + **/ + function migrateAccount( + bytes32 _userHash, + bytes32 _domainHash, + uint256 _tokenBalance, + uint256 _creationTime, + address _accountOwner + ) + external + { + require(_domainHash == ensNode, "Wrong Registry"); + migrateUsername(_userHash, _tokenBalance, _creationTime, _accountOwner); + } + + /** + * @dev clears ens registry. Callable only by parant registry for continue user opt-out migration + * @param _label any username hash coming from parent + */ + function dropUsername( + bytes32 _label + ) + external + onlyParentRegistry + { + require(accounts[_label].creationTime == 0, "Already migrated"); + bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label)); + ensRegistry.setSubnodeOwner(ensNode, _label, address(this)); + ensRegistry.setResolver(namehash, address(0)); + ensRegistry.setOwner(namehash, address(0)); + } + + /** + * @notice withdraw tokens forced into the contract + * @param _token address of ERC20 withdrawing excess, or address(0) if want ETH/ + * @param _beneficiary who gets the funds + **/ + function withdrawExcessBalance( + address _token, + address _beneficiary + ) + external + onlyController + { + require(_beneficiary != address(0), "Cannot burn token"); + if (_token == address(0)) { + _beneficiary.transfer(address(this).balance); + } else { + ERC20Token excessToken = ERC20Token(_token); + uint256 amount = excessToken.balanceOf(address(this)); + if(_token == address(token)){ + require(amount > reserveAmount, "Is not excess"); + amount -= reserveAmount; + } else { + require(amount > 0, "Is not excess"); + } + excessToken.transfer(_beneficiary, amount); + } + } + + /** + * @notice withdraw ens nodes not belonging to this contract + * @param _domainHash ens node namehash + * @param _beneficiary new owner of ens node + **/ + function withdrawWrongNode( + bytes32 _domainHash, + address _beneficiary + ) + external + onlyController + { + require(_beneficiary != address(0), "Cannot burn node"); + require(_domainHash != ensNode, "Cannot withdraw main node"); + require(ensRegistry.owner(_domainHash) == address(this), "Not owner of this node"); + ensRegistry.setOwner(_domainHash, _beneficiary); + } + + /** + * @notice gets registrar price + * @return registry price + **/ + function getPrice() + external + view + returns(uint256 registryPrice) + { + return price; + } + + /** + * @notice reads amount tokens locked in username + * @param _label hash of username + * @return locked username balance + **/ + function getAccountBalance(bytes32 _label) + external + view + returns(uint256 accountBalance) + { + accountBalance = accounts[_label].balance; + } + + /** + * @notice reads username owner at this contract, + * which can release or migrate in case of upgrade + * @param _label hash of username + * @return username owner + **/ + function getAccountOwner(bytes32 _label) + external + view + returns(address owner) + { + owner = accounts[_label].owner; + } + + /** + * @notice reads when the account was registered + * @param _label hash of username + * @return registration time + **/ + function getCreationTime(bytes32 _label) + external + view + returns(uint256 creationTime) + { + creationTime = accounts[_label].creationTime; + } + + /** + * @notice calculate time where username can be released + * @param _label hash of username + * @return exact time when username can be released + **/ + function getExpirationTime(bytes32 _label) + external + view + returns(uint256 expirationTime) + { + expirationTime = accounts[_label].creationTime + releaseDelay; + } + + /** + * @notice Receive approval, callable only by `token()`. + * @param _from who is approving + * @param _amount amount being approved, need to be equal `getPrice()` + * @param _token token being approved, need to be equal `token()` + * @param _data abi encoded data with selector of `register(bytes32,address,bytes32,bytes32)` + */ + function receiveApproval( + address _from, + uint256 _amount, + address _token, + bytes _data + ) + public + { + require(_amount == price, "Wrong value"); + require(_token == address(token), "Wrong token"); + require(_token == address(msg.sender), "Wrong call"); + require(_data.length <= 132, "Wrong data length"); + bytes4 sig; + bytes32 label; + address account; + bytes32 pubkeyA; + bytes32 pubkeyB; + (sig, label, account, pubkeyA, pubkeyB) = abiDecodeRegister(_data); + require( + sig == bytes4(0xb82fedbb), //bytes4(keccak256("register(bytes32,address,bytes32,bytes32)")) + "Wrong method selector" + ); + registerUser(_from, label, account, pubkeyA, pubkeyB); + } + + /** + * @dev decodes abi encoded data with selector for "register(bytes32,address,bytes32,bytes32)" + * @param _data abi encoded data + */ + function abiDecodeRegister( + bytes _data + ) + private + pure + returns( + bytes4 sig, + bytes32 label, + address account, + bytes32 pubkeyA, + bytes32 pubkeyB + ) + { + assembly { + sig := mload(add(_data, add(0x20, 0))) + label := mload(add(_data, 36)) + account := mload(add(_data, 68)) + pubkeyA := mload(add(_data, 100)) + pubkeyB := mload(add(_data, 132)) + } + } + /** + * @dev callable only by parent registry for continue user opt-in migration + * @param _label any username hash coming from parent + * @param _tokenBalance amount being transferred + * @param _creationTime any value coming from parent + * @param _accountOwner owner for opt-out/release at registry move + **/ + function migrateUsername( + bytes32 _label, + uint256 _tokenBalance, + uint256 _creationTime, + address _accountOwner + ) + public + onlyParentRegistry + { + if (_tokenBalance > 0) { + require( + token.transferFrom( + parentRegistry, + address(this), + _tokenBalance + ), + "Error moving funds from old registar." + ); + reserveAmount += _tokenBalance; + } + accounts[_label] = Account(_tokenBalance, _creationTime, _accountOwner); + } + + /** + * @dev callabe only by parent registry to continue migration + * of registry and activate registrar + * @param _price the price to setup and activate domain + **/ + function migrateRegistry( + uint256 _price + ) + public + onlyParentRegistry + { + require(state == RegistrarState.Unactive, "Not unactive"); + require(ensRegistry.owner(ensNode) == address(this), "ENS registry owner not transfered."); + price = _price; + state = RegistrarState.Active; + emit RegistryPrice(_price); + } + + /** + * @notice Registers `_label` username to `ensNode` setting msg.sender as owner. + * @param _owner address registering the user and paying registry price. + * @param _label choosen unowned username hash + * @param _account optional address to set at public resolver + * @param _pubkeyA optional pubkey part A to set at public resolver + * @param _pubkeyB optional pubkey part B to set at public resolver + */ + function registerUser( + address _owner, + bytes32 _label, + address _account, + bytes32 _pubkeyA, + bytes32 _pubkeyB + ) + internal + returns(bytes32 namehash) + { + require(state == RegistrarState.Active, "Registry unavailable."); + namehash = keccak256(abi.encodePacked(ensNode, _label)); + require(ensRegistry.owner(namehash) == address(0), "ENS node already owned."); + require(accounts[_label].creationTime == 0, "Username already registered."); + accounts[_label] = Account(price, block.timestamp, _owner); + if(price > 0) { + require(token.allowance(_owner, address(this)) >= price, "Unallowed to spend."); + require( + token.transferFrom( + _owner, + address(this), + price + ), + "Transfer failed" + ); + reserveAmount += price; + } + + bool resolvePubkey = _pubkeyA != 0 || _pubkeyB != 0; + bool resolveAccount = _account != address(0); + if (resolvePubkey || resolveAccount) { + //set to self the ownship to setup initial resolver + ensRegistry.setSubnodeOwner(ensNode, _label, address(this)); + ensRegistry.setResolver(namehash, resolver); //default resolver + if (resolveAccount) { + resolver.setAddr(namehash, _account); + } + if (resolvePubkey) { + resolver.setPubkey(namehash, _pubkeyA, _pubkeyB); + } + ensRegistry.setOwner(namehash, _owner); + } else { + //transfer ownship of subdone directly to registrant + ensRegistry.setSubnodeOwner(ensNode, _label, _owner); + } + emit UsernameOwner(namehash, _owner); + } + + /** + * @dev removes account hash of `_username` and send account.balance to msg.sender + * @param _username username being slashed + */ + function slashUsername(bytes _username) internal { + bytes32 label = keccak256(_username); + bytes32 namehash = keccak256(abi.encodePacked(ensNode, label)); + require(accounts[label].creationTime > 0, "Username not registered."); + + ensRegistry.setSubnodeOwner(ensNode, label, address(this)); + ensRegistry.setResolver(namehash, address(0)); + ensRegistry.setOwner(namehash, address(0)); + + uint256 amountToTransfer = accounts[label].balance; + delete accounts[label]; + if (amountToTransfer > 0) { + reserveAmount -= amountToTransfer; + require(token.transfer(msg.sender, amountToTransfer), "Error in transfer."); + } + emit UsernameOwner(namehash, address(0)); + } + +} diff --git a/contracts/token/ApprovalReceiver.sol b/contracts/token/ApprovalReceiver.sol deleted file mode 100644 index 866e1b0..0000000 --- a/contracts/token/ApprovalReceiver.sol +++ /dev/null @@ -1,5 +0,0 @@ -pragma solidity ^0.4.11; - -contract ApprovalReceiver { - function receiveApproval(address from, uint value, address tokenContract, bytes extraData) returns (bool); -} diff --git a/contracts/token/ApproveAndCallFallBack.sol b/contracts/token/ApproveAndCallFallBack.sol new file mode 100644 index 0000000..f348786 --- /dev/null +++ b/contracts/token/ApproveAndCallFallBack.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.11; + +contract ApproveAndCallFallBack { + function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public; +} diff --git a/contracts/token/StandardToken.sol b/contracts/token/StandardToken.sol index 484cfeb..826984b 100644 --- a/contracts/token/StandardToken.sol +++ b/contracts/token/StandardToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "./ERC20Token.sol"; @@ -20,23 +20,15 @@ contract StandardToken is ERC20Token { return transfer(msg.sender, _to, _value); } - /** - * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender. - * @param _spender The address which will spend the funds. - * @param _value The amount of tokens to be spent. - */ - function approve(address _spender, uint256 _value) returns (bool) { - - // To change the approve amount you first have to reduce the addresses` - // allowance to zero by calling `approve(_spender, 0)` if it is not - // already 0 to mitigate the race condition described here: - // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - require((_value == 0) || (allowed[msg.sender][_spender] == 0)); - - allowed[msg.sender][_spender] = _value; - Approval(msg.sender, _spender, _value); - return true; - } + function approve( + address _to, + uint256 _value + ) + external + returns (bool success) + { + return approve(msg.sender, _to, _value); + } function transferFrom( address _from, @@ -79,6 +71,25 @@ contract StandardToken is ERC20Token { { return supply; } + + /** + * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender. + * @param _from The address that is approving the spend + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _from, address _spender, uint256 _value) internal returns (bool) { + + // To change the approve amount you first have to reduce the addresses` + // allowance to zero by calling `approve(_spender, 0)` if it is not + // already 0 to mitigate the race condition described here: + // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + require((_value == 0) || (allowed[_from][_spender] == 0), "Bad usage"); + + allowed[_from][_spender] = _value; + emit Approval(_from, _spender, _value); + return true; + } function mint( address _to, diff --git a/contracts/token/TestToken.sol b/contracts/token/TestToken.sol index 0845c45..0e914b1 100644 --- a/contracts/token/TestToken.sol +++ b/contracts/token/TestToken.sol @@ -1,7 +1,7 @@ -pragma solidity ^0.4.23; +pragma solidity ^0.4.24; import "./StandardToken.sol"; -import './ApprovalReceiver.sol'; +import "./ApproveAndCallFallBack.sol"; /** * @notice ERC20Token for test scripts, can be minted by anyone. @@ -18,9 +18,13 @@ contract TestToken is StandardToken { mint(msg.sender, _amount); } - function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { - assert(approve(_spender, _value)); - return ApprovalReceiver(_spender).receiveApproval(msg.sender, _value, this, _extraData); + function approveAndCall(address _spender, uint256 _value, bytes _extraData) + external + returns (bool success) + { + approve(msg.sender, _spender, _value); + ApproveAndCallFallBack(_spender).receiveApproval(msg.sender, _value, this, _extraData); + return true; } } diff --git a/package.json b/package.json index fb8c79e..1bf4b36 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "homepage": "https://github.com/status-im/contracts#readme", "dependencies": { + "@babel/runtime": "7.0.0-beta.55", "@material-ui/core": "^1.2.1", "@material-ui/icons": "^1.1.0", "bignumber.js": "^5.0.0", diff --git a/test/enssubdomainregistry.spec.js b/test/enssubdomainregistry.spec.js deleted file mode 100644 index 19f7fba..0000000 --- a/test/enssubdomainregistry.spec.js +++ /dev/null @@ -1,723 +0,0 @@ -const utils = require('../utils/testUtils.js'); -const web3Utils = require('web3-utils'); -const namehash = require('eth-ens-namehash'); -const TestToken = require('Embark/contracts/TestToken'); -const ENSRegistry = require('Embark/contracts/ENSRegistry'); -const PublicResolver = require('Embark/contracts/PublicResolver'); -const ENSSubdomainRegistry = require('Embark/contracts/ENSSubdomainRegistry'); -const { MerkleTree } = require('../utils/merkleTree.js'); - -const domains = { - free : { - name: 'freedomain.eth', - price: 0, - namehash: namehash.hash('freedomain.eth') - }, - paid : { - name: 'stateofus.eth', - price: 100000000, - namehash: namehash.hash('stateofus.eth') - }, - temp : { - name: 'temporary.eth', - price: 100000000, - namehash: namehash.hash('temporary.eth') - } -} - -const reservedNames = [ - 'administrator', - 'support', - 'status', - 'network', -] - -// TODO: load file of reserved names and balance array lenght to be even - -const merkleTree = new MerkleTree(reservedNames); -const merkleRoot = merkleTree.getHexRoot(); - -var contractsConfig = { - "TestToken": { - - }, - "ENSRegistry": { - "onDeploy": [ - "ENSRegistry.methods.setSubnodeOwner('0x0000000000000000000000000000000000000000000000000000000000000000', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0', web3.eth.defaultAccount).send()" - ] - }, - "PublicResolver": { - "args": [ - "$ENSRegistry" - ] - }, - "ENSSubdomainRegistry": { - "args": [ - "$TestToken", - "$ENSRegistry", - "$PublicResolver", - "3", - [merkleRoot], - "0x0" - ], - "onDeploy": [ - "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', ENSSubdomainRegistry.address).send()", - "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x7b4768a525e733422bf968587a91b4036e5176d36f44a9fb5b29d0bca03ab3a3', ENSSubdomainRegistry.address).send()", - "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x44fa953dda0aa9a41a27e14a13ed7e08a0d7fc72873516f5b3bf9b50718610df', ENSSubdomainRegistry.address).send()" - ] - }, - "UpdatedENSSubdomainRegistry": { - "instanceOf" : "ENSSubdomainRegistry", - "args": [ - "$TestToken", - "$ENSRegistry", - "$PublicResolver", - "3", - [merkleRoot], - "$ENSSubdomainRegistry" - ] - } - -}; - -config({ contracts: contractsConfig }); - -contract('ENSSubdomainRegistry', function () { - let ens; - let accountsArr; - - before(function(done) { - web3.eth.getAccounts().then(async (accounts) => { - ens = ENSRegistry; - accountsArr = accounts; - await utils.increaseTime(1 * utils.timeUnits.days) //time cannot start zero - await utils.increaseTime(1000) - done(); - }) - }); - - describe('setDomainPrice()', function() { - it('should add free domain', async () => { - const domain = domains.free; - const resultSetDomainPrice = await ENSSubdomainRegistry.methods.setDomainPrice(domain.namehash, domain.price).send({from: accountsArr[0]}); - assert.equal(resultSetDomainPrice.events.DomainPrice.returnValues.price, domain.price, "event DomainPrice wrong price"); - assert.equal(resultSetDomainPrice.events.DomainPrice.returnValues.namehash, domain.namehash, "event DomainPrice wrong namehash"); - assert.equal(await ENSSubdomainRegistry.methods.getPrice(domain.namehash).call(), domain.price, "getPrice() wrong price"); - const resultDomainAccount = await ENSSubdomainRegistry.methods.domains(domain.namehash).call() - assert.equal(resultDomainAccount.state, 1, "Wrong domain state") - assert.equal(resultDomainAccount.price, domain.price, "Wrong domain price") - }); - it('should add paid domain', async () => { - const initialPrice = 100 - const domain = domains.paid; - const resultSetDomainPrice = await ENSSubdomainRegistry.methods.setDomainPrice(domain.namehash, initialPrice).send({from: accountsArr[0]}); - assert.equal(resultSetDomainPrice.events.DomainPrice.returnValues.price, initialPrice, "event DomainPrice wrong price"); - assert.equal(resultSetDomainPrice.events.DomainPrice.returnValues.namehash, domain.namehash, "event DomainPrice wrong namehash"); - assert.equal(await ENSSubdomainRegistry.methods.getPrice(domain.namehash).call(), initialPrice, "getPrice() wrong price"); - const resultDomainAccount = await ENSSubdomainRegistry.methods.domains(domain.namehash).call() - assert.equal(resultDomainAccount.state, 1, "Wrong domain state") - assert.equal(resultDomainAccount.price, initialPrice, "Wrong domain price") - }); - }); - - describe('updateDomainPrice()', function() { - it('should change paid domain price', async () => { - const newPrice = domains.paid.price; - const resultUpdateDomainPrice = await ENSSubdomainRegistry.methods.updateDomainPrice(domains.paid.namehash, newPrice).send({from: accountsArr[0]}); - assert.equal(resultUpdateDomainPrice.events.DomainPrice.returnValues.price, domains.paid.price, "event DomainPrice wrong price"); - assert.equal(resultUpdateDomainPrice.events.DomainPrice.returnValues.namehash, domains.paid.namehash, "event DomainPrice wrong namehash"); - assert.equal(await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call(), newPrice, "Wrong return value at getPrice"); - const resultDomainAccount= await ENSSubdomainRegistry.methods.domains(domains.paid.namehash).call() - assert.equal(resultDomainAccount.state, 1, "Wrong domain state") - assert.equal(resultDomainAccount.price, newPrice, "Wrong domain price") - }); - }); - - describe('register()', function() { - it('should register free subdomain', async () => { - const subdomain = 'alice'; - const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - const registrant = accountsArr[1]; - const label = web3Utils.sha3(subdomain); - const node = domains.free.namehash; - const resultRegister = await ENSSubdomainRegistry.methods.register( - label, - node, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - assert.equal(resultRegister.events['0'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); - assert.equal(resultRegister.events['0'].raw.topics[1], node, "Wrong Node"); - assert.equal(resultRegister.events['0'].raw.topics[2], label, "Wrong Label"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.data.substring(26)), registrant, "Wrong subnode owner"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch"); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch"); - assert.equal(await ens.methods.resolver(subdomainHash).call(), utils.zeroAddress, "Resolver wrongly defined"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Free domain accounts shouldn't have balance"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch"); - }); - it('should register free address only resolver-defined subdomain', async () => { - const registrant = accountsArr[2]; - const subdomain = 'bob'; - const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - const label = web3Utils.sha3(subdomain); - const node = domains.free.namehash; - const resultRegister = await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - registrant, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - assert.equal(resultRegister.events['0'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); - assert.equal(resultRegister.events['0'].raw.topics[1], node, "Wrong Node"); - assert.equal(resultRegister.events['0'].raw.topics[2], label, "Wrong Label"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.data.substring(26)), ENSSubdomainRegistry.address, "Wrong subnode owner"); - assert.equal(resultRegister.events['1'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event"); - assert.equal(resultRegister.events['1'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), PublicResolver.address, "Wrong Resolver"); - assert.equal(resultRegister.events['2'].raw.topics[0], '0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2', "Wrong Event"); - assert.equal(resultRegister.events['2'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), registrant, "Wrong address to resolve"); - assert.equal(resultRegister.events['3'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event"); - assert.equal(resultRegister.events['3'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['3'].raw.data.substring(26)), registrant, "Wrong node owner"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch"); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch"); - assert.equal(await ens.methods.resolver(subdomainHash).call(), PublicResolver.address, "Resolver wrongly defined"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Free domain accounts shouldn't have balance"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch"); - assert.equal(await PublicResolver.methods.addr(subdomainHash).call(), registrant, "Resolved address not set"); - const resolverPubKey = await PublicResolver.methods.pubkey(subdomainHash).call(); - assert.equal(resolverPubKey[0], utils.zeroBytes32 , "Unexpected resolved pubkey[0]"); - assert.equal(resolverPubKey[1], utils.zeroBytes32 , "Unexpected resolved pubkey[1]"); - }); - it('should register free status contact code and address resolver-defined subdomain', async () => { - const registrant = accountsArr[2]; - const subdomain = 'bob2'; - const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82'; - const points = utils.generateXY(contactCode); - const label = web3Utils.sha3(subdomain); - const node = domains.free.namehash; - const resultRegister = await ENSSubdomainRegistry.methods.register( - label, - node, - registrant, - points.x, - points.y - ).send({from: registrant}); - assert.equal(resultRegister.events['0'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); - assert.equal(resultRegister.events['0'].raw.topics[1], node, "Wrong Node"); - assert.equal(resultRegister.events['0'].raw.topics[2], label, "Wrong Label"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.data.substring(26)), ENSSubdomainRegistry.address, "Wrong subnode owner"); - assert.equal(resultRegister.events['1'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event"); - assert.equal(resultRegister.events['1'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), await ENSSubdomainRegistry.methods.resolver().call(), "Wrong Resolver"); - assert.equal(resultRegister.events['2'].raw.topics[0], '0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2', "Wrong Event"); - assert.equal(resultRegister.events['2'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), registrant, "Wrong address to resolve"); - assert.equal(resultRegister.events['3'].raw.topics[0], '0x1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46', "Wrong Event"); - assert.equal(resultRegister.events['3'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(resultRegister.events['3'].raw.data, points.x.concat(points.y.substr(2))) - assert.equal(resultRegister.events['4'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event"); - assert.equal(resultRegister.events['4'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['4'].raw.data.substring(26)), registrant, "Wrong node owner"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch"); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch"); - assert.equal(await ens.methods.resolver(subdomainHash).call(), PublicResolver.address, "Resolver wrongly defined"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Free domain accounts shouldn't have balance"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch"); - assert.equal(await PublicResolver.methods.addr(subdomainHash).call(), registrant, "Resolved address not set"); - const resolverPubKey = await PublicResolver.methods.pubkey(subdomainHash).call(); - const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]); - assert.equal(pubKey, contactCode, "pubKey does not match contract code"); - }); - it('should register free pubkey only resolver-defined subdomain', async () => { - const subdomain = 'carlos'; - const registrant = accountsArr[3]; - const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82'; - const points = utils.generateXY(contactCode); - const label = web3Utils.sha3(subdomain); - const node = domains.free.namehash; - const resultRegister = await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - utils.zeroAddress, - points.x, - points.y - ).send({from: registrant}); - assert.equal(resultRegister.events['0'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); - assert.equal(resultRegister.events['0'].raw.topics[1], node, "Wrong Node"); - assert.equal(resultRegister.events['0'].raw.topics[2], label, "Wrong Label"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.data.substring(26)), ENSSubdomainRegistry.address, "Wrong subnode owner"); - assert.equal(resultRegister.events['1'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event"); - assert.equal(resultRegister.events['1'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), PublicResolver.address, "Wrong Resolver"); - assert.equal(resultRegister.events['2'].raw.topics[0], '0x1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46', "Wrong Event"); - assert.equal(resultRegister.events['2'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(resultRegister.events['2'].raw.data, points.x.concat(points.y.substr(2))) - assert.equal(resultRegister.events['3'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event"); - assert.equal(resultRegister.events['3'].raw.topics[1], subdomainHash, "Wrong Subdomain"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['3'].raw.data.substring(26)), registrant, "Wrong node owner"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch"); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch"); - assert.equal(await ens.methods.resolver(subdomainHash).call(), PublicResolver.address, "Resolver wrongly defined"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Free domain accounts shouldn't have balance"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch"); - assert.equal(await PublicResolver.methods.addr(subdomainHash).call(), utils.zeroAddress, "Resolved address not set"); - const resolverPubKey = await PublicResolver.methods.pubkey(subdomainHash).call(); - const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]); - assert.equal(pubKey, contactCode, "pubKey does not match contract code"); - }); - it('should register empty subdomain with token cost', async () => { - const registrant = accountsArr[5]; - const subdomain = 'erin'; - const subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name); - const domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call() - const label = web3Utils.sha3(subdomain); - const node = domains.paid.namehash; - await TestToken.methods.mint(domainPrice).send({from: registrant}); - const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call(); - const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(); - await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant}); - const resultRegister = await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.paid.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - assert.equal(resultRegister.events['0'].raw.topics[0], '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', "Wrong Event"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.topics[1].substring(26)), registrant, "Wrong subnode owner"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.topics[2].substring(26)), ENSSubdomainRegistry.address, "Wrong subnode owner"); - assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); - assert.equal(resultRegister.events['1'].raw.topics[1], node, "Wrong Node"); - assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label"); - assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), registrant, "Wrong subnode owner"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch"); - assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch"); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch"); - assert.equal(await ens.methods.resolver(subdomainHash).call(), utils.zeroAddress, "Resolver wrongly defined"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), domainPrice, "Registry subdomain account balance wrong"); - assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch"); - assert.equal(await TestToken.methods.balanceOf(registrant).call(), +initialRegistrantBalance-domainPrice, "User final balance wrong") - assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)+(+domainPrice), "Registry final balance wrong") - }); - }); - - describe('release()', function() { - it('should not release subdomain due delay', async () => { - let registrant = accountsArr[6]; - let subdomain = 'mistaker'; - await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - let failed; - try{ - await ENSSubdomainRegistry.methods.release( - web3Utils.sha3(subdomain), - domains.free.namehash - ).send({from: registrant}); - failed = false; - } catch(e){ - failed = true; - } - assert(failed, "Released after delay period"); - }); - it('should release free subdomain', async () => { - let registrant = accountsArr[6]; - let subdomain = 'frank'; - let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - - await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - const releaseDelay = await ENSSubdomainRegistry.methods.releaseDelay().call(); - await utils.increaseTime(releaseDelay) - await utils.increaseTime(1000) - const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call(); - const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(); - await utils.increaseTime(1000) - const resultRelease = await ENSSubdomainRegistry.methods.release( - web3Utils.sha3(subdomain), - domains.free.namehash - ).send({from: registrant}); - //TODO: check events - assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress, "Not released name ownship"); - assert.equal(await TestToken.methods.balanceOf(registrant).call(), initialRegistrantBalance, "Registrant token balance unexpectectly changed") - assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), initialRegistryBalance, "Registry token balance unexpectectly changed") - }); - it('should release subdomain with cost', async () => {; - const registrant = accountsArr[6]; - const subdomain = 'frank'; - const subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name); - const labelHash = web3Utils.sha3(subdomain); - const domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call() - await TestToken.methods.mint(domainPrice).send({from: registrant}); - await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant}); - await ENSSubdomainRegistry.methods.register( - labelHash, - domains.paid.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - const releaseDelay = await ENSSubdomainRegistry.methods.releaseDelay().call(); - await utils.increaseTime(releaseDelay) - await utils.increaseTime(1000) - const initialAccountBalance = await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(); - const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call(); - const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(); - await utils.increaseTime(1000) - const resultRelease = await ENSSubdomainRegistry.methods.release( - web3Utils.sha3(subdomain), - domains.paid.namehash - ).send({from: registrant}); - //TODO: check events - assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Final balance didnt zeroed"); - assert.equal(await TestToken.methods.balanceOf(registrant).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "Releaser token balance didnt increase") - assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease") - }); - it('should release transfered subdomain with cost', async () => { - let registrant = accountsArr[7]; - let subdomain = 'grace'; - let subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name); - let labelHash = web3Utils.sha3(subdomain); - let newOwner = accountsArr[8]; - let domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call() - await TestToken.methods.mint(domainPrice).send({from: registrant}); - await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant}); - await ENSSubdomainRegistry.methods.register( - labelHash, - domains.paid.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - await ens.methods.setOwner(subdomainHash, newOwner).send({from: registrant}); - let releaseDelay = await ENSSubdomainRegistry.methods.releaseDelay().call(); - await utils.increaseTime(releaseDelay) - await utils.increaseTime(1000) - let initialAccountBalance = await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(); - let initialRegistrantBalance = await TestToken.methods.balanceOf(newOwner).call(); - let initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(); - await utils.increaseTime(1000) - let resultRelease = await ENSSubdomainRegistry.methods.release( - web3Utils.sha3(subdomain), - domains.paid.namehash - ).send({from: newOwner}); - //TODO: check events - assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Final balance didnt zeroed"); - assert.equal(await TestToken.methods.balanceOf(newOwner).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "New owner token balance didnt increase") - assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease") - }); - it('should release moved subdomain account balance by funds owner', async () => { - const domain = domains.temp; - await ENSSubdomainRegistry.methods.setDomainPrice(domain.namehash, domain.price).send({from: accountsArr[0]}); - const registrant = accountsArr[5]; - const subdomain = 'hardhead'; - const subdomainHash = namehash.hash(subdomain + '.' + domain.name); - const domainPrice = await ENSSubdomainRegistry.methods.getPrice(domain.namehash).call() - const label = web3Utils.sha3(subdomain); - const node = domain.namehash; - await TestToken.methods.mint(domainPrice).send({from: registrant}); - await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant}); - await ENSSubdomainRegistry.methods.register( - label, - node, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - let initialAccountBalance = await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(); - const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call(); - const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(); - await ENSSubdomainRegistry.methods.moveDomain(UpdatedENSSubdomainRegistry.address, domain.namehash).send(); - const resultRelease = await ENSSubdomainRegistry.methods.release( - label, - node - ).send({from: registrant}); - //TODO: verify events - assert.equal(await TestToken.methods.balanceOf(registrant).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "New owner token balance didnt increase") - assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease") - }); - }); - - describe('updateAccountOwner()', function() { - it('should update subdomain funds owner', async () => { - let subdomain = 'heidi'; - let labelHash = web3Utils.sha3(subdomain); - let registrant = accountsArr[8]; - let newOwner = accountsArr[9]; - let subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name); - let domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call() - await TestToken.methods.mint(domainPrice).send({from: registrant}); - await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant}); - await ENSSubdomainRegistry.methods.register( - labelHash, - domains.paid.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - await ens.methods.setOwner(subdomainHash, newOwner).send({from: registrant}); - let resultUpdateOwner = await ENSSubdomainRegistry.methods.updateAccountOwner( - labelHash, - domains.paid.namehash - ).send({from: newOwner}); - //TODO: check events - assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), newOwner, "Backup owner not updated"); - }); - }); - - describe('slashInvalidSubdomain()', function() { - it('should slash invalid subdomain', async () => { - let subdomain = 'alicé'; - let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - let registrant = accountsArr[1]; - await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant); - assert.notEqual(await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0); - await ENSSubdomainRegistry.methods.slashInvalidSubdomain(web3Utils.toHex(subdomain), domains.free.namehash, 4).send() - //TODO: check events - assert.equal(await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0); - assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress); - }); - it('should not slash valid subdomain', async () => { - let subdomain = 'legituser'; - let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - let registrant = accountsArr[1]; - await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - let failed; - try{ - await ENSSubdomainRegistry.methods.slashInvalidSubdomain(web3Utils.toHex(subdomain), domains.free.namehash, 4).send() - failed = false; - } catch(e){ - failed = true; - } - assert(failed, "Was slashed anyway"); - }); - }); - - describe('slashReservedSubdomain()', function() { - it('should slash reserved name subdomain', async () => { - let subdomain = reservedNames[0]; - let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - let registrant = accountsArr[1]; - await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant); - const proof = merkleTree.getHexProof(reservedNames[0]); - result = await ENSSubdomainRegistry.methods.slashReservedSubdomain(web3Utils.toHex(subdomain), domains.free.namehash, 0, proof).send() - assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress); - }); - }); - - describe('slashSmallSubdomain()', function() { - it('should not slash big subdomain', async() =>{ - let subdomain = '1234567890'; - let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - let registrant = accountsArr[1]; - await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - let failed; - try{ - await ENSSubdomainRegistry.methods.slashSmallSubdomain(web3Utils.toHex(subdomain), domains.free.namehash).send() - failed = false; - } catch(e){ - failed = true; - } - assert(failed, "Was slashed anyway"); - }) - it('should slash small subdomain', async () => { - let subdomain = 'a'; - let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - let registrant = accountsArr[1]; - await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.free.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant); - result = await ENSSubdomainRegistry.methods.slashSmallSubdomain(web3Utils.toHex(subdomain), domains.free.namehash).send() - assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress); - }); - }); - describe('slashAddressLikeSubdomain()', function() { - it('should slash subdomain that starts with 0x and is 12 of lenght or bigger', async () => { - let subdomain = "0xc6b95bd26123"; - let userlabelHash = "0xe311c0592b075c30277c679f0daea74ee1727547efa522fd28d20a8f2c3e435b"; //sha3("0xc6b95bd26123") - let subdomainHash = "0x5fcd61e83fda60beb7c9bff8e0e26a6f975a5154ff2e6f5464dc97571c95cdd4"; //namehash("0xc6b95bd26123.freedomain.eth") - let domainnameHash = "0x297836a76312224372ac04e26dd23d1294bb8256598ec113ecc52735f826beff"; //namehash("freedomain.eth") - let registrant = accountsArr[1]; - let result = await ENSSubdomainRegistry.methods.register( - userlabelHash, - domainnameHash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant); - result = await ENSSubdomainRegistry.methods.slashAddressLikeSubdomain(subdomain, domainnameHash).send() - assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress); - }); - it('should not slash subdomain that starts with 0x but is smaller then 12', async () => { - let subdomain = "0xc6b95bd26"; - let userlabelHash = "0x59bf8d16c517a40a5dacc3471abd002f3bc0850a13e930e4bee49070a58517e8"; //sha3("0xc6b95bd26") - let subdomainHash = "0x6f15e192c1c4537c2d774431219ed42efc6be95efe362104ba546eb574f3f1e5"; //namehash("0xc6b95bd26.freedomain.eth") - let domainnameHash = "0x297836a76312224372ac04e26dd23d1294bb8256598ec113ecc52735f826beff"; //namehash("freedomain.eth") - let registrant = accountsArr[1]; - await ENSSubdomainRegistry.methods.register( - userlabelHash, - domainnameHash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - let failed; - try{ - result = await ENSSubdomainRegistry.methods.slashAddressLikeSubdomain(subdomain, domainnameHash).send() - failed = false; - } catch(e){ - failed = true; - } - assert(failed, "Was slashed anyway"); - }); - it('should not slash subdomain that dont starts 0x and is bigger than 12', async () => { - const subdomain = "0a002322c6b95bd26"; - const userlabelHash = "0xe4769e5c31ff61ac50dce20559a4411a4ca45d94c733cbeda7ab9f28ed75cef1"; //sha3("0a002322c6b95bd26") - const subdomainHash = "0x549a8b62103d19b66f70bee21176514340094253a92123609c1df25b0812d40c"; //namehash("0a002322c6b95bd26.freedomain.eth") - const domainnameHash = "0x297836a76312224372ac04e26dd23d1294bb8256598ec113ecc52735f826beff"; //namehash("freedomain.eth") - const registrant = accountsArr[1]; - await ENSSubdomainRegistry.methods.register( - userlabelHash, - domainnameHash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - let failed; - try{ - await ENSSubdomainRegistry.methods.slashAddressLikeSubdomain(subdomain, domainnameHash).send() - failed = false; - } catch(e){ - failed = true; - } - assert(failed, "Was slashed anyway"); - }); - }); - describe('slashSubdomain()', function() { - it('should slash a paid subdomain and get funds from registrant', async () => { - const subdomain = 'b'; - const subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name); - const registrant = accountsArr[1]; - const slasher = accountsArr[2]; - const domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call() - await TestToken.methods.mint(domainPrice).send({from: registrant}); - await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant}); - await ENSSubdomainRegistry.methods.register( - web3Utils.sha3(subdomain), - domains.paid.namehash, - utils.zeroAddress, - utils.zeroBytes32, - utils.zeroBytes32 - ).send({from: registrant}); - assert.equal(await ens.methods.owner(subdomainHash).call(), registrant); - const initialSlasherBalance = await TestToken.methods.balanceOf(slasher).call(); - await ENSSubdomainRegistry.methods.slashSmallSubdomain(web3Utils.toHex(subdomain), domains.paid.namehash).send({from: slasher}) - //TODO: check events - assert.equal(await TestToken.methods.balanceOf(slasher).call(), (+initialSlasherBalance)+(+domainPrice)); - assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress); - }); - }); - - describe('moveDomain()', function() { - it('should move free domain to new registry and migrate', async () => { - const resultMoveDomain = await ENSSubdomainRegistry.methods.moveDomain(UpdatedENSSubdomainRegistry.address, domains.free.namehash).send(); - //TODO: check events - assert.equal(await ens.methods.owner(domains.free.namehash).call(), UpdatedENSSubdomainRegistry.address, "domain ownership not moved correctly") - }); - it('should move paid domain to new registry and migrate', async () => { - const price = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call() - const result = await ENSSubdomainRegistry.methods.moveDomain(UpdatedENSSubdomainRegistry.address, domains.paid.namehash).send(); - //TODO: check events - assert.equal(await ens.methods.owner(domains.paid.namehash).call(), UpdatedENSSubdomainRegistry.address, "domain ownership not moved correctly") - assert.equal(await UpdatedENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call(), price, "updated registry didnt migrated price") - }); - }); - - describe('moveAccount()', function() { - it('should move free subdomain to new registry by funds owner', async () => { - const subdomain = 'alice'; - const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name); - const registrant = accountsArr[1]; - const label = web3Utils.sha3(subdomain); - const node = domains.free.namehash; - const creationTime = await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(); - assert.notEqual(creationTime, 0); - assert.equal(await UpdatedENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0); - const result = await ENSSubdomainRegistry.methods.moveAccount(label,node).send({from: registrant}); - assert.equal(await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0); - assert.equal(await UpdatedENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), creationTime); - }); - it('should move paid subdomain to new registry by funds owner', async () => { - const registrant = accountsArr[5]; - const subdomain = 'erin'; - const subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name); - const label = web3Utils.sha3(subdomain); - const node = domains.paid.namehash; - const accountBalance = await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call() - assert.notEqual(accountBalance, 0); - const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(); - const initialUpdatedRegistryBalance = await TestToken.methods.balanceOf(UpdatedENSSubdomainRegistry.address).call(); - const creationTime = await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(); - assert.notEqual(creationTime, 0); - assert.equal(await UpdatedENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0); - const result = await ENSSubdomainRegistry.methods.moveAccount(label,node).send({from: registrant}); - assert.equal(await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0); - assert.equal(await UpdatedENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), creationTime); - assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)-(+accountBalance)) - assert.equal(await TestToken.methods.balanceOf(UpdatedENSSubdomainRegistry.address).call(), (+initialUpdatedRegistryBalance)+(+accountBalance)) - }); - }); - -}); diff --git a/test/usernameregistrar.spec.js b/test/usernameregistrar.spec.js new file mode 100644 index 0000000..b9d813f --- /dev/null +++ b/test/usernameregistrar.spec.js @@ -0,0 +1,806 @@ +const utils = require('../utils/testUtils.js'); +const web3Utils = require('web3-utils'); +const namehash = require('eth-ens-namehash'); +const TestToken = require('Embark/contracts/TestToken'); +const ENSRegistry = require('Embark/contracts/ENSRegistry'); +const PublicResolver = require('Embark/contracts/PublicResolver'); +const UsernameRegistrar = require('Embark/contracts/UsernameRegistrar'); +const { MerkleTree } = require('../utils/merkleTree.js'); +const { reservedNames } = require('../config/ens-usernames/reservedNames') +const registry = { + name: 'stateofus', + registry: 'stateofus.eth', + label: web3Utils.sha3('stateofus'), + namehash: namehash.hash('stateofus.eth'), + price: 100000000 +} + +const dummyRegistry = { + name: 'dummyreg', + registry: 'dummyreg.eth', + label: web3Utils.sha3('dummyreg'), + namehash: namehash.hash('dummyreg.eth'), + price: 100000000 +} + +// TODO: load file of reserved names and balance array lenght to be even + +const merkleTree = new MerkleTree(reservedNames); +const merkleRoot = merkleTree.getHexRoot(); + +var contractsConfig = { + "TestToken": { + + }, + "ENSRegistry": { + "onDeploy": [ + "ENSRegistry.methods.setSubnodeOwner('0x0000000000000000000000000000000000000000000000000000000000000000', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0', web3.eth.defaultAccount).send()" + ] + }, + "PublicResolver": { + "args": [ + "$ENSRegistry" + ] + }, + "UsernameRegistrar": { + "args": [ + "$TestToken", + "$ENSRegistry", + "$PublicResolver", + registry.namehash, + "3", + merkleRoot, + "0x0" + ], + "onDeploy": [ + "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '"+registry.label+"', UsernameRegistrar.address).send()", + ] + }, + "UpdatedUsernameRegistrar": { + "instanceOf" : "UsernameRegistrar", + "args": [ + "$TestToken", + "$ENSRegistry", + "$PublicResolver", + registry.namehash, + "3", + merkleRoot, + "$UsernameRegistrar" + ] + }, + "DummyUsernameRegistrar": { + "instanceOf" : "UsernameRegistrar", + "args": [ + "$TestToken", + "$ENSRegistry", + "$PublicResolver", + dummyRegistry.namehash, + "3", + merkleRoot, + "0x0" + ], + "onDeploy": [ + "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '"+dummyRegistry.label+"', DummyUsernameRegistrar.address).send()", + ] + }, + "UpdatedDummyUsernameRegistrar": { + "instanceOf" : "UsernameRegistrar", + "args": [ + "$TestToken", + "$ENSRegistry", + "$PublicResolver", + dummyRegistry.namehash, + "3", + merkleRoot, + "$DummyUsernameRegistrar" + ] + } + +}; + +config({ contracts: contractsConfig }); + +contract('UsernameRegistrar', function () { + let ens; + let accountsArr; + + before(function(done) { + web3.eth.getAccounts().then(async (accounts) => { + ens = ENSRegistry; + accountsArr = accounts; + await utils.increaseTime(1 * utils.timeUnits.days) //time cannot start zero + await utils.increaseTime(1000) + done(); + }) + }); + + describe('activate(uint256)', function() { + it('should activate registry', async () => { + const initialPrice = 100 + const resultSetRegistryPrice = await UsernameRegistrar.methods.activate(initialPrice).send({from: accountsArr[0]}); + assert.equal(resultSetRegistryPrice.events.RegistryPrice.returnValues.price, initialPrice, "event RegistryPrice wrong price"); + assert.equal(await UsernameRegistrar.methods.state().call(), 1, "Wrong registry state") + assert.equal(await UsernameRegistrar.methods.price().call(), initialPrice, "Wrong registry price") + }); + }); + + describe('updateRegistryPrice()', function() { + it('should change registry price', async () => { + const newPrice = registry.price; + const resultUpdateRegistryPrice = await UsernameRegistrar.methods.updateRegistryPrice(newPrice).send({from: accountsArr[0]}); + assert.equal(resultUpdateRegistryPrice.events.RegistryPrice.returnValues.price, registry.price, "event RegistryPrice wrong price"); + assert.equal(await UsernameRegistrar.methods.state().call(), 1, "Wrong registry state") + assert.equal(await UsernameRegistrar.methods.price().call(), newPrice, "Wrong registry price") + }); + }); + + describe('register()', function() { + it('should register username', async () => { + const registrant = accountsArr[5]; + const username = 'erin'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const label = web3Utils.sha3(username); + const registryPrice = await UsernameRegistrar.methods.getPrice().call() + await TestToken.methods.mint(registry.price).send({from: registrant}); + const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call(); + const initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call(); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + const resultRegister = await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(resultRegister.events['0'].raw.topics[0], '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', "Wrong Event"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.topics[1].substring(26)), registrant, "Wrong subnode owner"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.topics[2].substring(26)), UsernameRegistrar.address, "Wrong subnode owner"); + assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); + assert.equal(resultRegister.events['1'].raw.topics[1], registry.namehash, "Wrong Node"); + assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), registrant, "Wrong subnode owner"); + assert.equal(resultRegister.events.UsernameOwner.returnValues.owner, registrant, "event UsernameOwner owner mismatch"); + assert.equal(resultRegister.events.UsernameOwner.returnValues.nameHash, usernameHash, "event UsernameOwner usernameHash mismatch"); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), utils.zeroAddress, "Resolver wrongly defined"); + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registryPrice, "Registry username account balance wrong"); + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch"); + assert.equal(await TestToken.methods.balanceOf(registrant).call(), +initialRegistrantBalance-registryPrice, "User final balance wrong") + assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)+(+registry.price), "Registry final balance wrong") + }); + it('should register username only resolveing address ', async () => { + const registrant = accountsArr[2]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + const username = 'bob'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const label = web3Utils.sha3(username); + const resultRegister = await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + + registrant, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); + assert.equal(resultRegister.events['1'].raw.topics[1], registry.namehash, "Wrong Node"); + assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), UsernameRegistrar.address, "Wrong subnode owner"); + assert.equal(resultRegister.events['2'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event"); + assert.equal(resultRegister.events['2'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), PublicResolver.address, "Wrong Resolver"); + assert.equal(resultRegister.events['3'].raw.topics[0], '0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2', "Wrong Event"); + assert.equal(resultRegister.events['3'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['3'].raw.data.substring(26)), registrant, "Wrong address to resolve"); + assert.equal(resultRegister.events['4'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event"); + assert.equal(resultRegister.events['4'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['4'].raw.data.substring(26)), registrant, "Wrong registry.namehash owner"); + assert.equal(resultRegister.events.UsernameOwner.returnValues.owner, registrant, "event UsernameOwner owner mismatch"); + assert.equal(resultRegister.events.UsernameOwner.returnValues.nameHash, usernameHash, "event UsernameOwner usernameHash mismatch"); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined"); + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance"); + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch"); + assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set"); + const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call(); + assert.equal(resolverPubKey[0], utils.zeroBytes32 , "Unexpected resolved pubkey[0]"); + assert.equal(resolverPubKey[1], utils.zeroBytes32 , "Unexpected resolved pubkey[1]"); + }); + it('should register username with only status contact', async () => { + const username = 'carlos'; + const registrant = accountsArr[3]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + const usernameHash = namehash.hash(username + '.' + registry.registry); + const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82'; + const points = utils.generateXY(contactCode); + const label = web3Utils.sha3(username); + const resultRegister = await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + points.x, + points.y + ).send({from: registrant}); + assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); + assert.equal(resultRegister.events['1'].raw.topics[1], registry.namehash, "Wrong Node"); + assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), UsernameRegistrar.address, "Wrong subnode owner"); + assert.equal(resultRegister.events['2'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event"); + assert.equal(resultRegister.events['2'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), PublicResolver.address, "Wrong Resolver"); + assert.equal(resultRegister.events['3'].raw.topics[0], '0x1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46', "Wrong Event"); + assert.equal(resultRegister.events['3'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(resultRegister.events['3'].raw.data, points.x.concat(points.y.substr(2))) + assert.equal(resultRegister.events['4'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event"); + assert.equal(resultRegister.events['4'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['4'].raw.data.substring(26)), registrant, "Wrong registry.namehash owner"); + assert.equal(resultRegister.events.UsernameOwner.returnValues.owner, registrant, "event UsernameOwner owner mismatch"); + assert.equal(resultRegister.events.UsernameOwner.returnValues.nameHash, usernameHash, "event UsernameOwner usernameHash mismatch"); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined"); + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance"); + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch"); + assert.equal(await PublicResolver.methods.addr(usernameHash).call(), utils.zeroAddress, "Resolved address not set"); + const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call(); + const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]); + assert.equal(pubKey, contactCode, "pubKey does not match contract code"); + }); + it('should register username with status contact code and address', async () => { + const registrant = accountsArr[2]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + const username = 'bob2'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82'; + const points = utils.generateXY(contactCode); + const label = web3Utils.sha3(username); + const resultRegister = await UsernameRegistrar.methods.register( + label, + registrant, + points.x, + points.y + ).send({from: registrant}); + assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event"); + assert.equal(resultRegister.events['1'].raw.topics[1], registry.namehash, "Wrong Node"); + assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), UsernameRegistrar.address, "Wrong subnode owner"); + assert.equal(resultRegister.events['2'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event"); + assert.equal(resultRegister.events['2'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), await UsernameRegistrar.methods.resolver().call(), "Wrong Resolver"); + assert.equal(resultRegister.events['3'].raw.topics[0], '0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2', "Wrong Event"); + assert.equal(resultRegister.events['3'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['3'].raw.data.substring(26)), registrant, "Wrong address to resolve"); + assert.equal(resultRegister.events['4'].raw.topics[0], '0x1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46', "Wrong Event"); + assert.equal(resultRegister.events['4'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(resultRegister.events['4'].raw.data, points.x.concat(points.y.substr(2))) + assert.equal(resultRegister.events['5'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event"); + assert.equal(resultRegister.events['5'].raw.topics[1], usernameHash, "Wrong Username"); + assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['5'].raw.data.substring(26)), registrant, "Wrong registry.namehash owner"); + assert.equal(resultRegister.events.UsernameOwner.returnValues.owner, registrant, "event UsernameOwner owner mismatch"); + assert.equal(resultRegister.events.UsernameOwner.returnValues.nameHash, usernameHash, "event UsernameOwner usernameHash mismatch"); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined"); + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance"); + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch"); + assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set"); + const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call(); + const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]); + assert.equal(pubKey, contactCode, "pubKey does not match contract code"); + }); + }); + + describe('receiveApproval()', function() { + it('should register username', async () => { + const registrant = accountsArr[5]; + const username = 'erinauto'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const label = web3Utils.sha3(username); + const registryPrice = await UsernameRegistrar.methods.getPrice().call() + await TestToken.methods.mint(registry.price).send({from: registrant}); + const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call(); + const initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call(); + + const registerCall = UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).encodeABI(); + const approveAndCallResult = await TestToken.methods.approveAndCall(UsernameRegistrar.address, registry.price, registerCall).send({from: registrant}); + // TODO: check events + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), utils.zeroAddress, "Resolver wrongly defined"); + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registryPrice, "Registry username account balance wrong"); + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch"); + assert.equal(await TestToken.methods.balanceOf(registrant).call(), +initialRegistrantBalance-registryPrice, "User final balance wrong") + assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)+(+registry.price), "Registry final balance wrong") + }); + it('should register username only resolveing address ', async () => { + const registrant = accountsArr[2]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + const username = 'bobauto'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const label = web3Utils.sha3(username); + const registerCall = UsernameRegistrar.methods.register( + web3Utils.sha3(username), + + registrant, + utils.zeroBytes32, + utils.zeroBytes32 + ).encodeABI(); + + const approveAndCallResult = await TestToken.methods.approveAndCall(UsernameRegistrar.address, registry.price, registerCall).send({from: registrant}); + // TODO: check events + + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined"); + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance"); + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch"); + assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set"); + const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call(); + assert.equal(resolverPubKey[0], utils.zeroBytes32 , "Unexpected resolved pubkey[0]"); + assert.equal(resolverPubKey[1], utils.zeroBytes32 , "Unexpected resolved pubkey[1]"); + }); + + it('should register username with only status contact', async () => { + const username = 'carlosauto'; + const registrant = accountsArr[3]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + const usernameHash = namehash.hash(username + '.' + registry.registry); + const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82'; + const points = utils.generateXY(contactCode); + const label = web3Utils.sha3(username); + const registerCall = UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + points.x, + points.y + ).encodeABI(); + + const approveAndCallResult = await TestToken.methods.approveAndCall(UsernameRegistrar.address, registry.price, registerCall).send({from: registrant}); + // TODO: check events + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined"); + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance"); + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch"); + assert.equal(await PublicResolver.methods.addr(usernameHash).call(), utils.zeroAddress, "Resolved address not set"); + const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call(); + const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]); + assert.equal(pubKey, contactCode, "pubKey does not match contract code"); + }); + it('should register username with status contact code and address', async () => { + const registrant = accountsArr[2]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + const username = 'bob2auto'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82'; + const points = utils.generateXY(contactCode); + const label = web3Utils.sha3(username); + const registerCall = UsernameRegistrar.methods.register( + label, + registrant, + points.x, + points.y + ).encodeABI(); + + const approveAndCallResult = await TestToken.methods.approveAndCall(UsernameRegistrar.address, registry.price, registerCall).send({from: registrant}); + // TODO: check events + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined"); + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance"); + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch"); + assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set"); + const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call(); + const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]); + assert.equal(pubKey, contactCode, "pubKey does not match contract code"); + }); + }); + + describe('release()', function() { + it('should not release username due delay', async () => { + let registrant = accountsArr[6]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + let username = 'mistaker'; + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + let failed; + try{ + await UsernameRegistrar.methods.release( + web3Utils.sha3(username), + ).send({from: registrant}); + failed = false; + } catch(e){ + failed = true; + } + assert(failed, "Released after delay period"); + }); + it('should release username', async () => {; + const registrant = accountsArr[6]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + const username = 'frank'; + const label = web3Utils.sha3(username); + await UsernameRegistrar.methods.register( + label, + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + const releaseDelay = await UsernameRegistrar.methods.releaseDelay().call(); + await utils.increaseTime(releaseDelay) + await utils.increaseTime(1000) + const initialAccountBalance = await UsernameRegistrar.methods.getAccountBalance(label).call(); + const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call(); + const initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call(); + await utils.increaseTime(1000) + const resultRelease = await UsernameRegistrar.methods.release( + web3Utils.sha3(username), + + ).send({from: registrant}); + //TODO: check events + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), 0, "Final balance didnt zeroed"); + assert.equal(await TestToken.methods.balanceOf(registrant).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "Releaser token balance didnt increase") + assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease") + }); + it('should release transfered username', async () => { + let registrant = accountsArr[7]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + let username = 'grace'; + let usernameHash = namehash.hash(username + '.' + registry.registry); + let label = web3Utils.sha3(username); + let newOwner = accountsArr[8]; + await UsernameRegistrar.methods.register( + label, + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + await ens.methods.setOwner(usernameHash, newOwner).send({from: registrant}); + let releaseDelay = await UsernameRegistrar.methods.releaseDelay().call(); + await utils.increaseTime(releaseDelay) + await utils.increaseTime(1000) + let initialAccountBalance = await UsernameRegistrar.methods.getAccountBalance(label).call(); + let initialRegistrantBalance = await TestToken.methods.balanceOf(newOwner).call(); + let initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call(); + await utils.increaseTime(1000) + let resultRelease = await UsernameRegistrar.methods.release( + label + ).send({from: newOwner}); + //TODO: check events + assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), 0, "Final balance didnt zeroed"); + assert.equal(await TestToken.methods.balanceOf(newOwner).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "New owner token balance didnt increase") + assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease") + }); + it('should release moved username account balance by owner', async () => { + const registrant = accountsArr[5]; + UsernameRegistrar + UpdatedUsernameRegistrar + await TestToken.methods.mint(dummyRegistry.price).send({from: registrant}); + await DummyUsernameRegistrar.methods.activate(dummyRegistry.price).send({from: accountsArr[0]}); + await TestToken.methods.approve(DummyUsernameRegistrar.address, dummyRegistry.price).send({from: registrant}); + + const username = 'hardhead'; + const label = web3Utils.sha3(username); + const usernameHash = namehash.hash(username + '.' + dummyRegistry.registry); + await DummyUsernameRegistrar.methods.register( + label, + registrant, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + let initialAccountBalance = await DummyUsernameRegistrar.methods.getAccountBalance(label).call(); + const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call(); + const initialRegistryBalance = await TestToken.methods.balanceOf(DummyUsernameRegistrar.address).call(); + await DummyUsernameRegistrar.methods.moveRegistry(UpdatedDummyUsernameRegistrar.address).send(); + + assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch"); + assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined"); + assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set"); + + const resultRelease = await DummyUsernameRegistrar.methods.release( + label + ).send({from: registrant}); + //TODO: verify events + assert.equal(await TestToken.methods.balanceOf(registrant).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "New owner token balance didnt increase") + assert.equal(await TestToken.methods.balanceOf(DummyUsernameRegistrar.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease") + assert.equal(await ens.methods.resolver(usernameHash).call(), utils.zeroAddress, "Resolver not undefined"); + assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress, "Owner not removed"); + //We are not cleaning PublicResolver or any resolver, so the value should remain the same. + assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set"); + }); + }); + + describe('updateAccountOwner()', function() { + it('should update username account owner', async () => { + let username = 'heidi'; + let label = web3Utils.sha3(username); + let registrant = accountsArr[8]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + let newOwner = accountsArr[9]; + let usernameHash = namehash.hash(username + '.' + registry.registry); + + await UsernameRegistrar.methods.register( + label, + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + await ens.methods.setOwner(usernameHash, newOwner).send({from: registrant}); + let resultUpdateOwner = await UsernameRegistrar.methods.updateAccountOwner( + label + ).send({from: newOwner}); + //TODO: check events + assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), newOwner, "Backup owner not updated"); + }); + }); + + describe('slashInvalidUsername()', function() { + it('should slash invalid username', async () => { + let username = 'alicé'; + let label = web3Utils.sha3(username); + let usernameHash = namehash.hash(username + '.' + registry.registry); + let registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant); + assert.notEqual(await UsernameRegistrar.methods.getCreationTime(label).call(), 0); + await UsernameRegistrar.methods.slashInvalidUsername(web3Utils.toHex(username), 4).send() + //TODO: check events + assert.equal(await UsernameRegistrar.methods.getCreationTime(label).call(), 0); + assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress); + }); + it('should not slash valid username', async () => { + const username = 'legituser'; + const registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + let failed; + try{ + await UsernameRegistrar.methods.slashInvalidUsername(web3Utils.toHex(username), 4).send() + failed = false; + } catch(e){ + failed = true; + } + assert(failed, "Was slashed anyway"); + }); + }); + + describe('slashReservedUsername()', function() { + it('should not slash not reserved name username', async () => { + const username = 'somedummyname123'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant); + let failed; + try{ + await UsernameRegistrar.methods.slashReservedUsername(web3Utils.toHex(username), merkleTree.getHexProof(reservedNames[0])).send() + failed = false; + } catch(e){ + failed = true; + } + assert(failed, "Was slashed anyway"); + }); + it('should not slash reserved name username with wrong proof ', async () => { + const username = reservedNames[5]; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant); + let failed; + try{ + await UsernameRegistrar.methods.slashReservedUsername(web3Utils.toHex(username), merkleTree.getHexProof(reservedNames[1])).send() + failed = false; + } catch(e){ + failed = true; + } + assert(failed, "Was slashed anyway"); + }); + it('should slash reserved name username', async () => { + const username = reservedNames[7]; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant); + result = await UsernameRegistrar.methods.slashReservedUsername(web3Utils.toHex(username), merkleTree.getHexProof(username)).send() + //TODO: check events + assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress); + }); + }); + + describe('slashSmallUsername()', function() { + it('should not slash big username', async() =>{ + let username = '1234567890'; + let registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + let failed; + try{ + await UsernameRegistrar.methods.slashSmallUsername(web3Utils.toHex(username)).send() + failed = false; + } catch(e){ + failed = true; + } + assert(failed, "Was slashed anyway"); + }) + it('should slash small username', async () => { + let username = 'a'; + let usernameHash = namehash.hash(username + '.' + registry.registry); + let registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant); + result = await UsernameRegistrar.methods.slashSmallUsername(web3Utils.toHex(username)).send() + assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress); + }); + }); + + describe('slashAddressLikeUsername()', function() { + it('should slash username that starts with 0x and is 12 of lenght or bigger', async () => { + let username = "0xc6b95bd26123"; + let userlabelHash = "0xe311c0592b075c30277c679f0daea74ee1727547efa522fd28d20a8f2c3e435b"; //sha3("0xc6b95bd26123") + let usernameHash = "0xb707907c2749895522150dd9e6dec4f71f1662ce873f8bf0b8682fa052ff495e"; //namehash("0xc6b95bd26123.stateofus.eth") + let registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + userlabelHash, + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant); + result = await UsernameRegistrar.methods.slashAddressLikeUsername(web3Utils.toHex(username)).send() + assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress); + }); + it('should not slash username that starts with 0x but is smaller then 12', async () => { + let username = "0xc6b95bd26"; + let userlabelHash = "0x59bf8d16c517a40a5dacc3471abd002f3bc0850a13e930e4bee49070a58517e8"; //sha3("0xc6b95bd26") + let registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + userlabelHash, + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + let failed; + try{ + result = await UsernameRegistrar.methods.slashAddressLikeUsername(username).send() + failed = false; + } catch(e){ + failed = true; + } + assert(failed, "Was slashed anyway"); + }); + it('should not slash username that dont starts 0x and is bigger than 12', async () => { + const username = "0a002322c6b95bd26"; + const userlabelHash = "0xe4769e5c31ff61ac50dce20559a4411a4ca45d94c733cbeda7ab9f28ed75cef1"; //sha3("0a002322c6b95bd26") + const registrant = accountsArr[1]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + userlabelHash, + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + let failed; + try{ + await UsernameRegistrar.methods.slashAddressLikeUsername(username).send() + failed = false; + } catch(e){ + failed = true; + } + assert(failed, "Was slashed anyway"); + }); + }); + describe('slashUsername()', function() { + it('should slash a username and get funds from registrant', async () => { + const username = 'b'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const registrant = accountsArr[1]; + const slasher = accountsArr[2]; + await TestToken.methods.mint(registry.price).send({from: registrant}); + await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); + await UsernameRegistrar.methods.register( + web3Utils.sha3(username), + utils.zeroAddress, + utils.zeroBytes32, + utils.zeroBytes32 + ).send({from: registrant}); + assert.equal(await ens.methods.owner(usernameHash).call(), registrant); + const initialSlasherBalance = await TestToken.methods.balanceOf(slasher).call(); + await UsernameRegistrar.methods.slashSmallUsername(web3Utils.toHex(username)).send({from: slasher}) + //TODO: check events + assert.equal(await TestToken.methods.balanceOf(slasher).call(), (+initialSlasherBalance)+(+registry.price)); + assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress); + }); + }); + + describe('moveRegistry()', function() { + it('should move registry to new registry and migrate', async () => { + const result = await UsernameRegistrar.methods.moveRegistry(UpdatedUsernameRegistrar.address).send(); + //TODO: check events + assert.equal(await ens.methods.owner(registry.namehash).call(), UpdatedUsernameRegistrar.address, "registry ownership not moved correctly") + assert.equal(await UpdatedUsernameRegistrar.methods.getPrice().call(), registry.price, "updated registry didnt migrated price") + }); + }); + + describe('moveAccount()', function() { + it('should move username to new registry by account owner', async () => { + const registrant = accountsArr[5]; + const username = 'erin'; + const usernameHash = namehash.hash(username + '.' + registry.registry); + const label = web3Utils.sha3(username); + + const accountBalance = await UsernameRegistrar.methods.getAccountBalance(label).call() + assert.notEqual(accountBalance, 0); + const initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call(); + const initialUpdatedRegistryBalance = await TestToken.methods.balanceOf(UpdatedUsernameRegistrar.address).call(); + const creationTime = await UsernameRegistrar.methods.getCreationTime(label).call(); + assert.notEqual(creationTime, 0); + assert.equal(await UpdatedUsernameRegistrar.methods.getCreationTime(label).call(), 0); + const result = await UsernameRegistrar.methods.moveAccount(label).send({from: registrant}); + assert.equal(await UsernameRegistrar.methods.getCreationTime(label).call(), 0); + assert.equal(await UpdatedUsernameRegistrar.methods.getCreationTime(label).call(), creationTime); + assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)-(+accountBalance)) + assert.equal(await TestToken.methods.balanceOf(UpdatedUsernameRegistrar.address).call(), (+initialUpdatedRegistryBalance)+(+accountBalance)) + }); + }); + +});