Removes multiple domain functionaility from project (#20)

* refactor ENSSubdomainRegistry

* Rename contract

* remove obsolete file

* typo

* Add legacy contract upgradability adaptor

* refactor Dapp to use UsernameRegistrar contract

* use reservedNames.js in usernameregistrar.spec.js

* change array of merkle roots to single merkle root

* load reserved words to deploy contract

* reorganized contract methods

* add withdraw methods for controller move excess funds & wrong ens nodes

* add deployed MerkleProof addresses

* include contract terms in the @notice of register

* approve and call fix & solidity update

* add approveAndCall support

* clear subnode owner and resolver
This commit is contained in:
Ricardo Guilherme Schmidt 2018-09-04 18:49:39 -03:00 committed by GitHub
parent ad432139d0
commit cbfdd9cc72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1588 additions and 1556 deletions

View File

@ -19,4 +19,4 @@ Usage:
| token/ERC20Token | No | Yes | Yes | | token/ERC20Token | No | Yes | Yes |
| ens/ENSRegistry | Yes | Yes | No | | ens/ENSRegistry | Yes | Yes | No |
| ens/PublicRegistry | Yes | Yes | No | | ens/PublicRegistry | Yes | Yes | No |
| registry/ENSSubdomainRegistry | Yes | Yes | No | | registry/UsernameRegistrar | Yes | Yes | Yes |

View File

@ -1,5 +1,5 @@
import ERC20Token from 'Embark/contracts/ERC20Token' 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 TestToken from 'Embark/contracts/TestToken'
import { getDefaultAccount } from '../utils/web3Helpers' import { getDefaultAccount } from '../utils/web3Helpers'
@ -42,7 +42,7 @@ export const checkAndDispatchStatusContactCode = dispatch => {
export const fetchAndDispatchSNTAllowance = dispatch => { export const fetchAndDispatchSNTAllowance = dispatch => {
const { methods: { allowance } } = TestToken; const { methods: { allowance } } = TestToken;
const { receiveSntAllowance } = accountActions; const { receiveSntAllowance } = accountActions;
const spender = ENSSubdomainRegistry._address; const spender = UsernameRegistrar._address;
allowance(getDefaultAccount(), spender) allowance(getDefaultAccount(), spender)
.call() .call()
.then(allowance => { .then(allowance => {

View File

@ -1,4 +1,4 @@
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import web3 from 'web3'; import web3 from 'web3';
import ENSRegistry from 'Embark/contracts/ENSRegistry'; import ENSRegistry from 'Embark/contracts/ENSRegistry';
import React from 'react'; import React from 'react';
@ -11,16 +11,16 @@ import { debounce } from 'lodash/fp';
const { methods: { owner } } = ENSRegistry; const { methods: { owner } } = ENSRegistry;
const delay = debounce(500); const delay = debounce(500);
const getDomain = (hashedDomain, domains) => domains(hashedDomain).call(); const getRegistry = (hashedRegistry, registrys) => registrys(hashedRegistry).call();
const registryIsOwner = address => address == ENSSubdomainRegistry._address; const registryIsOwner = address => address == UsernameRegistrar._address;
const fetchOwner = domainName => owner(hash(domainName)).call(); const fetchOwner = registryName => owner(hash(registryName)).call();
const debounceFetchOwner = delay(fetchOwner); const debounceFetchOwner = delay(fetchOwner);
const getAndIsOwner = async domainName => { const getAndIsOwner = async registryName => {
const address = await debounceFetchOwner(domainName); const address = await debounceFetchOwner(registryName);
return registryIsOwner(address); return registryIsOwner(address);
} }
const fetchDomain = delay(getDomain); const fetchRegistry = delay(getRegistry);
const setPrice = (domainFn, hashedDomain, price) => domainFn(hashedDomain, price || 0).send(); const setPrice = (registryFn, price) => registryFn(price || 0).send();
const InnerForm = ({ const InnerForm = ({
values, values,
@ -33,53 +33,50 @@ const InnerForm = ({
}) => ( }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<FieldGroup <FieldGroup
id="domainName" id="registryName"
name="domainName" name="registryName"
type="text" type="text"
label="Domain Name" label="Registry Name"
onChange={handleChange} onChange={handleChange}
onBlur={handleBlur} onBlur={handleBlur}
value={values.domainName} value={values.registryName}
error={errors.domainName} error={errors.registryName}
/> />
<FieldGroup <FieldGroup
id="domainPrice" id="registryPrice"
name="domainPrice" name="registryPrice"
type="number" type="number"
label="Domain Price" label="Registry Price"
placeholder="(Optional) Domain will be free if left blank" placeholder="(Optional) Registry will be free if left blank"
onChange={handleChange} onChange={handleChange}
onBlur={handleBlur} onBlur={handleBlur}
value={values.domainPrice} value={values.registryPrice}
/> />
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button> <Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
</form> </form>
) )
const AddDomain = withFormik({ const AddRegistry = withFormik({
mapPropsToValues: props => ({ domainName: '', domainPrice: '' }), mapPropsToValues: props => ({ registryName: '', registryPrice: '' }),
async validate(values) { async validate(values) {
const { domainName } = values; const { registryName } = values;
const errors = {}; const errors = {};
if (!domainName) errors.domainName = 'Required'; if (!registryName) errors.registryName = 'Required';
if (domainName && !await getAndIsOwner(domainName)) errors.domainName = 'This domain is not owned by registry'; if (registryName && !await getAndIsOwner(registryName)) errors.registryName = 'This registry is not owned by registry';
if (Object.keys(errors).length) throw errors; if (Object.keys(errors).length) throw errors;
}, },
async handleSubmit(values, { setSubmitting }) { async handleSubmit(values, { setSubmitting }) {
const { domainName, domainPrice } = values; const { registryName, registryPrice } = values;
const { methods: { domains, setDomainPrice, updateDomainPrice } } = ENSSubdomainRegistry; const { methods: { state, activate, updateRegistryPrice } } = UsernameRegistrar;
const hashedDomain = hash(domainName); const { registryState } = await state();
const { state } = await getDomain(hashedDomain, domains);
console.log( console.log(
'Inputs for setPrice', 'Inputs for setPrice',
Number(state) ? 'updateDomainPrice' : 'setDomainPrice', Number(registryState) ? 'updateRegistryPrice' : 'activate',
hashedDomain, web3.utils.toWei(registryPrice.toString(), 'ether'),
web3.utils.toWei(domainPrice.toString(), 'ether'),
); );
setPrice( setPrice(
Number(state) ? updateDomainPrice : setDomainPrice, Number(registryState) ? updateRegistryPrice : activate,
hashedDomain, web3.utils.toWei(registryPrice.toString(), 'ether'),
web3.utils.toWei(domainPrice.toString(), 'ether'),
) )
.then(res => { .then(res => {
setSubmitting(false); setSubmitting(false);
@ -92,4 +89,4 @@ const AddDomain = withFormik({
} }
})(InnerForm); })(InnerForm);
export default AddDomain; export default AddRegistry;

View File

@ -2,7 +2,7 @@ export default {
release: { release: {
title: { title: {
sub: 'Done!', 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.' subheading: 'Follow the progress in the Transaction History section of your wallet.'
}, },

View File

@ -1,7 +1,6 @@
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import web3 from 'web3'; import web3 from 'web3';
import React from 'react'; import React from 'react';
import { hash } from 'eth-ens-namehash';
import { Button } from 'react-bootstrap'; import { Button } from 'react-bootstrap';
import FieldGroup from '../standard/FieldGroup'; import FieldGroup from '../standard/FieldGroup';
import { withFormik } from 'formik'; import { withFormik } from 'formik';
@ -25,16 +24,6 @@ const InnerForm = ({
value={values.newAddress} value={values.newAddress}
error={errors.newAddress} error={errors.newAddress}
/> />
<FieldGroup
id="domainName"
name="domainName"
type="text"
label="Domain Name"
onChange={handleChange}
onBlur={handleBlur}
value={values.domainName}
error={errors.domainName}
/>
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button> <Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
</form> </form>
) )
@ -49,16 +38,14 @@ const MoveDomain = withFormik({
if (Object.keys(errors).length) throw errors; if (Object.keys(errors).length) throw errors;
}, },
async handleSubmit(values, { setSubmitting }) { async handleSubmit(values, { setSubmitting }) {
const { newAddress, domainName } = values; const { newAddress } = values;
const { methods: { moveDomain } } = ENSSubdomainRegistry; const { methods: { moveDomain } } = UsernameRegistrar;
const hashedDomain = hash(domainName);
console.log( console.log(
`inputs for moveDomain of domain name: ${domainName}`, `inputs for moveDomain:}`,
newAddress, newAddress
hashedDomain,
); );
moveDomain(newAddress, hashedDomain) moveDomain(newAddress)
.send() .send()
.then((res) => { .then((res) => {
setSubmitting(false); setSubmitting(false);

View File

@ -7,7 +7,7 @@ import { hash } from 'eth-ens-namehash';
import { isNil } from 'lodash'; import { isNil } from 'lodash';
import Hidden from '@material-ui/core/Hidden'; import Hidden from '@material-ui/core/Hidden';
import Typography from '@material-ui/core/Typography'; 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 ENSRegistry from 'Embark/contracts/ENSRegistry';
import { Button, Field, TextInput, MobileSearch, MobileButton, Card, Info, Text } from '../../ui/components' import { Button, Field, TextInput, MobileSearch, MobileButton, Card, Info, Text } from '../../ui/components'
import { IconCheck } from '../../ui/icons' 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 EnsLogo from '../../ui/icons/logos/ens.png';
import { formatPrice } from '../ens/utils'; import { formatPrice } from '../ens/utils';
import CheckCircle from '../../ui/icons/components/baseline_check_circle_outline.png'; 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 NotInterested from '@material-ui/icons/NotInterested';
import Face from '@material-ui/icons/Face'; import Face from '@material-ui/icons/Face';
import Copy from './copy'; 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 formatName = domainName => domainName.includes('.') ? normalizer.normalize(domainName) : normalizer.normalize(`${domainName}.stateofus.eth`);
const getDomain = fullDomain => formatName(fullDomain).split('.').slice(1).join('.'); const getDomain = fullDomain => formatName(fullDomain).split('.').slice(1).join('.');
const hashedDomain = domainName => hash(getDomain(domainName)); const hashedDomain = domainName => hash(getDomain(domainName));
const registryIsOwner = address => address == ENSSubdomainRegistry._address; const registryIsOwner = address => address == UsernameRegistrar._address;
const { soliditySha3, fromWei } = web3.utils; const { soliditySha3, fromWei } = web3.utils;
@ -123,8 +123,7 @@ class RenderAddresses extends PureComponent {
if (!isNil(value)) { if (!isNil(value)) {
this.setState({ submitted: true }) this.setState({ submitted: true })
release( release(
soliditySha3(domainName), soliditySha3(domainName)
hash('stateofus.eth'),
) )
.send() .send()
} else { } else {
@ -205,7 +204,7 @@ class Register extends PureComponent {
componentDidMount() { componentDidMount() {
const { domainName } = this.props; const { domainName } = this.props;
getPrice(hashedDomain(domainName)) getPrice()
.call() .call()
.then((res) => { this.setState({ domainPrice: res })}); .then((res) => { this.setState({ domainPrice: res })});
} }

View File

@ -1,5 +1,6 @@
import web3 from "Embark/web3" 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 TestToken from 'Embark/contracts/TestToken';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -60,7 +61,7 @@ const InnerForm = ({
mode="strong" mode="strong"
style={{ marginTop: '5px' }} style={{ marginTop: '5px' }}
onClick={() => { onClick={() => {
ENSSubdomainRegistry.methods.getPrice(hash(values.domainName)) UsernameRegistrar.methods.getPrice()
.call() .call()
.then((res) => { setFieldValue('price', fromWei(res)); }); .then((res) => { setFieldValue('price', fromWei(res)); });
}} }}
@ -126,7 +127,7 @@ const InnerForm = ({
<div style={{ position: 'relative', left: 0, right: 0, bottom: 0 }}> <div style={{ position: 'relative', left: 0, right: 0, bottom: 0 }}>
{!Number(SNTAllowance) || (Number(domainPrice) && !Number(SNTBalance)) ? <TokenPermissions {!Number(SNTAllowance) || (Number(domainPrice) && !Number(SNTBalance)) ? <TokenPermissions
symbol="SNT" symbol="SNT"
spender={ENSSubdomainRegistry.address} spender={UsernameRegistrar.address}
methods={TestToken.methods} methods={TestToken.methods}
mobile mobile
/> />
@ -151,7 +152,8 @@ const RegisterSubDomain = withFormik({
const { editAccount, preRegisteredCallback } = props; const { editAccount, preRegisteredCallback } = props;
const { address, statusAddress } = values; const { address, statusAddress } = values;
const { subDomain, domainName, registeredCallbackFn } = props || 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 subdomainHash = soliditySha3(subDomain);
const domainNameHash = hash(domainName); const domainNameHash = hash(domainName);
const resolveToAddr = address || zeroAddress; const resolveToAddr = address || zeroAddress;
@ -162,7 +164,6 @@ const RegisterSubDomain = withFormik({
const funcsToSend = []; const funcsToSend = [];
const args = [ const args = [
subdomainHash, subdomainHash,
domainNameHash,
resolveToAddr, resolveToAddr,
points ? points.x : zeroBytes32, points ? points.x : zeroBytes32,
points ? points.y : zeroBytes32, points ? points.y : zeroBytes32,

View File

@ -1,6 +1,6 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap'; 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 web3Utils from 'web3-utils'
import { hash } from 'eth-ens-namehash' import { hash } from 'eth-ens-namehash'
@ -12,10 +12,10 @@ const dispatchSetup = (ENSRegistry) => {
setSubnodeOwner(zeroBytes32, sha3('eth'), getUserAddress(ENSRegistry)) setSubnodeOwner(zeroBytes32, sha3('eth'), getUserAddress(ENSRegistry))
.send() .send()
.then(res => { console.log(res) }) .then(res => { console.log(res) })
setSubnodeOwner(hash('eth'), sha3('stateofus'), ENSSubdomainRegistry._address) setSubnodeOwner(hash('eth'), sha3('stateofus'), UsernameRegistrar._address)
.send() .send()
.then(res => { console.log(res) }) .then(res => { console.log(res) })
setSubnodeOwner(hash('eth'), sha3('stateofus'), ENSSubdomainRegistry._address) setSubnodeOwner(hash('eth'), sha3('stateofus'), UsernameRegistrar._address)
.send() .send()
.then(res => { console.log(res) }) .then(res => { console.log(res) })
} }

View File

@ -1,4 +1,4 @@
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import web3 from 'web3'; import web3 from 'web3';
import React from 'react'; import React from 'react';
import { Button } from 'react-bootstrap'; import { Button } from 'react-bootstrap';
@ -39,7 +39,7 @@ const UpdateController = withFormik({
}, },
async handleSubmit(values, { setSubmitting }) { async handleSubmit(values, { setSubmitting }) {
const { newAddress } = values; const { newAddress } = values;
const { methods: { changeController } } = ENSSubdomainRegistry; const { methods: { changeController } } = UsernameRegistrar;
changeController(newAddress) changeController(newAddress)
.send() .send()
.then((res) => { .then((res) => {

View File

@ -1,6 +1,6 @@
import EmbarkJS from 'Embark/EmbarkJS'; import EmbarkJS from 'Embark/EmbarkJS';
import ENSRegistry from 'Embark/contracts/ENSRegistry'; 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 TestToken from 'Embark/contracts/TestToken';
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap'; import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
@ -24,7 +24,7 @@ const ENSSubManagement = props => (
<h2 style={{ textAlign: 'center' }}>Subdomain Management</h2> <h2 style={{ textAlign: 'center' }}>Subdomain Management</h2>
<h3>Change Registry Controller</h3> <h3>Change Registry Controller</h3>
<UpdateController /> <UpdateController />
<h3>Add/Update Domain Price</h3> <h3>Activate Registry/Update Registry Price</h3>
<AddDomain /> <AddDomain />
<h3>Move Domain To Another Registry</h3> <h3>Move Domain To Another Registry</h3>
<MoveDomain /> <MoveDomain />
@ -34,7 +34,7 @@ const ENSSubManagement = props => (
<hr/> <hr/>
<TokenPermissions <TokenPermissions
symbol='SNT' symbol='SNT'
spender={ENSSubdomainRegistry._address} spender={UsernameRegistrar._address}
methods={TestToken.methods} /> methods={TestToken.methods} />
<hr/> <hr/>
<SetupENS ENSRegistry={ENSRegistry} /> <SetupENS ENSRegistry={ENSRegistry} />

View File

@ -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 (<React.Fragment>
<h2> Subdomain management</h2>
<Form inline>
<FormGroup>
<FormControl
type="text"
onChange={(e) => this.update_username(e)} />
<FormControl
type="text"
onChange={(e) => this.update_address(e)} />
<FormControl
type="text"
onChange={(e) => this.update_pubkey(e) } />
<Button bsStyle="primary" onClick={(e) => this.register(e)}>Register Subdomain</Button>
<Button bsStyle="primary" onClick={(e) => this.updateResolver(e)}>Update Resolver</Button>
<Button bsStyle="primary" onClick={(e) => this.updateFundsOwner(e)}>Update Funds Owner</Button>
<Button bsStyle="primary" onClick={(e) => this.moveSubdomain(e)}>Update Account</Button>
<Button bsStyle="primary" onClick={(e) => this.releaseSubdomain(e)}>Release Subdomain</Button>
</FormGroup>
</Form>
<h3> Contract Calls </h3>
<p>Javascript calls being made: </p>
<div className="logs">
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default ENSSubdomainRegistryUI;

View File

@ -10,7 +10,7 @@ import TestTokenUI from './components/testtoken';
import ERC20TokenUI from './components/erc20token'; import ERC20TokenUI from './components/erc20token';
import TestToken from 'Embark/contracts/TestToken'; import TestToken from 'Embark/contracts/TestToken';
import ENSSubManagement from './components/ensSubManagement'; import ENSSubManagement from './components/ensSubManagement';
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'; import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
import NameLookup from './components/ens/nameLookup'; import NameLookup from './components/ens/nameLookup';
import AdminMode from './components/AdminMode'; import AdminMode from './components/AdminMode';
import TokenPermissions from './components/standard/TokenPermissionConnect'; import TokenPermissions from './components/standard/TokenPermissionConnect';
@ -66,14 +66,14 @@ class App extends React.Component {
</div> </div>
</Fade>} </Fade>}
{searching && <Fade in={searching}> {searching && <Fade in={searching}>
<Web3Render ready={isRopsten} network="ropsten"> <Web3Render ready={network == 'private'} network={network}>
<div> <div>
<NameLookup /> <NameLookup />
<Hidden mdDown> <Hidden mdDown>
<div style={{ textAlign: 'center', margin: '0px 40px' }}> <div style={{ textAlign: 'center', margin: '0px 40px' }}>
<TokenPermissions <TokenPermissions
symbol={symbols[network] || 'SNT'} symbol={symbols[network] || 'SNT'}
spender={ENSSubdomainRegistry._address} spender={UsernameRegistrar._address}
methods={TestToken.methods} /> methods={TestToken.methods} />
<hr/> <hr/>
<Toggle onChange={() => { this.setState({ admin: !admin })}} /> <Toggle onChange={() => { this.setState({ admin: !admin })}} />

View File

@ -27,13 +27,15 @@ module.exports = {
"args": ["$ENSRegistry"], "args": ["$ENSRegistry"],
"deploy": true "deploy": true
}, },
"ENSSubdomainRegistry": {
"UsernameRegistrar": {
"args": [ "args": [
"$TestToken", "$TestToken",
"$ENSRegistry", "$ENSRegistry",
"$PublicResolver", "$PublicResolver",
"3", "0x5f7791d31ca0493e9ca7c9ca16695ecd9d5044768674d14d31ab5d8277518fff",
[merkleRoot], 3,
merkleTree.getHexRoot(),
"0x9e183BC54Bb4f3cCa1A478CA6f2c3EdC37B60478" "0x9e183BC54Bb4f3cCa1A478CA6f2c3EdC37B60478"
] ]
} }
@ -56,18 +58,18 @@ module.exports = {
"$ENSRegistry" "$ENSRegistry"
] ]
}, },
"ENSSubdomainRegistry": { "UsernameRegistrar": {
"args": [ "args": [
"$TestToken", "$TestToken",
"$ENSRegistry", "$ENSRegistry",
"$PublicResolver", "$PublicResolver",
"3", "0x5f7791d31ca0493e9ca7c9ca16695ecd9d5044768674d14d31ab5d8277518fff",
[merkleRoot], 3,
merkleTree.getHexRoot(),
"0x0" "0x0"
], ],
"onDeploy": [ "onDeploy": [
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', ENSSubdomainRegistry.address).send()", "ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', UsernameRegistrar.address).send()"
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x7b4768a525e733422bf968587a91b4036e5176d36f44a9fb5b29d0bca03ab3a3', ENSSubdomainRegistry.address).send()"
] ]
} }
} }
@ -83,7 +85,7 @@ module.exports = {
"TestToken": { "TestToken": {
"address": "0x744d70fdbe2ba4cf95131626614a1763df805b9e" "address": "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
}, },
"ENSSubdomainRegistry": { "UsernameRegistrar": {
"address": "0xDBf9038cf5Aaa030890790dB87E746E00Fc352b3" "address": "0xDBf9038cf5Aaa030890790dB87E746E00Fc352b3"
}, },
"ERC20Receiver": { "deploy": false }, "ERC20Receiver": { "deploy": false },
@ -93,6 +95,7 @@ module.exports = {
"MerkleProofWrapper": { "MerkleProofWrapper": {
"address": "0x76E55E13C5891a90f7fCA2e1238a6B3463F564e2" "address": "0x76E55E13C5891a90f7fCA2e1238a6B3463F564e2"
}, },
"ERC20Receiver": { "deploy": false },
"SafeMath": { "SafeMath": {
"address": "0xA115a57952D3337e2a1aB3Cb82bA376EEcDDc469" "address": "0xA115a57952D3337e2a1aB3Cb82bA376EEcDDc469"
} }
@ -118,7 +121,7 @@ module.exports = {
"MerkleProofWrapper": { "MerkleProofWrapper": {
"address": "0x58E01078d14142E0370526dFdAE44E4f508c844B" "address": "0x58E01078d14142E0370526dFdAE44E4f508c844B"
}, },
"ENSSubdomainRegistry": { "UsernameRegistrar": {
"address": "0x028F3Df706c5295Ba283c326F4692c375D14cb68" "address": "0x028F3Df706c5295Ba283c326F4692c375D14cb68"
}, },
"ERC20Receiver": { "deploy": false } "ERC20Receiver": { "deploy": false }

View File

@ -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"
}
}
}
}

View File

@ -1,6 +1,4 @@
exports.ReservedUsernames = [ exports.reservedNames = [
//Ethereum reserved words //Ethereum reserved words
'eth', 'eth',
'ether', 'ether',

View File

@ -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;
}
}

View File

@ -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 changee.g. Status makes contract upgradesthe 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));
}
}

View File

@ -1,5 +0,0 @@
pragma solidity ^0.4.11;
contract ApprovalReceiver {
function receiveApproval(address from, uint value, address tokenContract, bytes extraData) returns (bool);
}

View File

@ -0,0 +1,5 @@
pragma solidity ^0.4.11;
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.23; pragma solidity ^0.4.24;
import "./ERC20Token.sol"; import "./ERC20Token.sol";
@ -20,22 +20,14 @@ contract StandardToken is ERC20Token {
return transfer(msg.sender, _to, _value); return transfer(msg.sender, _to, _value);
} }
/** function approve(
* @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender. address _to,
* @param _spender The address which will spend the funds. uint256 _value
* @param _value The amount of tokens to be spent. )
*/ external
function approve(address _spender, uint256 _value) returns (bool) { returns (bool success)
{
// To change the approve amount you first have to reduce the addresses` return approve(msg.sender, _to, _value);
// 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 transferFrom( function transferFrom(
@ -80,6 +72,25 @@ contract StandardToken is ERC20Token {
return supply; 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( function mint(
address _to, address _to,
uint256 _amount uint256 _amount

View File

@ -1,7 +1,7 @@
pragma solidity ^0.4.23; pragma solidity ^0.4.24;
import "./StandardToken.sol"; import "./StandardToken.sol";
import './ApprovalReceiver.sol'; import "./ApproveAndCallFallBack.sol";
/** /**
* @notice ERC20Token for test scripts, can be minted by anyone. * @notice ERC20Token for test scripts, can be minted by anyone.
@ -18,9 +18,13 @@ contract TestToken is StandardToken {
mint(msg.sender, _amount); mint(msg.sender, _amount);
} }
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { function approveAndCall(address _spender, uint256 _value, bytes _extraData)
assert(approve(_spender, _value)); external
return ApprovalReceiver(_spender).receiveApproval(msg.sender, _value, this, _extraData); returns (bool success)
{
approve(msg.sender, _spender, _value);
ApproveAndCallFallBack(_spender).receiveApproval(msg.sender, _value, this, _extraData);
return true;
} }
} }

View File

@ -18,6 +18,7 @@
}, },
"homepage": "https://github.com/status-im/contracts#readme", "homepage": "https://github.com/status-im/contracts#readme",
"dependencies": { "dependencies": {
"@babel/runtime": "7.0.0-beta.55",
"@material-ui/core": "^1.2.1", "@material-ui/core": "^1.2.1",
"@material-ui/icons": "^1.1.0", "@material-ui/icons": "^1.1.0",
"bignumber.js": "^5.0.0", "bignumber.js": "^5.0.0",

View File

@ -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))
});
});
});

View File

@ -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))
});
});
});