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:
parent
ad432139d0
commit
cbfdd9cc72
|
@ -19,4 +19,4 @@ Usage:
|
|||
| token/ERC20Token | No | Yes | Yes |
|
||||
| ens/ENSRegistry | Yes | Yes | No |
|
||||
| ens/PublicRegistry | Yes | Yes | No |
|
||||
| registry/ENSSubdomainRegistry | Yes | Yes | No |
|
||||
| registry/UsernameRegistrar | Yes | Yes | Yes |
|
|
@ -1,5 +1,5 @@
|
|||
import ERC20Token from 'Embark/contracts/ERC20Token'
|
||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'
|
||||
import TestToken from 'Embark/contracts/TestToken'
|
||||
|
||||
import { getDefaultAccount } from '../utils/web3Helpers'
|
||||
|
@ -42,7 +42,7 @@ export const checkAndDispatchStatusContactCode = dispatch => {
|
|||
export const fetchAndDispatchSNTAllowance = dispatch => {
|
||||
const { methods: { allowance } } = TestToken;
|
||||
const { receiveSntAllowance } = accountActions;
|
||||
const spender = ENSSubdomainRegistry._address;
|
||||
const spender = UsernameRegistrar._address;
|
||||
allowance(getDefaultAccount(), spender)
|
||||
.call()
|
||||
.then(allowance => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||
import web3 from 'web3';
|
||||
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
||||
import React from 'react';
|
||||
|
@ -11,16 +11,16 @@ import { debounce } from 'lodash/fp';
|
|||
const { methods: { owner } } = ENSRegistry;
|
||||
|
||||
const delay = debounce(500);
|
||||
const getDomain = (hashedDomain, domains) => domains(hashedDomain).call();
|
||||
const registryIsOwner = address => address == ENSSubdomainRegistry._address;
|
||||
const fetchOwner = domainName => owner(hash(domainName)).call();
|
||||
const getRegistry = (hashedRegistry, registrys) => registrys(hashedRegistry).call();
|
||||
const registryIsOwner = address => address == UsernameRegistrar._address;
|
||||
const fetchOwner = registryName => owner(hash(registryName)).call();
|
||||
const debounceFetchOwner = delay(fetchOwner);
|
||||
const getAndIsOwner = async domainName => {
|
||||
const address = await debounceFetchOwner(domainName);
|
||||
const getAndIsOwner = async registryName => {
|
||||
const address = await debounceFetchOwner(registryName);
|
||||
return registryIsOwner(address);
|
||||
}
|
||||
const fetchDomain = delay(getDomain);
|
||||
const setPrice = (domainFn, hashedDomain, price) => domainFn(hashedDomain, price || 0).send();
|
||||
const fetchRegistry = delay(getRegistry);
|
||||
const setPrice = (registryFn, price) => registryFn(price || 0).send();
|
||||
|
||||
const InnerForm = ({
|
||||
values,
|
||||
|
@ -33,53 +33,50 @@ const InnerForm = ({
|
|||
}) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<FieldGroup
|
||||
id="domainName"
|
||||
name="domainName"
|
||||
id="registryName"
|
||||
name="registryName"
|
||||
type="text"
|
||||
label="Domain Name"
|
||||
label="Registry Name"
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.domainName}
|
||||
error={errors.domainName}
|
||||
value={values.registryName}
|
||||
error={errors.registryName}
|
||||
/>
|
||||
<FieldGroup
|
||||
id="domainPrice"
|
||||
name="domainPrice"
|
||||
id="registryPrice"
|
||||
name="registryPrice"
|
||||
type="number"
|
||||
label="Domain Price"
|
||||
placeholder="(Optional) Domain will be free if left blank"
|
||||
label="Registry Price"
|
||||
placeholder="(Optional) Registry will be free if left blank"
|
||||
onChange={handleChange}
|
||||
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>
|
||||
</form>
|
||||
)
|
||||
|
||||
const AddDomain = withFormik({
|
||||
mapPropsToValues: props => ({ domainName: '', domainPrice: '' }),
|
||||
const AddRegistry = withFormik({
|
||||
mapPropsToValues: props => ({ registryName: '', registryPrice: '' }),
|
||||
async validate(values) {
|
||||
const { domainName } = values;
|
||||
const { registryName } = values;
|
||||
const errors = {};
|
||||
if (!domainName) errors.domainName = 'Required';
|
||||
if (domainName && !await getAndIsOwner(domainName)) errors.domainName = 'This domain is not owned by registry';
|
||||
if (!registryName) errors.registryName = 'Required';
|
||||
if (registryName && !await getAndIsOwner(registryName)) errors.registryName = 'This registry is not owned by registry';
|
||||
if (Object.keys(errors).length) throw errors;
|
||||
},
|
||||
async handleSubmit(values, { setSubmitting }) {
|
||||
const { domainName, domainPrice } = values;
|
||||
const { methods: { domains, setDomainPrice, updateDomainPrice } } = ENSSubdomainRegistry;
|
||||
const hashedDomain = hash(domainName);
|
||||
const { state } = await getDomain(hashedDomain, domains);
|
||||
const { registryName, registryPrice } = values;
|
||||
const { methods: { state, activate, updateRegistryPrice } } = UsernameRegistrar;
|
||||
const { registryState } = await state();
|
||||
console.log(
|
||||
'Inputs for setPrice',
|
||||
Number(state) ? 'updateDomainPrice' : 'setDomainPrice',
|
||||
hashedDomain,
|
||||
web3.utils.toWei(domainPrice.toString(), 'ether'),
|
||||
Number(registryState) ? 'updateRegistryPrice' : 'activate',
|
||||
web3.utils.toWei(registryPrice.toString(), 'ether'),
|
||||
);
|
||||
setPrice(
|
||||
Number(state) ? updateDomainPrice : setDomainPrice,
|
||||
hashedDomain,
|
||||
web3.utils.toWei(domainPrice.toString(), 'ether'),
|
||||
Number(registryState) ? updateRegistryPrice : activate,
|
||||
web3.utils.toWei(registryPrice.toString(), 'ether'),
|
||||
)
|
||||
.then(res => {
|
||||
setSubmitting(false);
|
||||
|
@ -92,4 +89,4 @@ const AddDomain = withFormik({
|
|||
}
|
||||
})(InnerForm);
|
||||
|
||||
export default AddDomain;
|
||||
export default AddRegistry;
|
||||
|
|
|
@ -2,7 +2,7 @@ export default {
|
|||
release: {
|
||||
title: {
|
||||
sub: 'Done!',
|
||||
body: 'The released domain will be available to other users'
|
||||
body: 'The released username will be available to other users'
|
||||
},
|
||||
subheading: 'Follow the progress in the Transaction History section of your wallet.'
|
||||
},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||
import web3 from 'web3';
|
||||
import React from 'react';
|
||||
import { hash } from 'eth-ens-namehash';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import FieldGroup from '../standard/FieldGroup';
|
||||
import { withFormik } from 'formik';
|
||||
|
@ -25,16 +24,6 @@ const InnerForm = ({
|
|||
value={values.newAddress}
|
||||
error={errors.newAddress}
|
||||
/>
|
||||
<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>
|
||||
</form>
|
||||
)
|
||||
|
@ -49,16 +38,14 @@ const MoveDomain = withFormik({
|
|||
if (Object.keys(errors).length) throw errors;
|
||||
},
|
||||
async handleSubmit(values, { setSubmitting }) {
|
||||
const { newAddress, domainName } = values;
|
||||
const { methods: { moveDomain } } = ENSSubdomainRegistry;
|
||||
const hashedDomain = hash(domainName);
|
||||
const { newAddress } = values;
|
||||
const { methods: { moveDomain } } = UsernameRegistrar;
|
||||
console.log(
|
||||
`inputs for moveDomain of domain name: ${domainName}`,
|
||||
newAddress,
|
||||
hashedDomain,
|
||||
`inputs for moveDomain:}`,
|
||||
newAddress
|
||||
);
|
||||
|
||||
moveDomain(newAddress, hashedDomain)
|
||||
moveDomain(newAddress)
|
||||
.send()
|
||||
.then((res) => {
|
||||
setSubmitting(false);
|
||||
|
|
|
@ -7,7 +7,7 @@ import { hash } from 'eth-ens-namehash';
|
|||
import { isNil } from 'lodash';
|
||||
import Hidden from '@material-ui/core/Hidden';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
||||
import { Button, Field, TextInput, MobileSearch, MobileButton, Card, Info, Text } from '../../ui/components'
|
||||
import { IconCheck } from '../../ui/icons'
|
||||
|
@ -23,7 +23,7 @@ import StatusLogo from '../../ui/icons/components/StatusLogo'
|
|||
import EnsLogo from '../../ui/icons/logos/ens.png';
|
||||
import { formatPrice } from '../ens/utils';
|
||||
import CheckCircle from '../../ui/icons/components/baseline_check_circle_outline.png';
|
||||
const { getPrice, getExpirationTime, release } = ENSSubdomainRegistry.methods;
|
||||
const { getPrice, getExpirationTime, release } = UsernameRegistrar.methods;
|
||||
import NotInterested from '@material-ui/icons/NotInterested';
|
||||
import Face from '@material-ui/icons/Face';
|
||||
import Copy from './copy';
|
||||
|
@ -37,7 +37,7 @@ const validStatusAddress = address => !address.includes(invalidSuffix);
|
|||
const formatName = domainName => domainName.includes('.') ? normalizer.normalize(domainName) : normalizer.normalize(`${domainName}.stateofus.eth`);
|
||||
const getDomain = fullDomain => formatName(fullDomain).split('.').slice(1).join('.');
|
||||
const hashedDomain = domainName => hash(getDomain(domainName));
|
||||
const registryIsOwner = address => address == ENSSubdomainRegistry._address;
|
||||
const registryIsOwner = address => address == UsernameRegistrar._address;
|
||||
const { soliditySha3, fromWei } = web3.utils;
|
||||
|
||||
|
||||
|
@ -123,8 +123,7 @@ class RenderAddresses extends PureComponent {
|
|||
if (!isNil(value)) {
|
||||
this.setState({ submitted: true })
|
||||
release(
|
||||
soliditySha3(domainName),
|
||||
hash('stateofus.eth'),
|
||||
soliditySha3(domainName)
|
||||
)
|
||||
.send()
|
||||
} else {
|
||||
|
@ -205,7 +204,7 @@ class Register extends PureComponent {
|
|||
|
||||
componentDidMount() {
|
||||
const { domainName } = this.props;
|
||||
getPrice(hashedDomain(domainName))
|
||||
getPrice()
|
||||
.call()
|
||||
.then((res) => { this.setState({ domainPrice: res })});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import web3 from "Embark/web3"
|
||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||
import PublicResolver from 'Embark/contracts/PublicResolver';
|
||||
import TestToken from 'Embark/contracts/TestToken';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -60,7 +61,7 @@ const InnerForm = ({
|
|||
mode="strong"
|
||||
style={{ marginTop: '5px' }}
|
||||
onClick={() => {
|
||||
ENSSubdomainRegistry.methods.getPrice(hash(values.domainName))
|
||||
UsernameRegistrar.methods.getPrice()
|
||||
.call()
|
||||
.then((res) => { setFieldValue('price', fromWei(res)); });
|
||||
}}
|
||||
|
@ -126,7 +127,7 @@ const InnerForm = ({
|
|||
<div style={{ position: 'relative', left: 0, right: 0, bottom: 0 }}>
|
||||
{!Number(SNTAllowance) || (Number(domainPrice) && !Number(SNTBalance)) ? <TokenPermissions
|
||||
symbol="SNT"
|
||||
spender={ENSSubdomainRegistry.address}
|
||||
spender={UsernameRegistrar.address}
|
||||
methods={TestToken.methods}
|
||||
mobile
|
||||
/>
|
||||
|
@ -151,7 +152,8 @@ const RegisterSubDomain = withFormik({
|
|||
const { editAccount, preRegisteredCallback } = props;
|
||||
const { address, statusAddress } = values;
|
||||
const { subDomain, domainName, registeredCallbackFn } = props || values;
|
||||
const { methods: { register } } = ENSSubdomainRegistry;
|
||||
const { methods: { register } } = UsernameRegistrar;
|
||||
const { methods: { setAddr, setPubkey } } = PublicResolver;
|
||||
const subdomainHash = soliditySha3(subDomain);
|
||||
const domainNameHash = hash(domainName);
|
||||
const resolveToAddr = address || zeroAddress;
|
||||
|
@ -162,7 +164,6 @@ const RegisterSubDomain = withFormik({
|
|||
const funcsToSend = [];
|
||||
const args = [
|
||||
subdomainHash,
|
||||
domainNameHash,
|
||||
resolveToAddr,
|
||||
points ? points.x : zeroBytes32,
|
||||
points ? points.y : zeroBytes32,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
|
||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||
import web3Utils from 'web3-utils'
|
||||
import { hash } from 'eth-ens-namehash'
|
||||
|
||||
|
@ -12,10 +12,10 @@ const dispatchSetup = (ENSRegistry) => {
|
|||
setSubnodeOwner(zeroBytes32, sha3('eth'), getUserAddress(ENSRegistry))
|
||||
.send()
|
||||
.then(res => { console.log(res) })
|
||||
setSubnodeOwner(hash('eth'), sha3('stateofus'), ENSSubdomainRegistry._address)
|
||||
setSubnodeOwner(hash('eth'), sha3('stateofus'), UsernameRegistrar._address)
|
||||
.send()
|
||||
.then(res => { console.log(res) })
|
||||
setSubnodeOwner(hash('eth'), sha3('stateofus'), ENSSubdomainRegistry._address)
|
||||
setSubnodeOwner(hash('eth'), sha3('stateofus'), UsernameRegistrar._address)
|
||||
.send()
|
||||
.then(res => { console.log(res) })
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||
import web3 from 'web3';
|
||||
import React from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
|
@ -39,7 +39,7 @@ const UpdateController = withFormik({
|
|||
},
|
||||
async handleSubmit(values, { setSubmitting }) {
|
||||
const { newAddress } = values;
|
||||
const { methods: { changeController } } = ENSSubdomainRegistry;
|
||||
const { methods: { changeController } } = UsernameRegistrar;
|
||||
changeController(newAddress)
|
||||
.send()
|
||||
.then((res) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import EmbarkJS from 'Embark/EmbarkJS';
|
||||
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||
import TestToken from 'Embark/contracts/TestToken';
|
||||
import React, { Fragment } from 'react';
|
||||
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
|
||||
|
@ -24,7 +24,7 @@ const ENSSubManagement = props => (
|
|||
<h2 style={{ textAlign: 'center' }}>Subdomain Management</h2>
|
||||
<h3>Change Registry Controller</h3>
|
||||
<UpdateController />
|
||||
<h3>Add/Update Domain Price</h3>
|
||||
<h3>Activate Registry/Update Registry Price</h3>
|
||||
<AddDomain />
|
||||
<h3>Move Domain To Another Registry</h3>
|
||||
<MoveDomain />
|
||||
|
@ -34,7 +34,7 @@ const ENSSubManagement = props => (
|
|||
<hr/>
|
||||
<TokenPermissions
|
||||
symbol='SNT'
|
||||
spender={ENSSubdomainRegistry._address}
|
||||
spender={UsernameRegistrar._address}
|
||||
methods={TestToken.methods} />
|
||||
<hr/>
|
||||
<SetupENS ENSRegistry={ENSRegistry} />
|
||||
|
|
|
@ -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;
|
|
@ -10,7 +10,7 @@ import TestTokenUI from './components/testtoken';
|
|||
import ERC20TokenUI from './components/erc20token';
|
||||
import TestToken from 'Embark/contracts/TestToken';
|
||||
import ENSSubManagement from './components/ensSubManagement';
|
||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
||||
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||
import NameLookup from './components/ens/nameLookup';
|
||||
import AdminMode from './components/AdminMode';
|
||||
import TokenPermissions from './components/standard/TokenPermissionConnect';
|
||||
|
@ -66,14 +66,14 @@ class App extends React.Component {
|
|||
</div>
|
||||
</Fade>}
|
||||
{searching && <Fade in={searching}>
|
||||
<Web3Render ready={isRopsten} network="ropsten">
|
||||
<Web3Render ready={network == 'private'} network={network}>
|
||||
<div>
|
||||
<NameLookup />
|
||||
<Hidden mdDown>
|
||||
<div style={{ textAlign: 'center', margin: '0px 40px' }}>
|
||||
<TokenPermissions
|
||||
symbol={symbols[network] || 'SNT'}
|
||||
spender={ENSSubdomainRegistry._address}
|
||||
spender={UsernameRegistrar._address}
|
||||
methods={TestToken.methods} />
|
||||
<hr/>
|
||||
<Toggle onChange={() => { this.setState({ admin: !admin })}} />
|
||||
|
|
|
@ -27,13 +27,15 @@ module.exports = {
|
|||
"args": ["$ENSRegistry"],
|
||||
"deploy": true
|
||||
},
|
||||
"ENSSubdomainRegistry": {
|
||||
|
||||
"UsernameRegistrar": {
|
||||
"args": [
|
||||
"$TestToken",
|
||||
"$ENSRegistry",
|
||||
"$PublicResolver",
|
||||
"3",
|
||||
[merkleRoot],
|
||||
"0x5f7791d31ca0493e9ca7c9ca16695ecd9d5044768674d14d31ab5d8277518fff",
|
||||
3,
|
||||
merkleTree.getHexRoot(),
|
||||
"0x9e183BC54Bb4f3cCa1A478CA6f2c3EdC37B60478"
|
||||
]
|
||||
}
|
||||
|
@ -56,18 +58,18 @@ module.exports = {
|
|||
"$ENSRegistry"
|
||||
]
|
||||
},
|
||||
"ENSSubdomainRegistry": {
|
||||
"UsernameRegistrar": {
|
||||
"args": [
|
||||
"$TestToken",
|
||||
"$ENSRegistry",
|
||||
"$PublicResolver",
|
||||
"3",
|
||||
[merkleRoot],
|
||||
"0x5f7791d31ca0493e9ca7c9ca16695ecd9d5044768674d14d31ab5d8277518fff",
|
||||
3,
|
||||
merkleTree.getHexRoot(),
|
||||
"0x0"
|
||||
],
|
||||
"onDeploy": [
|
||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', ENSSubdomainRegistry.address).send()",
|
||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x7b4768a525e733422bf968587a91b4036e5176d36f44a9fb5b29d0bca03ab3a3', ENSSubdomainRegistry.address).send()"
|
||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', UsernameRegistrar.address).send()"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +85,7 @@ module.exports = {
|
|||
"TestToken": {
|
||||
"address": "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
|
||||
},
|
||||
"ENSSubdomainRegistry": {
|
||||
"UsernameRegistrar": {
|
||||
"address": "0xDBf9038cf5Aaa030890790dB87E746E00Fc352b3"
|
||||
},
|
||||
"ERC20Receiver": { "deploy": false },
|
||||
|
@ -93,6 +95,7 @@ module.exports = {
|
|||
"MerkleProofWrapper": {
|
||||
"address": "0x76E55E13C5891a90f7fCA2e1238a6B3463F564e2"
|
||||
},
|
||||
"ERC20Receiver": { "deploy": false },
|
||||
"SafeMath": {
|
||||
"address": "0xA115a57952D3337e2a1aB3Cb82bA376EEcDDc469"
|
||||
}
|
||||
|
@ -118,7 +121,7 @@ module.exports = {
|
|||
"MerkleProofWrapper": {
|
||||
"address": "0x58E01078d14142E0370526dFdAE44E4f508c844B"
|
||||
},
|
||||
"ENSSubdomainRegistry": {
|
||||
"UsernameRegistrar": {
|
||||
"address": "0x028F3Df706c5295Ba283c326F4692c375D14cb68"
|
||||
},
|
||||
"ERC20Receiver": { "deploy": false }
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
exports.ReservedUsernames = [
|
||||
|
||||
|
||||
exports.reservedNames = [
|
||||
//Ethereum reserved words
|
||||
'eth',
|
||||
'ether',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,661 @@
|
|||
pragma solidity ^0.4.24;
|
||||
|
||||
import "../common/MerkleProof.sol";
|
||||
import "../common/Controlled.sol";
|
||||
import "../token/ERC20Token.sol";
|
||||
import "../token/ApproveAndCallFallBack.sol";
|
||||
import "../ens/ENS.sol";
|
||||
import "../ens/PublicResolver.sol";
|
||||
|
||||
/**
|
||||
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||
* @notice Registers usernames as ENS subnodes of the domain `ensNode`
|
||||
*/
|
||||
contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
|
||||
|
||||
ERC20Token public token;
|
||||
ENS public ensRegistry;
|
||||
PublicResolver public resolver;
|
||||
address public parentRegistry;
|
||||
|
||||
uint256 public releaseDelay = 365 days;
|
||||
mapping (bytes32 => Account) public accounts;
|
||||
|
||||
//slashing conditions
|
||||
uint256 public usernameMinLenght;
|
||||
bytes32 public reservedUsernamesMerkleRoot;
|
||||
|
||||
event RegistryPrice(uint256 price);
|
||||
event RegistryMoved(address newRegistry);
|
||||
event UsernameOwner(bytes32 indexed nameHash, address owner);
|
||||
|
||||
enum RegistrarState { Unactive, Active, Moved }
|
||||
bytes32 public ensNode;
|
||||
uint256 public price;
|
||||
RegistrarState public state;
|
||||
uint256 private reserveAmount;
|
||||
|
||||
struct Account {
|
||||
uint256 balance;
|
||||
uint256 creationTime;
|
||||
address owner;
|
||||
}
|
||||
|
||||
modifier onlyParentRegistry {
|
||||
require(msg.sender == parentRegistry, "Migration only.");
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Initializes a UserRegistry contract
|
||||
* @param _token fee token base
|
||||
* @param _ensRegistry Ethereum Name Service root address
|
||||
* @param _resolver Default resolver to use in initial settings
|
||||
* @param _ensNode ENS node (registry) being used for usernames subnodes (subregistry)
|
||||
* @param _usernameMinLenght Minimum length of usernames
|
||||
* @param _reservedUsernamesMerkleRoot Merkle Roots of reserved usernames
|
||||
* @param _parentRegistry Address of old registry (if any) for account migration.
|
||||
*/
|
||||
constructor(
|
||||
ERC20Token _token,
|
||||
ENS _ensRegistry,
|
||||
PublicResolver _resolver,
|
||||
bytes32 _ensNode,
|
||||
uint256 _usernameMinLenght,
|
||||
bytes32 _reservedUsernamesMerkleRoot,
|
||||
address _parentRegistry
|
||||
)
|
||||
public
|
||||
{
|
||||
token = _token;
|
||||
ensRegistry = _ensRegistry;
|
||||
resolver = _resolver;
|
||||
ensNode = _ensNode;
|
||||
usernameMinLenght = _usernameMinLenght;
|
||||
reservedUsernamesMerkleRoot = _reservedUsernamesMerkleRoot;
|
||||
parentRegistry = _parentRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Registers `_label` username to `ensNode` setting msg.sender as owner.
|
||||
* Terms of name registration:
|
||||
* - SNT is deposited, not spent; the amount is locked up for 1 year.
|
||||
* - After 1 year, the user can release the name and receive their deposit back (at any time).
|
||||
* - User deposits are completely protected. The contract controller cannot access them.
|
||||
* - User's address(es) will be publicly associated with the ENS name.
|
||||
* - User must authorise the contract to transfer `price` `token.name()` on their behalf.
|
||||
* - Usernames registered with less then `usernameMinLenght` characters can be slashed.
|
||||
* - Usernames contained in the merkle tree of root `reservedUsernamesMerkleRoot` can be slashed.
|
||||
* - Usernames starting with `0x` and bigger then 12 characters can be slashed.
|
||||
* - If terms of the contract change—e.g. Status makes contract upgrades—the user has the right to release the username and get their deposit back.
|
||||
* @param _label choosen unowned username hash
|
||||
* @param _account optional address to set at public resolver
|
||||
* @param _pubkeyA optional pubkey part A to set at public resolver
|
||||
* @param _pubkeyB optional pubkey part B to set at public resolver
|
||||
*/
|
||||
function register(
|
||||
bytes32 _label,
|
||||
address _account,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB
|
||||
)
|
||||
external
|
||||
returns(bytes32 namehash)
|
||||
{
|
||||
return registerUser(msg.sender, _label, _account, _pubkeyA, _pubkeyB);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice release username and retrieve locked fee, needs to be called after `releasePeriod` from creation time.
|
||||
* @param _label `msg.sender` owned username hash
|
||||
*/
|
||||
function release(
|
||||
bytes32 _label
|
||||
)
|
||||
external
|
||||
{
|
||||
bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label));
|
||||
Account memory account = accounts[_label];
|
||||
require(account.creationTime > 0, "Username not registered.");
|
||||
if (state == RegistrarState.Active) {
|
||||
require(msg.sender == ensRegistry.owner(namehash), "Not owner of ENS node.");
|
||||
require(block.timestamp > account.creationTime + releaseDelay, "Release period not reached.");
|
||||
ensRegistry.setSubnodeOwner(ensNode, _label, address(this));
|
||||
ensRegistry.setResolver(namehash, address(0));
|
||||
ensRegistry.setOwner(namehash, address(0));
|
||||
} else {
|
||||
require(msg.sender == account.owner, "Not the former account owner.");
|
||||
address newOwner = ensRegistry.owner(ensNode);
|
||||
//low level call, case dropUsername not implemented or failing, proceed release.
|
||||
newOwner.call(
|
||||
abi.encodeWithSignature(
|
||||
"dropUsername(bytes32)",
|
||||
_label
|
||||
)
|
||||
);
|
||||
}
|
||||
delete accounts[_label];
|
||||
if (account.balance > 0) {
|
||||
reserveAmount -= account.balance;
|
||||
require(token.transfer(msg.sender, account.balance), "Transfer failed");
|
||||
}
|
||||
emit UsernameOwner(_label, address(0));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice updates funds owner, useful to move username account to new registry.
|
||||
* @param _label `msg.sender` owned username hash
|
||||
**/
|
||||
function updateAccountOwner(
|
||||
bytes32 _label
|
||||
)
|
||||
external
|
||||
{
|
||||
bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label));
|
||||
require(msg.sender == ensRegistry.owner(namehash), "Caller not owner of ENS node.");
|
||||
require(accounts[_label].creationTime > 0, "Username not registered.");
|
||||
require(ensRegistry.owner(ensNode) == address(this), "Registry not owner of registry.");
|
||||
accounts[_label].owner = msg.sender;
|
||||
emit UsernameOwner(namehash, msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice slash account due too length restriction
|
||||
* @param _username raw value of offending username
|
||||
*/
|
||||
function slashSmallUsername(
|
||||
bytes _username
|
||||
)
|
||||
external
|
||||
{
|
||||
require(_username.length < usernameMinLenght, "Not a small username.");
|
||||
slashUsername(_username);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice slash account due look like an address
|
||||
* @param _username raw value of offending username
|
||||
*/
|
||||
function slashAddressLikeUsername(
|
||||
string _username
|
||||
)
|
||||
external
|
||||
{
|
||||
bytes memory username = bytes(_username);
|
||||
require(username.length > 12, "Too small to look like an address.");
|
||||
require(username[0] == byte("0"), "First character need to be 0");
|
||||
require(username[1] == byte("x"), "Second character need to be x");
|
||||
slashUsername(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice slash account due reserved name
|
||||
* @param _username raw value of offending username
|
||||
*/
|
||||
function slashReservedUsername(
|
||||
bytes _username,
|
||||
bytes32[] _proof
|
||||
)
|
||||
external
|
||||
{
|
||||
require(
|
||||
MerkleProof.verifyProof(
|
||||
_proof,
|
||||
reservedUsernamesMerkleRoot,
|
||||
keccak256(_username)
|
||||
),
|
||||
"Invalid Proof."
|
||||
);
|
||||
slashUsername(_username);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice slash account of invalid username
|
||||
* @param _username raw value of offending username
|
||||
* @param _offendingPos position of invalid character
|
||||
*/
|
||||
function slashInvalidUsername(
|
||||
bytes _username,
|
||||
uint256 _offendingPos
|
||||
)
|
||||
external
|
||||
{
|
||||
require(_username.length > _offendingPos, "Invalid position.");
|
||||
byte b = _username[_offendingPos];
|
||||
|
||||
require(!((b >= 48 && b <= 57) || (b >= 97 && b <= 122)), "Not invalid character.");
|
||||
|
||||
slashUsername(_username);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Migrate account to new registry
|
||||
* @param _label `msg.sender` owned username hash
|
||||
**/
|
||||
function moveAccount(
|
||||
bytes32 _label
|
||||
)
|
||||
external
|
||||
{
|
||||
require(msg.sender == accounts[_label].owner, "Callable only by account owner.");
|
||||
UsernameRegistrar _newRegistry = UsernameRegistrar(ensRegistry.owner(ensNode));
|
||||
Account memory account = accounts[_label];
|
||||
delete accounts[_label];
|
||||
|
||||
token.approve(_newRegistry, account.balance);
|
||||
_newRegistry.migrateUsername(
|
||||
_label,
|
||||
account.balance,
|
||||
account.creationTime,
|
||||
account.owner
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev callabe only by parent registry to continue migration of ENSSubdomainRegistry
|
||||
* @param _domainHash needs to be this contract ensNode
|
||||
**/
|
||||
function migrateDomain(
|
||||
uint256 _price,
|
||||
bytes32 _domainHash
|
||||
)
|
||||
external
|
||||
onlyParentRegistry
|
||||
{
|
||||
require(_domainHash == ensNode, "Wrong Registry");
|
||||
migrateRegistry(_price);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Activate registry
|
||||
* @param _price The price of username registry
|
||||
*/
|
||||
function activate(
|
||||
uint256 _price
|
||||
)
|
||||
external
|
||||
onlyController
|
||||
{
|
||||
require(state == RegistrarState.Unactive, "Registry state is not unactive");
|
||||
require(ensRegistry.owner(ensNode) == address(this), "Registry does not own registry");
|
||||
price = _price;
|
||||
state = RegistrarState.Active;
|
||||
emit RegistryPrice(_price);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice updates default public resolver for newly registred usernames
|
||||
* @param _resolver new default resolver
|
||||
*/
|
||||
function setResolver(
|
||||
address _resolver
|
||||
)
|
||||
external
|
||||
onlyController
|
||||
{
|
||||
resolver = PublicResolver(_resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice updates registry price
|
||||
* @param _price new price
|
||||
*/
|
||||
function updateRegistryPrice(
|
||||
uint256 _price
|
||||
)
|
||||
external
|
||||
onlyController
|
||||
{
|
||||
require(state == RegistrarState.Active, "Registry not owned");
|
||||
price = _price;
|
||||
emit RegistryPrice(_price);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice moves a registry to other Registry (will not move usernames accounts)
|
||||
* @param _newRegistry new registry hodling this registry
|
||||
*/
|
||||
function moveRegistry(
|
||||
UsernameRegistrar _newRegistry
|
||||
)
|
||||
external
|
||||
onlyController
|
||||
{
|
||||
require(state == RegistrarState.Active, "Wrong registry");
|
||||
require(ensRegistry.owner(ensNode) == address(this), "Registry not owned anymore.");
|
||||
state = RegistrarState.Moved;
|
||||
ensRegistry.setOwner(ensNode, _newRegistry);
|
||||
_newRegistry.migrateRegistry(price);
|
||||
emit RegistryMoved(_newRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev callable only by parent registry for continue user opt-in migration
|
||||
* @param _domainHash needs to be this contract ensNode
|
||||
**/
|
||||
function migrateAccount(
|
||||
bytes32 _userHash,
|
||||
bytes32 _domainHash,
|
||||
uint256 _tokenBalance,
|
||||
uint256 _creationTime,
|
||||
address _accountOwner
|
||||
)
|
||||
external
|
||||
{
|
||||
require(_domainHash == ensNode, "Wrong Registry");
|
||||
migrateUsername(_userHash, _tokenBalance, _creationTime, _accountOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev clears ens registry. Callable only by parant registry for continue user opt-out migration
|
||||
* @param _label any username hash coming from parent
|
||||
*/
|
||||
function dropUsername(
|
||||
bytes32 _label
|
||||
)
|
||||
external
|
||||
onlyParentRegistry
|
||||
{
|
||||
require(accounts[_label].creationTime == 0, "Already migrated");
|
||||
bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label));
|
||||
ensRegistry.setSubnodeOwner(ensNode, _label, address(this));
|
||||
ensRegistry.setResolver(namehash, address(0));
|
||||
ensRegistry.setOwner(namehash, address(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice withdraw tokens forced into the contract
|
||||
* @param _token address of ERC20 withdrawing excess, or address(0) if want ETH/
|
||||
* @param _beneficiary who gets the funds
|
||||
**/
|
||||
function withdrawExcessBalance(
|
||||
address _token,
|
||||
address _beneficiary
|
||||
)
|
||||
external
|
||||
onlyController
|
||||
{
|
||||
require(_beneficiary != address(0), "Cannot burn token");
|
||||
if (_token == address(0)) {
|
||||
_beneficiary.transfer(address(this).balance);
|
||||
} else {
|
||||
ERC20Token excessToken = ERC20Token(_token);
|
||||
uint256 amount = excessToken.balanceOf(address(this));
|
||||
if(_token == address(token)){
|
||||
require(amount > reserveAmount, "Is not excess");
|
||||
amount -= reserveAmount;
|
||||
} else {
|
||||
require(amount > 0, "Is not excess");
|
||||
}
|
||||
excessToken.transfer(_beneficiary, amount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice withdraw ens nodes not belonging to this contract
|
||||
* @param _domainHash ens node namehash
|
||||
* @param _beneficiary new owner of ens node
|
||||
**/
|
||||
function withdrawWrongNode(
|
||||
bytes32 _domainHash,
|
||||
address _beneficiary
|
||||
)
|
||||
external
|
||||
onlyController
|
||||
{
|
||||
require(_beneficiary != address(0), "Cannot burn node");
|
||||
require(_domainHash != ensNode, "Cannot withdraw main node");
|
||||
require(ensRegistry.owner(_domainHash) == address(this), "Not owner of this node");
|
||||
ensRegistry.setOwner(_domainHash, _beneficiary);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice gets registrar price
|
||||
* @return registry price
|
||||
**/
|
||||
function getPrice()
|
||||
external
|
||||
view
|
||||
returns(uint256 registryPrice)
|
||||
{
|
||||
return price;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice reads amount tokens locked in username
|
||||
* @param _label hash of username
|
||||
* @return locked username balance
|
||||
**/
|
||||
function getAccountBalance(bytes32 _label)
|
||||
external
|
||||
view
|
||||
returns(uint256 accountBalance)
|
||||
{
|
||||
accountBalance = accounts[_label].balance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice reads username owner at this contract,
|
||||
* which can release or migrate in case of upgrade
|
||||
* @param _label hash of username
|
||||
* @return username owner
|
||||
**/
|
||||
function getAccountOwner(bytes32 _label)
|
||||
external
|
||||
view
|
||||
returns(address owner)
|
||||
{
|
||||
owner = accounts[_label].owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice reads when the account was registered
|
||||
* @param _label hash of username
|
||||
* @return registration time
|
||||
**/
|
||||
function getCreationTime(bytes32 _label)
|
||||
external
|
||||
view
|
||||
returns(uint256 creationTime)
|
||||
{
|
||||
creationTime = accounts[_label].creationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice calculate time where username can be released
|
||||
* @param _label hash of username
|
||||
* @return exact time when username can be released
|
||||
**/
|
||||
function getExpirationTime(bytes32 _label)
|
||||
external
|
||||
view
|
||||
returns(uint256 expirationTime)
|
||||
{
|
||||
expirationTime = accounts[_label].creationTime + releaseDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Receive approval, callable only by `token()`.
|
||||
* @param _from who is approving
|
||||
* @param _amount amount being approved, need to be equal `getPrice()`
|
||||
* @param _token token being approved, need to be equal `token()`
|
||||
* @param _data abi encoded data with selector of `register(bytes32,address,bytes32,bytes32)`
|
||||
*/
|
||||
function receiveApproval(
|
||||
address _from,
|
||||
uint256 _amount,
|
||||
address _token,
|
||||
bytes _data
|
||||
)
|
||||
public
|
||||
{
|
||||
require(_amount == price, "Wrong value");
|
||||
require(_token == address(token), "Wrong token");
|
||||
require(_token == address(msg.sender), "Wrong call");
|
||||
require(_data.length <= 132, "Wrong data length");
|
||||
bytes4 sig;
|
||||
bytes32 label;
|
||||
address account;
|
||||
bytes32 pubkeyA;
|
||||
bytes32 pubkeyB;
|
||||
(sig, label, account, pubkeyA, pubkeyB) = abiDecodeRegister(_data);
|
||||
require(
|
||||
sig == bytes4(0xb82fedbb), //bytes4(keccak256("register(bytes32,address,bytes32,bytes32)"))
|
||||
"Wrong method selector"
|
||||
);
|
||||
registerUser(_from, label, account, pubkeyA, pubkeyB);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev decodes abi encoded data with selector for "register(bytes32,address,bytes32,bytes32)"
|
||||
* @param _data abi encoded data
|
||||
*/
|
||||
function abiDecodeRegister(
|
||||
bytes _data
|
||||
)
|
||||
private
|
||||
pure
|
||||
returns(
|
||||
bytes4 sig,
|
||||
bytes32 label,
|
||||
address account,
|
||||
bytes32 pubkeyA,
|
||||
bytes32 pubkeyB
|
||||
)
|
||||
{
|
||||
assembly {
|
||||
sig := mload(add(_data, add(0x20, 0)))
|
||||
label := mload(add(_data, 36))
|
||||
account := mload(add(_data, 68))
|
||||
pubkeyA := mload(add(_data, 100))
|
||||
pubkeyB := mload(add(_data, 132))
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @dev callable only by parent registry for continue user opt-in migration
|
||||
* @param _label any username hash coming from parent
|
||||
* @param _tokenBalance amount being transferred
|
||||
* @param _creationTime any value coming from parent
|
||||
* @param _accountOwner owner for opt-out/release at registry move
|
||||
**/
|
||||
function migrateUsername(
|
||||
bytes32 _label,
|
||||
uint256 _tokenBalance,
|
||||
uint256 _creationTime,
|
||||
address _accountOwner
|
||||
)
|
||||
public
|
||||
onlyParentRegistry
|
||||
{
|
||||
if (_tokenBalance > 0) {
|
||||
require(
|
||||
token.transferFrom(
|
||||
parentRegistry,
|
||||
address(this),
|
||||
_tokenBalance
|
||||
),
|
||||
"Error moving funds from old registar."
|
||||
);
|
||||
reserveAmount += _tokenBalance;
|
||||
}
|
||||
accounts[_label] = Account(_tokenBalance, _creationTime, _accountOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev callabe only by parent registry to continue migration
|
||||
* of registry and activate registrar
|
||||
* @param _price the price to setup and activate domain
|
||||
**/
|
||||
function migrateRegistry(
|
||||
uint256 _price
|
||||
)
|
||||
public
|
||||
onlyParentRegistry
|
||||
{
|
||||
require(state == RegistrarState.Unactive, "Not unactive");
|
||||
require(ensRegistry.owner(ensNode) == address(this), "ENS registry owner not transfered.");
|
||||
price = _price;
|
||||
state = RegistrarState.Active;
|
||||
emit RegistryPrice(_price);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Registers `_label` username to `ensNode` setting msg.sender as owner.
|
||||
* @param _owner address registering the user and paying registry price.
|
||||
* @param _label choosen unowned username hash
|
||||
* @param _account optional address to set at public resolver
|
||||
* @param _pubkeyA optional pubkey part A to set at public resolver
|
||||
* @param _pubkeyB optional pubkey part B to set at public resolver
|
||||
*/
|
||||
function registerUser(
|
||||
address _owner,
|
||||
bytes32 _label,
|
||||
address _account,
|
||||
bytes32 _pubkeyA,
|
||||
bytes32 _pubkeyB
|
||||
)
|
||||
internal
|
||||
returns(bytes32 namehash)
|
||||
{
|
||||
require(state == RegistrarState.Active, "Registry unavailable.");
|
||||
namehash = keccak256(abi.encodePacked(ensNode, _label));
|
||||
require(ensRegistry.owner(namehash) == address(0), "ENS node already owned.");
|
||||
require(accounts[_label].creationTime == 0, "Username already registered.");
|
||||
accounts[_label] = Account(price, block.timestamp, _owner);
|
||||
if(price > 0) {
|
||||
require(token.allowance(_owner, address(this)) >= price, "Unallowed to spend.");
|
||||
require(
|
||||
token.transferFrom(
|
||||
_owner,
|
||||
address(this),
|
||||
price
|
||||
),
|
||||
"Transfer failed"
|
||||
);
|
||||
reserveAmount += price;
|
||||
}
|
||||
|
||||
bool resolvePubkey = _pubkeyA != 0 || _pubkeyB != 0;
|
||||
bool resolveAccount = _account != address(0);
|
||||
if (resolvePubkey || resolveAccount) {
|
||||
//set to self the ownship to setup initial resolver
|
||||
ensRegistry.setSubnodeOwner(ensNode, _label, address(this));
|
||||
ensRegistry.setResolver(namehash, resolver); //default resolver
|
||||
if (resolveAccount) {
|
||||
resolver.setAddr(namehash, _account);
|
||||
}
|
||||
if (resolvePubkey) {
|
||||
resolver.setPubkey(namehash, _pubkeyA, _pubkeyB);
|
||||
}
|
||||
ensRegistry.setOwner(namehash, _owner);
|
||||
} else {
|
||||
//transfer ownship of subdone directly to registrant
|
||||
ensRegistry.setSubnodeOwner(ensNode, _label, _owner);
|
||||
}
|
||||
emit UsernameOwner(namehash, _owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev removes account hash of `_username` and send account.balance to msg.sender
|
||||
* @param _username username being slashed
|
||||
*/
|
||||
function slashUsername(bytes _username) internal {
|
||||
bytes32 label = keccak256(_username);
|
||||
bytes32 namehash = keccak256(abi.encodePacked(ensNode, label));
|
||||
require(accounts[label].creationTime > 0, "Username not registered.");
|
||||
|
||||
ensRegistry.setSubnodeOwner(ensNode, label, address(this));
|
||||
ensRegistry.setResolver(namehash, address(0));
|
||||
ensRegistry.setOwner(namehash, address(0));
|
||||
|
||||
uint256 amountToTransfer = accounts[label].balance;
|
||||
delete accounts[label];
|
||||
if (amountToTransfer > 0) {
|
||||
reserveAmount -= amountToTransfer;
|
||||
require(token.transfer(msg.sender, amountToTransfer), "Error in transfer.");
|
||||
}
|
||||
emit UsernameOwner(namehash, address(0));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
contract ApprovalReceiver {
|
||||
function receiveApproval(address from, uint value, address tokenContract, bytes extraData) returns (bool);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
contract ApproveAndCallFallBack {
|
||||
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
pragma solidity ^0.4.23;
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./ERC20Token.sol";
|
||||
|
||||
|
@ -20,23 +20,15 @@ contract StandardToken is ERC20Token {
|
|||
return transfer(msg.sender, _to, _value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
|
||||
* @param _spender The address which will spend the funds.
|
||||
* @param _value The amount of tokens to be spent.
|
||||
*/
|
||||
function approve(address _spender, uint256 _value) returns (bool) {
|
||||
|
||||
// To change the approve amount you first have to reduce the addresses`
|
||||
// allowance to zero by calling `approve(_spender, 0)` if it is not
|
||||
// already 0 to mitigate the race condition described here:
|
||||
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
require((_value == 0) || (allowed[msg.sender][_spender] == 0));
|
||||
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
function approve(
|
||||
address _to,
|
||||
uint256 _value
|
||||
)
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
return approve(msg.sender, _to, _value);
|
||||
}
|
||||
|
||||
function transferFrom(
|
||||
address _from,
|
||||
|
@ -79,6 +71,25 @@ contract StandardToken is ERC20Token {
|
|||
{
|
||||
return supply;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
|
||||
* @param _from The address that is approving the spend
|
||||
* @param _spender The address which will spend the funds.
|
||||
* @param _value The amount of tokens to be spent.
|
||||
*/
|
||||
function approve(address _from, address _spender, uint256 _value) internal returns (bool) {
|
||||
|
||||
// To change the approve amount you first have to reduce the addresses`
|
||||
// allowance to zero by calling `approve(_spender, 0)` if it is not
|
||||
// already 0 to mitigate the race condition described here:
|
||||
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
require((_value == 0) || (allowed[_from][_spender] == 0), "Bad usage");
|
||||
|
||||
allowed[_from][_spender] = _value;
|
||||
emit Approval(_from, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function mint(
|
||||
address _to,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pragma solidity ^0.4.23;
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./StandardToken.sol";
|
||||
import './ApprovalReceiver.sol';
|
||||
import "./ApproveAndCallFallBack.sol";
|
||||
|
||||
/**
|
||||
* @notice ERC20Token for test scripts, can be minted by anyone.
|
||||
|
@ -18,9 +18,13 @@ contract TestToken is StandardToken {
|
|||
mint(msg.sender, _amount);
|
||||
}
|
||||
|
||||
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
|
||||
assert(approve(_spender, _value));
|
||||
return ApprovalReceiver(_spender).receiveApproval(msg.sender, _value, this, _extraData);
|
||||
function approveAndCall(address _spender, uint256 _value, bytes _extraData)
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
approve(msg.sender, _spender, _value);
|
||||
ApproveAndCallFallBack(_spender).receiveApproval(msg.sender, _value, this, _extraData);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/status-im/contracts#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.0.0-beta.55",
|
||||
"@material-ui/core": "^1.2.1",
|
||||
"@material-ui/icons": "^1.1.0",
|
||||
"bignumber.js": "^5.0.0",
|
||||
|
|
|
@ -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))
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -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))
|
||||
});
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue