diff --git a/client/app/src/containers/App/App.css b/client/app/src/containers/App/App.css
new file mode 100644
index 00000000..3467f538
--- /dev/null
+++ b/client/app/src/containers/App/App.css
@@ -0,0 +1,26 @@
+.incorrectNetwork {
+ width: 700px;
+ margin: 0 auto;
+ padding-top: 100px;
+ color: #7c7c7c;
+}
+
+.incorrectNetwork h2 {
+ font-size: 34px;
+ margin-bottom: 40px;
+}
+
+.incorrectNetwork p {
+ font-size: 20px;
+ padding-bottom: 10px;
+}
+
+.incorrectNetwork p span {
+ display: inline-block;
+ padding-left: 20px;
+}
+
+.incorrectNetwork img {
+ padding-top: 40px;
+ float: right;
+}
diff --git a/client/app/src/containers/App/App.jsx b/client/app/src/containers/App/App.jsx
new file mode 100644
index 00000000..ded0c6b3
--- /dev/null
+++ b/client/app/src/containers/App/App.jsx
@@ -0,0 +1,55 @@
+import React, {Component} from 'react';
+
+import {checkNetwork, getNetworkStr} from '../../utils/cyber';
+
+import './App.css';
+
+class App extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ isCorrectNetwork: false,
+ networkId: null,
+ contractNetworks: [],
+ };
+ }
+
+ componentDidMount() {
+ checkNetwork().then(({isCorrectNetwork, networkId, contractNetworks}) => {
+ this.setState({
+ isCorrectNetwork,
+ networkId,
+ contractNetworks,
+ });
+ });
+ }
+
+ render() {
+ const {isCorrectNetwork, networkId, contractNetworks} = this.state;
+
+ if (!isCorrectNetwork) {
+ return (
+
+
+
Please change network.
+
Current networkId: {getNetworkStr(networkId)}
+
Contract networks:
+
+ {contractNetworks.map(netId => getNetworkStr(netId)).join(', ')}
+
+
+
+ );
+ }
+}
+
+
+export default App;
diff --git a/client/app/src/containers/App/error.svg b/client/app/src/containers/App/error.svg
new file mode 100644
index 00000000..13fb4117
--- /dev/null
+++ b/client/app/src/containers/App/error.svg
@@ -0,0 +1,17 @@
+
diff --git a/client/app/src/containers/database/Beneficiaries.jsx b/client/app/src/containers/database/Beneficiaries.jsx
new file mode 100644
index 00000000..d7501343
--- /dev/null
+++ b/client/app/src/containers/database/Beneficiaries.jsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Subscribe } from 'unstated';
+import {
+ Centred, SectionContent, BenContainer, BenPieChart,
+} from '@cybercongress/ui';
+import { calculateBensShares } from '../../utils/utils';
+
+import page from './page';
+
+const Beneficiaries = () => (
+
+);
+
+export default Beneficiaries;
diff --git a/client/app/src/containers/database/DatabasePopups.jsx b/client/app/src/containers/database/DatabasePopups.jsx
new file mode 100644
index 00000000..3b71e143
--- /dev/null
+++ b/client/app/src/containers/database/DatabasePopups.jsx
@@ -0,0 +1,161 @@
+import React from 'react';
+import { Subscribe } from 'unstated';
+import {
+ LinkHash, Popup, PopupContent, PopupFooter, PopupTitle,
+ LineTitle, LineControl, WideInput, Button, ContentLineFund,
+ Text, ContentLine, ContentLineTextInput,
+} from '@cybercongress/ui';
+import page from './page';
+
+const DatabasePopups = () => (
+
+);
+
+export default DatabasePopups;
diff --git a/client/app/src/containers/database/FormField.jsx b/client/app/src/containers/database/FormField.jsx
new file mode 100644
index 00000000..5c0b7f66
--- /dev/null
+++ b/client/app/src/containers/database/FormField.jsx
@@ -0,0 +1,90 @@
+import React, { Component } from 'react';
+
+import {
+ FormFieldContainer,
+ FormLabel,
+ FormValue,
+ ButtonContainer,
+} from '../../components/FormField';
+
+
+import {
+ EditButton,
+ UpdateButton,
+ CancelButton,
+} from '../../components/DatabaseItem';
+
+class FormField extends Component {
+ state = {
+ edit: false,
+ }
+
+ startEdit = () => {
+ this.setState({ edit: true });
+ }
+
+ save = () => {
+ const { onUpdate, children } = this.props;
+
+ this.setState({ edit: false });
+
+ if (children) {
+ onUpdate();
+ } else {
+ onUpdate(this.refs.input.value);
+ }
+ }
+
+ cancel = () => {
+ this.setState({ edit: false });
+ }
+
+ render() {
+ const {
+ label, value, valueType, onUpdate, children,
+ } = this.props;
+ const { edit } = this.state;
+
+ return (
+
+ );
+ }
+}
+
+export default FormField;
diff --git a/client/app/src/containers/database/General.jsx b/client/app/src/containers/database/General.jsx
new file mode 100644
index 00000000..acbd7734
--- /dev/null
+++ b/client/app/src/containers/database/General.jsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import { Subscribe } from 'unstated';
+import {
+ LinkHash, Section, SectionContent,
+ Text, CentredPanel,
+} from '@cybercongress/ui';
+import page from './page';
+import { formatDate } from '../../utils/utils';
+
+const General = () => (
+
+);
+
+export default General;
diff --git a/client/app/src/containers/database/Header.jsx b/client/app/src/containers/database/Header.jsx
new file mode 100644
index 00000000..b4916a51
--- /dev/null
+++ b/client/app/src/containers/database/Header.jsx
@@ -0,0 +1,141 @@
+import React from 'react';
+import { hashHistory } from 'react-router';
+import { Subscribe } from 'unstated';
+import {
+ DbMenu, MenuPopup, MenuPopupItem, MenuSeparator,
+ MenuPopupDeleteIcon, MenuPopupAccountIcon, MenuPopupTransferIcon,
+ MenuPopupResumeIcon, MenuPopupPauseIcon,
+ PageTitle, ProgressBar, CircleLable, Section,
+ PopupBar, Text, PopupBarFooter, Button,
+ FlexContainer, FlexContainerLeft, FlexContainerRight,
+} from '@cybercongress/ui';
+
+import page from './page';
+
+const Header = () => (
+
+);
+
+export default Header;
diff --git a/client/app/src/containers/database/ItemEditPopup.jsx b/client/app/src/containers/database/ItemEditPopup.jsx
new file mode 100644
index 00000000..bc89c077
--- /dev/null
+++ b/client/app/src/containers/database/ItemEditPopup.jsx
@@ -0,0 +1,189 @@
+import * as React from 'react';
+import {
+ ContentLineTextInput, LineControl, LineTitle, WideInput,
+ Popup, PopupTitle, PopupContent, PopupFooter, Button,
+} from '@cybercongress/ui';
+import { Subscribe } from 'unstated';
+import { debounce } from '../../utils/utils';
+import page from './page';
+
+const web3Utils = require('web3-utils');
+
+export default class ItemEditPopup extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.inputRefs = {};
+
+ this.state = {
+ item: page.state.recordForAction,
+ errors: {},
+ };
+
+ this.updateField = debounce(this.updateField, 1000);
+ }
+
+ isValidValue = (value, type) => {
+ switch (type) {
+ case 'int256':
+ return /^[-+]?\d+$/.test(value);
+ case 'uint256':
+ return /^\d+$/.test(value);
+ case 'address':
+ return web3Utils.isAddress(value);
+ case 'string':
+ return value.length <= 30;
+ default:
+ return true;
+ }
+ };
+
+ isUniqueFiled = (field, value, records) => {
+ const {
+ item,
+ } = this.state;
+
+ let duplicateFound = false;
+
+ for (let index = 0; index < records.length; index += 1) {
+ if (records[index].id !== item.id && records[index][field.name] === value) {
+ duplicateFound = true;
+ break;
+ }
+ }
+
+ return !duplicateFound;
+ };
+
+ updateField = (event, field, fieldIndex, dbPage) => {
+ const { item, errors } = this.state;
+ const { items: records, fields } = dbPage.state;
+ let { value } = event.target;
+ let errorMessage = null;
+
+ if (field.type === 'bool') {
+ value = event.target.checked;
+ }
+
+ if (!this.isValidValue(value, field.type)) {
+ errorMessage = `Check the type, ${field.type} was expected`;
+ }
+
+ if (field.unique && !errorMessage && !this.isUniqueFiled(field, value, records)) {
+ errorMessage = 'This field must be unique';
+ }
+
+ const filteredErrors = fields
+ .map(fieldM => fieldM.name)
+ .reduce((obj, key) => {
+ if (errors[key] && key !== field.name) {
+ obj[key] = errors[key];
+ }
+
+ if (key === field.name && errorMessage) {
+ obj[key] = errorMessage;
+ }
+
+ return obj;
+ }, {});
+
+ this.setState({
+ item: {
+ ...item,
+ [field.name]: value,
+ },
+ errors: filteredErrors,
+ });
+ };
+
+ onValueChange = (event, field, fieldIndex, dbPage) => {
+ event.persist();
+
+ this.updateField(event, field, fieldIndex, dbPage);
+ };
+
+ onCancelClick = () => {
+ const { item, onCancelClick } = this.props;
+
+ this.setState({
+ item,
+ });
+
+ onCancelClick();
+ };
+
+ onConfirmClick = (dbPage) => {
+ const { item } = this.state;
+ const { fields } = dbPage.state;
+ const values = fields.map(field => item[field.name]);
+
+ dbPage.closePopups();
+ dbPage.updateRecord(values, item.id);
+ };
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/client/app/src/containers/database/Overview.jsx b/client/app/src/containers/database/Overview.jsx
new file mode 100644
index 00000000..19357c1f
--- /dev/null
+++ b/client/app/src/containers/database/Overview.jsx
@@ -0,0 +1,111 @@
+import React from 'react';
+import { Subscribe } from 'unstated';
+import {
+ SectionContent, InfoButton,
+ FormField, WideSelect,
+} from '@cybercongress/ui';
+import page from './page';
+
+const Permission = {
+ OnlyAdmin: 0,
+ Whitelist: 1,
+ AllUsers: 2,
+};
+
+const CreateEntryPermissionGroup = {
+ [Permission.OnlyAdmin]: {
+ key: 'OnlyAdmin',
+ label: 'ONLY OWNER',
+ },
+ // 1: {
+ // key: 'Whitelist',
+ // label: 'Whitelist',
+ // },
+ [Permission.AllUsers]: {
+ key: 'AllUsers',
+ label: 'All Users',
+ },
+};
+
+
+const Overview = () => (
+
+);
+
+export default Overview;
diff --git a/client/app/src/containers/database/PageLoading.jsx b/client/app/src/containers/database/PageLoading.jsx
new file mode 100644
index 00000000..697cb848
--- /dev/null
+++ b/client/app/src/containers/database/PageLoading.jsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { Subscribe } from 'unstated';
+import { StatusBar } from '@cybercongress/ui';
+import page from './page';
+
+const PageLoading = () => (
+
+);
+
+export default PageLoading;
diff --git a/client/app/src/containers/database/RecordPopups.jsx b/client/app/src/containers/database/RecordPopups.jsx
new file mode 100644
index 00000000..a7a69260
--- /dev/null
+++ b/client/app/src/containers/database/RecordPopups.jsx
@@ -0,0 +1,158 @@
+import React from 'react';
+import { Subscribe } from 'unstated';
+import {
+ LinkHash, Popup, PopupContent, PopupFooter, PopupTitle,
+ LineTitle, LineControl, WideInput, Button, ContentLineFund,
+ Text, ContentLineTextInput, ContentLine,
+} from '@cybercongress/ui';
+import ItemEditPopup from './ItemEditPopup';
+import page from './page';
+
+const DatabasePopups = () => (
+
+);
+
+export default DatabasePopups;
diff --git a/client/app/src/containers/database/Records.jsx b/client/app/src/containers/database/Records.jsx
new file mode 100644
index 00000000..530e6147
--- /dev/null
+++ b/client/app/src/containers/database/Records.jsx
@@ -0,0 +1,174 @@
+import React from 'react';
+import { Subscribe } from 'unstated';
+import {
+ FlexContainer, FlexContainerLeft, FlexContainerRight, AddNewRecordButton,
+ DatabaseItemsContainer, TableRecords, DbMenuPoints, MenuPopup,
+ MenuPopupItem, MenuPopupTransferIcon, MenuPopupEditIcon, MenuPopupDeleteIcon,
+ MenuSeparator, MenuPopupAccountIcon, LinkHash,
+} from '@cybercongress/ui';
+import page from './page';
+
+const Permission = {
+ OnlyAdmin: 0,
+ Whitelist: 1,
+ AllUsers: 2,
+};
+
+const Records = () => (
+
+);
+
+export default Records;
diff --git a/client/app/src/containers/database/index.jsx b/client/app/src/containers/database/index.jsx
new file mode 100644
index 00000000..9482ecaf
--- /dev/null
+++ b/client/app/src/containers/database/index.jsx
@@ -0,0 +1,48 @@
+import React, { Component } from 'react';
+import { Provider } from 'unstated';
+import { MainContainer, Section } from '@cybercongress/ui';
+import Records from './Records';
+import DatabasePopups from './DatabasePopups';
+import RecordPopups from './RecordPopups';
+import Header from './Header';
+import General from './General';
+import Overview from './Overview';
+import Beneficiaries from './Beneficiaries';
+import PageLoading from './PageLoading';
+import page from './page';
+
+class Database extends Component {
+ componentDidMount() {
+ const { dbsymbol } = this.props.params;
+
+ page.init(dbsymbol);
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default Database;
diff --git a/client/app/src/containers/database/page.jsx b/client/app/src/containers/database/page.jsx
new file mode 100644
index 00000000..bf59df0e
--- /dev/null
+++ b/client/app/src/containers/database/page.jsx
@@ -0,0 +1,669 @@
+import { Container } from 'unstated';
+import { hashHistory } from 'react-router';
+import * as cyber from '../../utils/cyber';
+import DatabaseV1 from '../../../../../build/contracts/DatabaseV1.json';
+
+let _databaseContract = null;
+const initialState = {
+ items: [],
+ fields: [],
+ loading: true,
+ isOwner: false,
+ totalFee: 0,
+ funded: '',
+
+ beneficiaries: [],
+ databaseContract: null,
+ web3: null,
+ ipfsGateway: null,
+
+ name: '',
+ description: '',
+ tag: '',
+ createdTimestamp: null,
+ entryCreationFee: 0,
+ admin: '',
+ userAccount: null,
+ abi: [],
+ isDbPaused: null,
+
+ contractVersion: null,
+ databaseAddress: null,
+ databaseSymbol: null,
+ databaseId: null,
+ entryCoreAddress: null,
+ entryCoreContract: null,
+ abiIpfsHash: null,
+ isSchemaExist: false,
+
+ claimFundOpen: false,
+ claimFeeOpen: false,
+ transferOwnershipOpen: false,
+ fundDatabaseOpen: false,
+ pauseDatabaseOpen: false,
+ resumeDatabaseOpen: false,
+ deleteDatabaseOpen: false,
+
+ claimRecordFundOpen: false,
+ transferRecordOwnershipOpen: false,
+ fundRecordOpen: false,
+ deleteRecordOpen: false,
+ editRecordOpen: false,
+
+ permissionGroup: 0,
+ recordForAction: null,
+};
+
+class ViewRegistry extends Container {
+ state = initialState;
+
+
+ init = (databaseSymbol) => {
+ this.setState(initialState);
+
+ let _databaseAddress = null;
+ let _databases = null;
+ let _userAccount = null;
+ let _web3 = null;
+ let _database = null;
+ let _databaseId = null;
+ let _chaingearContract = null;
+ let _abi = null;
+ let _fields = null;
+ let _entryCoreAddress = null;
+ let _entryCoreContract = null;
+ let _isDbPaused = null;
+ let _ipfsGateway = null;
+ let _beneficiaries = null;
+ let _permissionGroup = 0;
+ let _abiIpfsHash = null;
+
+ let _newState = {};
+
+ cyber.init()
+ .then(({ contract, web3 }) => {
+ _web3 = web3;
+ _chaingearContract = contract;
+ })
+ .then(() => cyber.getIpfsGateway())
+ .then((ipfsGateway) => {
+ _ipfsGateway = ipfsGateway;
+ })
+ .then(() => cyber.getDefaultAccount())
+ .then((defaultAccount) => {
+ _userAccount = defaultAccount;
+ })
+ .then(() => cyber.callContractMethod(_chaingearContract, 'getDatabaseIDBySymbol', databaseSymbol))
+ .then((databaseId) => {
+ _databaseId = databaseId;
+ })
+ .then(() => cyber.callContractMethod(_chaingearContract, 'getDatabase', _databaseId))
+ .then((database) => {
+ _database = cyber.mapDatabase(database);
+ _databaseAddress = _database.address;
+ _databaseContract = _web3.eth.contract(DatabaseV1.abi).at(_databaseAddress);
+ })
+ .then(() => cyber.getBeneficiaries(_databaseContract))
+ .then((beneficiaries) => {
+ _beneficiaries = beneficiaries;
+ })
+ .then(() => cyber.callContractMethod(_databaseContract, 'getPaused'))
+ .then((isDbPaused) => {
+ _isDbPaused = isDbPaused;
+ })
+ .then(() => cyber.callContractMethod(_databaseContract, 'getDatabasePermissions'))
+ .then((permissionGroup) => {
+ _permissionGroup = permissionGroup.toNumber();
+ })
+ .then(() => {
+ const fundedPromise = cyber.callContractMethod(_chaingearContract, 'getDatabaseBalance', _databaseId);
+ const totalFeePromise = cyber.callWeb3EthMethod(_web3, 'getBalance', _databaseAddress);
+ const ownerPromise = cyber.callContractMethod(_databaseContract, 'getAdmin');
+ const descriptionPromise = cyber.callContractMethod(_databaseContract, 'getDatabaseDescription');
+ const entryCreationFeePromise = cyber.callContractMethod(_databaseContract, 'getEntryCreationFee');
+
+ return Promise
+ .all([fundedPromise, totalFeePromise,
+ ownerPromise, descriptionPromise, entryCreationFeePromise]);
+ })
+ .then(([funded, totalFee, owner, description, entryCreationFee]) => {
+
+ const _funded = _web3.fromWei(_web3.toDecimal(funded[0].toNumber()));
+ const _entryCreationFee = _web3.fromWei(entryCreationFee, 'ether').toNumber();
+ const _totalFee = _web3.fromWei(_web3.toDecimal(totalFee), 'ether');
+
+ _newState = {
+ ..._newState,
+ ...{
+ name: _database.name,
+ createdTimestamp: _database.createdTimestamp,
+ admin: _database.admin,
+ contractVersion: _database.contractVersion,
+ tag: '',
+ web3: _web3,
+ databases: _databases,
+ databaseAddress: _databaseAddress,
+ databaseContract: _databaseContract,
+ funded: _funded,
+ totalFee: _totalFee,
+ userAccount: _userAccount,
+ isOwner: _userAccount === owner,
+ owner,
+ description,
+ databaseSymbol,
+ entryCreationFee: _entryCreationFee,
+ isDbPaused: _isDbPaused,
+ ipfsGateway: _ipfsGateway,
+ beneficiaries: _beneficiaries,
+ permissionGroup: _permissionGroup,
+ },
+ };
+ })
+ .then(() => cyber.callContractMethod(_databaseContract, 'getDatabaseInitStatus'))
+ .then((isSchemaExist) => {
+ if (!isSchemaExist) {
+ this.setState({
+ ..._newState,
+ isSchemaExist,
+ databaseId: _databaseId,
+ loading: false,
+ });
+ throw new Error('Schema is not exist');
+ } else {
+ _newState = {
+ ..._newState,
+ isSchemaExist,
+ databaseId: _databaseId,
+ };
+ }
+ })
+ //
+ //
+ // with schema
+ //
+ //
+ .then(() => cyber.callContractMethod(_databaseContract, 'getEntriesStorage'))
+ .then((entryAddress) => {
+ _entryCoreAddress = entryAddress;
+ })
+ .then(() => cyber.callContractMethod(_databaseContract, 'getSchemaDefinition'))
+ .then((schemaDefinitionJson) => {
+ const schemaDefinition = JSON.parse(schemaDefinitionJson);
+
+ _fields = schemaDefinition.fields.map(field => ({
+ ...field,
+ unique: field.unique === 1,
+ }));
+
+ _abiIpfsHash = schemaDefinition.build.ABI;
+
+ return schemaDefinition.build;
+ })
+ .then(buildOpts => cyber.getAbiByFields(_newState.name, _fields, buildOpts))
+ .then((abi) => {
+ _abi = abi;
+ _entryCoreContract = _web3.eth.contract(_abi).at(_entryCoreAddress);
+ })
+ .then(() => this.setState({
+ databaseContract: _databaseContract,
+ entryCoreAddress: _entryCoreAddress,
+ entryCoreContract: _entryCoreContract,
+ fields: _fields,
+ abi: _abi,
+ web3: _web3,
+ abiIpfsHash: _abiIpfsHash,
+ }))
+ .then(() => this.getDatabaseItems())
+ .then((items) => {
+ _newState = {
+ ..._newState,
+ ...{
+ items,
+ entriesAmount: items.length,
+ loading: false,
+ },
+ };
+ })
+ .then(() => {
+ this.setState(_newState);
+ })
+ .catch((error) => {
+ console.log(`Cannot load database data. Error: ${error}`);
+ });
+ };
+
+ getDatabaseItems = () => {
+ const {
+ databaseContract: contract, fields, abi, web3,
+ } = this.state;
+
+ return new Promise((topResolve, reject) => {
+
+ cyber.getDatabaseData(contract, fields, abi)
+ .then(({ items, fields, entryAddress }) => {
+ return Promise.all([
+ entryAddress,
+ items,
+ ...items.map(item => new Promise((resolve) => {
+ cyber.callContractMethod(contract, 'readEntryMeta', item.__index)
+ .then(data => resolve(data));
+ })),
+ ]);
+ })
+ .then(([entryAddress, items, ...data]) => {
+ const _items = items.map((item, index) => {
+ const currentEntryBalanceETH = web3.fromWei(data[index][4]).toNumber();
+ const owner = data[index][0];
+
+ return {
+ ...item,
+ currentEntryBalanceETH,
+ owner,
+ id: item.__index,
+ };
+ });
+
+ topResolve(_items);
+ });
+ });
+ };
+
+ /*
+ * Database Actions
+ */
+
+ claimDbFee = () => {
+ // not implemented
+ };
+
+ onUpdatePermissionGroup = () => {
+ const newPermissionGroup = this.permissionGroup.value;
+
+ this.setLoading(true);
+
+ cyber.sendTransactionMethod(
+ _databaseContract.updateCreateEntryPermissionGroup, newPermissionGroup,
+ )
+ .then(hash => cyber.eventPromise(_databaseContract.PermissionGroupChanged()))
+ .then(() => {
+ this.setState({
+ permissionGroup: +newPermissionGroup,
+ loading: false,
+ });
+ });
+ };
+
+ changeDescription = (description) => {
+ const { databaseContract, databaseSymbol } = this.state;
+
+ this.setLoading(true);
+
+ cyber.callContractMethod(databaseContract, 'updateDatabaseDescription', description)
+ .then(data => console.log(`Description change. Tx:${data}`))
+ .then(() => cyber.eventPromise(databaseContract.DescriptionUpdated()))
+ .then(results => console.log(`Description changed. Results: ${results}`))
+ .then(() => this.init(databaseSymbol))
+ .catch(() => this.setLoading(false));
+ };
+
+ changeDbTag = (tag) => {
+ // not implemented
+ };
+
+ changeEntryCreationFee = (newFee) => {
+ const { databaseContract, web3 } = this.state;
+ const fee = web3.toWei(newFee, 'ether');
+
+ this.setLoading(true);
+ cyber.callContractMethod(databaseContract, 'updateEntryCreationFee', fee)
+ .then(data => console.log(`Update entry creation fee. Data: ${data}`))
+ .then(() => cyber.eventPromise(databaseContract.EntryCreationFeeUpdated()))
+ .then(results => console.log(`Update entry creation fee. Results: ${results}`))
+ .then(() => this.setState({
+ entryCreationFee: newFee,
+ loading: false,
+ }))
+ .catch(() => this.setLoading(false));
+ };
+
+ fundDatabase = (amount) => {
+ const { databaseId, web3, databaseSymbol } = this.state;
+ let chaingerContract;
+
+ this.closePopups();
+
+ this.setLoading(true);
+ cyber.getChaingearContract()
+ .then((contract) => {
+ chaingerContract = contract;
+ })
+ .then(() => cyber.callContractMethod(chaingerContract, 'fundDatabase', databaseId, {
+ value: web3.toWei(amount, 'ether'),
+ }))
+ .then(() => cyber.eventPromise(chaingerContract.DatabaseFunded()))
+ .then(() => {
+ this.init(databaseSymbol);
+ });
+ };
+
+ claimDatabaseFunds = (amount) => {
+ const { databaseId, web3, databaseSymbol } = this.state;
+ let chaingerContract;
+
+ this.closePopups();
+
+ this.setLoading(true);
+ cyber.getChaingearContract()
+ .then((contract) => {
+ chaingerContract = contract;
+ })
+ .then(() => cyber.callContractMethod(
+ chaingerContract, 'claimDatabaseFunds', databaseId, web3.toWei(amount, 'ether'),
+ ))
+ .then(data => console.log(`Claim database funds. Data: ${data}`))
+ .then(() => cyber.eventPromise(chaingerContract.DatabaseFundsClaimed()))
+ .then(() => this.init(databaseSymbol))
+ .catch((error) => {
+ console.log(`Cant claim database funds. Details: ${error}`);
+ this.setLoading(false);
+ });
+ };
+
+ transferDatabaseOwnership = (currentOwner, newOwner) => {
+ const { databaseId, databaseSymbol } = this.state;
+ let chaingerContract;
+
+ this.closePopups();
+
+ this.setLoading(true);
+ cyber.getChaingearContract()
+ .then((contract) => {
+ chaingerContract = contract;
+ })
+ .then(() => cyber.callContractMethod(
+ chaingerContract, 'transferFrom', currentOwner, newOwner, databaseId,
+ ))
+ .then(data => console.log(`Transfer db ownership. Data: ${data}`))
+ .then(() => cyber.eventPromise(
+ chaingerContract.Transfer(),
+ ))
+ .then(() => this.init(databaseSymbol))
+ .catch((error) => {
+ console.log(`Cant transfer db ownership. Error: ${error}`);
+ this.setLoading(false);
+ });
+ };
+
+ pauseDb = () => {
+ const { databaseContract, databaseSymbol } = this.state;
+
+ this.closePopups();
+
+ this.setLoading(true);
+ cyber.callContractMethod(databaseContract, 'pause')
+ .then(data => console.log(`Pause DB. Data: ${data}`))
+ .then(() => cyber.eventPromise(databaseContract.Pause()))
+ .then(results => console.log(`Db paused. Results: ${results}`))
+ .then(() => this.init(databaseSymbol))
+ .catch((error) => {
+ console.log(`Cant pause db. Error: ${error}`);
+ this.setLoading(false);
+ });
+ };
+
+ unpauseDb = () => {
+ const { databaseContract, databaseSymbol } = this.state;
+
+ this.closePopups();
+
+ this.setLoading(true);
+ cyber.callContractMethod(databaseContract, 'unpause')
+ .then(data => console.log(`Unpause DB. Data: ${data}`))
+ .then(() => cyber.eventPromise(databaseContract.Unpause()))
+ .then(results => console.log(`Db unpaused. Results: ${results}`))
+ .then(() => this.init(databaseSymbol))
+ .catch((error) => {
+ console.log(`Cant unpause db. Error: ${error}`);
+ this.setLoading(false);
+ });
+ };
+
+ deleteDb = () => {
+ const { databaseId } = this.state;
+ let chaingerContract;
+
+ this.closePopups();
+
+ this.setLoading(true);
+ cyber.getChaingearContract()
+ .then((contract) => {
+ chaingerContract = contract;
+ })
+ .then(() => cyber.callContractMethod(chaingerContract, 'deleteDatabase', databaseId))
+ .then(data => console.log(`DeleteDB: ${databaseId}. Tx: ${data}`))
+ .then(() => cyber.eventPromise(chaingerContract.DatabaseDeleted()))
+ .then(() => hashHistory.push('/'))
+ .catch((error) => {
+ console.log(`Cant delete database. Details: ${error}`);
+ this.closePopups();
+ });
+ };
+
+ onTransferOwnership = () => {
+ this.setState({
+ transferOwnershipOpen: true,
+ });
+ };
+
+ onFundDb = () => {
+ this.setState({
+ fundDatabaseOpen: true,
+ });
+ };
+
+ onClaimFunds = () => {
+ this.setState({
+ claimFundOpen: true,
+ });
+ };
+
+ onClaimFee = () => {
+ this.setState({
+ claimFeeOpen: true,
+ });
+ };
+
+ onDeleteDb = () => {
+ this.setState({
+ deleteDatabaseOpen: true,
+ });
+ };
+
+ onPauseDb = () => {
+ this.setState({
+ pauseDatabaseOpen: true,
+ });
+ };
+
+ onResumeDb = () => {
+ this.setState({
+ resumeDatabaseOpen: true,
+ });
+ };
+
+ /*
+ * Record Actions
+ */
+
+ addRecord = () => {
+ const { databaseContract, name, databaseSymbol } = this.state;
+
+ if (!databaseContract) {
+ return;
+ }
+
+ cyber.callContractMethod(databaseContract, 'getEntryCreationFee')
+ .then(fee => fee.toNumber())
+ .then(fee => cyber.callContractMethod(databaseContract, 'createEntry', { value: fee }))
+ .then((entryId) => {
+ console.log(`New Entry created: ${entryId}`);
+ this.setState({
+ loading: true,
+ });
+ return cyber.eventPromise(databaseContract.EntryCreated());
+ })
+ .then(() => this.init(databaseSymbol))
+ .catch(() => {
+ console.log(`Cannot add entry to ${name}`);
+ });
+ };
+
+ transferRecordOwnership = (currentOwner, newOwner, entryID) => {
+ const { databaseSymbol, databaseContract } = this.state;
+
+ this.closePopups();
+
+ this.setLoading(true);
+ cyber.callContractMethod(
+ databaseContract, 'transferFrom', currentOwner, newOwner, entryID,
+ )
+ .then(data => console.log(`Transfer entry #${entryID} ownership. Tx: ${data}`))
+ .then(() => cyber.eventPromise(databaseContract.Transfer()))
+ .then(() => this.init(databaseSymbol));
+ };
+
+ claimRecord = (entryID, amount) => {
+ const { databaseSymbol, databaseContract, web3 } = this.state;
+
+ this.closePopups();
+
+ this.setLoading(true);
+ cyber.callContractMethod(
+ databaseContract, 'claimEntryFunds', entryID, web3.toWei(amount, 'ether'),
+ )
+ .then(data => console.log(`Claim entry #${entryID} funds(${amount} ETH). Tx: ${data}`))
+ .then(() => cyber.eventPromise(databaseContract.EntryFundsClaimed()))
+ .then(() => this.init(databaseSymbol));
+ };
+
+ fundRecord = (id, value) => {
+ this.setState({ loading: true });
+
+ this.closePopups();
+ const { databaseContract, web3 } = this.state;
+
+ cyber.callContractMethod(databaseContract, 'fundEntry', id, {
+ value: web3.toWei(value, 'ether'),
+ })
+ .then((data) => {
+ console.log(`Entry ${id} funded. ETH: ${value}. Data: ${data}`);
+ })
+ .then(() => cyber.eventPromise(databaseContract.EntryFunded()))
+ .then(results => console.log(`Entry ${id} funded. Results: ${results}`))
+ .then(() => this.getDatabaseItems())
+ .then((items) => {
+ this.setState({
+ items,
+ loading: false,
+ });
+ })
+ .catch(() => this.setLoading(false));
+ };
+
+ updateRecord = (values, entryId) => {
+ const { entryCoreContract } = this.state;
+
+ this.setLoading(true);
+
+ cyber.callContractMethod(entryCoreContract, 'updateEntry', entryId, ...values)
+ .then(data => console.log(`Update record. Data: ${data}`))
+ .then(() => cyber.eventPromise(entryCoreContract.EntryUpdated()))
+ .then(() => this.getDatabaseItems())
+ .then(items => this.setState({
+ items,
+ loading: false,
+ }))
+ .catch(() => this.setLoading(false));
+ };
+
+ deleteRecord = (id) => {
+ const { databaseContract } = this.state;
+
+ this.closePopups();
+ this.setLoading(true);
+
+ cyber.callContractMethod(databaseContract, 'deleteEntry', id)
+ .then(() => cyber.eventPromise(databaseContract.EntryDeleted()))
+ .then(() => this.getDatabaseItems())
+ .then(items => this.setState({
+ items,
+ loading: false,
+ }))
+ .catch(() => this.setLoading(false));
+ };
+
+ onRecordTransferOwnership = (record) => {
+ this.setState({
+ transferRecordOwnershipOpen: true,
+ recordForAction: record,
+ });
+ };
+
+ onFundRecord = (record) => {
+ this.setState({
+ fundRecordOpen: true,
+ recordForAction: record,
+ });
+ };
+
+ onClaimRecordFunds = (record) => {
+ this.setState({
+ claimRecordFundOpen: true,
+ recordForAction: record,
+ });
+ };
+
+ onDeleteRecord = (record) => {
+ this.setState({
+ deleteRecordOpen: true,
+ recordForAction: record,
+ });
+ };
+
+ onRecordEdit = (record) => {
+ this.setState({
+ editRecordOpen: true,
+ recordForAction: record,
+ });
+ };
+
+ /*
+ * Page Actions
+ */
+
+ setLoading = (value) => {
+ this.setState({
+ loading: value,
+ });
+ };
+
+ closePopups = () => {
+ this.setState({
+ claimFundOpen: false,
+ claimFeeOpen: false,
+ transferOwnershipOpen: false,
+ fundDatabaseOpen: false,
+ pauseDatabaseOpen: false,
+ resumeDatabaseOpen: false,
+ deleteDatabaseOpen: false,
+
+ claimRecordFundOpen: false,
+ transferRecordOwnershipOpen: false,
+ fundRecordOpen: false,
+ deleteRecordOpen: false,
+ editRecordOpen: false,
+ });
+ };
+}
+
+export default new ViewRegistry();
diff --git a/client/app/src/containers/home/index.jsx b/client/app/src/containers/home/index.jsx
new file mode 100644
index 00000000..5a9320cd
--- /dev/null
+++ b/client/app/src/containers/home/index.jsx
@@ -0,0 +1,186 @@
+import React, { Component } from 'react';
+
+import { Link } from 'react-router';
+
+import {
+ Section, SectionContent, Badge,
+ FooterButton, Container, Text,
+ HomeTable, LinkHash, Button,
+} from '@cybercongress/ui';
+import {
+ getDatabases, getDefaultAccount, init,
+} from '../../utils/cyber';
+import { formatDate } from '../../utils/utils';
+
+class Home extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ databases: [],
+ account: null,
+ };
+ }
+
+ componentDidMount() {
+ init()
+ .then(() => getDefaultAccount())
+ .then(account => this.setState({
+ account: account.toLowerCase(),
+ }))
+ .then(() => getDatabases())
+ .then(databases => this.setState({
+ databases,
+ }));
+ }
+
+ render() {
+ const { databases, account } = this.state;
+
+ const rows = databases.map(database => (
+
+ ));
+
+ const myRows = databases.filter(x => x.admin === account).map(database => (
+
+ );
+
+ if (myRows.length > 0) {
+ content = (
+
+ );
+ }
+}
+
+
+export default Home;
diff --git a/client/app/src/containers/new/DatabaseInitialization.jsx b/client/app/src/containers/new/DatabaseInitialization.jsx
new file mode 100644
index 00000000..847a0423
--- /dev/null
+++ b/client/app/src/containers/new/DatabaseInitialization.jsx
@@ -0,0 +1,433 @@
+import React, { Component } from 'react';
+
+import {
+ Content, ContainerRegister, SideBar,
+ Panel, PageTitle, RemoveButton,
+ Message, StatusBar, LinkHash,
+ ParamRow, WideInput, WideSelect,
+ AddButton, Code, ProgressBar,
+ CircleLable, TableItemBen, TableRegistry,
+ FlexContainer, FlexContainerLeft, FlexContainerRight,
+ Button, Text,
+} from '@cybercongress/ui';
+
+import {
+ getDefaultAccount,
+ deployDatabase,
+ getChaingearContract,
+ eventPromise, callContractMethod, init,
+} from '../../utils/cyber';
+
+import DatabaseSource from '../../resources/DatabaseV1.sol';
+import { calculateBensShares, debounce } from '../../utils/utils';
+
+const DB_NAME_REGEXP = /\b[a-z0-9][a-z0-9-]*$/;
+const DB_SYMBOL_REGEXP = /\b[A-Z0-9][A-Z0-9-]*$/;
+
+class NewDatabase extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ dbBuilders: [],
+ dbDescription: '',
+
+ beneficiaries: [],
+ databaseId: null,
+
+ dbName: '',
+ dbSymbol: '',
+ dbVersion: '',
+
+ nameErrorMessage: null,
+ symbolErrorMessage: null,
+
+ inProgress: false,
+ message: '',
+ type: 'processing',
+ };
+
+ this.checkDbName = debounce(this.checkDbName, 1000);
+ this.checkDbSymbol = debounce(this.checkDbSymbol, 1000);
+ }
+
+ componentWillMount() {
+ init()
+ .then(() => getDefaultAccount())
+ .then(defaultAccount => this.setState({
+ beneficiaries: [{
+ address: defaultAccount,
+ stake: 1,
+ share: 100,
+ }],
+ }))
+ .then(() => this.getDatabaseVersions());
+ }
+
+ createDatabase = () => {
+ const { beneficiaries, dbName, dbSymbol, dbVersion } = this.state;
+ const bens = beneficiaries.map(ben => ben.address);
+ const stakes = beneficiaries.map(ben => ben.stake);
+
+ this.setState({ message: 'processing...', inProgress: true, type: 'processing' });
+ deployDatabase(dbName, dbSymbol, dbVersion, bens, stakes)
+ .then(({ txHash }) => getChaingearContract())
+ .then(contract => eventPromise(contract.DatabaseCreated()))
+ .then((results) => {
+ this.setState({
+ inProgress: false,
+ databaseId: results.args.databaseChaingearID.toNumber(),
+ });
+ })
+ .catch((error) => {
+ console.log(`Cannot create database ${dbName}. Error: ${error}`);
+ this.setState({
+ inProgress: false,
+ });
+ });
+ };
+
+ getDatabaseVersions = () => {
+ let chaingerContract;
+
+ return getChaingearContract()
+ .then((contract) => {
+ chaingerContract = contract;
+ return callContractMethod(contract, 'getAmountOfBuilders');
+ })
+ .then((buildersCount) => {
+ const buildersCountNumber = buildersCount.toNumber();
+ const buildersPromises = [];
+
+ for (let index = 0; index < buildersCountNumber; index += 1) {
+ const builderPromise = callContractMethod(chaingerContract, 'getBuilderByID', index)
+ .then(builderVersion => Promise.all([
+ callContractMethod(chaingerContract, 'getDatabaseBuilder', builderVersion),
+ builderVersion,
+ ]))
+ .then(([builderMeta, builderVersion]) => (
+ {
+ version: builderVersion,
+ description: builderMeta[2],
+ }
+ ));
+
+ buildersPromises.push(builderPromise);
+ }
+
+ return Promise.all(buildersPromises);
+ })
+ .then(dbBuilders => this.setState({ dbBuilders }));
+ };
+
+ checkDbName = (dbName) => {
+ if (!dbName) {
+ this.setState({
+ dbName,
+ });
+
+ return;
+ }
+
+ let errorMessage = null;
+
+ this.checkRegexp(DB_NAME_REGEXP, dbName)
+ .then((isValid) => {
+ if (!isValid) {
+ errorMessage = 'Lowercase letters, digits and dash only';
+
+ throw new Error('invalid string');
+ }
+ })
+ .then(() => getChaingearContract())
+ .then(contract => callContractMethod(contract, 'getNameExist', dbName))
+ .then((isNameExist) => {
+ if (isNameExist) {
+ errorMessage = 'Database name already exist';
+ }
+ })
+ .then(() => {
+ this.setState({
+ dbName,
+ nameErrorMessage: errorMessage,
+ });
+ })
+ .catch(() => {
+ this.setState({
+ dbName,
+ nameErrorMessage: errorMessage,
+ });
+ });
+ };
+
+ checkDbSymbol = (dbSymbol) => {
+ if (!dbSymbol) {
+ this.setState({
+ dbSymbol,
+ });
+
+ return;
+ }
+
+ let errorMessage = '';
+
+ this.checkRegexp(DB_SYMBOL_REGEXP, dbSymbol)
+ .then((isValid) => {
+ if (!isValid) {
+ errorMessage = 'Uppercase letters, digits and dash only';
+
+ throw new Error('invalid string');
+ }
+ })
+ .then(() => getChaingearContract())
+ .then(contract => callContractMethod(contract, 'getSymbolExist', dbSymbol))
+ .then((isSymbolExist) => {
+ if (isSymbolExist) {
+ errorMessage = 'Symbol already exist';
+ }
+ })
+ .then(() => {
+ this.setState({
+ dbSymbol,
+ symbolErrorMessage: errorMessage,
+ });
+ })
+ .catch(() => {
+ this.setState({
+ dbSymbol,
+ symbolErrorMessage: errorMessage,
+ });
+ });
+ };
+
+ checkRegexp = (regexp, value) => new Promise(resolve => resolve(regexp.test(value)));
+
+ onDbNameChange = (event) => {
+ event.persist();
+
+ const { value } = event.target;
+
+ this.dbSymbol.value = value.toUpperCase();
+ this.checkDbName(value);
+ this.checkDbSymbol(this.dbSymbol.value);
+ };
+
+ onDbSymbolChange = (event) => {
+ event.persist();
+
+ this.checkDbSymbol(event.target.value);
+ };
+
+ onDbVersionChange = (event) => {
+ const { dbBuilders } = this.state;
+ const builderVersion = event.target.value;
+ const dbBuilder = dbBuilders.find(builder => builder.version === builderVersion);
+ const description = dbBuilder ? dbBuilder.description : null;
+
+ this.setState({
+ dbVersion: event.target.value,
+ dbDescription: description,
+ });
+ };
+
+ onStakeChange = (e) => {
+ const { value } = e.target;
+
+ if (isNaN(value)) {
+ e.target.value = '';
+ }
+ };
+
+ addBeneficiary = () => {
+ const address = this.benAddress.value;
+ const stake = this.benStake.value;
+ const { beneficiaries } = this.state;
+
+ if (!address || !stake) {
+ return;
+ }
+
+ this.benAddress.value = '';
+ this.benStake.value = '';
+
+ this.setState({
+ beneficiaries: beneficiaries.concat([{
+ address,
+ stake,
+ share: 0,
+ }]),
+ });
+ };
+
+ removeBeneficiary = (address) => {
+ const { beneficiaries } = this.state;
+
+ this.setState({
+ beneficiaries: beneficiaries.filter(ben => ben.address !== address),
+ });
+ };
+
+ render() {
+ const {
+ dbName, dbSymbol, dbVersion, dbBuilders, dbDescription,
+ databaseId, beneficiaries,
+ message, inProgress, type,
+ nameErrorMessage, symbolErrorMessage,
+ } = this.state;
+
+ const bens = calculateBensShares(beneficiaries);
+ const benCount = beneficiaries.length;
+ const canCreate = dbName.length > 0 && dbSymbol.length > 0 && dbVersion.length > 0
+ && benCount > 0;
+
+ return (
+
+ );
+ }
+}
+
+
+export default NewDatabase;
diff --git a/client/app/src/containers/new/SchemaDefinition.jsx b/client/app/src/containers/new/SchemaDefinition.jsx
new file mode 100644
index 00000000..47186845
--- /dev/null
+++ b/client/app/src/containers/new/SchemaDefinition.jsx
@@ -0,0 +1,276 @@
+import React, { Component } from 'react';
+
+import {
+ Content, ContainerRegister, SideBar,
+ FieldsTable, PageTitle, RemoveButton,
+ Message, StatusBar, WideSelect,
+ Code, CircleLable, ProgressBar,
+ Checkbox, TableRegistry, WideInput,
+ AddButton, Panel, FlexContainer,
+ FlexContainerLeft, FlexContainerRight,
+ Button,
+} from '@cybercongress/ui';
+
+import {
+ generateContractCode,
+ deploySchema,
+ getDatabaseContract,
+ getChaingearContract,
+ callContractMethod,
+ mapDatabase,
+ eventPromise,
+} from '../../utils/cyber';
+
+const MAX_FIELD_COUNT = 10;
+
+class SchemaDefinition extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ fields: [],
+ databaseName: '',
+ databaseAddress: null,
+ databaseSymbol: null,
+
+ inProgress: false,
+ message: '',
+ type: 'processing',
+
+ isSchemaCreated: false,
+
+ disableUniqueCheckbox: false,
+ };
+ }
+
+ componentDidMount() {
+ const { dbsymbol } = this.props.params;
+ let chaingearContract;
+ let databaseId;
+
+ getChaingearContract()
+ .then((contract) => {
+ chaingearContract = contract;
+
+ return callContractMethod(contract, 'getDatabaseIDBySymbol', dbsymbol);
+ })
+ .then((dbId) => {
+ databaseId = dbId;
+
+ return callContractMethod(chaingearContract, 'getDatabase', databaseId);
+ })
+ .then(database => mapDatabase(database, databaseId))
+ .then(database => this.setState({
+ databaseAddress: database.address,
+ databaseName: database.name,
+ databaseSymbol: dbsymbol,
+ }));
+ }
+
+ add = () => {
+ const { fields } = this.state;
+ const name = this.fieldName.value;
+ const type = this.fieldType.value;
+ const unique = type === 'bool' ? false : this.fieldUnique.checked;
+
+ const newItem = {
+ name,
+ type,
+ unique,
+ };
+
+ this.fieldName.value = '';
+ this.fieldUnique.checked = false;
+
+ this.setState({
+ fields: fields.concat(newItem),
+ });
+ };
+
+ remove = (name) => {
+ const { fields } = this.state;
+
+ this.setState({
+ fields: fields.filter(x => x.name !== name),
+ });
+ };
+
+ createSchema = () => {
+ const { databaseName, fields, databaseAddress } = this.state;
+
+ this.setState({ message: 'processing...', inProgress: true, type: 'processing' });
+
+ let databaseContract;
+
+ getDatabaseContract(databaseAddress)
+ .then((dbContract) => {
+ databaseContract = dbContract;
+ return deploySchema(databaseName, fields, databaseContract);
+ })
+ .then(() => eventPromise(databaseContract.DatabaseInitialized()))
+ .then(() => {
+ this.setState({
+ inProgress: false,
+ isSchemaCreated: true,
+ });
+ })
+ .catch(() => this.setState({
+ inProgress: false,
+ }));
+ };
+
+ onFieldTypeChange = (event) => {
+ if (event.target.value === 'bool') {
+ this.setState({
+ disableUniqueCheckbox: true,
+ });
+ } else {
+ this.setState({
+ disableUniqueCheckbox: false,
+ });
+ }
+ };
+
+ render() {
+ const {
+ databaseName, fields, message, inProgress, type,
+ isSchemaCreated, databaseSymbol, disableUniqueCheckbox,
+ } = this.state;
+ const code = generateContractCode(databaseName, fields);
+ const fieldsCount = fields.length;
+ const canCreateSchema = fieldsCount > 0
+ && fieldsCount <= MAX_FIELD_COUNT && !isSchemaCreated;
+
+ return (
+
+ );
+ }
+}
+
+export default SchemaDefinition;
diff --git a/client/app/src/main.jsx b/client/app/src/main.jsx
new file mode 100644
index 00000000..3cf08944
--- /dev/null
+++ b/client/app/src/main.jsx
@@ -0,0 +1,9 @@
+import * as React from 'react';
+import * as ReactDOM from 'react-dom';
+import { Root } from './root';
+import '@cybercongress/ui/lib/styles.css';
+
+ReactDOM.render(
+
,
+ document.getElementById('root'),
+);
diff --git a/client/app/src/resources/DatabaseV1.sol b/client/app/src/resources/DatabaseV1.sol
new file mode 120000
index 00000000..6b2b015e
--- /dev/null
+++ b/client/app/src/resources/DatabaseV1.sol
@@ -0,0 +1 @@
+../../../../contracts/databases/DatabaseV1.sol
\ No newline at end of file
diff --git a/client/app/src/resources/Dependencies.sol b/client/app/src/resources/Dependencies.sol
new file mode 100644
index 00000000..82edbf59
--- /dev/null
+++ b/client/app/src/resources/Dependencies.sol
@@ -0,0 +1,188 @@
+pragma solidity 0.4.25;
+
+// File: contracts/common/ISchema.sol
+
+interface ISchema {
+
+ function createEntry() external;
+ function deleteEntry(uint256) external;
+}
+
+// File: contracts/common/IDatabase.sol
+
+interface IDatabase {
+
+ function createEntry() external payable returns (uint256);
+ function auth(uint256, address) external;
+ function deleteEntry(uint256) external;
+ function fundEntry(uint256) external payable;
+ function claimEntryFunds(uint256, uint256) external;
+ function updateEntryCreationFee(uint256) external;
+ function updateDatabaseDescription(string) external;
+ function addDatabaseTag(bytes32) external;
+ function updateDatabaseTag(uint8, bytes32) external;
+ function removeDatabaseTag(uint8) external;
+ function readEntryMeta(uint256) external view returns (
+ address,
+ address,
+ uint256,
+ uint256,
+ uint256,
+ uint256
+ );
+ function getChaingearID() external view returns (uint256);
+ function getEntriesIDs() external view returns (uint256[]);
+ function getIndexByID(uint256) external view returns (uint256);
+ function getEntryCreationFee() external view returns (uint256);
+ function getEntriesStorage() external view returns (address);
+ function getSchemaDefinition() external view returns (string);
+ function getDatabaseBalance() external view returns (uint256);
+ function getDatabaseDescription() external view returns (string);
+ function getDatabaseTags() external view returns (bytes32[]);
+ function getDatabaseSafe() external view returns (address);
+ function getSafeBalance() external view returns (uint256);
+ function getDatabaseInitStatus() external view returns (bool);
+ function pause() external;
+ function unpause() external;
+ function transferAdminRights(address) external;
+ function getAdmin() external view returns (address);
+ function getPaused() external view returns (bool);
+ function transferOwnership(address) external;
+ function deletePayees() external;
+}
+
+// File: openzeppelin-solidity/contracts/ownership/Ownable.sol
+
+/**
+ * @title Ownable
+ * @dev The Ownable contract has an owner address, and provides basic authorization control
+ * functions, this simplifies the implementation of "user permissions".
+ */
+contract Ownable {
+ address public owner;
+
+
+ event OwnershipRenounced(address indexed previousOwner);
+ event OwnershipTransferred(
+ address indexed previousOwner,
+ address indexed newOwner
+ );
+
+
+ /**
+ * @dev The Ownable constructor sets the original `owner` of the contract to the sender
+ * account.
+ */
+ constructor() public {
+ owner = msg.sender;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwner() {
+ require(msg.sender == owner);
+ _;
+ }
+
+ /**
+ * @dev Allows the current owner to relinquish control of the contract.
+ * @notice Renouncing to ownership will leave the contract without an owner.
+ * It will not be possible to call the functions with the `onlyOwner`
+ * modifier anymore.
+ */
+ function renounceOwnership() public onlyOwner {
+ emit OwnershipRenounced(owner);
+ owner = address(0);
+ }
+
+ /**
+ * @dev Allows the current owner to transfer control of the contract to a newOwner.
+ * @param _newOwner The address to transfer ownership to.
+ */
+ function transferOwnership(address _newOwner) public onlyOwner {
+ _transferOwnership(_newOwner);
+ }
+
+ /**
+ * @dev Transfers control of the contract to a newOwner.
+ * @param _newOwner The address to transfer ownership to.
+ */
+ function _transferOwnership(address _newOwner) internal {
+ require(_newOwner != address(0));
+ emit OwnershipTransferred(owner, _newOwner);
+ owner = _newOwner;
+ }
+}
+
+// File: openzeppelin-solidity/contracts/introspection/ERC165.sol
+
+/**
+ * @title ERC165
+ * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
+ */
+interface ERC165 {
+
+ /**
+ * @notice Query if a contract implements an interface
+ * @param _interfaceId The interface identifier, as specified in ERC-165
+ * @dev Interface identification is specified in ERC-165. This function
+ * uses less than 30,000 gas.
+ */
+ function supportsInterface(bytes4 _interfaceId)
+ external
+ view
+ returns (bool);
+}
+
+// File: openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol
+
+/**
+ * @title SupportsInterfaceWithLookup
+ * @author Matt Condon (@shrugs)
+ * @dev Implements ERC165 using a lookup table.
+ */
+contract SupportsInterfaceWithLookup is ERC165 {
+
+ bytes4 public constant InterfaceId_ERC165 = 0x01ffc9a7;
+ /**
+ * 0x01ffc9a7 ===
+ * bytes4(keccak256('supportsInterface(bytes4)'))
+ */
+
+ /**
+ * @dev a mapping of interface id to whether or not it's supported
+ */
+ mapping(bytes4 => bool) internal supportedInterfaces;
+
+ /**
+ * @dev A contract implementing SupportsInterfaceWithLookup
+ * implement ERC165 itself
+ */
+ constructor()
+ public
+ {
+ _registerInterface(InterfaceId_ERC165);
+ }
+
+ /**
+ * @dev implement supportsInterface(bytes4) using a lookup table
+ */
+ function supportsInterface(bytes4 _interfaceId)
+ external
+ view
+ returns (bool)
+ {
+ return supportedInterfaces[_interfaceId];
+ }
+
+ /**
+ * @dev private method for registering an interface
+ */
+ function _registerInterface(bytes4 _interfaceId)
+ internal
+ {
+ require(_interfaceId != 0xffffffff);
+ supportedInterfaces[_interfaceId] = true;
+ }
+}
\ No newline at end of file
diff --git a/client/app/src/root.jsx b/client/app/src/root.jsx
new file mode 100644
index 00000000..3f6648f8
--- /dev/null
+++ b/client/app/src/root.jsx
@@ -0,0 +1,26 @@
+import * as React from 'react';
+import { Router, Route } from 'react-router';
+import { createHashHistory } from 'history'; // use hash becouse ipfs use path /ipfs/hash/{app}
+import { MainContainer } from '@cybercongress/ui';
+import HomePage from './containers/home';
+import NewDatabase from './containers/new/DatabaseInitialization';
+import DatabasePage from './containers/database';
+import App from './containers/App/App';
+import SchemaDefinition from './containers/new/SchemaDefinition';
+
+export const history = createHashHistory({ });
+
+export function Root() {
+ return (
+
+ );
+}
diff --git a/client/app/src/utils/cyber.js b/client/app/src/utils/cyber.js
new file mode 100644
index 00000000..e822da90
--- /dev/null
+++ b/client/app/src/utils/cyber.js
@@ -0,0 +1,623 @@
+import Web3 from 'web3';
+import ChaingearBuild from '../../../../build/contracts/Chaingear.json';
+import generateContractCode from './generateContractCode';
+import DatabaseV1 from '../../../../build/contracts/DatabaseV1.json';
+
+/*
+* WEB3
+*/
+
+let currentWeb3;
+
+const loadWeb3 = new Promise(((resolve, reject) => {
+ // Wait for loading completion to avoid race conditions with web3 injection timing.
+ window.addEventListener('load', () => {
+ let results;
+ let { web3 } = window;
+
+ // Checking if Web3 has been injected by the browser (Mist/MetaMask)
+ if (typeof web3 !== 'undefined') {
+ // Use Mist/MetaMask's provider.
+
+ if (web3.currentProvider) {
+ web3 = new Web3(web3.currentProvider);
+ } else {
+ const provider = new Web3.providers.HttpProvider();
+
+ web3 = new Web3(provider);
+ }
+
+ results = {
+ web3,
+ };
+
+ console.log('Injected web3 detected.');
+
+ resolve(results);
+ } else {
+ // Fallback to localhost if no web3 injection. We've configured this to
+ // use the development console's port by default.
+ web3 = new Web3(new Web3.providers.HttpProvider());
+
+ results = {
+ web3,
+ };
+
+ console.log('No web3 instance injected, using Local web3.');
+
+ resolve(results);
+ }
+ });
+}));
+
+export const getWeb3 = new Promise((resolve) => {
+ if (currentWeb3) {
+ resolve({ web3: currentWeb3 });
+ } else {
+ loadWeb3.then(({ web3 }) => {
+ currentWeb3 = web3;
+ resolve({ web3 });
+ });
+ }
+});
+
+export const getDefaultAccount = () => new Promise(resolve => getWeb3
+ .then(({ web3 }) => {
+ const { defaultAccount } = web3.eth;
+
+ if (defaultAccount) {
+ resolve(defaultAccount);
+ } else {
+ console.log('ETH default account is null');
+ resolve(null);
+ }
+ }));
+
+export const setDefaultAccount = address => new Promise(resolve => getWeb3
+ .then(() => { currentWeb3.eth.defaultAccount = address; })
+ .then(() => resolve()));
+
+/*
+* IPFS
+*/
+
+const IPFS = require('ipfs-api');
+
+const getIpfsConfig = () => {
+ if (window.getIpfsConfig) {
+ return window.getIpfsConfig();
+ }
+
+ return Promise.resolve({
+ host: 'localhost',
+ port: 5001,
+ protocol: 'http',
+ });
+};
+
+export const getIpfsGateway = () => {
+ if (window.getIpfsGateway) {
+ return window.getIpfsGateway();
+ }
+
+ return Promise.resolve('http://localhost:8080');
+};
+
+/*
+* Networks
+*/
+
+let currentNetworkId;
+
+const networksIds = {
+ 42: 'Kovan',
+ 1: 'Main',
+ 5777: 'TestNet',
+ 4: 'Rinkeby',
+};
+
+export const checkNetwork = () => new Promise((resolve) => {
+ const networks = Object.keys(ChaingearBuild.networks);
+
+ getWeb3.then(({ web3 }) => {
+ web3.version.getNetwork((err, netId) => {
+ currentNetworkId = netId;
+
+ resolve({
+ isCorrectNetwork: networks.indexOf(netId) !== -1,
+ networkId: netId,
+ contractNetworks: networks,
+ });
+ });
+ });
+});
+
+export const getNetworkStr = (networkId) => {
+ if (networksIds[networkId]) {
+ return networksIds[networkId];
+ }
+
+ return 'Unknown network';
+};
+
+/*
+* Wrappers
+*/
+
+export const callContractMethod = (contract, method, ...args) => new Promise((resolve, reject) => {
+ contract[method].apply(contract, [...args, (e, data) => {
+ if (e) {
+ console.log('Rejected contract call. Method: ', method, ' args: ', args);
+ reject(e);
+ } else {
+ resolve(data);
+ }
+ }]);
+});
+
+export const sendTransactionMethod = (contractMethod, ...args) => new Promise((resolve, reject) => {
+ contractMethod.sendTransaction.apply(contractMethod, [...args, (e, data) => {
+ if (e) {
+ console.log('Rejected send transaction method. Args: ', args);
+ reject(e);
+ } else {
+ resolve(data);
+ }
+ }]);
+});
+
+export const callWeb3EthMethod = (web3, method, ...args) => new Promise((resolve, reject) => {
+ web3.eth[method].apply(web3, [...args, (e, data) => {
+ if (e) {
+ console.log('Rejected web3.eth call. Method: ', method, ' args: ', args);
+ reject(e);
+ } else {
+ resolve(data);
+ }
+ }]);
+});
+
+export const eventPromise = event => new Promise((resolve, reject) => {
+ event.watch((error, results) => {
+ event.stopWatching(() => {});
+ if (error) {
+ reject(error);
+ } else {
+ resolve(results);
+ }
+ });
+});
+
+let chaingearContract;
+
+export const getChaingearContract = () => new Promise((resolve) => {
+ getWeb3
+ .then(({ web3 }) => {
+ if (!chaingearContract) {
+ chaingearContract = web3.eth
+ .contract(ChaingearBuild.abi)
+ .at(ChaingearBuild.networks[currentNetworkId].address);
+ }
+
+ resolve(chaingearContract);
+ });
+});
+
+
+let ethAccounts;
+
+export const getEthAccounts = () => new Promise((resolve) => {
+ if (ethAccounts) {
+ resolve(ethAccounts);
+ } else {
+ getWeb3
+ .then(({ web3 }) => callWeb3EthMethod(web3, 'getAccounts'))
+ .then((accounts) => {
+ ethAccounts = accounts;
+ resolve(accounts);
+ });
+ }
+});
+
+
+let abis = {};
+
+export const init = () => new Promise((resolve) => {
+ const abisCache = localStorage.getItem('abis') || '{}';
+
+ abis = JSON.parse(abisCache);
+
+ if (chaingearContract && currentWeb3 && ethAccounts) {
+ resolve({
+ contract: chaingearContract,
+ web3: currentWeb3,
+ accounts: ethAccounts,
+ });
+ } else {
+ getWeb3
+ .then(() => getEthAccounts())
+ .then(() => getChaingearContract())
+ .then(() => setDefaultAccount(ethAccounts[0])) // todo: set correct account
+ .then(() => resolve({
+ contract: chaingearContract,
+ web3: currentWeb3,
+ accounts: ethAccounts,
+ }));
+ }
+});
+
+// TODO: move in npm package
+export const loadCompiler = (cb) => {
+ setTimeout(() => {
+ window.BrowserSolc.loadVersion('soljson-v0.4.25+commit.59dbf8f1.js', cb);
+ }, 30);
+};
+
+export const mapDatabase = (rawDatabase, id) => ({
+ id,
+ name: rawDatabase[0],
+ symbol: rawDatabase[1],
+ address: rawDatabase[2],
+ contractVersion: rawDatabase[3],
+ createdTimestamp: rawDatabase[4],
+ ipfsHash: '',
+ admin: rawDatabase[5],
+ supply: rawDatabase[6],
+});
+
+export const getItems = (contract, getIdsMethod,
+ getEntryByIdMethod, mapFn) => new Promise((topResolve) => {
+
+ contract[getIdsMethod]((e, ids) => {
+ const idsArray = ids.map(id => id.toNumber());
+
+ const promises = idsArray.map(id => new Promise((itemResolve, itemReject) => {
+ contract[getEntryByIdMethod](id, (error, data) => {
+ if (error) {
+ itemReject(error);
+ } else {
+ itemResolve({
+ data,
+ id,
+ });
+ }
+ });
+ }));
+
+ Promise.all(promises).then((items) => {
+ const results = items.map(item => mapFn(item.data, item.id));
+
+ topResolve(results);
+ });
+ });
+});
+
+export const getItemsByIds = (contract, idsArray,
+ getEntryByIdMethod, mapFn) => new Promise((topResolve) => {
+
+ const promises = idsArray.map(id => new Promise((itemResolve, itemReject) => {
+ contract[getEntryByIdMethod](id, (error, data) => {
+ if (error) {
+ itemReject(error);
+ } else {
+ itemResolve({
+ data,
+ id,
+ });
+ }
+ });
+ }));
+
+ Promise.all(promises).then((items) => {
+ const results = items.map(item => mapFn(item.data, item.id));
+
+ topResolve(results);
+ });
+});
+
+
+const Dependencies = require('../resources/Dependencies.sol');
+
+export const compileDatabase = (code, contractName, compiler, compilerOpts = {
+ isOptimizerEnabled: true,
+ runs: 200,
+}) => new Promise((resolve, reject) => {
+
+ const sources = {
+ 'Dependencies.sol': Dependencies,
+ [contractName]: `pragma solidity 0.4.25; ${code}`,
+ };
+
+ const settings = {
+ optimizer: {
+ enabled: compilerOpts.isOptimizerEnabled,
+ },
+ runs: compilerOpts.runs,
+ };
+
+ setTimeout(() => {
+ const compiledContract = compiler.compile({
+ sources, settings,
+ }, 1);
+
+ console.log(compiledContract);
+ if (compiledContract.errors && compiledContract.errors.length > 0) {
+ reject(compiledContract.errors[0]);
+ return;
+ }
+
+ try {
+ const abi = compiledContract.contracts[`${contractName}:Schema`].interface;
+ const bytecode = `0x${compiledContract.contracts[`${contractName}:Schema`].bytecode}`;
+
+ resolve({
+ abi,
+ bytecode,
+ });
+ } catch (e) {
+ reject(e);
+ }
+ }, 20);
+});
+
+export const getDatabases = () => {
+ const mapFunc = (item, id) => ({
+ name: item[0],
+ symbol: item[1],
+ address: item[2],
+ contractVersion: item[3],
+ createdTimestamp: item[4],
+ ipfsHash: '',
+ admin: item[5],
+ supply: item[6],
+ id,
+ });
+
+ return getChaingearContract()
+ .then(contract => getItems(contract, 'getDatabasesIDs', 'getDatabase', mapFunc));
+};
+
+export const removeDatabase = (address, cb) => getChaingearContract()
+ .then(contract => contract.deleteDatabase(address));
+
+export const getDatabaseFieldsByHash = ipfsHash => new Promise((resolve) => {
+ getIpfsConfig().then((config) => {
+ const ipfs = new IPFS(config);
+
+ ipfs.get(ipfsHash, (err, files) => {
+ const buf = files[0].content;
+ const abi = JSON.parse(buf.toString());
+ // TODO move extraction from entries to other ABIs object,
+ // entries should be internal, now public for supporting frontend
+ let fields = abi.filter(x => x.name === 'entries')[0].outputs;
+
+ fields = fields.filter(x => x.name !== 'metainformation' && x.name !== 'owner' && x.name !== 'lastUpdateTime');
+ resolve({
+ ipfsHash,
+ abi,
+ fields,
+ });
+ });
+ });
+});
+
+export const saveInIPFS = jsonStr => new Promise((resolve, reject) => {
+ getIpfsConfig().then((config) => {
+ const ipfs = new IPFS(config);
+ const buffer = Buffer.from(jsonStr);
+
+ ipfs.add(buffer, (err, ipfsHash) => {
+ if (err) {
+ reject(err);
+ } else {
+ const hash = ipfsHash[0].path;
+
+ resolve(hash);
+ }
+ });
+ });
+});
+
+export const deploySchema = (name, fields, databaseContract) => {
+ const code = generateContractCode(name, fields);
+
+ let tempByteCode;
+
+ //todo: fix default account
+ let account;
+
+ return new Promise((resolve, reject) => {
+ loadCompiler((compiler) => {
+ getDefaultAccount()
+ .then((defaultAccount) => {
+ account = defaultAccount;
+ })
+ .then(() => compileDatabase(code, name, compiler))
+ .then(({ abi, bytecode }) => {
+ tempByteCode = bytecode;
+ return saveInIPFS(abi);
+ })
+ .then(ipfsHash => JSON.stringify({
+ build: {
+ compiler: '0.4.25+commit.59dbf8f1.Emscripten.clang',
+ optimizer: true,
+ runs: 200,
+ ABI: ipfsHash,
+ },
+ fields: fields.map(field => ({
+ ...field,
+ unique: field.unique ? 1 : 0,
+ })),
+ }))
+ .then(schemaDefinition => callContractMethod(databaseContract, 'initializeDatabase', schemaDefinition, tempByteCode, {from: account}))
+ .then((data) => {
+ console.log(`Schema created for ${name}. Data: ${data}`);
+ resolve(data);
+ })
+ .catch((error) => {
+ console.log(`Cannot create shema for ${name}. Error: ${error}`);
+ reject(error);
+ });
+ });
+ });
+};
+
+export const deployDatabase = (name, symbol, version, beneficiaries, stakes) => {
+ let tChaingearContract;
+ let tdefaultAccount;
+
+ return new Promise((resolve, reject) => {
+ getDefaultAccount()
+ .then((defaultAccount) => {
+ tdefaultAccount = defaultAccount;
+ return getChaingearContract();
+ })
+ .then((contract) => {
+ tChaingearContract = contract;
+ return callContractMethod(contract, 'getCreationFeeWei');
+ })
+ .then((fee) => {
+ const creationFee = fee.toNumber();
+
+ return sendTransactionMethod(tChaingearContract.createDatabase,
+ version, beneficiaries, stakes, name, symbol, {
+ from: tdefaultAccount,
+ value: creationFee,
+ });
+ })
+ .then((txHash) => {
+ console.log(`Database creation ${name} tx: ${txHash}`);
+ resolve(txHash);
+ })
+ .catch((error) => {
+ console.log(`Cannot create database ${name}. Error: ${error}`);
+ reject(error);
+ });
+ });
+};
+
+export const getDatabaseContract = address => getWeb3
+ .then(({ web3 }) => web3.eth.contract(DatabaseV1.abi).at(address));
+
+export const getDatabaseData = (databaseContract, fields, abi) => new Promise((resolve) => {
+ let tEntryCoreAddress;
+ let tEntryCore;
+
+ const mapFn = (item, id) => {
+ const aItem = Array.isArray(item) ? item : [item];
+
+ return fields.reduce((o, field, index) => ({
+ ...o, [field.name]: aItem[index],
+ }), {
+ __index: id,
+ });
+ };
+
+ callContractMethod(databaseContract, 'getEntriesStorage')
+ .then((entryAddress) => {
+ tEntryCoreAddress = entryAddress;
+ tEntryCore = currentWeb3.eth.contract(abi).at(entryAddress);
+ })
+ .then(() => callContractMethod(databaseContract, 'getEntriesIDs'))
+ .then((entriesIDs) => {
+ const idsArray = entriesIDs.map(id => id.toNumber());
+
+ getItemsByIds(tEntryCore, idsArray, 'readEntry', mapFn)
+ .then((items) => {
+ resolve({
+ items,
+ fields,
+ entryAddress: tEntryCoreAddress,
+ });
+ });
+ });
+});
+
+export const fundEntry = (address, id, value) => new Promise((resolve) => {
+ const databaseContract = currentWeb3.eth.contract(DatabaseV1.abi).at(address);
+
+ const event = databaseContract.EntryFunded();
+
+ event.watch((e, results) => {
+ event.stopWatching(() => {});
+ resolve(results.args);
+ });
+ databaseContract.fundEntry(id, {
+ value: currentWeb3.toWei(value, 'ether'),
+ }, (e, d) => {
+
+ });
+});
+
+export const getSafeBalance = address => new Promise((resolve) => {
+ const databaseContract = currentWeb3.eth.contract(DatabaseV1.abi).at(address);
+
+ databaseContract.safeBalance((e, data) => {
+ resolve(data);
+ });
+});
+
+export const updateEntryCreationFee = (address, newfee) => new Promise((resolve, reject) => {
+ const database = currentWeb3.eth.contract(DatabaseV1.abi).at(address);
+
+ database.updateEntryCreationFee(currentWeb3.toWei(newfee, 'ether'), (e, data) => {
+ if (e) { reject(e); } else { resolve(data); }
+ });
+});
+
+export const getBeneficiaries = dbContract => callContractMethod(dbContract, 'getPayeesCount')
+ .then(bensCount => [...Array(bensCount.toNumber()).keys()])
+ .then(benIndexArray => benIndexArray.map((benIndex) => {
+ const ben = {};
+
+ return callContractMethod(dbContract, 'getPayee', benIndex)
+ .then((benAddress) => {
+ ben.address = benAddress;
+ })
+ .then(() => callContractMethod(dbContract, 'getShares', ben.address))
+ .then((benStake) => {
+ ben.stake = benStake.toNumber();
+ })
+ .then(() => callContractMethod(dbContract, 'getReleased', ben.address))
+ .then((benReleased) => {
+ ben.released = benReleased.toNumber();
+ })
+ .then(() => ben)
+ .catch((error) => {
+ console.log(`Cannot get beneficiary for DB. Error: ${error}`);
+ });
+ }))
+ .then(benPromisses => Promise.all(benPromisses));
+
+export const getAbiByFields = (contractName, fields, buildOpts) => new Promise((resolve, reject) => {
+
+ if (abis[contractName]) {
+ resolve(abis[contractName]);
+ return;
+ }
+
+ const code = generateContractCode(contractName, fields);
+ const compilerOpts = {
+ isOptimizerEnabled: buildOpts.optimizer,
+ runs: buildOpts.runs,
+ };
+
+ loadCompiler((compiler) => {
+ compileDatabase(code, contractName, compiler, compilerOpts)
+ .then(({ abi }) => {
+ const abiObj = JSON.parse(abi);
+
+ abis[contractName] = abiObj;
+ localStorage.setItem('abis', JSON.stringify(abis));
+ resolve(abiObj);
+ })
+ .catch((error) => {
+ console.log(`Cannot get abi for contract ${contractName}`);
+ reject(error);
+ });
+ });
+});
+
+export {
+ generateContractCode,
+};
diff --git a/client/app/src/utils/generateContractCode.js b/client/app/src/utils/generateContractCode.js
new file mode 100644
index 00000000..87d23e82
--- /dev/null
+++ b/client/app/src/utils/generateContractCode.js
@@ -0,0 +1,151 @@
+const generateContractCode = (name, fields) => {
+
+ const structBodyStr = fields.map(f => `${f.type} ${f.name};`).join('\n');
+
+ const createArgsStr = fields.map(f => `${f.type} _${f.name}`).join(', ');
+ // const createItemStr = fields.map(f => `${f.name}: _${f.name}`).join(',\n');
+
+ const empty = (type) => {
+ if (type === 'string') return '""';
+ if (type === 'address') return 'address(0)';
+ if (type === 'bool') return 'false';
+ if (type === 'uint256') return 'uint256(0)';
+ if (type === 'int256') return 'int256(0)';
+
+ return '""';
+ };
+
+ const uniqueFields = fields.filter(field => field.unique);
+
+ const mappings = uniqueFields.map(filed => `mapping(${filed.type} => bool) private ${filed.name}UniqIndex;`);
+
+ const onUpdate = uniqueFields.map(field => {
+ let ifStatement;
+
+ if (field.type === 'string') {
+ ifStatement = `if (bytes(_${field.name}).length > 0) `;
+ } else {
+ ifStatement = `if (_${field.name} != ${empty(field.type)}) `;
+ }
+
+ const ifBody = `{
+ require(${field.name}UniqIndex[_${field.name}] == false);
+ ${field.name}UniqIndex[_${field.name}] = true;
+ ${field.name}UniqIndex[entries[entryIndex].${field.name}] = false;
+ }`;
+
+ return ifStatement + ifBody;
+ });
+
+ const onDelete = uniqueFields.map(field => `${field.name}UniqIndex[entries[_entryIndex].${field.name}] = false;`);
+
+//TODO Entry[] public entries move to internal, change source in ABI for extracting fields;
+
+ return `
+
+import './Dependencies.sol';
+
+
+contract Schema is ISchema, Ownable, SupportsInterfaceWithLookup {
+
+ bytes4 constant internal INTERFACE_SCHEMA_ID = 0x153366ed;
+ bool[${fields.length}] private uniqValidationStatus = [${fields.map(field => field.unique)}];
+
+ struct Entry {
+ ${structBodyStr}
+
+ }
+
+ Entry[] public entries;
+
+ ${mappings.join('\n')}
+
+ IDatabase internal database;
+
+ event EntryUpdated(
+ uint256 _entryID,
+ ${createArgsStr}
+ );
+
+ constructor()
+ public
+ {
+ _registerInterface(INTERFACE_SCHEMA_ID);
+ database = IDatabase(owner);
+ }
+
+ function() external {}
+
+ function createEntry()
+ external
+ onlyOwner
+ {
+ Entry memory m = (Entry(
+ {
+ ${fields.map(({name, type}) => `${name}: ${empty(type)}`).join(',\n')}
+ }));
+ entries.push(m);
+ }
+
+
+ function readEntry(uint256 _entryID)
+ external
+ view
+ returns (${fields.map(({name, type}) => type).join(', ')})
+ {
+ uint256 entryIndex = database.getIndexByID(_entryID);
+ return (
+ ${fields.map(({name, type}) => `entries[entryIndex].${name}`).join(',\n')}
+ );
+ }
+
+
+ function updateEntry(
+ uint256 _entryID,
+ ${createArgsStr}
+ )
+ external
+ {
+ database.auth(_entryID, msg.sender);
+
+ uint256 entryIndex = database.getIndexByID(_entryID);
+
+ ${onUpdate.join('\n')}
+
+ Entry memory m = (Entry(
+ {
+ ${fields.map(f => `${f.name}: _${f.name}`).join(',\n')}
+ }));
+ entries[entryIndex] = m;
+
+ emit EntryUpdated(_entryID, ${fields.map(field => `_${field.name}`).join(', ')});
+ }
+
+
+ function deleteEntry(uint256 _entryIndex)
+ external
+ onlyOwner
+ {
+
+ ${onDelete.join('\n')}
+
+ uint256 lastEntryIndex = entries.length - 1;
+ Entry memory lastEntry = entries[lastEntryIndex];
+
+ entries[_entryIndex] = lastEntry;
+ delete entries[lastEntryIndex];
+ entries.length--;
+ }
+
+ function getUniqStatus(uint256 _id)
+ external
+ view
+ returns (bool)
+ {
+ return uniqValidationStatus[_id];
+ }
+
+}`;
+};
+
+module.exports = generateContractCode;
diff --git a/client/app/src/utils/utils.js b/client/app/src/utils/utils.js
new file mode 100644
index 00000000..d092cf07
--- /dev/null
+++ b/client/app/src/utils/utils.js
@@ -0,0 +1,43 @@
+function debounce(func, wait, immediate) {
+ var timeout;
+ return function() {
+ var context = this, args = arguments;
+ var later = function() {
+ timeout = null;
+ if (!immediate) func.apply(context, args);
+ };
+ var callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) func.apply(context, args);
+ };
+};
+
+const calculateBensShares = (beneficiaries, fixed = 0) => {
+ let allStake = 0;
+
+ beneficiaries.forEach((ben) => {
+ allStake += +ben.stake;
+ });
+
+ return beneficiaries.map(ben => ({
+ ...ben,
+ share: (ben.stake / allStake * 100).toFixed(fixed),
+ }));
+};
+
+/*
+* Date
+*/
+
+const moment = require('moment');
+
+const dateFormat = 'DD/MM/YYYY H:mm';
+
+const formatDate = (solidityDate) => {
+ const jsDate = new Date(solidityDate * 1000);
+
+ return moment(jsDate).format(dateFormat);
+};
+
+export { debounce, calculateBensShares, formatDate };
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 00000000..5596cf40
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,74 @@
+{
+ "name": "@cybercongress/chaingear",
+ "version": "0.3.1",
+ "main": "app/src/main.jsx",
+ "repository": {
+ "type": "git",
+ "url": "git+ssh://git@github.com/cybercongress/chaingear.git"
+ },
+ "author": "",
+ "dependencies": {
+ "@cybercongress/ui": ">=0.0.16",
+ "axios": "0.16.2",
+ "classnames": "2.2.5",
+ "eslint": "5.8.0",
+ "eslint-config-airbnb": "17.1.0",
+ "eslint-config-standard-react": "7.0.2",
+ "eslint-plugin-import": "2.14.0",
+ "eslint-plugin-jsx-a11y": "6.1.2",
+ "eslint-plugin-react": "7.11.1",
+ "highlight.js": "9.12.0",
+ "history": "3.0.0",
+ "ipfs-api": "18.2.1",
+ "moment": "2.21.0",
+ "react": "16.6.3",
+ "react-click-outside": "github:tj/react-click-outside",
+ "react-dom": "16.6.3",
+ "react-router": "3.0.5",
+ "unstated": "2.1.1",
+ "web3": "0.20.3",
+ "web3-utils": "^1.0.0-beta.34"
+ },
+ "devDependencies": {
+ "@babel/core": "7.2.2",
+ "@babel/plugin-proposal-class-properties": "7.3.0",
+ "@babel/preset-env": "7.3.1",
+ "@babel/preset-react": "7.0.0",
+ "babel-loader": "8.0.5",
+ "babel-eslint": "10.0.1",
+ "eslint-loader": "2.1.1",
+ "css-loader": "0.28.7",
+ "file-loader": "3.0.1",
+ "html-webpack-plugin": "3.2.0",
+ "raw-loader": "0.5.1",
+ "style-loader": "0.18.2",
+ "mini-css-extract-plugin": "0.5.0",
+ "copy-webpack-plugin": "4.6.0",
+ "webpack": "4.29.0",
+ "webpack-cli": "3.2.1",
+ "webpack-dev-server": "3.1.14"
+ },
+ "scripts": {
+ "start": "webpack-dev-server --mode=development",
+ "build": "rimraf distribution && webpack --mode=production"
+ },
+ "description": "New Frontier of chaingear, registries grow here!",
+ "bugs": {
+ "url": "https://github.com/cybercongress/chaingear/issues"
+ },
+ "homepage": "https://github.com/cybercongress/chaingear",
+ "keywords": [
+ "chaingear",
+ "web3",
+ "cyb",
+ "browser",
+ "ethereum",
+ "dapp",
+ "ERC721",
+ "registry"
+ ],
+ "license": "MIT",
+ "directories": {
+ "../test": "test"
+ }
+}
diff --git a/client/webpack.config.js b/client/webpack.config.js
new file mode 100644
index 00000000..219ecf82
--- /dev/null
+++ b/client/webpack.config.js
@@ -0,0 +1,125 @@
+const path = require('path');
+const webpack = require('webpack');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+
+function getPlugins(isProduction) {
+ const plugins = [
+ new MiniCssExtractPlugin(),
+ new HtmlWebpackPlugin({
+ title: 'Chaingear',
+ template: path.resolve(__dirname, 'app', 'index.html'),
+ favicon: path.resolve(__dirname, 'app', 'favicon.ico'),
+ hash: true,
+ }),
+ new CopyWebpackPlugin([
+ '../browser-solc.min.js',
+ ]),
+ new webpack.SourceMapDevToolPlugin({
+ test: /\.(js|jsx|css)$/,
+ exclude: [/\.vendor$/],
+ }),
+ ];
+
+ if (!isProduction) {
+ plugins.push(new webpack.HotModuleReplacementPlugin());
+ }
+
+ return plugins;
+}
+
+module.exports = (env = {}, argv = {}) => {
+ const isProduction = argv.mode === 'production';
+
+ return {
+ context: path.join(__dirname, 'app', 'src'),
+ entry: {
+ main: path.join(__dirname, 'app', 'src', 'main.jsx'),
+ },
+ output: {
+ path: path.resolve(__dirname, 'distribution'),
+ filename: '[name].js?[hash]',
+ publicPath: '',
+ },
+ module: {
+ rules: [
+ {
+ test: /\.sol$/,
+ use: 'raw-loader',
+ },
+ {
+ test: /\.(js|jsx)$/,
+ // exclude: /node_modules/,
+ include: /src/,
+ use: [
+ { loader: 'babel-loader' },
+/* {
+ loader: 'eslint-loader',
+ options: {
+ emitWarning: true,
+ },
+ },*/
+ ],
+ },
+ {
+ test: /\.less$/,
+ use: [
+ { loader: 'style-loader' },
+ {
+ loader: 'css-loader',
+ options: {
+ modules: true,
+ localIdentName: '[name]_[local]',
+ },
+ },
+ ],
+ },
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader'],
+ },
+ {
+ test: /\.(png|jpg|svg|woff|woff2|ttf|eot|otf)(\?.*)?$/,
+ use: {
+ loader: 'file-loader',
+ options: {
+ name: '[name].[hash:10].[ext]',
+ outputPath: '',
+ publicPath: '',
+ useRelativePath: false,
+ },
+ },
+ },
+ ],
+ },
+ resolve: {
+ extensions: ['*', '.js', '.jsx'],
+ alias: {},
+ },
+ devtool: false,
+ plugins: getPlugins(isProduction),
+ optimization: {
+ runtimeChunk: 'single',
+ splitChunks: {
+ cacheGroups: {
+ commons: {
+ test: /[\\/]node_modules|react|react-dom|moment|lodash[\\/]/,
+ name: 'vendors',
+ chunks: 'all',
+ },
+ },
+ },
+ },
+ bail: false,
+ devServer: {
+ port: 5600,
+ hot: true,
+ before(app, server) {
+ app.head('/', (req, res) => {
+ res.json({ custom: 'response' });
+ });
+ },
+ },
+ };
+};
diff --git a/config.js b/config.js
deleted file mode 100644
index 018bf09f..00000000
--- a/config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-var env = {
- "CYBER_CHAINGEAR_API": "http://93.125.26.210:32600",
- "CYBER_SEARCH_API": "http://93.125.26.210:32901",
- "CYBER_MARKETS_API": "http://93.125.26.210:32800",
- // "CYBER_MARKETS_API": "https://min-api.cryptocompare.com/data",
- "CYBER_MARKETS_STREAM_API": "ws://93.125.26.210:32801"
-}
diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol
index 2706d77e..ac0188ec 100644
--- a/contracts/Migrations.sol
+++ b/contracts/Migrations.sol
@@ -1,4 +1,4 @@
-pragma solidity 0.4.24;
+pragma solidity 0.4.25;
contract Migrations {
diff --git a/contracts/builders/DatabaseBuilderV1.sol b/contracts/builders/DatabaseBuilderV1.sol
new file mode 100644
index 00000000..29dce878
--- /dev/null
+++ b/contracts/builders/DatabaseBuilderV1.sol
@@ -0,0 +1,108 @@
+pragma solidity 0.4.25;
+
+import "../databases/DatabaseV1.sol";
+import "../common/IDatabase.sol";
+import "../common/IDatabaseBuilder.sol";
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
+
+
+/**
+* @title Chaingear - the novel Ethereum database framework
+* @author cyber•Congress, Valery litvin (@litvintech)
+* @notice not audited, not recommend to use in mainnet
+*/
+contract DatabaseBuilderV1 is IDatabaseBuilder, SupportsInterfaceWithLookup {
+
+ /*
+ * Storage
+ */
+
+ address private chaingear;
+ address private owner;
+
+ bytes4 private constant INTERFACE_CHAINGEAR_EULER_ID = 0xea1db66f;
+ bytes4 private constant INTERFACE_DATABASE_BUILDER_EULER_ID = 0xce8bbf93;
+
+ /*
+ * Events
+ */
+
+ event DatabaseDeployed(
+ string name,
+ string symbol,
+ IDatabase database
+ );
+
+ /*
+ * Constructor
+ */
+
+ constructor() public {
+ chaingear = address(0);
+ owner = msg.sender;
+ _registerInterface(INTERFACE_DATABASE_BUILDER_EULER_ID);
+ }
+
+ /*
+ * Fallback
+ */
+
+ function() external {}
+
+ /*
+ * External Functions
+ */
+
+ function deployDatabase(
+ address[] _benefitiaries,
+ uint256[] _shares,
+ string _name,
+ string _symbol
+ )
+ external
+ returns (IDatabase)
+ {
+ require(msg.sender == chaingear);
+
+ IDatabase databaseContract = new DatabaseV1(
+ _benefitiaries,
+ _shares,
+ _name,
+ _symbol
+ );
+ databaseContract.transferOwnership(chaingear);
+ emit DatabaseDeployed(_name, _symbol, databaseContract);
+
+ return databaseContract;
+ }
+
+ /*
+ * Views
+ */
+
+ function setChaingearAddress(address _chaingear)
+ external
+ {
+ require(msg.sender == owner);
+
+ SupportsInterfaceWithLookup support = SupportsInterfaceWithLookup(_chaingear);
+ require(support.supportsInterface(INTERFACE_CHAINGEAR_EULER_ID));
+ chaingear = _chaingear;
+ }
+
+ function getChaingearAddress()
+ external
+ view
+ returns (address)
+ {
+ return chaingear;
+ }
+
+ function getOwner()
+ external
+ view
+ returns (address)
+ {
+ return owner;
+ }
+}
diff --git a/contracts/chaingear/Chaingear.sol b/contracts/chaingear/Chaingear.sol
index 9228ac3f..93c8eab1 100644
--- a/contracts/chaingear/Chaingear.sol
+++ b/contracts/chaingear/Chaingear.sol
@@ -1,261 +1,585 @@
-pragma solidity 0.4.24;
+pragma solidity 0.4.25;
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
import "openzeppelin-solidity/contracts/token/ERC721/ERC721Token.sol";
+import "openzeppelin-solidity/contracts/lifecycle/Pausable.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
-import "../common/SplitPaymentChangeable.sol";
-import "../common/RegistryInterface.sol";
+
+
+import "../common/IDatabaseBuilder.sol";
+import "../common/IDatabase.sol";
import "../common/Safe.sol";
-import "./ChaingearCore.sol";
-import "./RegistryCreator.sol";
+import "../common/IChaingear.sol";
+import "./FeeSplitterChaingear.sol";
+import "../common/ERC721MetadataValidation.sol";
/**
-* @title Chaingear - the most expensive database
+* @title Chaingear - the novel Ethereum database framework
* @author cyber•Congress, Valery litvin (@litvintech)
-* @dev Main metaregistry contract
-* @notice Proof-of-Concept. Chaingear it's a metaregistry/fabric for Creator Curated Registries
-* @notice where each registry are ERC721.
-* @notice not recommend to use before release!
+* @notice not audited, not recommend to use in mainnet
*/
-contract Chaingear is SplitPaymentChangeable, ChaingearCore, ERC721Token {
+contract Chaingear is IChaingear, Ownable, SupportsInterfaceWithLookup, Pausable, FeeSplitterChaingear, ERC721Token {
using SafeMath for uint256;
+ using ERC721MetadataValidation for string;
+
+ /*
+ * Storage
+ */
+
+ struct DatabaseMeta {
+ IDatabase databaseContract;
+ address creatorOfDatabase;
+ string versionOfDatabase;
+ string linkABI;
+ uint256 createdTimestamp;
+ uint256 currentWei;
+ uint256 accumulatedWei;
+ }
+
+ struct DatabaseBuilder {
+ IDatabaseBuilder builderAddress;
+ string linkToABI;
+ string description;
+ bool operational;
+ }
+
+ DatabaseMeta[] private databases;
+ mapping(string => bool) private databasesNamesIndex;
+ mapping(string => bool) private databasesSymbolsIndex;
+
+ uint256 private headTokenID = 0;
+ mapping(address => uint256) private databasesIDsByAddressesIndex;
+ mapping(string => address) private databasesAddressesByNameIndex;
+ mapping(uint256 => string) private databasesSymbolsByIDIndex;
+ mapping(string => uint256) private databasesIDsBySymbolIndex;
+
+ uint256 private amountOfBuilders = 0;
+ mapping(uint256 => string) private buildersVersionIndex;
+ mapping(string => DatabaseBuilder) private buildersVersion;
+
+ Safe private chaingearSafe;
+ uint256 private databaseCreationFeeWei = 1 ether;
+
+ string private constant CHAINGEAR_DESCRIPTION = "The novel Ethereum database framework";
+ bytes4 private constant INTERFACE_CHAINGEAR_EULER_ID = 0xea1db66f;
+ bytes4 private constant INTERFACE_DATABASE_V1_EULER_ID = 0xf2c320c4;
+ bytes4 private constant INTERFACE_DATABASE_BUILDER_EULER_ID = 0xce8bbf93;
+
+ /*
+ * Events
+ */
+ event DatabaseBuilderAdded(
+ string version,
+ IDatabaseBuilder builderAddress,
+ string linkToABI,
+ string description
+ );
+ event DatabaseDescriptionUpdated(string version, string description);
+ event DatabaseBuilderDepricated(string version);
+ event DatabaseCreated(
+ string name,
+ address databaseAddress,
+ address creatorAddress,
+ uint256 databaseChaingearID
+ );
+ event DatabaseDeleted(
+ string name,
+ address databaseAddress,
+ address creatorAddress,
+ uint256 databaseChaingearID
+ );
+ event DatabaseFunded(
+ uint256 databaseID,
+ address sender,
+ uint256 amount
+ );
+ event DatabaseFundsClaimed(
+ uint256 databaseID,
+ address claimer,
+ uint256 amount
+ );
+ event CreationFeeUpdated(uint256 newFee);
/*
* Constructor
*/
- /**
- * @dev Chaingear constructor
- * @param _benefitiaries address[] addresses of Chaingear benefitiaries
- * @param _shares uint256[] array with amount of shares by benefitiary
- * @param _description string description of Chaingear
- * @param _registrationFee uint Fee for registry creation, wei
- * @param _chaingearName string Chaingear's name, use for chaingear's ERC721
- * @param _chaingearSymbol string Chaingear's NFT symbol, use for chaingear's ERC721
- */
- constructor(
- address[] _benefitiaries,
- uint256[] _shares,
- string _description,
- uint _registrationFee,
- string _chaingearName,
- string _chaingearSymbol
- )
- SplitPaymentChangeable(_benefitiaries, _shares)
- ERC721Token(_chaingearName, _chaingearSymbol)
+ constructor(address[] _beneficiaries, uint256[] _shares)
public
+ ERC721Token ("CHAINGEAR", "CHG")
+ FeeSplitterChaingear (_beneficiaries, _shares)
{
- registryRegistrationFee = _registrationFee;
- chaingearDescription = _description;
- // Only chaingear as owner can transfer either to and out from Safe
chaingearSafe = new Safe();
+ _registerInterface(INTERFACE_CHAINGEAR_EULER_ID);
}
-
- function() public payable {}
-
+
/*
- * External functions
+ * Modifiers
*/
- /**
- * @dev Add and tokenize registry with specified parameters.
- * @dev Registration fee is required to send with tx.
- * @dev Tx sender become Creator/Admin of Registry, Chaingear become Owner of Registry
- * @param _version version of registry from which Registry will be boostrapped
- * @param _benefitiaries address[] addresses of Chaingear benefitiaries
- * @param _shares uint256[] array with amount of shares by benefitiary
- * @param _name string, Registry name, use for registry ERC721
- * @param _symbol string, Registry NFT symbol, use for registry ERC721
- * @return address new Registry contract address
- * @return uint256 new Registry ID in Chaingear contract, ERC721 NFT ID
+ modifier onlyOwnerOf(uint256 _databaseID){
+ require(ownerOf(_databaseID) == msg.sender);
+ _;
+ }
+
+ /*
+ * External functions
*/
- function registerRegistry(
+
+ function addDatabaseBuilderVersion(
string _version,
- address[] _benefitiaries,
+ IDatabaseBuilder _builderAddress,
+ string _linkToABI,
+ string _description
+ )
+ external
+ onlyOwner
+ whenNotPaused
+ {
+ require(buildersVersion[_version].builderAddress == address(0));
+
+ SupportsInterfaceWithLookup support = SupportsInterfaceWithLookup(_builderAddress);
+ require(support.supportsInterface(INTERFACE_DATABASE_BUILDER_EULER_ID));
+
+ buildersVersion[_version] = (DatabaseBuilder(
+ {
+ builderAddress: _builderAddress,
+ linkToABI: _linkToABI,
+ description: _description,
+ operational: true
+ }));
+ buildersVersionIndex[amountOfBuilders] = _version;
+ amountOfBuilders = amountOfBuilders.add(1);
+
+ emit DatabaseBuilderAdded(
+ _version,
+ _builderAddress,
+ _linkToABI,
+ _description
+ );
+ }
+
+ function updateDatabaseBuilderDescription(string _version, string _description)
+ external
+ onlyOwner
+ whenNotPaused
+ {
+ require(buildersVersion[_version].builderAddress != address(0));
+ buildersVersion[_version].description = _description;
+ emit DatabaseDescriptionUpdated(_version, _description);
+ }
+
+ function depricateDatabaseBuilder(string _version)
+ external
+ onlyOwner
+ whenPaused
+ {
+ require(buildersVersion[_version].builderAddress != address(0));
+ require(buildersVersion[_version].operational == true);
+ buildersVersion[_version].operational = false;
+ emit DatabaseBuilderDepricated(_version);
+ }
+
+ function createDatabase(
+ string _version,
+ address[] _beneficiaries,
uint256[] _shares,
- string _name,
- string _symbol
+ string _name,
+ string _symbol
)
external
payable
whenNotPaused
- returns (
- address,
- uint256
- )
+ returns (address, uint256)
{
- require(registryAddresses[_version] != 0x0);
- require(registryRegistrationFee == msg.value);
-
- //checking uniqueness of name AND symbol of NFT in metaregistry
- require(registryNamesIndex[_name] == false);
- require(registrySymbolsIndex[_symbol] == false);
+ _name.validateName();
+ _symbol.validateSymbol();
+ require(buildersVersion[_version].builderAddress != address(0));
+ require(buildersVersion[_version].operational == true);
+ require(databaseCreationFeeWei == msg.value);
+ require(databasesNamesIndex[_name] == false);
+ require(databasesSymbolsIndex[_symbol] == false);
- return createRegistry(
+ return _deployDatabase(
_version,
- _benefitiaries,
+ _beneficiaries,
_shares,
_name,
_symbol
);
}
-
- function transferFrom(
- address _from,
- address _to,
- uint256 _tokenId
- )
- public
- canTransfer(_tokenId)
+
+ function deleteDatabase(uint256 _databaseID)
+ external
+ onlyOwnerOf(_databaseID)
+ whenNotPaused
{
- require(_from != address(0));
- require(_to != address(0));
+ uint256 databaseIndex = allTokensIndex[_databaseID];
+ IDatabase database = databases[databaseIndex].databaseContract;
+ require(database.getSafeBalance() == uint256(0));
+ require(database.getPaused() == true);
+
+ string memory databaseName = ERC721(database).name();
+ string memory databaseSymbol = ERC721(database).symbol();
+
+ delete databasesNamesIndex[databaseName];
+ delete databasesSymbolsIndex[databaseSymbol];
+ delete databasesIDsByAddressesIndex[database];
+ delete databasesIDsBySymbolIndex[databaseSymbol];
+ delete databasesSymbolsByIDIndex[_databaseID];
+
+ uint256 lastDatabaseIndex = databases.length.sub(1);
+ DatabaseMeta memory lastDatabase = databases[lastDatabaseIndex];
+ databases[databaseIndex] = lastDatabase;
+ delete databases[lastDatabaseIndex];
+ databases.length--;
- clearApproval(_from, _tokenId);
- removeTokenFrom(_from, _tokenId);
- addTokenTo(_to, _tokenId);
+ super._burn(msg.sender, _databaseID);
+ database.transferOwnership(msg.sender);
- address registryAddress = registries[_tokenId].contractAddress;
- RegistryInterface(registryAddress).transferAdminRights(_to);
+ emit DatabaseDeleted(
+ databaseName,
+ database,
+ msg.sender,
+ _databaseID
+ );
+ }
- emit Transfer(_from, _to, _tokenId);
- }
+ function fundDatabase(uint256 _databaseID)
+ external
+ whenNotPaused
+ payable
+ {
+ require(exists(_databaseID) == true);
+ uint256 databaseIndex = allTokensIndex[_databaseID];
- /**
- * @dev Allows to unregister Registry from Chaingear
- * @dev Only possible when safe of Registry is empty
- * @dev Burns associated registry token and transfer Registry adminship to current token owner
- * @param _registryID uint256 Registry-token ID
- */
- function unregisterRegistry(
- uint256 _registryID
- )
+ uint256 currentWei = databases[databaseIndex].currentWei.add(msg.value);
+ databases[databaseIndex].currentWei = currentWei;
+
+ uint256 accumulatedWei = databases[databaseIndex].accumulatedWei.add(msg.value);
+ databases[databaseIndex].accumulatedWei = accumulatedWei;
+
+ emit DatabaseFunded(_databaseID, msg.sender, msg.value);
+ address(chaingearSafe).transfer(msg.value);
+ }
+
+ function claimDatabaseFunds(uint256 _databaseID, uint256 _amount)
external
- onlyOwnerOf(_registryID)
+ onlyOwnerOf(_databaseID)
whenNotPaused
- {
- address registryAddress = registries[_registryID].contractAddress;
- require(RegistryInterface(registryAddress).getSafeBalance() == 0);
+ {
+ uint256 databaseIndex = allTokensIndex[_databaseID];
- uint256 registryIndex = allTokensIndex[_registryID];
- uint256 lastRegistryIndex = registries.length.sub(1);
- RegistryMeta storage lastRegistry = registries[lastRegistryIndex];
+ uint256 currentWei = databases[databaseIndex].currentWei;
+ require(_amount <= currentWei);
- registries[registryIndex] = lastRegistry;
- delete registries[lastRegistryIndex];
- registries.length--;
+ databases[databaseIndex].currentWei = currentWei.sub(_amount);
- _burn(msg.sender, _registryID);
+ emit DatabaseFundsClaimed(_databaseID, msg.sender, _amount);
+ chaingearSafe.claim(msg.sender, _amount);
+ }
- string memory registryName = RegistryInterface(registryAddress).name();
- emit RegistryUnregistered(msg.sender, registryName);
-
- //Sets current admin as owner of registry, transfers full control
- RegistryInterface(registryAddress).transferOwnership(msg.sender);
+ function updateCreationFee(uint256 _newFee)
+ external
+ onlyOwner
+ whenPaused
+ {
+ databaseCreationFeeWei = _newFee;
+ emit CreationFeeUpdated(_newFee);
+ }
+
+ /*
+ * Views
+ */
+
+ function getAmountOfBuilders()
+ external
+ view
+ returns(uint256)
+ {
+ return amountOfBuilders;
+ }
+
+ function getBuilderByID(uint256 _id)
+ external
+ view
+ returns(string)
+ {
+ return buildersVersionIndex[_id];
+ }
+
+ function getDatabaseBuilder(string _version)
+ external
+ view
+ returns (
+ address,
+ string,
+ string,
+ bool
+ )
+ {
+ return(
+ buildersVersion[_version].builderAddress,
+ buildersVersion[_version].linkToABI,
+ buildersVersion[_version].description,
+ buildersVersion[_version].operational
+ );
+ }
+
+ function getDatabasesIDs()
+ external
+ view
+ returns(uint256[])
+ {
+ return allTokens;
+ }
+
+ function getDatabaseIDByAddress(address _databaseAddress)
+ external
+ view
+ returns(uint256)
+ {
+ uint256 databaseID = databasesIDsByAddressesIndex[_databaseAddress];
+ return databaseID;
}
- /**
- * @dev Gets funding and allocate funds of Registry to Chaingear's Safe
- * @param _registryID uint256 Registry-token ID
+ function getDatabaseAddressByName(string _name)
+ external
+ view
+ returns(address)
+ {
+ return databasesAddressesByNameIndex[_name];
+ }
+
+ function getDatabaseSymbolByID(uint256 _databaseID)
+ external
+ view
+ returns(string)
+ {
+ return databasesSymbolsByIDIndex[_databaseID];
+ }
+
+ function getDatabaseIDBySymbol(string _symbol)
+ external
+ view
+ returns(uint256)
+ {
+ return databasesIDsBySymbolIndex[_symbol];
+ }
+
+ function getDatabase(uint256 _databaseID)
+ external
+ view
+ returns (
+ string,
+ string,
+ address,
+ string,
+ uint256,
+ address,
+ uint256
+ )
+ {
+ uint256 databaseIndex = allTokensIndex[_databaseID];
+ IDatabase databaseAddress = databases[databaseIndex].databaseContract;
+
+ return (
+ ERC721(databaseAddress).name(),
+ ERC721(databaseAddress).symbol(),
+ databaseAddress,
+ databases[databaseIndex].versionOfDatabase,
+ databases[databaseIndex].createdTimestamp,
+ databaseAddress.getAdmin(),
+ ERC721(databaseAddress).totalSupply()
+ );
+ }
+
+ function getDatabaseBalance(uint256 _databaseID)
+ external
+ view
+ returns (uint256, uint256)
+ {
+ uint256 databaseIndex = allTokensIndex[_databaseID];
+
+ return (
+ databases[databaseIndex].currentWei,
+ databases[databaseIndex].accumulatedWei
+ );
+ }
+
+ function getChaingearDescription()
+ external
+ pure
+ returns (string)
+ {
+ return CHAINGEAR_DESCRIPTION;
+ }
+
+ function getCreationFeeWei()
+ external
+ view
+ returns (uint256)
+ {
+ return databaseCreationFeeWei;
+ }
+
+ function getSafeBalance()
+ external
+ view
+ returns (uint256)
+ {
+ return address(chaingearSafe).balance;
+ }
+
+ function getSafeAddress()
+ external
+ view
+ returns (address)
+ {
+ return chaingearSafe;
+ }
+
+ function getNameExist(string _name)
+ external
+ view
+ returns (bool)
+ {
+ return databasesNamesIndex[_name];
+ }
+
+ function getSymbolExist(string _symbol)
+ external
+ view
+ returns (bool)
+ {
+ return databasesSymbolsIndex[_symbol];
+ }
+
+ /*
+ * Public functions
*/
- function fundRegistry(
- uint256 _registryID
+
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
)
- external
+ public
whenNotPaused
- payable
{
- registries[_registryID].currentRegistryBalanceETH = registries[_registryID].currentRegistryBalanceETH.add(msg.value);
- registries[_registryID].accumulatedRegistryETH = registries[_registryID].accumulatedRegistryETH.add(msg.value);
-
- emit RegistryFunded(_registryID, msg.sender, msg.value);
+ uint256 databaseIndex = allTokensIndex[_tokenId];
+ IDatabase database = databases[databaseIndex].databaseContract;
+ require(address(database).balance == 0);
+ require(database.getPaused() == true);
+ super.transferFrom(_from, _to, _tokenId);
- chaingearSafe.transfer(msg.value);
+ IDatabase databaseAddress = databases[databaseIndex].databaseContract;
+ databaseAddress.deletePayees();
+ databaseAddress.transferAdminRights(_to);
}
- /**
- * @dev Gets funding and allocate funds of Registry to Safe
- * @param _registryID uint256 Registry-token ID
- * @param _amount uint256 Amount which admin of registry claims
- */
- function claimEntryFunds(
- uint256 _registryID,
- uint256 _amount
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
)
- external
- onlyOwnerOf(_registryID)
+ public
whenNotPaused
{
- require(_amount <= registries[_registryID].currentRegistryBalanceETH);
- registries[_registryID].currentRegistryBalanceETH = registries[_registryID].currentRegistryBalanceETH.sub(_amount);
+ safeTransferFrom(
+ _from,
+ _to,
+ _tokenId,
+ ""
+ );
+ }
- emit RegistryFundsClaimed(_registryID, msg.sender, _amount);
-
- Safe(chaingearSafe).claim(msg.sender, _amount);
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId,
+ bytes _data
+ )
+ public
+ whenNotPaused
+ {
+ transferFrom(_from, _to, _tokenId);
+
+ require(
+ checkAndCallSafeTransfer(
+ _from,
+ _to,
+ _tokenId,
+ _data
+ ));
}
/*
* Private functions
*/
- /**
- * @dev Private function for registry creation
- * @dev Pass Registry params and bytecode to RegistryCreator to current builder
- * @param _version version of registry code which added to chaingear
- * @param _benefitiaries address[] addresses of Registry benefitiaries
- * @param _shares uint256[] array with amount of shares to benefitiaries
- * @param _name string, Registry name, use for registry ERC721
- * @param _symbol string, Registry NFT symbol, use for registry ERC721
- * @return address new Registry contract address
- * @return uint256 new Registry ID in Chaingear contract, ERC721 NFT ID
- */
- function createRegistry(
- string _version,
- address[] _benefitiaries,
+ function _deployDatabase(
+ string _version,
+ address[] _beneficiaries,
uint256[] _shares,
- string _name,
- string _symbol
+ string _name,
+ string _symbol
)
private
- returns (
- address,
- uint256
- )
+ returns (address, uint256)
{
- address registryContract = RegistryCreator(registryAddresses[_version]).create(
- _benefitiaries,
+ IDatabaseBuilder builder = buildersVersion[_version].builderAddress;
+ IDatabase databaseContract = builder.deployDatabase(
+ _beneficiaries,
_shares,
_name,
_symbol
);
-
- RegistryMeta memory registry = (RegistryMeta(
+
+ address databaseAddress = address(databaseContract);
+
+ SupportsInterfaceWithLookup support = SupportsInterfaceWithLookup(databaseAddress);
+ require(support.supportsInterface(INTERFACE_DATABASE_V1_EULER_ID));
+ require(support.supportsInterface(InterfaceId_ERC721));
+ require(support.supportsInterface(InterfaceId_ERC721Metadata));
+ require(support.supportsInterface(InterfaceId_ERC721Enumerable));
+
+ DatabaseMeta memory database = (DatabaseMeta(
{
- contractAddress: registryContract,
- creator: msg.sender,
- version: _version,
- linkABI: registryABIsLinks[_version],
- registrationTimestamp: block.timestamp,
- currentRegistryBalanceETH: 0,
- accumulatedRegistryETH: 0
+ databaseContract: databaseContract,
+ creatorOfDatabase: msg.sender,
+ versionOfDatabase: _version,
+ linkABI: buildersVersion[_version].linkToABI,
+ createdTimestamp: block.timestamp,
+ currentWei: 0,
+ accumulatedWei: 0
}));
- uint256 registryID = registries.push(registry) - 1;
- _mint(msg.sender, registryID);
-
- registryNamesIndex[_name] = true;
- registrySymbolsIndex[_symbol] = true;
-
- emit RegistryRegistered(_name, registryContract, msg.sender, registryID);
-
- //Metaregistry as owner sets creator as admin of Registry
- RegistryInterface(registryContract).transferAdminRights(msg.sender);
+ databases.push(database);
- return (
- registryContract,
- registryID
+ databasesNamesIndex[_name] = true;
+ databasesSymbolsIndex[_symbol] = true;
+
+ uint256 newTokenID = headTokenID;
+ databasesIDsByAddressesIndex[databaseAddress] = newTokenID;
+ super._mint(msg.sender, newTokenID);
+ databasesSymbolsByIDIndex[newTokenID] = _symbol;
+ databasesIDsBySymbolIndex[_symbol] = newTokenID;
+ databasesAddressesByNameIndex[_name] = databaseAddress;
+ headTokenID = headTokenID.add(1);
+
+ emit DatabaseCreated(
+ _name,
+ databaseAddress,
+ msg.sender,
+ newTokenID
);
+
+ databaseContract.transferAdminRights(msg.sender);
+ return (databaseAddress, newTokenID);
}
-
+
}
diff --git a/contracts/chaingear/ChaingearCore.sol b/contracts/chaingear/ChaingearCore.sol
deleted file mode 100644
index 2649ad38..00000000
--- a/contracts/chaingear/ChaingearCore.sol
+++ /dev/null
@@ -1,202 +0,0 @@
-pragma solidity 0.4.24;
-
-import "openzeppelin-solidity/contracts/lifecycle/Destructible.sol";
-import "openzeppelin-solidity/contracts/lifecycle/Pausable.sol";
-import "./RegistryBase.sol";
-
-
-/**
-* @title Chaingear core contract
-* @author cyber•Congress, Valery Litvin (@litvintech)
-* @dev Storage of core data and setters/getters
-* @notice not recommend to use before release!
-*/
-
-contract ChaingearCore is RegistryBase, Destructible, Pausable {
-
- /*
- * Storage
- */
-
- // @dev Mapping which allow control of name uniqueness in metaregistry
- mapping(string => bool) internal registryNamesIndex;
-
- // @dev Mapping which allow control of symbol uniqueness in metaregistry
- mapping(string => bool) internal registrySymbolsIndex;
-
- // @dev Short Chaingear's description, less than 128 symbols
- string internal chaingearDescription;
-
- // @dev Amount that registrys creator should pay for registry creation/registring
- uint internal registryRegistrationFee;
-
- // @dev Address of contract where their funds allocates
- address internal chaingearSafe;
-
- // @dev mapping with address of registry creators with different code base of registries
- mapping (string => address) internal registryAddresses;
-
- // @dev mapping with ipfs links to json with ABI of different registries
- mapping (string => string) internal registryABIsLinks;
-
- // @dev mapping description of different registries types/versions
- mapping (string => string) internal registryDescriptions;
-
- /*
- * Events
- */
-
- // @dev Signals that given Registry funded
- event RegistryFunded(
- uint registryID,
- address sender,
- uint amount
- );
-
- // @dev Signals that given Registry funds claimed by their admin
- event RegistryFundsClaimed(
- uint registryID,
- address claimer,
- uint amout
- );
-
- /*
- * External Functions
- */
-
- /**
- * @dev Provides funcitonality for adding fabrics of different kind of registries
- * @param _nameOfVersion string which represents name of registry type/version
- * @param _addressRegistryCreator address of registry creator/fabric
- * @param _link string which represents IPFS hash to JSON with ABI of registry
- * @param _description string which resprent info about registry fabric type
- * @notice Only owner of metaregistry/chaingear allowed to add fabrics
- */
- function addRegistryCreatorVersion(
- string _nameOfVersion,
- address _addressRegistryCreator,
- string _link,
- string _description
- )
- external
- onlyOwner
- {
- require(registryAddresses[_nameOfVersion] == 0x0);
- registryAddresses[_nameOfVersion] = _addressRegistryCreator;
- registryABIsLinks[_nameOfVersion] = _link;
- registryDescriptions[_nameOfVersion] = _description;
- }
-
- /*
- * External Functions
- */
-
- /**
- * @dev Chaingear' registry creation/registration fee setter
- * @param _newFee uint new fee amount
- * @notice Only owner of metaregistry/chaingear allowed to set fee
- */
- function updateRegistrationFee(
- uint _newFee
- )
- external
- onlyOwner
- {
- registryRegistrationFee = _newFee;
- }
-
- /**
- * @dev Chaingear' description setter
- * @param _description string with new description
- * @notice description should be less than 128 symbols
- * @notice Only owner of metaregistry/chaingear allowed to change description
- */
- function updateDescription(
- string _description
- )
- external
- onlyOwner
- {
- uint len = bytes(_description).length;
- require(len <= 256);
-
- chaingearDescription = _description;
- }
-
- /*
- * View Functions
- */
-
- /**
- * @dev Allows get information about given version of registry fabric
- * @param _nameOfVersion address which represents name of registry type
- * @return _addressRegistryCreator address of registry fabric for this version
- * @return _link string which represents IPFS hash to JSON with ABI of registry
- * @return _description string which resprent info about this registry
- */
- function getRegistryCreatorInfo(
- string _nameOfVersion
- )
- external
- view
- returns (
- address _addressRegistryCreator,
- string _link,
- string _description
- )
- {
- return(
- registryAddresses[_nameOfVersion],
- registryABIsLinks[_nameOfVersion],
- registryDescriptions[_nameOfVersion]
- );
- }
-
- /**
- * @dev Chaingear description getter
- * @return string description of Chaingear
- */
- function getDescription()
- external
- view
- returns (string)
- {
- return chaingearDescription;
- }
-
- /**
- * @dev Chaingear registration fee getter
- * @return uint amount of fee in wei
- */
- function getRegistrationFee()
- external
- view
- returns (uint)
- {
- return registryRegistrationFee;
- }
-
- /**
- * @dev Safe balence getter
- * @return uint amount of fee in wei
- */
- function getSafeBalance()
- external
- view
- returns (uint)
- {
- return address(chaingearSafe).balance;
- }
-
- /**
- * @dev Safe contract address getter
- * @return uint amount of fee in wei
- */
- function getSafe()
- external
- view
- returns (address)
- {
- return chaingearSafe;
- }
-}
diff --git a/contracts/common/SplitPaymentChangeable.sol b/contracts/chaingear/FeeSplitterChaingear.sol
similarity index 58%
rename from contracts/common/SplitPaymentChangeable.sol
rename to contracts/chaingear/FeeSplitterChaingear.sol
index dbf9080c..6a9668e9 100644
--- a/contracts/common/SplitPaymentChangeable.sol
+++ b/contracts/chaingear/FeeSplitterChaingear.sol
@@ -1,35 +1,31 @@
-pragma solidity 0.4.24;
+pragma solidity 0.4.25;
-import "openzeppelin-solidity/contracts/payment/SplitPayment.sol";
+import "../common/PaymentSplitter.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
-contract SplitPaymentChangeable is SplitPayment, Ownable {
-
+contract FeeSplitterChaingear is PaymentSplitter, Ownable {
+
event PayeeAddressChanged(
- uint payeeIndex,
+ uint8 payeeIndex,
address oldAddress,
address newAddress
);
- constructor(
- address[] _payees,
- uint256[] _shares
- )
+ constructor(address[] _payees, uint256[] _shares)
public
payable
- SplitPayment(_payees, _shares)
+ PaymentSplitter(_payees, _shares)
{ }
-
- function changePayeeAddress(
- uint _payeeIndex,
- address _newAddress
- )
+
+ function changePayeeAddress(uint8 _payeeIndex, address _newAddress)
external
onlyOwner
{
+ require(_payeeIndex < 12);
+ require(payees[_payeeIndex] != _newAddress);
+
address oldAddress = payees[_payeeIndex];
-
shares[_newAddress] = shares[oldAddress];
released[_newAddress] = released[oldAddress];
payees[_payeeIndex] = _newAddress;
@@ -39,4 +35,5 @@ contract SplitPaymentChangeable is SplitPayment, Ownable {
emit PayeeAddressChanged(_payeeIndex, oldAddress, _newAddress);
}
-}
+
+}
\ No newline at end of file
diff --git a/contracts/chaingear/RegistryBase.sol b/contracts/chaingear/RegistryBase.sol
deleted file mode 100644
index 7fc4b8da..00000000
--- a/contracts/chaingear/RegistryBase.sol
+++ /dev/null
@@ -1,138 +0,0 @@
-pragma solidity 0.4.24;
-import "../common/RegistryInterface.sol";
-
-
-/**
-* @title RegistryBase contract
-* @author cyber•Congress, Valery Litvin (@litvintech)
-* @dev Contracts which holds logic and struct of data witch describes registry metainformation which
-* associated with token, provides views function for registry metainformation.
-* @notice not recommend to use before release!
-*/
-
-//todo rename: we have RegistryBase and RegistryInterface
-contract RegistryBase {
-
- /*
- * Storage
- */
-
- // @dev Sctruct which describes registry metainformation with balance state and status
- struct RegistryMeta {
- address contractAddress;
- address creator;
- string version;
- string linkABI;
- uint registrationTimestamp;
- uint256 currentRegistryBalanceETH;
- uint256 accumulatedRegistryETH;
- }
-
-
- // @dev Array of registries data
- RegistryMeta[] internal registries;
-
- /*
- * Events
- */
-
- // @dev Events witch signals that new Registry registered
- event RegistryRegistered(
- string name,
- address registryAddress,
- address creator,
- uint registryID
- );
-
- // @dev Events witch signals that Registry adminship transferred
- // @notice that also means associated token transferred too
- event RegistryChangedOwner(
- address caller,
- uint256 registyID,
- address newOwner
- );
-
- // @dev Events witch signals that Registry unregistered from Chaingear
- // @notice adminship of Registry transfers from Chaingear to Admin
- event RegistryUnregistered(
- address admin,
- string name
- );
-
- /*
- * External Functions
- */
-
- /**
- * @dev Registy metainfo getter
- * @param _registryID uint256 Registry ID, associated ERC721 token ID
- * @return string Registy name
- * @return string Registy symbol
- * @return address Registy address
- * @return address Registy creator address
- * @return string Registy version
- * @return uint Registy creation timestamp
- * @return address Registy admin address
- */
- function registryInfo(
- uint256 _registryID
- )
- external
- view
- returns (
- string,
- string,
- address,
- address,
- string,
- uint,
- address
- )
- {
- address contractAddress = registries[_registryID].contractAddress;
-
- return (
- RegistryInterface(contractAddress).name(),
- RegistryInterface(contractAddress).symbol(),
- contractAddress,
- registries[_registryID].creator,
- registries[_registryID].version,
- registries[_registryID].registrationTimestamp,
- RegistryInterface(contractAddress).getAdmin()
- );
- }
-
- /**
- * @dev Registy funding stats getter
- * @param _registryID uint256 Registry ID
- * @return uint Registy current balance in wei, which stored in Safe
- * @return uint Registy total accumulated balance in wei
- */
- function registryBalanceInfo(
- uint256 _registryID
- )
- external
- view
- returns (
- uint256,
- uint256
- )
- {
- return (
- registries[_registryID].currentRegistryBalanceETH,
- registries[_registryID].accumulatedRegistryETH
- );
- }
-
- /**
- * @dev Registies amount getter
- * @return uint256 amounts of Registries
- */
- function registriesAmount()
- external
- view
- returns (uint256)
- {
- return registries.length;
- }
-}
diff --git a/contracts/chaingear/RegistryCreator.sol b/contracts/chaingear/RegistryCreator.sol
deleted file mode 100644
index e123007d..00000000
--- a/contracts/chaingear/RegistryCreator.sol
+++ /dev/null
@@ -1,107 +0,0 @@
-pragma solidity 0.4.24;
-
-import "../registry/Registry.sol";
-import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
-
-
-/**
-* @title Registry Creator engine/fabric
-* @author cyber•Congress
-* @dev Allows to Chaingear contract as builder create new Registries
-* @dev with codebase which imported and deployed with this fabric
-* @notice not recommend to use before release!
-*/
-contract RegistryCreator is Ownable {
-
- /*
- * @dev Storage
- */
-
- // @dev Holds address of contract which can call creation, means Chaingear
- address internal builder;
-
- /*
- * @dev Constructor
- */
-
- /**
- * @dev Contructor of RegistryCreators
- * @notice setting 0x0, then allows to owner to set builder/Chaingear
- * @notice after deploying needs to set up builder/Chaingear address
- */
- constructor()
- public
- {
- builder = 0x0;
- }
-
- /**
- * @dev Disallows direct send by settings a default function without the `payable` flag.
- */
- function() external {}
-
- /*
- * @dev External Functions
- */
-
- /**
- * @dev Allows chaingear (builder) create new registry
- * @param _benefitiaries address[] array of beneficiaries addresses
- * @param _shares uint256[] array of shares amont ot each beneficiary
- * @param _name string name of Registry and token
- * @param _symbol string symbol of Registry and token
- * @return address Address of new initialized Registry
- */
- function create(
- address[] _benefitiaries,
- uint256[] _shares,
- string _name,
- string _symbol
- )
- external
- returns (address)
- {
- require(msg.sender == builder);
-
- address registryContract = new Registry(
- _benefitiaries,
- _shares,
- _name,
- _symbol
- );
- //Fabric as owner transfers ownership to Chaingear contract after creation
- Registry(registryContract).transferOwnership(builder);
-
- return registryContract;
- }
-
- /**
- * @dev Registry builder setter, owners sets Chaingear address
- * @param _builder address
- */
- function setBuilder(
- address _builder
- )
- external
- onlyOwner
- {
- require(_builder != 0x0);
- builder = _builder;
- }
-
- /*
- * View Functions
- */
-
- /**
- * @dev RegistryCreator's builder getter
- * @return address of setted Registry builder (Chaingear contract)
- */
- function getRegistryBuilder()
- external
- view
- returns (address)
- {
- return builder;
- }
-}
diff --git a/contracts/common/ERC721MetadataValidation.sol b/contracts/common/ERC721MetadataValidation.sol
new file mode 100644
index 00000000..4bdd42a6
--- /dev/null
+++ b/contracts/common/ERC721MetadataValidation.sol
@@ -0,0 +1,25 @@
+pragma solidity 0.4.25;
+
+
+library ERC721MetadataValidation {
+
+ function validateName(string _base)
+ internal
+ pure
+ {
+ bytes memory _baseBytes = bytes(_base);
+ for (uint i = 0; i < _baseBytes.length; i++) {
+ require(_baseBytes[i] >= 0x61 && _baseBytes[i] <= 0x7A || _baseBytes[i] >= 0x30 && _baseBytes[i] <= 0x39 || _baseBytes[i] == 0x2D);
+ }
+ }
+
+ function validateSymbol(string _base)
+ internal
+ pure
+ {
+ bytes memory _baseBytes = bytes(_base);
+ for (uint i = 0; i < _baseBytes.length; i++) {
+ require(_baseBytes[i] >= 0x41 && _baseBytes[i] <= 0x5A || _baseBytes[i] >= 0x30 && _baseBytes[i] <= 0x39);
+ }
+ }
+}
\ No newline at end of file
diff --git a/contracts/common/EntryInterface.sol b/contracts/common/EntryInterface.sol
deleted file mode 100644
index 49d5edcd..00000000
--- a/contracts/common/EntryInterface.sol
+++ /dev/null
@@ -1,9 +0,0 @@
-pragma solidity 0.4.24;
-
-
-contract EntryInterface {
-
- function entriesAmount() external view returns (uint256);
- function createEntry() external returns (uint256);
- function deleteEntry(uint256) external;
-}
diff --git a/contracts/common/IChaingear.sol b/contracts/common/IChaingear.sol
new file mode 100644
index 00000000..83e25e8c
--- /dev/null
+++ b/contracts/common/IChaingear.sol
@@ -0,0 +1,51 @@
+pragma solidity 0.4.25;
+
+import "../common/IDatabaseBuilder.sol";
+
+
+interface IChaingear {
+
+ function addDatabaseBuilderVersion(
+ string,
+ IDatabaseBuilder,
+ string,
+ string
+ ) external;
+ function updateDatabaseBuilderDescription(string, string) external;
+ function depricateDatabaseBuilder(string) external;
+ function createDatabase(
+ string,
+ address[],
+ uint256[],
+ string,
+ string
+ ) external payable returns (address, uint256);
+ function deleteDatabase(uint256) external;
+ function fundDatabase(uint256) external payable;
+ function claimDatabaseFunds(uint256, uint256) external;
+ function updateCreationFee(uint256) external;
+ function getAmountOfBuilders() external view returns (uint256);
+ function getBuilderByID(uint256) external view returns(string);
+ function getDatabaseBuilder(string) external view returns(address, string, string, bool);
+ function getDatabasesIDs() external view returns (uint256[]);
+ function getDatabaseIDByAddress(address) external view returns (uint256);
+ function getDatabaseAddressByName(string) external view returns (address);
+ function getDatabaseSymbolByID(uint256) external view returns (string);
+ function getDatabaseIDBySymbol(string) external view returns (uint256);
+ function getDatabase(uint256) external view returns (
+ string,
+ string,
+ address,
+ string,
+ uint256,
+ address,
+ uint256
+ );
+ function getDatabaseBalance(uint256) external view returns (uint256, uint256);
+ function getChaingearDescription() external pure returns (string);
+ function getCreationFeeWei() external view returns (uint256);
+ function getSafeBalance() external view returns (uint256);
+ function getSafeAddress() external view returns (address);
+ function getNameExist(string) external view returns (bool);
+ function getSymbolExist(string) external view returns (bool);
+}
diff --git a/contracts/common/IDatabase.sol b/contracts/common/IDatabase.sol
new file mode 100644
index 00000000..e1a583f3
--- /dev/null
+++ b/contracts/common/IDatabase.sol
@@ -0,0 +1,42 @@
+pragma solidity 0.4.25;
+
+interface IDatabase {
+
+ function createEntry() external payable returns (uint256);
+ function auth(uint256, address) external;
+ function deleteEntry(uint256) external;
+ function fundEntry(uint256) external payable;
+ function claimEntryFunds(uint256, uint256) external;
+ function updateEntryCreationFee(uint256) external;
+ function updateDatabaseDescription(string) external;
+ function addDatabaseTag(bytes32) external;
+ function updateDatabaseTag(uint8, bytes32) external;
+ function removeDatabaseTag(uint8) external;
+ function readEntryMeta(uint256) external view returns (
+ address,
+ address,
+ uint256,
+ uint256,
+ uint256,
+ uint256
+ );
+ function getChaingearID() external view returns (uint256);
+ function getEntriesIDs() external view returns (uint256[]);
+ function getIndexByID(uint256) external view returns (uint256);
+ function getEntryCreationFee() external view returns (uint256);
+ function getEntriesStorage() external view returns (address);
+ function getSchemaDefinition() external view returns (string);
+ function getDatabaseBalance() external view returns (uint256);
+ function getDatabaseDescription() external view returns (string);
+ function getDatabaseTags() external view returns (bytes32[]);
+ function getDatabaseSafe() external view returns (address);
+ function getSafeBalance() external view returns (uint256);
+ function getDatabaseInitStatus() external view returns (bool);
+ function pause() external;
+ function unpause() external;
+ function transferAdminRights(address) external;
+ function getAdmin() external view returns (address);
+ function getPaused() external view returns (bool);
+ function transferOwnership(address) external;
+ function deletePayees() external;
+}
diff --git a/contracts/common/IDatabaseBuilder.sol b/contracts/common/IDatabaseBuilder.sol
new file mode 100644
index 00000000..e1ab8fd1
--- /dev/null
+++ b/contracts/common/IDatabaseBuilder.sol
@@ -0,0 +1,17 @@
+pragma solidity 0.4.25;
+
+import "../common/IDatabase.sol";
+
+
+interface IDatabaseBuilder {
+
+ function deployDatabase(
+ address[],
+ uint256[],
+ string,
+ string
+ ) external returns (IDatabase);
+ function setChaingearAddress(address) external;
+ function getChaingearAddress() external view returns (address);
+ function getOwner() external view returns (address);
+}
diff --git a/contracts/common/ISchema.sol b/contracts/common/ISchema.sol
new file mode 100644
index 00000000..a6b5b0e2
--- /dev/null
+++ b/contracts/common/ISchema.sol
@@ -0,0 +1,8 @@
+pragma solidity 0.4.25;
+
+
+interface ISchema {
+
+ function createEntry() external;
+ function deleteEntry(uint256) external;
+}
diff --git a/contracts/common/PaymentSplitter.sol b/contracts/common/PaymentSplitter.sol
new file mode 100644
index 00000000..5a9a396b
--- /dev/null
+++ b/contracts/common/PaymentSplitter.sol
@@ -0,0 +1,134 @@
+pragma solidity 0.4.25;
+
+import "openzeppelin-solidity/contracts/math/SafeMath.sol";
+
+
+/**
+ * @title PaymentSplitter
+ * @dev This contract can be used when payments need to be received by a group
+ * of people and split proportionately to some number of shares they own.
+ */
+contract PaymentSplitter {
+
+ using SafeMath for uint256;
+
+ uint256 internal totalShares;
+ uint256 internal totalReleased;
+
+ mapping(address => uint256) internal shares;
+ mapping(address => uint256) internal released;
+ address[] internal payees;
+
+ event PayeeAdded(address account, uint256 shares);
+ event PaymentReleased(address to, uint256 amount);
+ event PaymentReceived(address from, uint256 amount);
+
+ constructor (address[] _payees, uint256[] _shares)
+ public
+ payable
+ {
+ _initializePayess(_payees, _shares);
+ }
+
+ function ()
+ external
+ payable
+ {
+ emit PaymentReceived(msg.sender, msg.value);
+ }
+
+ function getTotalShares()
+ external
+ view
+ returns (uint256)
+ {
+ return totalShares;
+ }
+
+ function getTotalReleased()
+ external
+ view
+ returns (uint256)
+ {
+ return totalReleased;
+ }
+
+ function getShares(address _account)
+ external
+ view
+ returns (uint256)
+ {
+ return shares[_account];
+ }
+
+ function getReleased(address _account)
+ external
+ view
+ returns (uint256)
+ {
+ return released[_account];
+ }
+
+ function getPayee(uint256 _index)
+ external
+ view
+ returns (address)
+ {
+ return payees[_index];
+ }
+
+ function getPayeesCount()
+ external
+ view
+ returns (uint256)
+ {
+ return payees.length;
+ }
+
+ function release(address _account)
+ public
+ {
+ require(shares[_account] > 0);
+
+ uint256 totalReceived = address(this).balance.add(totalReleased);
+ uint256 payment = totalReceived.mul(shares[_account]).div(totalShares).sub(released[_account]);
+
+ require(payment != 0);
+
+ released[_account] = released[_account].add(payment);
+ totalReleased = totalReleased.add(payment);
+
+ _account.transfer(payment);
+
+ emit PaymentReleased(_account, payment);
+ }
+
+ function _initializePayess(address[] _payees, uint256[] _shares)
+ internal
+ {
+ require(payees.length == 0);
+ require(_payees.length == _shares.length);
+ require(_payees.length > 0 && _payees.length <= 8);
+
+ for (uint256 i = 0; i < _payees.length; i++) {
+ _addPayee(_payees[i], _shares[i]);
+ }
+ }
+
+ function _addPayee(
+ address _account,
+ uint256 _shares
+ )
+ internal
+ {
+ require(_account != address(0));
+ require(_shares > 0);
+ require(shares[_account] == 0);
+
+ payees.push(_account);
+ shares[_account] = _shares;
+ totalShares = totalShares.add(_shares);
+
+ emit PayeeAdded(_account, _shares);
+ }
+}
\ No newline at end of file
diff --git a/contracts/common/RegistryInterface.sol b/contracts/common/RegistryInterface.sol
deleted file mode 100644
index 0ba3d4e6..00000000
--- a/contracts/common/RegistryInterface.sol
+++ /dev/null
@@ -1,15 +0,0 @@
-pragma solidity 0.4.24;
-
-
-contract RegistryInterface {
- function getSafeBalance() external view returns (uint256);
- function getAdmin() external view returns (address);
- function createEntry() external payable returns (uint256);
- function deleteEntry(uint256 _entryId) external;
- function fundEntry(uint256 _entryId) external payable;
- function claimEntryFunds(uint256 _entryId, uint _amount) external;
- function transferAdminRights(address _newOnwer) public;
- function transferOwnership(address _newOwner) public;
- function name() public view returns (string);
- function symbol() public view returns (string);
-}
diff --git a/contracts/common/Safe.sol b/contracts/common/Safe.sol
index 7dda02e9..4ec73422 100644
--- a/contracts/common/Safe.sol
+++ b/contracts/common/Safe.sol
@@ -1,27 +1,20 @@
-pragma solidity 0.4.24;
+pragma solidity 0.4.25;
/**
-* @title Safe contract
-* @author cyber•Congress, Valery Litvin (@litvintech)
-* @dev Allows store etheirs which funded to Registry
-* @dev and claim them by Registry/associated token via Chaingear
-* @notice not recommend to use before release!
+* @title Chaingear - the novel Ethereum database framework
+* @author cyber•Congress, Valery litvin (@litvintech)
+* @notice not audited, not recommend to use in mainnet
*/
contract Safe {
- address public owner;
+ address private owner;
- constructor()
- public
- payable
+ constructor() public
{
owner = msg.sender;
}
- /**
- * @dev Allows direct send only by owner.
- */
function()
external
payable
@@ -29,21 +22,21 @@ contract Safe {
require(msg.sender == owner);
}
- /**
- * @dev Allows owner (chaingear) claim funds and transfer them to Registry admin
- * @param _entryOwner address transfer to, Registry-token admin
- * @param _amount uint claimed amount by Registry-token admin
- */
- function claim(
- address _entryOwner,
- uint256 _amount
- )
+ function claim(address _entryOwner, uint256 _amount)
external
{
require(msg.sender == owner);
require(_amount <= address(this).balance);
- require(_entryOwner != 0x0);
+ require(_entryOwner != address(0));
+
_entryOwner.transfer(_amount);
}
+ function getOwner()
+ external
+ view
+ returns(address)
+ {
+ return owner;
+ }
}
diff --git a/contracts/databases/DatabasePermissionControl.sol b/contracts/databases/DatabasePermissionControl.sol
new file mode 100644
index 00000000..000113f9
--- /dev/null
+++ b/contracts/databases/DatabasePermissionControl.sol
@@ -0,0 +1,165 @@
+pragma solidity 0.4.25;
+
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+
+
+/**
+* @title Chaingear - the novel Ethereum database framework
+* @author cyber•Congress, Valery litvin (@litvintech)
+* @notice not audited, not recommend to use in mainnet
+*/
+contract DatabasePermissionControl is Ownable {
+
+ /*
+ * Storage
+ */
+
+ enum CreateEntryPermissionGroup {OnlyAdmin, Whitelist, AllUsers}
+
+ address private admin;
+ bool private paused = true;
+
+ mapping(address => bool) private whitelist;
+
+ CreateEntryPermissionGroup private permissionGroup = CreateEntryPermissionGroup.OnlyAdmin;
+
+ /*
+ * Events
+ */
+
+ event Pause();
+ event Unpause();
+ event PermissionGroupChanged(CreateEntryPermissionGroup);
+ event AddedToWhitelist(address);
+ event RemovedFromWhitelist(address);
+ event AdminshipTransferred(address, address);
+
+ /*
+ * Constructor
+ */
+
+ constructor()
+ public
+ { }
+
+ /*
+ * Modifiers
+ */
+
+ modifier whenNotPaused() {
+ require(!paused);
+ _;
+ }
+
+ modifier whenPaused() {
+ require(paused);
+ _;
+ }
+
+ modifier onlyAdmin() {
+ require(msg.sender == admin);
+ _;
+ }
+
+ modifier onlyPermissionedToCreateEntries() {
+ if (permissionGroup == CreateEntryPermissionGroup.OnlyAdmin) {
+ require(msg.sender == admin);
+ } else if (permissionGroup == CreateEntryPermissionGroup.Whitelist) {
+ require(whitelist[msg.sender] == true || msg.sender == admin);
+ }
+ _;
+ }
+
+ /*
+ * External functions
+ */
+
+ function pause()
+ external
+ onlyAdmin
+ whenNotPaused
+ {
+ paused = true;
+ emit Pause();
+ }
+
+ function unpause()
+ external
+ onlyAdmin
+ whenPaused
+ {
+ paused = false;
+ emit Unpause();
+ }
+
+ function transferAdminRights(address _newAdmin)
+ external
+ onlyOwner
+ whenPaused
+ {
+ require(_newAdmin != address(0));
+ emit AdminshipTransferred(admin, _newAdmin);
+ admin = _newAdmin;
+ }
+
+ function updateCreateEntryPermissionGroup(CreateEntryPermissionGroup _newPermissionGroup)
+ external
+ onlyAdmin
+ whenPaused
+ {
+ require(CreateEntryPermissionGroup.AllUsers >= _newPermissionGroup);
+
+ permissionGroup = _newPermissionGroup;
+ emit PermissionGroupChanged(_newPermissionGroup);
+ }
+
+ function addToWhitelist(address _address)
+ external
+ onlyAdmin
+ whenPaused
+ {
+ whitelist[_address] = true;
+ emit AddedToWhitelist(_address);
+ }
+
+ function removeFromWhitelist(address _address)
+ external
+ onlyAdmin
+ whenPaused
+ {
+ whitelist[_address] = false;
+ emit RemovedFromWhitelist(_address);
+ }
+
+ function getAdmin()
+ external
+ view
+ returns (address)
+ {
+ return admin;
+ }
+
+ function getDatabasePermissions()
+ external
+ view
+ returns (CreateEntryPermissionGroup)
+ {
+ return permissionGroup;
+ }
+
+ function checkWhitelisting(address _address)
+ external
+ view
+ returns (bool)
+ {
+ return whitelist[_address];
+ }
+
+ function getPaused()
+ external
+ view
+ returns (bool)
+ {
+ return paused;
+ }
+}
diff --git a/contracts/databases/DatabaseV1.sol b/contracts/databases/DatabaseV1.sol
new file mode 100644
index 00000000..cd193c1b
--- /dev/null
+++ b/contracts/databases/DatabaseV1.sol
@@ -0,0 +1,469 @@
+pragma solidity 0.4.25;
+
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
+import "openzeppelin-solidity/contracts/token/ERC721/ERC721Token.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+import "openzeppelin-solidity/contracts/math/SafeMath.sol";
+
+import "../common/IChaingear.sol";
+import "../common/ISchema.sol";
+import "../common/IDatabase.sol";
+import "../common/Safe.sol";
+import "./FeeSplitterDatabase.sol";
+import "./DatabasePermissionControl.sol";
+
+
+/**
+* @title Chaingear - the novel Ethereum database framework
+* @author cyber•Congress, Valery litvin (@litvintech)
+* @notice not audited, not recommend to use in mainnet
+*/
+contract DatabaseV1 is IDatabase, Ownable, DatabasePermissionControl, SupportsInterfaceWithLookup, FeeSplitterDatabase, ERC721Token {
+
+ using SafeMath for uint256;
+
+ /*
+ * Storage
+ */
+
+ bytes4 private constant INTERFACE_SCHEMA_EULER_ID = 0x153366ed;
+ bytes4 private constant INTERFACE_DATABASE_V1_EULER_ID = 0xf2c320c4;
+
+ // @dev Metadata of entry, holds ownership data and funding info
+ struct EntryMeta {
+ address creator;
+ uint256 createdAt;
+ uint256 lastUpdateTime;
+ uint256 currentWei;
+ uint256 accumulatedWei;
+ }
+
+ EntryMeta[] private entriesMeta;
+ Safe private databaseSafe;
+
+ uint256 private headTokenID = 0;
+ uint256 private entryCreationFeeWei = 0;
+
+ bytes32[] private databaseTags;
+ string private databaseDescription;
+
+ string private schemaDefinition;
+ ISchema private entriesStorage;
+ bool private databaseInitStatus = false;
+
+ /*
+ * Modifiers
+ */
+
+ modifier onlyOwnerOf(uint256 _entryID){
+ require(ownerOf(_entryID) == msg.sender);
+ _;
+ }
+
+ modifier databaseInitialized {
+ require(databaseInitStatus == true);
+ _;
+ }
+
+ /**
+ * Events
+ */
+
+ event EntryCreated(uint256 entryID, address creator);
+ event EntryDeleted(uint256 entryID, address owner);
+ event EntryFunded(
+ uint256 entryID,
+ address funder,
+ uint256 amount
+ );
+ event EntryFundsClaimed(
+ uint256 entryID,
+ address claimer,
+ uint256 amount
+ );
+ event EntryCreationFeeUpdated(uint256 newFees);
+ event DescriptionUpdated(string newDescription);
+ event DatabaseInitialized();
+ event TagAdded(bytes32 tag);
+ event TagUpdated(uint8 index, bytes32 tag);
+ event TagDeleted(uint8 index);
+
+ /*
+ * Constructor
+ */
+
+ constructor(
+ address[] _beneficiaries,
+ uint256[] _shares,
+ string _name,
+ string _symbol
+ )
+ ERC721Token (_name, _symbol)
+ FeeSplitterDatabase (_beneficiaries, _shares)
+ public
+ payable
+ {
+ _registerInterface(INTERFACE_DATABASE_V1_EULER_ID);
+ databaseSafe = new Safe();
+ }
+
+ /*
+ * External functions
+ */
+
+ function createEntry()
+ external
+ databaseInitialized
+ onlyPermissionedToCreateEntries
+ whenNotPaused
+ payable
+ returns (uint256)
+ {
+ require(msg.value == entryCreationFeeWei);
+
+ EntryMeta memory meta = (EntryMeta(
+ {
+ lastUpdateTime: block.timestamp,
+ createdAt: block.timestamp,
+ creator: msg.sender,
+ currentWei: 0,
+ accumulatedWei: 0
+ }));
+ entriesMeta.push(meta);
+
+ uint256 newTokenID = headTokenID;
+ super._mint(msg.sender, newTokenID);
+ headTokenID = headTokenID.add(1);
+
+ emit EntryCreated(newTokenID, msg.sender);
+
+ entriesStorage.createEntry();
+
+ return newTokenID;
+ }
+
+ function auth(uint256 _entryID, address _caller)
+ external
+ whenNotPaused
+ {
+ require(msg.sender == address(entriesStorage));
+ require(ownerOf(_entryID) == _caller);
+ uint256 entryIndex = allTokensIndex[_entryID];
+ entriesMeta[entryIndex].lastUpdateTime = block.timestamp;
+ }
+
+ function deleteEntry(uint256 _entryID)
+ external
+ databaseInitialized
+ onlyOwnerOf(_entryID)
+ whenNotPaused
+ {
+ uint256 entryIndex = allTokensIndex[_entryID];
+ require(entriesMeta[entryIndex].currentWei == 0);
+
+ uint256 lastEntryIndex = entriesMeta.length.sub(1);
+ EntryMeta memory lastEntry = entriesMeta[lastEntryIndex];
+
+ entriesMeta[entryIndex] = lastEntry;
+ delete entriesMeta[lastEntryIndex];
+ entriesMeta.length--;
+
+ super._burn(msg.sender, _entryID);
+ emit EntryDeleted(_entryID, msg.sender);
+
+ entriesStorage.deleteEntry(entryIndex);
+ }
+
+ function fundEntry(uint256 _entryID)
+ external
+ databaseInitialized
+ whenNotPaused
+ payable
+ {
+ require(exists(_entryID) == true);
+
+ uint256 entryIndex = allTokensIndex[_entryID];
+ uint256 currentWei = entriesMeta[entryIndex].currentWei.add(msg.value);
+ entriesMeta[entryIndex].currentWei = currentWei;
+
+ uint256 accumulatedWei = entriesMeta[entryIndex].accumulatedWei.add(msg.value);
+ entriesMeta[entryIndex].accumulatedWei = accumulatedWei;
+
+ emit EntryFunded(_entryID, msg.sender, msg.value);
+ address(databaseSafe).transfer(msg.value);
+ }
+
+ function claimEntryFunds(uint256 _entryID, uint256 _amount)
+ external
+ databaseInitialized
+ onlyOwnerOf(_entryID)
+ whenNotPaused
+ {
+ uint256 entryIndex = allTokensIndex[_entryID];
+
+ uint256 currentWei = entriesMeta[entryIndex].currentWei;
+ require(_amount <= currentWei);
+ entriesMeta[entryIndex].currentWei = currentWei.sub(_amount);
+
+ emit EntryFundsClaimed(_entryID, msg.sender, _amount);
+ databaseSafe.claim(msg.sender, _amount);
+ }
+
+ function updateEntryCreationFee(uint256 _newFee)
+ external
+ onlyAdmin
+ whenPaused
+ {
+ entryCreationFeeWei = _newFee;
+ emit EntryCreationFeeUpdated(_newFee);
+ }
+
+ function updateDatabaseDescription(string _newDescription)
+ external
+ onlyAdmin
+ {
+ databaseDescription = _newDescription;
+ emit DescriptionUpdated(_newDescription);
+ }
+
+ function addDatabaseTag(bytes32 _tag)
+ external
+ onlyAdmin
+ {
+ require(databaseTags.length < 16);
+ databaseTags.push(_tag);
+ emit TagAdded(_tag);
+ }
+
+ function updateDatabaseTag(uint8 _index, bytes32 _tag)
+ external
+ onlyAdmin
+ {
+ require(_index < databaseTags.length);
+ databaseTags[_index] = _tag;
+ emit TagUpdated(_index, _tag);
+ }
+
+ function removeDatabaseTag(uint8 _index)
+ external
+ onlyAdmin
+ {
+ require(databaseTags.length > 0);
+ require(_index < databaseTags.length);
+
+ uint256 lastTagIndex = databaseTags.length.sub(1);
+ bytes32 lastTag = databaseTags[lastTagIndex];
+
+ databaseTags[_index] = lastTag;
+ databaseTags[lastTagIndex] = "";
+ databaseTags.length--;
+
+ emit TagDeleted(_index);
+ }
+
+ /*
+ * View functions
+ */
+
+ function readEntryMeta(uint256 _entryID)
+ external
+ view
+ returns (
+ address,
+ address,
+ uint256,
+ uint256,
+ uint256,
+ uint256
+ )
+ {
+ require(exists(_entryID) == true);
+ uint256 entryIndex = allTokensIndex[_entryID];
+
+ EntryMeta memory m = entriesMeta[entryIndex];
+ return(
+ ownerOf(_entryID),
+ m.creator,
+ m.createdAt,
+ m.lastUpdateTime,
+ m.currentWei,
+ m.accumulatedWei
+ );
+ }
+
+ function getChaingearID()
+ external
+ view
+ returns(uint256)
+ {
+ return IChaingear(owner).getDatabaseIDByAddress(address(this));
+ }
+
+ function getEntriesIDs()
+ external
+ view
+ returns (uint256[])
+ {
+ return allTokens;
+ }
+
+ function getIndexByID(uint256 _entryID)
+ external
+ view
+ returns (uint256)
+ {
+ require(exists(_entryID) == true);
+ return allTokensIndex[_entryID];
+ }
+
+ function getEntryCreationFee()
+ external
+ view
+ returns (uint256)
+ {
+ return entryCreationFeeWei;
+ }
+
+ function getEntriesStorage()
+ external
+ view
+ returns (address)
+ {
+ return address(entriesStorage);
+ }
+
+ function getSchemaDefinition()
+ external
+ view
+ returns (string)
+ {
+ return schemaDefinition;
+ }
+
+ function getDatabaseBalance()
+ external
+ view
+ returns (uint256)
+ {
+ return address(this).balance;
+ }
+
+ function getDatabaseDescription()
+ external
+ view
+ returns (string)
+ {
+ return databaseDescription;
+ }
+
+ function getDatabaseTags()
+ external
+ view
+ returns (bytes32[])
+ {
+ return databaseTags;
+ }
+
+ function getDatabaseSafe()
+ external
+ view
+ returns (address)
+ {
+ return databaseSafe;
+ }
+
+ function getSafeBalance()
+ external
+ view
+ returns (uint256)
+ {
+ return address(databaseSafe).balance;
+ }
+
+ function getDatabaseInitStatus()
+ external
+ view
+ returns (bool)
+ {
+ return databaseInitStatus;
+ }
+
+ /**
+ * Public functions
+ */
+
+ function initializeDatabase(string _schemaDefinition, bytes _schemaBytecode)
+ public
+ onlyAdmin
+ whenPaused
+ returns (address)
+ {
+ require(databaseInitStatus == false);
+ address deployedAddress;
+
+ assembly {
+ let s := mload(_schemaBytecode)
+ let p := add(_schemaBytecode, 0x20)
+ deployedAddress := create(0, p, s)
+ }
+
+ require(deployedAddress != address(0));
+ require(SupportsInterfaceWithLookup(deployedAddress).supportsInterface(INTERFACE_SCHEMA_EULER_ID));
+ entriesStorage = ISchema(deployedAddress);
+
+ schemaDefinition = _schemaDefinition;
+ databaseInitStatus = true;
+
+ emit DatabaseInitialized();
+ return deployedAddress;
+ }
+
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
+ )
+ public
+ databaseInitialized
+ whenNotPaused
+ {
+ super.transferFrom(_from, _to, _tokenId);
+ }
+
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
+ )
+ public
+ databaseInitialized
+ whenNotPaused
+ {
+ safeTransferFrom(
+ _from,
+ _to,
+ _tokenId,
+ ""
+ );
+ }
+
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId,
+ bytes _data
+ )
+ public
+ databaseInitialized
+ whenNotPaused
+ {
+ transferFrom(_from, _to, _tokenId);
+ require(
+ checkAndCallSafeTransfer(
+ _from,
+ _to,
+ _tokenId,
+ _data
+ ));
+ }
+}
diff --git a/contracts/databases/FeeSplitterDatabase.sol b/contracts/databases/FeeSplitterDatabase.sol
new file mode 100644
index 00000000..746c9364
--- /dev/null
+++ b/contracts/databases/FeeSplitterDatabase.sol
@@ -0,0 +1,75 @@
+pragma solidity 0.4.25;
+
+import "../common/PaymentSplitter.sol";
+import "./DatabasePermissionControl.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+
+
+contract FeeSplitterDatabase is PaymentSplitter, DatabasePermissionControl {
+
+ event PayeeAddressChanged(
+ uint8 payeeIndex,
+ address oldAddress,
+ address newAddress
+ );
+ event PayeesDeleted();
+
+ constructor(address[] _payees, uint256[] _shares)
+ public
+ payable
+ PaymentSplitter(_payees, _shares)
+ { }
+
+ function ()
+ external
+ payable
+ whenNotPaused
+ {
+ emit PaymentReceived(msg.sender, msg.value);
+ }
+
+ function changePayeeAddress(uint8 _payeeIndex, address _newAddress)
+ external
+ whenNotPaused
+ {
+ require(_payeeIndex < 8);
+ require(msg.sender == payees[_payeeIndex]);
+ require(payees[_payeeIndex] != _newAddress);
+
+ address oldAddress = payees[_payeeIndex];
+
+ shares[_newAddress] = shares[oldAddress];
+ released[_newAddress] = released[oldAddress];
+ payees[_payeeIndex] = _newAddress;
+
+ delete shares[oldAddress];
+ delete released[oldAddress];
+
+ emit PayeeAddressChanged(_payeeIndex, oldAddress, _newAddress);
+ }
+
+ function setPayess(address[] _payees, uint256[] _shares)
+ external
+ whenPaused
+ onlyAdmin
+ {
+ _initializePayess(_payees, _shares);
+ }
+
+ function deletePayees()
+ external
+ whenPaused
+ onlyOwner
+ {
+ for (uint8 i = 0; i < payees.length; i++) {
+ address account = payees[i];
+ delete shares[account];
+ delete released[account];
+ }
+ payees.length = 0;
+ totalShares = 0;
+ totalReleased = 0;
+
+ emit PayeesDeleted();
+ }
+}
\ No newline at end of file
diff --git a/contracts/registry/Chaingeareable.sol b/contracts/registry/Chaingeareable.sol
deleted file mode 100644
index 87316713..00000000
--- a/contracts/registry/Chaingeareable.sol
+++ /dev/null
@@ -1,281 +0,0 @@
-pragma solidity 0.4.24;
-
-import "./RegistryPermissionControl.sol";
-
-
-/**
-* @title Chaingeareable
-* @author cyber•Congress, Valery Litvin (@litvintech)
-* @dev Storage of core data and setters/getters
-* @notice not recommend to use before release!
-*/
-contract Chaingeareable is RegistryPermissionControl {
-
- /*
- * Storage
- */
-
- // @dev entry creation fee
- uint internal entryCreationFee;
-
- // @dev registry description string
- string internal registryDescription;
-
- // @dev registry tags
- bytes32[] internal registryTags;
-
- // @dev address of EntryCore contract, which specifies data schema and operations
- address internal entriesStorage;
-
- // @dev link to IPFS hash to ABI of EntryCore contract
- string internal linkToABIOfEntriesContract;
-
- // @dev address of Registry safe where funds store
- address internal registrySafe;
-
- // @dev state of was registry initialized with EntryCore or not
- bool internal registryInitStatus;
-
- /*
- * Modifiers
- */
-
- // @dev don't allow to call registry entry functions before initialization
- modifier registryInitialized {
- require(registryInitStatus == true);
- _;
- }
-
- /**
- * Events
- */
-
- // @dev Signals that new entry-token added to Registry
- event EntryCreated(
- uint entryID,
- address creator
- );
-
- // @dev Signals that entry-token changed owner
- event EntryChangedOwner(
- uint entryID,
- address newOwner
- );
-
- // @dev Signals that entry-token deleted
- event EntryDeleted(
- uint entryID,
- address owner
- );
-
- // @dev Signals that entry-token funded with given amount
- event EntryFunded(
- uint entryID,
- address funder,
- uint amount
- );
-
- // @dev Signals that entry-token funds claimed by owner with given amount
- event EntryFundsClaimed(
- uint entryID,
- address owner,
- uint amount
- );
-
- /**
- * External Functions
- */
-
- /**
- * @dev Allows admin set new registration fee, which entry creators should pay
- * @param _fee uint In wei which should be payed for creation/registration
- */
- function updateEntryCreationFee(
- uint _fee
- )
- external
- onlyAdmin
- {
- entryCreationFee = _fee;
- }
-
- /**
- * @dev Allows admin update registry description
- * @param _registryDescription string Which represents description
- * @notice Length of description should be less than 256 bytes
- */
- function updateRegistryDescription(
- string _registryDescription
- )
- external
- onlyAdmin
- {
- uint len = bytes(_registryDescription).length;
- require(len <= 256);
-
- registryDescription = _registryDescription;
- }
-
- /**
- * @dev Allows admin to add tag to registry
- * @param _tag bytes32 Tag
- * @notice Tags amount should be less than 16
- */
- function addRegistryTag(
- bytes32 _tag
- )
- external
- onlyAdmin
- {
- require(_tag.length <= 16);
- registryTags.push(_tag);
- }
-
- /**
- * @dev Allows admin to update update specified tag
- * @param _index uint16 Index of tag to update
- * @param _tag bytes32 New tag value
- */
- function updateRegistryTag(
- uint16 _index,
- bytes32 _tag
- )
- external
- onlyAdmin
- {
- require(_tag.length <= 16);
- require(_index <= registryTags.length-1);
-
- registryTags[_index] = _tag;
- }
-
- /**
- * @dev Remove registry tag
- * @param _index uint16 Index of tag to delete
- */
- function removeRegistryTag(
- uint16 _index
- )
- external
- onlyAdmin
- {
- uint256 lastTagIndex = registryTags.length - 1;
- bytes32 lastTag = registryTags[lastTagIndex];
-
- registryTags[_index] = lastTag;
- registryTags[lastTagIndex] = "";
- registryTags.length--;
- }
-
- /**
- * View functions
- */
-
- /**
- * @dev Allows to get EntryCore contract which specified entry schema and operations
- * @return address of that contract
- */
- function getEntriesStorage()
- external
- view
- returns (address)
- {
- return entriesStorage;
- }
-
- /**
- * @dev Allows to get link interface of EntryCore contract
- * @return string with IPFS hash to JSON with ABI
- */
- function getInterfaceEntriesContract()
- external
- view
- returns (string)
- {
- return linkToABIOfEntriesContract;
- }
-
- /**
- * @dev Allows to get registry balance which represents accumulated fees for entry creations
- * @return uint Amount in wei accumulated in Registry Contract
- */
- function getRegistryBalance()
- external
- view
- returns (uint)
- {
- return address(this).balance;
- }
-
- /**
- * @dev Allows to check which amount fee needed for entry creation/registration
- * @return uint Current amount in wei needed for registration
- */
- function getEntryCreationFee()
- external
- view
- returns (uint)
- {
- return entryCreationFee;
- }
-
- /**
- * @dev Allows to get description of Registry
- * @return string which represents description
- */
- function getRegistryDescription()
- external
- view
- returns (string)
- {
- return registryDescription;
- }
-
- /**
- * @dev Allows to get Registry Tags
- * @return bytes32[] array of tags
- */
- function getRegistryTags()
- external
- view
- returns (bytes32[])
- {
- return registryTags;
- }
-
- /**
- * @dev Allows to get address of Safe which Registry control (owns)
- * @return address of Safe contract
- */
- function getRegistrySafe()
- external
- view
- returns (address)
- {
- return registrySafe;
- }
-
- /**
- * @dev Allows to get amount of funds aggregated in Safe
- * @return uint Amount of funds in wei
- */
- function getSafeBalance()
- external
- view
- returns (uint balance)
- {
- return address(registrySafe).balance;
- }
-
- /**
- * @dev Allows to check state of Registry init with EntryCore
- * @return bool Yes/No
- */
- function getRegistryInitStatus()
- external
- view
- returns (bool)
- {
- return registryInitStatus;
- }
-}
diff --git a/contracts/registry/EntryCore.sol b/contracts/registry/EntryCore.sol
deleted file mode 100644
index 9f8017c8..00000000
--- a/contracts/registry/EntryCore.sol
+++ /dev/null
@@ -1,113 +0,0 @@
-pragma solidity 0.4.24;
-
-import "../common/EntryInterface.sol";
-import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
-
-
-//This is Example of EntryCore
-contract EntryCore is EntryInterface, Ownable {
-
- struct Entry {
- address expensiveAddress;
- uint256 expensiveUint;
- int128 expensiveInt;
- string expensiveString;
- }
-
- mapping(string => bool) internal entryExpensiveStringIndex;
-
- Entry[] internal entries;
-
- function() external {}
-
- function createEntry()
- external
- onlyOwner
- returns (uint256)
- {
- Entry memory m = (Entry(
- {
- expensiveAddress: address(0),
- expensiveUint: uint256(0),
- expensiveInt: int128(0),
- expensiveString: ""
- }));
-
- uint256 newEntryID = entries.push(m) - 1;
-
- return newEntryID;
- }
-
- function updateEntry(
- uint256 _entryID,
- address _newAddress,
- uint256 _newUint,
- int128 _newInt,
- string _newString
- )
- external
- {
- require(owner.call(bytes4(keccak256("checkAuth(uint256, address)")), _entryID, msg.sender));
-
- // for uniq check example
- require(entryExpensiveStringIndex[_newString] == false);
- // for uniq check example
- entryExpensiveStringIndex[_newString] = true;
- string storage lastIndexValue = entries[_entryID].expensiveString;
- entryExpensiveStringIndex[lastIndexValue] = false;
-
-
- Entry memory m = (Entry({
- expensiveAddress: _newAddress,
- expensiveUint: _newUint,
- expensiveInt: _newInt,
- expensiveString: _newString
- }));
- entries[_entryID] = m;
-
- require(owner.call(bytes4(keccak256("updateEntryTimestamp(uint256)")), _entryID));
- }
-
- function deleteEntry(
- uint256 _entryIndex
- )
- external
- onlyOwner
- {
- uint256 lastEntryIndex = entries.length - 1;
- Entry storage lastEntry = entries[lastEntryIndex];
-
- entries[_entryIndex] = lastEntry;
- delete entries[lastEntryIndex];
- entries.length--;
- }
-
- function entriesAmount()
- external
- view
- returns (uint256 entryID)
- {
- return entries.length;
- }
-
- function entryInfo(
- uint256 _entryID
- )
- external
- view
- returns (
- address,
- uint256,
- int128,
- string
- )
- {
- return (
- entries[_entryID].expensiveAddress,
- entries[_entryID].expensiveUint,
- entries[_entryID].expensiveInt,
- entries[_entryID].expensiveString
- );
- }
-
-}
diff --git a/contracts/registry/Registry.sol b/contracts/registry/Registry.sol
deleted file mode 100644
index a439aaae..00000000
--- a/contracts/registry/Registry.sol
+++ /dev/null
@@ -1,317 +0,0 @@
-pragma solidity 0.4.24;
-
-import "openzeppelin-solidity/contracts/token/ERC721/ERC721Token.sol";
-import "openzeppelin-solidity/contracts/math/SafeMath.sol";
-import "../common/SplitPaymentChangeable.sol";
-import "./Chaingeareable.sol";
-import "../common/EntryInterface.sol";
-import "../common/RegistryInterface.sol";
-import "../common/Safe.sol";
-
-
-/**
-* @title Registry First Type Contract
-* @author cyber•Congress, Valery litvin (@litvintech)
-* @dev This contract in current flow used for Registry creation throught fabric in Chaingear
-* @dev Registry creates ERC721 for each entry, entry may be funded/funds claimed
-* @dev Admin sets contracts (EntrCore) which defines entry schema
-* @dev Entry creation/deletion/update permission are tokenized
-* @notice not recommend to use before release!
-*/
-contract Registry is RegistryInterface, Chaingeareable, SplitPaymentChangeable, ERC721Token {
-
- using SafeMath for uint256;
-
- /*
- * Storage
- */
-
- // @dev Metadata of entry, holds ownership data and funding info
- struct EntryMeta {
- address creator;
- uint createdAt;
- uint lastUpdateTime;
- uint256 currentEntryBalanceETH;
- uint256 accumulatedOverallEntryETH;
- }
-
- // @dev Array of associated to entry/token metadata
- EntryMeta[] internal entriesMeta;
-
- /*
- * Constructor
- */
-
- /**
- * @dev Constructor of Registry, creates from fabric which triggers by chaingear
- * @param _benefitiaries address[] addresses of Registry benefitiaries
- * @param _shares uint256[] array with amount of shares by benefitiary
- * @param _name string Registry name, use for Registry ERC721
- * @param _symbol string Registry NFT symbol, use for Registry ERC721
- * @notice sets default entry creation policy to onlyAdmin
- * @notice sets default creation fee to zero
- * @notice Registry are inactive till he is not initialized with schema (EntryCore)
- */
- constructor(
- address[] _benefitiaries,
- uint256[] _shares,
- string _name,
- string _symbol
- )
- SplitPaymentChangeable(_benefitiaries, _shares)
- ERC721Token(_name, _symbol)
- public
- payable
- {
- createEntryPermissionGroup = CreateEntryPermissionGroup.OnlyAdmin;
- entryCreationFee = 0;
- registrySafe = new Safe();
- registryInitStatus = false;
- }
-
- function() public payable {}
-
- /*
- * External functions
- */
-
- /**
- * @dev Creates ERC721 and init asscociated epmty entry in EntryCore
- * @return uint256 ID of ERC721 token
- * @notice Entry owner should to after token creation and entry init set
- * @notice entry data throught EntryCore updateEntry()
- * @notice This (and deletion) would work if EntryCore correctly written
- * @notice otherwise nothing should happen with Registry
- */
- function createEntry()
- external
- registryInitialized
- onlyPermissionedToCreateEntries
- whenNotPaused
- payable
- returns (uint256)
- {
- require(msg.value == entryCreationFee);
-
- EntryMeta memory meta = (EntryMeta(
- {
- lastUpdateTime: block.timestamp,
- createdAt: block.timestamp,
- creator: msg.sender,
- currentEntryBalanceETH: 0,
- accumulatedOverallEntryETH: 0
- }));
-
- entriesMeta.push(meta);
-
- //newEntryID equals current entriesAmount number, because token IDs starts from 0
- uint256 newEntryID = EntryInterface(entriesStorage).entriesAmount();
- require(newEntryID == totalSupply());
-
- _mint(msg.sender, newEntryID);
- emit EntryCreated(newEntryID, msg.sender);
-
- uint256 createdEntryID = EntryInterface(entriesStorage).createEntry();
- require(newEntryID == createdEntryID);
-
- return newEntryID;
- }
-
- /**
- * @dev Allow entry owner delete Entry-token and also Entry-data in EntryCore
- * @param _entryID uint256 Entry-token ID
- */
- function deleteEntry(
- uint256 _entryID
- )
- external
- registryInitialized
- onlyOwnerOf(_entryID)
- whenNotPaused
- {
- require(entriesMeta[_entryID].currentEntryBalanceETH == 0);
-
- uint256 entryIndex = allTokensIndex[_entryID];
-
- uint256 lastEntryIndex = entriesMeta.length - 1;
- EntryMeta storage lastEntry = entriesMeta[lastEntryIndex];
-
- entriesMeta[_entryID] = lastEntry;
- delete entriesMeta[lastEntryIndex];
- entriesMeta.length--;
-
- _burn(msg.sender, _entryID);
- emit EntryDeleted(_entryID, msg.sender);
-
- EntryInterface(entriesStorage).deleteEntry(entryIndex);
- }
-
- function transferFrom(
- address _from,
- address _to,
- uint256 _tokenId
- )
- public
- canTransfer(_tokenId)
- registryInitialized
- {
- require(_from != address(0));
- require(_to != address(0));
-
- clearApproval(_from, _tokenId);
- removeTokenFrom(_from, _tokenId);
- addTokenTo(_to, _tokenId);
-
- emit Transfer(_from, _to, _tokenId);
- }
-
- /**
- * @dev Allows anyone fund specified entry
- * @param _entryID uint256 Entry-token ID
- * @notice Funds tracks in EntryMeta, stores in Registry Safe
- */
- function fundEntry(
- uint256 _entryID
- )
- external
- registryInitialized
- whenNotPaused
- payable
- {
- entriesMeta[_entryID].currentEntryBalanceETH = entriesMeta[_entryID].currentEntryBalanceETH.add(msg.value);
- entriesMeta[_entryID].accumulatedOverallEntryETH = entriesMeta[_entryID].accumulatedOverallEntryETH.add(msg.value);
- emit EntryFunded(_entryID, msg.sender, msg.value);
-
- registrySafe.transfer(msg.value);
- }
-
- /**
- * @dev Allows entry token owner claim entry funds
- * @param _entryID uint256 Entry-token ID
- * @param _amount uint Amount in wei which token owner claims
- * @notice Funds tracks in EntryMeta, transfers from Safe to claimer (owner)
- */
- function claimEntryFunds(
- uint256 _entryID,
- uint _amount
- )
- external
- registryInitialized
- onlyOwnerOf(_entryID)
- whenNotPaused
- {
- require(_amount <= entriesMeta[_entryID].currentEntryBalanceETH);
- entriesMeta[_entryID].currentEntryBalanceETH = entriesMeta[_entryID].currentEntryBalanceETH.sub(_amount);
-
- emit EntryFundsClaimed(_entryID, msg.sender, _amount);
-
- Safe(registrySafe).claim(msg.sender, _amount);
- }
-
- /**
- * @dev Allow to set last entry data update for entry-token meta
- * @param _entryID uint256 Entry-token ID
- * @notice Can be (should be) called only by EntryCore (updateEntry)
- */
- function updateEntryTimestamp(
- uint256 _entryID
- )
- external
- {
- entriesMeta[_entryID].lastUpdateTime = block.timestamp;
- require(entriesStorage == msg.sender);
- }
-
- /*
- * View functions
- */
-
- function getEntryMeta(
- uint256 _entryID
- )
- external
- view
- returns (
- address,
- address,
- uint,
- uint,
- uint256,
- uint256
- )
- {
- return(
- ownerOf(_entryID),
- entriesMeta[_entryID].creator,
- entriesMeta[_entryID].createdAt,
- entriesMeta[_entryID].lastUpdateTime,
- entriesMeta[_entryID].currentEntryBalanceETH,
- entriesMeta[_entryID].accumulatedOverallEntryETH
- );
- }
-
- /**
- * @dev Verification function which auth user to update specified entry data in EntryCore
- * @param _entryID uint256 Entry-token ID
- * @param _caller address of caller which trying to update entry throught EntryCore
- * @return
- */
- function checkAuth(
- uint256 _entryID,
- address _caller
- )
- external
- view
- returns (bool)
- {
- require(ownerOf(_entryID) == _caller);
- }
-
- /**
- * @dev Allows update ERC721 token name (Registry Name)
- * @param _name string which represents name
- */
- function updateName(
- string _name
- )
- external
- onlyAdmin
- {
- name_ = _name;
- }
-
- /*
- * Public functions
- */
-
- /**
- * @dev Registry admin sets generated by them EntryCore contrats with thier schema and
- * @dev needed supported entry-token logic
- * @param _linkToABIOfEntriesContract string link to IPFS hash which holds EntryCore's ABI
- * @param _entryCore bytes of contract which holds schema and accociated CRUD functions
- * @return address of deployed EntryCore
- */
- function initializeRegistry(
- string _linkToABIOfEntriesContract,
- bytes _entryCore
- )
- public
- onlyAdmin
- returns (address)
- {
- address deployedAddress;
- assembly {
- let s := mload(_entryCore)
- let p := add(_entryCore, 0x20)
- deployedAddress := create(0, p, s)
- }
-
- assert(deployedAddress != 0x0);
- entriesStorage = deployedAddress;
- registryInitStatus = true;
- linkToABIOfEntriesContract = _linkToABIOfEntriesContract;
-
- return entriesStorage;
- }
-
-}
diff --git a/contracts/registry/RegistryPermissionControl.sol b/contracts/registry/RegistryPermissionControl.sol
deleted file mode 100644
index dcc360f1..00000000
--- a/contracts/registry/RegistryPermissionControl.sol
+++ /dev/null
@@ -1,110 +0,0 @@
-pragma solidity 0.4.24;
-
-import "openzeppelin-solidity/contracts/lifecycle/Pausable.sol";
-
-
-/**
-* @title RegistryPermissionControl contract
-* @author cyber•Congress, Valery litvin (@litvintech)
-* @dev Controls registry adminship logic and permission to entry management
-* @notice Now support only OnlyAdmin/AllUsers permissions
-* @notice not recommend to use before release!
-*/
-contract RegistryPermissionControl is Pausable {
-
- /*
- * Storage
- */
-
- // @dev
- address internal admin;
-
- // @dev Holds supported permission to create entry rights
- enum CreateEntryPermissionGroup {OnlyAdmin, AllUsers}
-
- // @dev Holds current permission group, onlyAdmin by default
- CreateEntryPermissionGroup internal createEntryPermissionGroup;
-
- /*
- * Modifiers
- */
-
- // @dev Controls access granted only to admin
- modifier onlyAdmin() {
- require(msg.sender == admin);
- _;
- }
-
- // @dev Controls access to entry creation granted by setted permission group
- modifier onlyPermissionedToCreateEntries() {
- if (createEntryPermissionGroup == CreateEntryPermissionGroup.OnlyAdmin) {
- require(msg.sender == admin);
- }
- _;
- }
-
- /*
- * View functions
- */
-
- /**
- * @dev Allows to get current admin address account
- * @return address of admin
- */
- function getAdmin()
- external
- view
- returns (address)
- {
- return admin;
- }
-
- /**
- * @dev Allows to get current permission group
- * @return uint8 index of current group
- */
- function getRegistryPermissions()
- external
- view
- returns (uint8)
- {
- return uint8(createEntryPermissionGroup);
- }
-
- /*
- * Public functions
- */
-
- /**
- * @dev Allows owner (in main workflow - chaingear) transfer admin right
- * @dev if previous admin transfer associated ERC721 token.
- * @param _newAdmin address of new token holder/registry admin
- * @notice triggers by chaingear in main workflow (when registry non-registered from CH)
- */
- function transferAdminRights(
- address _newAdmin
- )
- public
- onlyOwner
- whenNotPaused
- {
- require(_newAdmin != 0x0);
- admin = _newAdmin;
- }
-
- /**
- * @dev Allows admin to set new permission group granted to create entries
- * @param _createEntryPermissionGroup uint8 index of needed group
- */
- function updateCreateEntryPermissionGroup(
- uint8 _createEntryPermissionGroup
- )
- public
- onlyAdmin
- whenNotPaused
- {
- require(uint8(CreateEntryPermissionGroup.AllUsers) >= _createEntryPermissionGroup);
- createEntryPermissionGroup = CreateEntryPermissionGroup(_createEntryPermissionGroup);
- }
-
-}
diff --git a/contracts/schemas/AppsSchema.sol b/contracts/schemas/AppsSchema.sol
new file mode 100644
index 00000000..7bead536
--- /dev/null
+++ b/contracts/schemas/AppsSchema.sol
@@ -0,0 +1,108 @@
+pragma solidity 0.4.25;
+
+import "../common/ISchema.sol";
+import "../common/IDatabase.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
+
+
+contract AppsSchema is ISchema, Ownable, SupportsInterfaceWithLookup {
+
+ bytes4 private constant INTERFACE_SCHEMA_EULER_ID = 0x153366ed;
+
+ struct Entry {
+ string name;
+ string manifest;
+ string extension;
+ string content;
+ string logo;
+ }
+
+ Entry[] public entries;
+
+ IDatabase internal database;
+
+ constructor()
+ public
+ {
+ _registerInterface(INTERFACE_SCHEMA_EULER_ID);
+ database = IDatabase(owner);
+ }
+
+ function() external {}
+
+ function createEntry()
+ external
+ onlyOwner
+ {
+ Entry memory m = (Entry(
+ {
+ name: "",
+ manifest: "",
+ extension: "",
+ content: "",
+ logo: ""
+ }));
+
+ entries.push(m);
+ }
+
+ function readEntry(uint256 _entryID)
+ external
+ view
+ returns (
+ string,
+ string,
+ string,
+ string,
+ string
+ )
+ {
+ uint256 entryIndex = database.getIndexByID(_entryID);
+ return (
+ entries[entryIndex].name,
+ entries[entryIndex].manifest,
+ entries[entryIndex].extension,
+ entries[entryIndex].content,
+ entries[entryIndex].logo
+ );
+ }
+
+ function updateEntry(
+ uint256 _entryID,
+ string _name,
+ string _manifest,
+ string _extension,
+ string _content,
+ string _logo
+ )
+ external
+ {
+ database.auth(_entryID, msg.sender);
+
+ uint256 entryIndex = database.getIndexByID(_entryID);
+
+ Entry memory m = (Entry(
+ {
+ name: _name,
+ manifest: _manifest,
+ extension: _extension,
+ content: _content,
+ logo: _logo
+ }));
+ entries[entryIndex] = m;
+ }
+
+ function deleteEntry(uint256 _entryIndex)
+ external
+ onlyOwner
+ {
+ uint256 lastEntryIndex = entries.length - 1;
+ Entry memory lastEntry = entries[lastEntryIndex];
+
+ entries[_entryIndex] = lastEntry;
+ delete entries[lastEntryIndex];
+ entries.length--;
+ }
+
+}
diff --git a/contracts/schemas/FeaturesSchema.sol b/contracts/schemas/FeaturesSchema.sol
new file mode 100644
index 00000000..e76c6a30
--- /dev/null
+++ b/contracts/schemas/FeaturesSchema.sol
@@ -0,0 +1,102 @@
+pragma solidity 0.4.25;
+
+import "../common/ISchema.sol";
+import "../common/IDatabase.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
+
+
+contract FeaturesSchema is ISchema, Ownable, SupportsInterfaceWithLookup {
+
+ bytes4 private constant INTERFACE_SCHEMA_EULER_ID = 0x153366ed;
+
+ struct Entry {
+ string name;
+ string description;
+ string github;
+ string maintainer;
+ }
+
+ Entry[] public entries;
+
+ IDatabase internal database;
+
+ constructor()
+ public
+ {
+ _registerInterface(INTERFACE_SCHEMA_EULER_ID);
+ database = IDatabase(owner);
+ }
+
+ function() external {}
+
+ function createEntry()
+ external
+ onlyOwner
+ {
+ Entry memory m = (Entry(
+ {
+ name: "",
+ description: "",
+ github: "",
+ maintainer: ""
+ }));
+
+ entries.push(m);
+ }
+
+ function readEntry(uint256 _entryID)
+ external
+ view
+ returns (
+ string,
+ string,
+ string,
+ string
+ )
+ {
+ uint256 entryIndex = database.getIndexByID(_entryID);
+ return (
+ entries[entryIndex].name,
+ entries[entryIndex].description,
+ entries[entryIndex].github,
+ entries[entryIndex].maintainer
+ );
+ }
+
+ function updateEntry(
+ uint256 _entryID,
+ string _name,
+ string _description,
+ string _github,
+ string _maintainer
+ )
+ external
+ {
+ database.auth(_entryID, msg.sender);
+
+ uint256 entryIndex = database.getIndexByID(_entryID);
+
+ Entry memory m = (Entry(
+ {
+ name: _name,
+ description: _description,
+ github: _github,
+ maintainer: _maintainer
+ }));
+ entries[entryIndex] = m;
+ }
+
+ function deleteEntry(uint256 _entryIndex)
+ external
+ onlyOwner
+ {
+ uint256 lastEntryIndex = entries.length - 1;
+ Entry memory lastEntry = entries[lastEntryIndex];
+
+ entries[_entryIndex] = lastEntry;
+ delete entries[lastEntryIndex];
+ entries.length--;
+ }
+
+}
diff --git a/contracts/schemas/NodesSchema.sol b/contracts/schemas/NodesSchema.sol
new file mode 100644
index 00000000..1c32daf3
--- /dev/null
+++ b/contracts/schemas/NodesSchema.sol
@@ -0,0 +1,109 @@
+pragma solidity 0.4.25;
+
+import "../common/ISchema.sol";
+import "../common/IDatabase.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
+
+
+contract NodesSchema is ISchema, Ownable, SupportsInterfaceWithLookup {
+
+ bytes4 private constant INTERFACE_SCHEMA_EULER_ID = 0x153366ed;
+
+ struct Entry {
+ string name;
+ string addressNode;
+ string services;
+ string description;
+ string operating;
+ }
+
+ Entry[] public entries;
+
+ IDatabase internal database;
+
+ constructor()
+ public
+ {
+ _registerInterface(INTERFACE_SCHEMA_EULER_ID);
+ database = IDatabase(owner);
+ }
+
+ function() external {}
+
+ function createEntry()
+ external
+ onlyOwner
+ {
+ Entry memory m = (Entry(
+ {
+ name: "",
+ addressNode: "",
+ services: "",
+ description: "",
+ operating: ""
+
+ }));
+
+ entries.push(m);
+ }
+
+ function readEntry(uint256 _entryID)
+ external
+ view
+ returns (
+ string,
+ string,
+ string,
+ string,
+ string
+ )
+ {
+ uint256 entryIndex = database.getIndexByID(_entryID);
+ return (
+ entries[entryIndex].name,
+ entries[entryIndex].addressNode,
+ entries[entryIndex].services,
+ entries[entryIndex].description,
+ entries[entryIndex].operating
+ );
+ }
+
+ function updateEntry(
+ uint256 _entryID,
+ string _name,
+ string _addressNode,
+ string _services,
+ string _description,
+ string _operating
+ )
+ external
+ {
+ database.auth(_entryID, msg.sender);
+
+ uint256 entryIndex = database.getIndexByID(_entryID);
+
+ Entry memory m = (Entry(
+ {
+ name: _name,
+ addressNode: _addressNode,
+ services: _services,
+ description: _description,
+ operating: _operating
+ }));
+ entries[entryIndex] = m;
+ }
+
+ function deleteEntry(uint256 _entryIndex)
+ external
+ onlyOwner
+ {
+ uint256 lastEntryIndex = entries.length - 1;
+ Entry memory lastEntry = entries[lastEntryIndex];
+
+ entries[_entryIndex] = lastEntry;
+ delete entries[lastEntryIndex];
+ entries.length--;
+ }
+
+}
diff --git a/contracts/schemas/PortsSchema.sol b/contracts/schemas/PortsSchema.sol
new file mode 100644
index 00000000..a1c6f438
--- /dev/null
+++ b/contracts/schemas/PortsSchema.sol
@@ -0,0 +1,96 @@
+pragma solidity 0.4.25;
+
+import "../common/ISchema.sol";
+import "../common/IDatabase.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
+
+
+contract PortsSchema is ISchema, Ownable, SupportsInterfaceWithLookup {
+
+ bytes4 private constant INTERFACE_SCHEMA_EULER_ID = 0x153366ed;
+
+ struct Entry {
+ string portName;
+ uint16 portNumber;
+ string service;
+ }
+
+ Entry[] public entries;
+
+ IDatabase internal database;
+
+ constructor()
+ public
+ {
+ _registerInterface(INTERFACE_SCHEMA_EULER_ID);
+ database = IDatabase(owner);
+ }
+
+ function() external {}
+
+ function createEntry()
+ external
+ onlyOwner
+ {
+ Entry memory m = (Entry(
+ {
+ portName: "",
+ portNumber: uint16(0),
+ service: ""
+ }));
+
+ entries.push(m);
+ }
+
+ function readEntry(uint256 _entryID)
+ external
+ view
+ returns (
+ string,
+ uint16,
+ string
+ )
+ {
+ uint256 entryIndex = database.getIndexByID(_entryID);
+ return (
+ entries[entryIndex].portName,
+ entries[entryIndex].portNumber,
+ entries[entryIndex].service
+ );
+ }
+
+ function updateEntry(
+ uint256 _entryID,
+ string _portName,
+ uint16 _portNumber,
+ string _service
+ )
+ external
+ {
+ database.auth(_entryID, msg.sender);
+
+ uint256 entryIndex = database.getIndexByID(_entryID);
+
+ Entry memory m = (Entry(
+ {
+ portName: _portName,
+ portNumber: _portNumber,
+ service: _service
+ }));
+ entries[entryIndex] = m;
+ }
+
+ function deleteEntry(uint256 _entryIndex)
+ external
+ onlyOwner
+ {
+ uint256 lastEntryIndex = entries.length - 1;
+ Entry memory lastEntry = entries[lastEntryIndex];
+
+ entries[_entryIndex] = lastEntry;
+ delete entries[lastEntryIndex];
+ entries.length--;
+ }
+
+}
diff --git a/contracts/schemas/TeamSchema.sol b/contracts/schemas/TeamSchema.sol
new file mode 100644
index 00000000..debbc908
--- /dev/null
+++ b/contracts/schemas/TeamSchema.sol
@@ -0,0 +1,114 @@
+pragma solidity 0.4.25;
+
+import "../common/ISchema.sol";
+import "../common/IDatabase.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
+
+
+contract TeamSchema is ISchema, Ownable, SupportsInterfaceWithLookup {
+
+ bytes4 private constant INTERFACE_SCHEMA_EULER_ID = 0x153366ed;
+
+ struct Entry {
+ string name;
+ address gitcoin;
+ address payouts;
+ string github;
+ string telegram;
+ string keybase;
+ }
+
+ Entry[] public entries;
+
+ IDatabase internal database;
+
+ constructor()
+ public
+ {
+ _registerInterface(INTERFACE_SCHEMA_EULER_ID);
+ database = IDatabase(owner);
+ }
+
+ function() external {}
+
+ function createEntry()
+ external
+ onlyOwner
+ {
+ Entry memory m = (Entry(
+ {
+ name: "",
+ gitcoin: address(0),
+ payouts: address(0),
+ github: "",
+ telegram: "",
+ keybase: ""
+ }));
+
+ entries.push(m);
+ }
+
+ function readEntry(uint256 _entryID)
+ external
+ view
+ returns (
+ string,
+ address,
+ address,
+ string,
+ string,
+ string
+ )
+ {
+ uint256 entryIndex = database.getIndexByID(_entryID);
+ return (
+ entries[entryIndex].name,
+ entries[entryIndex].gitcoin,
+ entries[entryIndex].payouts,
+ entries[entryIndex].github,
+ entries[entryIndex].telegram,
+ entries[entryIndex].keybase
+ );
+ }
+
+ function updateEntry(
+ uint256 _entryID,
+ string _name,
+ address _gitcoin,
+ address _payouts,
+ string _github,
+ string _telegram,
+ string _keybase
+ )
+ external
+ {
+ database.auth(_entryID, msg.sender);
+
+ uint256 entryIndex = database.getIndexByID(_entryID);
+
+ Entry memory m = (Entry(
+ {
+ name: _name,
+ gitcoin: _gitcoin,
+ payouts: _payouts,
+ github: _github,
+ telegram: _telegram,
+ keybase: _keybase
+ }));
+ entries[entryIndex] = m;
+ }
+
+ function deleteEntry(uint256 _entryIndex)
+ external
+ onlyOwner
+ {
+ uint256 lastEntryIndex = entries.length - 1;
+ Entry memory lastEntry = entries[lastEntryIndex];
+
+ entries[_entryIndex] = lastEntry;
+ delete entries[lastEntryIndex];
+ entries.length--;
+ }
+
+}
diff --git a/contracts/schemas/TestSchema.sol b/contracts/schemas/TestSchema.sol
new file mode 100644
index 00000000..8db5cff6
--- /dev/null
+++ b/contracts/schemas/TestSchema.sol
@@ -0,0 +1,136 @@
+pragma solidity 0.4.25;
+
+import "../common/ISchema.sol";
+import "../common/IDatabase.sol";
+import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+import "openzeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol";
+
+
+contract TestSchema is ISchema, Ownable, SupportsInterfaceWithLookup {
+
+ bytes4 private constant INTERFACE_SCHEMA_EULER_ID = 0x153366ed;
+
+ struct Entry {
+ string stringField;
+ address addressField;
+ uint256 uint256Field;
+ int256 int256Field;
+ }
+
+ Entry[] public entries;
+
+ mapping(string => bool) private stringFieldUniqIndex;
+ mapping(address => bool) private addressFieldUniqIndex;
+ mapping(uint256 => bool) private uint256FieldUniqIndex;
+ mapping(int256 => bool) private int256FieldUniqIndex;
+
+ IDatabase internal database;
+
+ constructor()
+ public
+ {
+ _registerInterface(INTERFACE_SCHEMA_EULER_ID);
+ database = IDatabase(owner);
+ }
+
+ function() external {}
+
+ function createEntry()
+ external
+ onlyOwner
+ {
+ Entry memory m = (Entry(
+ {
+ stringField: "",
+ addressField: address(0),
+ uint256Field: uint256(0),
+ int256Field: int256(0)
+ }));
+
+ entries.push(m);
+ }
+
+ function readEntry(uint256 _entryID)
+ external
+ view
+ returns (
+ string,
+ address,
+ uint256,
+ int256
+ )
+ {
+ uint256 entryIndex = database.getIndexByID(_entryID);
+ return (
+ entries[entryIndex].stringField,
+ entries[entryIndex].addressField,
+ entries[entryIndex].uint256Field,
+ entries[entryIndex].int256Field
+ );
+ }
+
+ function updateEntry(
+ uint256 _entryID,
+ string _stringField,
+ address _addressField,
+ uint256 _uint256Field,
+ int256 _int256Field
+ )
+ external
+ {
+ database.auth(_entryID, msg.sender);
+
+ uint256 entryIndex = database.getIndexByID(_entryID);
+
+ if (bytes(_stringField).length > 0) {
+ require(stringFieldUniqIndex[_stringField] == false);
+ stringFieldUniqIndex[_stringField] = true;
+ stringFieldUniqIndex[entries[entryIndex].stringField] = false;
+ }
+
+ if (_addressField != address(0)) {
+ require(addressFieldUniqIndex[_addressField] == false);
+ addressFieldUniqIndex[_addressField] = true;
+ addressFieldUniqIndex[entries[entryIndex].addressField] = false;
+ }
+
+ if (_uint256Field != uint256(0)) {
+ require(uint256FieldUniqIndex[_uint256Field] == false);
+ uint256FieldUniqIndex[_uint256Field] = true;
+ uint256FieldUniqIndex[entries[entryIndex].uint256Field] = false;
+ }
+
+ if (_int256Field != int256(0)) {
+ require(int256FieldUniqIndex[_int256Field] == false);
+ int256FieldUniqIndex[_int256Field] = true;
+ int256FieldUniqIndex[entries[entryIndex].int256Field] = false;
+ }
+
+ Entry memory m = (Entry(
+ {
+ stringField: _stringField,
+ addressField: _addressField,
+ uint256Field: _uint256Field,
+ int256Field: _int256Field
+ }));
+ entries[entryIndex] = m;
+ }
+
+ function deleteEntry(uint256 _entryIndex)
+ external
+ onlyOwner
+ {
+ stringFieldUniqIndex[entries[_entryIndex].stringField] = false;
+ addressFieldUniqIndex[entries[_entryIndex].addressField] = false;
+ uint256FieldUniqIndex[entries[_entryIndex].uint256Field] = false;
+ int256FieldUniqIndex[entries[_entryIndex].int256Field] = false;
+
+ uint256 lastEntryIndex = entries.length - 1;
+ Entry memory lastEntry = entries[lastEntryIndex];
+
+ entries[_entryIndex] = lastEntry;
+ delete entries[lastEntryIndex];
+ entries.length--;
+ }
+
+}
diff --git a/contracts/tests/registry/RegistryPermissionControlTestContract.sol b/contracts/tests/registry/RegistryPermissionControlTestContract.sol
deleted file mode 100644
index 6e4a3dc8..00000000
--- a/contracts/tests/registry/RegistryPermissionControlTestContract.sol
+++ /dev/null
@@ -1,16 +0,0 @@
-pragma solidity 0.4.24;
-
-import "../../registry/RegistryPermissionControl.sol";
-
-
-contract RegistryPermissionControlTestContract is RegistryPermissionControl {
-
- uint public uintValue_;
-
- function testOnlyPermissionedToCreateEntries(uint _newUintValue)
- external
- onlyPermissionedToCreateEntries
- {
- uintValue_ = _newUintValue;
- }
-}
diff --git a/distribution/app.js b/distribution/app.js
deleted file mode 100644
index c1f11b91..00000000
--- a/distribution/app.js
+++ /dev/null
@@ -1 +0,0 @@
-webpackJsonp([0],[,function(e,t,n){(function(e){var t;t=function(){"use strict";var t,r;function i(){return t.apply(null,arguments)}function a(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function f(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function s(e){return void 0===e}function o(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function l(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function c(e,t){var n,r=[];for(n=0;n