This commit is contained in:
Cale Teeter 2021-07-21 12:18:35 -04:00
Родитель 76ccf24724
Коммит fefe1744b5
64 изменённых файлов: 15 добавлений и 3399 удалений

Просмотреть файл

@ -2,6 +2,16 @@
All notable changes to the "azure blockchain" extension will be documented in this file.
## 1.6.2
### Enhancements
- Removed the Smart Contract Interaction options for contracts. A new version of this interaction will be implemented in a future release.
### Fixes
### Internal Improvements
## 1.6.1
### Enhancements

Просмотреть файл

@ -1,17 +0,0 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-syntax-bigint",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread",
[
"@babel/plugin-transform-runtime",
{
"regenerator": true
}
]
]
}

Просмотреть файл

@ -1,13 +0,0 @@
# EditorConfig is awesome: http://EditorConfig.org
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
[**.*]
indent_style = space
indent_size = 2

Просмотреть файл

@ -1,90 +0,0 @@
{
"parser": "babel-eslint",
"rules": {
"indent": ["error", 2, { "SwitchCase": 1 }],
"no-param-reassign": "error",
"semi": 2,
"sort-imports": [
2,
{
"ignoreCase": true,
"memberSyntaxSortOrder": ["single", "multiple", "all", "none"]
}
],
"operator-linebreak": "off",
"react/display-name": "off",
"func-names": "off",
"max-len": 0,
"prefer-destructuring": 0,
"arrow-parens": 0,
"import/newline-after-import": 0,
"no-prototype-builtins": 0,
"lines-around-directive": 0,
"no-unsafe-negation": 0,
"import/first": 0,
"import/order": 0,
"import/no-unresolved": 0,
"import/no-webpack-loader-syntax": 0,
"prefer-spread": 0,
"no-underscore-dangle": 0,
"no-template-curly-in-string": 0,
"class-methods-use-this": 0,
"no-confusing-arrow": 0,
"no-mixed-operators": 0,
"no-undef-init": 0,
"no-plusplus": 0,
"linebreak-style": 0,
"import/prefer-default-export": 0,
"no-useless-escape": 0,
"no-loop-func": 0,
"import/no-extraneous-dependencies": 0,
"import/extensions": 0,
"no-lonely-if": 0,
"comma-dangle": 0,
"no-extra-boolean-cast": 0,
"no-multi-assign": 0,
"newline-per-chained-call": 0,
"block-spacing": 0,
"object-property-newline": 0,
"no-useless-rename": 0,
"no-restricted-syntax": 0,
"no-useless-return": 0,
"no-continue": 0,
"no-console": 0,
"function-paren-newline": 0,
"object-curly-newline": 0,
"arrow-body-style": 0,
"react/react-in-jsx-scope": 0,
"react/prop-types": 1,
"react/sort-comp": 2,
"no-await-in-loop": "off",
"react/jsx-no-bind": [
2,
{
"allowArrowFunctions": true,
"allowBind": false
}
]
},
"env": {
"es6": true,
"browser": true,
"jest/globals": true
},
"globals": {
"shallow": true,
"mount": true,
"BigInt": true
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"airbnb/base",
"plugin:react/recommended",
"plugin:jest/recommended"
],
"plugins": ["react", "import", "jest"]
}

7
drizzleUI/.gitignore поставляемый
Просмотреть файл

@ -1,7 +0,0 @@
/node_modules/
/.idea/
/build/
/dist/
package-lock.json
.DS_Store

Просмотреть файл

@ -1,57 +0,0 @@
{
"name": "client",
"version": "0.1.0",
"private": true,
"scripts": {
"build": "cross-env NODE_ENV=production webpack --mode production",
"build:dev": "cross-env NODE_ENV=development webpack-dev-server --open",
"build:ext": "cross-env NODE_ENV=production EXT_ENV=true webpack --mode production",
"build:ext:dev": "cross-env NODE_ENV=development EXT_ENV=true webpack --mode development",
"lint": "eslint ./src --ext .js",
"lint:fix": "npm run lint -- --fix"
},
"devDependencies": {
"@babel/core": "^7.7.7",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-proposal-object-rest-spread": "^7.7.7",
"@babel/plugin-syntax-bigint": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.7",
"@babel/preset-react": "^7.7.4",
"@babel/runtime": "^7.7.7",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"cross-env": "^6.0.3",
"css-loader": "^3.4.0",
"eslint": "^6.8.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-loader": "^3.0.3",
"eslint-plugin-import": "^2.19.1",
"eslint-plugin-jest": "^23.2.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-react-hooks": "^2.3.0",
"file-loader": "^5.0.2",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.9.0",
"prop-types": "^15.7.2",
"string-replace-loader": "^2.2.0",
"style-loader": "^1.1.2",
"url-loader": "^3.0.0",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
},
"dependencies": {
"@drizzle/react-components": "^1.5.1",
"@drizzle/react-plugin": "^1.5.1",
"@drizzle/store": "^1.5.1",
"@material-ui/core": "^4.8.2",
"@material-ui/icons": "^4.5.1",
"@truffle/hdwallet-provider": "^1.0.28",
"react": "^16.12.0",
"react-dom": "^16.12.0"
}
}

Просмотреть файл

@ -1,195 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Constants } from './constants';
import { Container } from '@material-ui/core';
import { DrizzleContext } from '@drizzle/react-plugin';
import HDWalletProvider from '@truffle/hdwallet-provider';
import { IPC } from 'services';
import PropTypes from 'prop-types';
import React from 'react';
import { SingleContractView } from 'views';
import { Url } from 'helpers/url';
import {
ContractSelector,
Message
} from 'components';
import {
createMuiTheme,
MuiThemeProvider
} from '@material-ui/core/styles';
import './app.less';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
updating: false,
contractInstances: [],
selectedContractId: undefined,
theme: createMuiTheme({
palette: {
type: 'light'
}
})
};
this.target = document.querySelector('body');
this.observer = new MutationObserver(this.updateTheme);
this.observer.observe(this.target, { attributes: true, childList: false });
}
componentDidMount() {
IPC.on('contracts', this.updateContracts);
IPC.postMessage('documentReady', true);
}
componentDidUpdate() {
const {
updating,
contractInstances,
selectedContractId
} = this.state;
if (
!updating
&& !selectedContractId
&& contractInstances.length !== 0
) {
const contractInstance = contractInstances[0];
this.setContract(contractInstance);
}
}
updateContracts = contractInstances => this.setState({
contractInstances: contractInstances.reverse()
});
onContractChange = (e) => {
const { value } = e.target;
const { contractInstances } = this.state;
this.setContract(contractInstances.find(instance => instance.id === value));
}
setContract = async contractInstance => {
// Workaround. Used to prevent drizzle fallback actions.
window.showErrors = false;
this.setState({ updating: true });
const { drizzle } = this.props;
const { contract, provider } = contractInstance;
drizzle.deleteAllContracts();
if (provider) {
const { WebsocketProvider } = drizzle.web3.providers;
const host = new URL(Url.normalize(provider.host));
let web3Provider;
if (
Constants.regexps.providerTypes.azure.test(host.toString()) &&
provider.options &&
provider.options.mnemonic
) {
host.protocol = 'wss';
host.port = 3300;
web3Provider = new HDWalletProvider(
provider.options.mnemonic,
new WebsocketProvider(host.toString()),
);
} else {
host.protocol = 'ws';
web3Provider = new WebsocketProvider(host.toString());
}
drizzle.web3.setProvider(web3Provider);
}
const address = contractInstance.address;
const networkName = contractInstance.network.name;
const accounts = await drizzle.web3.eth.getAccounts();
const contractConfig = {
contractName: contract.contractName,
web3Contract: new drizzle.web3.eth.Contract(
contract.abi,
address,
{
from: accounts[0],
data: contract.bytecode,
networks: contract.networks,
isOnline: !!provider,
networkName,
enumsInfo: contractInstance.enumsInfo,
}
),
};
drizzle.addContract(contractConfig);
// Workaround. Used to prevent drizzle fallback actions.
setTimeout(() => { window.showErrors = true; }, 2000);
this.setState({
selectedContractId: contractInstance.id,
updating: false
});
}
updateTheme = () => {
const type = Array
.from(this.target.classList)
.includes('vscode-dark') ? 'dark' : 'light';
if (this.state.theme.palette.type !== type) {
this.setState({
theme: createMuiTheme({ palette: { type } })
});
}
}
render() {
const { contractInstances, selectedContractId, theme } = this.state;
const { drizzle: { contractList }, initialized } = this.props;
if (!initialized) {
return <Container className='message container'>
<Message message='⚙️ Loading dapp...' />
</Container>;
}
if (!contractList || contractList.length === 0) {
return <Container className='message container'>
<Message message='⚠️ No contracts available' />
</Container>;
}
return <MuiThemeProvider theme={theme}>
<Container>
<ContractSelector
selectedContractId={selectedContractId}
contractInstances={contractInstances}
onChange={this.onContractChange}
/>
<SingleContractView />
</Container>
</MuiThemeProvider>;
}
}
const HOC = () => <DrizzleContext.Consumer>
{props => <App {...props} />}
</DrizzleContext.Consumer>;
export default HOC;
App.propTypes = {
drizzle: PropTypes.any,
drizzleState: PropTypes.any,
initialized: PropTypes.bool.isRequired,
};

Просмотреть файл

@ -1,50 +0,0 @@
*,
*:before,
*:after {
box-sizing: border-box;
}
body,
html {
position: relative;
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
text-align: center;
}
.vscode-dark {
background-color: #292929;
.MuiChip-outlinedSecondary {
color: #fff;
border: 1px solid #fff;
}
}
.vscode-light {
background-color: #fdfdfd;
}
#root {
width: 100%;
height: 100%;
position: relative;
color: #d4d4d4;
.paper {
padding: 30px;
}
.message.container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
}

Просмотреть файл

@ -1,38 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import {
Paper,
Typography
} from '@material-ui/core';
class BaseViewComponent extends React.PureComponent {
render() {
const {
header,
children,
className
} = this.props;
return <Paper className={`paper ${className || ''}`}>
<Typography
variant='h5'
component='h3'
align='left'
>
{header}
</Typography>
{children}
</Paper>;
}
}
export default BaseViewComponent;
BaseViewComponent.propTypes = {
header: PropTypes.string.isRequired,
className: PropTypes.string,
children: PropTypes.any
};

Просмотреть файл

@ -1,65 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { BaseViewComponent } from 'components';
import PropTypes from 'prop-types';
import React from 'react';
import {
Container,
InputLabel,
MenuItem,
Select
} from '@material-ui/core';
import './contractSelector.less';
class ContractSelector extends React.Component {
renderOption = (contractInstance, index) => {
const {
id,
updateDate
} = contractInstance;
const itemName = (new Date(updateDate)).toGMTString();
return <MenuItem
key={index}
value={id}
>
{itemName}
</MenuItem>;
}
render() {
const {
selectedContractId = '',
contractInstances = [],
onChange
} = this.props;
return <BaseViewComponent
className='selector component'
header='Select a contract version'
>
<Container className='container'>
<InputLabel htmlFor='contract-selector'>
Contract deployment date
</InputLabel>
<Select
id='contract-selector'
value={selectedContractId}
onChange={onChange}
>
{contractInstances.map(this.renderOption)}
</Select>
</Container>
</BaseViewComponent>;
}
}
export default ContractSelector;
ContractSelector.propTypes = {
selectedContractId: PropTypes.string,
contractInstances: PropTypes.array,
onChange: PropTypes.func.isRequired
};

Просмотреть файл

@ -1,24 +0,0 @@
.selector.component {
@media (min-width: 600px) {
margin: 15px 24px 0 24px;
}
@media (min-width: 960px) {
margin: 15px 32px 0 32px;
}
.container {
display: flex;
align-items: center;
padding: 12px;
color: white;
.MuiFormLabel-root {
margin-right: 15px;
}
.MuiSelect-select {
min-width: 180px;
}
}
}

Просмотреть файл

@ -1,31 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import { TextField } from '@material-ui/core';
class EventSection extends React.Component {
render() {
const { events } = this.props;
const eventsNotifications = events
.reverse()
.map(({ event }) => `${event} was called`)
.join('\n');
return <TextField
className='metadata text field'
disabled
multiline
fullWidth
rows={8}
id='contract-address'
label='event:'
value={eventsNotifications}
/>;
}
}
export default EventSection;
EventSection.propTypes = {
events: PropTypes.array.isRequired
};

Просмотреть файл

@ -1,160 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { createInputComponent } from '../factory/InputComponentFactory';
import { newContextComponents } from '@drizzle/react-components';
import PropTypes from 'prop-types';
import React from 'react';
import {
Button,
Container,
InputLabel,
MenuItem,
Select
} from '@material-ui/core';
import './executionSection.less';
const { ContractForm } = newContextComponents;
class ExecutionSection extends React.Component {
constructor(props) {
super(props);
this.state = {
executedMethod: '',
disabledExecute: false,
transactionPending: false,
formControlsValidation: {},
unsubscribe: undefined,
};
}
componentDidMount() {
const { actions, drizzle } = this.props;
const unsubscribe = drizzle.store.subscribe(this.updateTransactionStatus);
const formControlsValidation = this.createFormControlsList(actions[0]);
this.setState({
executedMethod: actions[0],
formControlsValidation,
unsubscribe,
});
}
componentDidUpdate = () => {
const isUnsupportedInputExist = !!document.getElementsByClassName('unsupported-input').length;
const validationValues = Object.values(this.state.formControlsValidation);
const errors = validationValues.filter(value => value !== '' && value !== undefined);
const isDisabled = isUnsupportedInputExist || errors.length > 0;
if (isDisabled !== this.state.disabledExecute) {
this.setState({ disabledExecute: isDisabled });
}
};
componentWillUnmount() {
this.state.unsubscribe();
}
updateTransactionStatus = () => {
const { store } = this.props.drizzle;
const { transactionStack, transactions } = store.getState();
const lastTransaction = transactionStack[transactionStack.length - 1];
if (!!lastTransaction && !!transactions[lastTransaction]) {
const transactionPending = transactions[lastTransaction].status !== 'success';
if (this.state.transactionPending !== transactionPending) {
this.setState({
transactionPending,
});
}
}
}
createFormControlsList = (executedMethod) => {
const method = this.props.drizzle.contracts[this.props.contractName].abi.find(
element => !element.constant && element.name === executedMethod);
const formControlsValidation = {};
method.inputs.forEach(input => {
formControlsValidation[input.name] = '';
});
return formControlsValidation;
};
onChange = (e) => {
const formControlsValidation = this.createFormControlsList(e.target.value);
this.setState({
executedMethod: e.target.value,
formControlsValidation,
});
};
updateValidationResult = (name, errorMessage) => {
this.setState({ formControlsValidation: { ...this.state.formControlsValidation, [name]: errorMessage } });
}
renderExecutionSection = ({ inputs, handleInputChange, handleSubmit }) => {
return <Container className='execution-section'>
<Container className='action'>
<InputLabel className='input-label'>Contract Action</InputLabel>
<Select
className='execution-method'
value={this.state.executedMethod}
onChange={this.onChange}
inputProps={{
id: 'action-method',
name: 'action'
}}
>
{this.props.actions.map((action, index) => {
return (
<MenuItem key={index} value={action}>{action}</MenuItem>
);
})}
</Select>
</Container>
<Container className='input-fields'>
{inputs.map((item, index) => {
return createInputComponent(
item,
handleInputChange,
this.updateValidationResult.bind(this, item.name),
index,
{
executedMethod: this.state.executedMethod,
enumsInfo: this.props.drizzle.contracts[this.props.contractName].options.enumsInfo,
});
})}
</Container>
<Button
className='execute-button'
variant='contained'
onClick={handleSubmit}
disabled={this.state.disabledExecute || this.state.transactionPending}
>
Execute
</Button>
</Container>;
};
render() {
return <ContractForm
key={this.state.executedMethod}
contract={this.props.contractName}
method={this.state.executedMethod}
drizzle={this.props.drizzle}
render={this.renderExecutionSection}
/>;
}
}
export default ExecutionSection;
ExecutionSection.propTypes = {
actions: PropTypes.any.isRequired,
contractName: PropTypes.string.isRequired,
drizzle: PropTypes.any.isRequired,
};

Просмотреть файл

@ -1,49 +0,0 @@
.execution-section {
&.MuiContainer-root {
padding: 0;
}
.action {
display: flex;
align-items: center;
padding: 0;
.input-label {
padding-left: 0;
flex: initial;
margin-right: 12px;
}
.execution-method {
flex: 1;
}
}
.input-fields {
padding: 0;
.input-field {
margin-top: 16px;
}
.MuiTextField-root {
width: 100%;
}
.boolean-input,
.enum-select {
margin-top: 16px;
padding-left: 0px;
}
.unsupported-input {
margin-top: 16px;
padding-left: 0px;
}
}
.execute-button {
margin-top: 24px;
}
}

Просмотреть файл

@ -1,60 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Constants } from 'constants';
import PropTypes from 'prop-types';
import React from 'react';
import { TextField } from '@material-ui/core';
const { placeholder, validationMessages, validationRegexps } = Constants.executionSection;
class AddressInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
errorMessage: '',
};
}
onChange = (event) => {
const { value } = event.target;
const errorMessage = this.getErrorMessage(value);
this.setState({ value, errorMessage });
this.props.updateValidationResult(errorMessage);
this.props.handleInputChange(event);
};
getErrorMessage = (value) => {
if (value && !value.match(validationRegexps.address)) {
return validationMessages.address(this.props.item.name);
}
return '';
};
render() {
const { name } = this.props.item;
return <TextField
error={!!this.state.errorMessage}
helperText={this.state.errorMessage}
className='address-input'
label={`${name}:`}
name={name}
onChange={this.onChange}
value={this.state.value}
placeholder={placeholder.address}
/>;
}
}
export default AddressInput;
AddressInput.propTypes = {
item: PropTypes.shape({
name: PropTypes.string,
}),
handleInputChange: PropTypes.func.isRequired,
updateValidationResult: PropTypes.func.isRequired,
};

Просмотреть файл

@ -1,72 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import { TextField } from '@material-ui/core';
class ArrayInput extends React.Component {
constructor(props) {
super(props);
const value = '';
const errorMessage = this.getErrorMessage(value);
this.props.updateValidationResult(errorMessage);
this.state = {
value,
errorMessage,
};
}
onChange = (event) => {
const errorMessage = this.getErrorMessage(event.target.value);
this.props.updateValidationResult(errorMessage);
this.setState({ value: event.target.value, errorMessage });
this.props.handleInputChange({
target: {
name: this.props.item.name,
type: this.props.item.type,
value: !errorMessage
? JSON.parse(event.target.value)
: undefined,
}
});
};
getErrorMessage = (value) => {
try {
JSON.parse(value);
return '';
} catch {
return 'Should be a valid JSON';
}
};
render() {
return <TextField
multiline
fullWidth
rowsMax={10}
error={!!this.state.errorMessage}
helperText={this.state.errorMessage}
placeholder={this.props.item.type}
id={this.props.item.name}
label={`${this.props.item.name} :`}
name={this.props.item.name}
onChange={this.onChange}
value={this.state.value}
/>;
}
}
export default ArrayInput;
ArrayInput.propTypes = {
item: PropTypes.shape({
type: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
}).isRequired,
handleInputChange: PropTypes.func.isRequired,
updateValidationResult: PropTypes.func.isRequired,
};

Просмотреть файл

@ -1,49 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import {
Container,
InputLabel,
MenuItem,
Select
} from '@material-ui/core';
class BooleanInput extends React.Component {
constructor(props) {
super(props);
this.state = { value: false };
}
onChange = (event) => {
this.setState({ value: event.target.value });
this.props.handleInputChange(event);
}
render() {
return <Container className='boolean-input'>
<InputLabel shrink>
{this.props.item.name}
</InputLabel>
<Select
id={this.props.item.name}
name={this.props.item.name}
onChange={this.onChange}
value={this.state.value}
displayEmpty
>
<MenuItem key='false' value={false}>False</MenuItem>
<MenuItem key='true' value={true}>True</MenuItem>
</Select>
</Container>;
}
}
export default BooleanInput;
BooleanInput.propTypes = {
item: PropTypes.any.isRequired,
handleInputChange: PropTypes.func.isRequired,
};

Просмотреть файл

@ -1,62 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import {
Container,
InputLabel,
MenuItem,
Select
} from '@material-ui/core';
class EnumInput extends React.Component {
constructor(props) {
super(props);
this.state = { value: undefined };
this.onChange = this.onChange.bind(this);
}
onChange(event) {
this.setState({ value: event.target.value });
this.props.handleInputChange({
target: {
name: this.props.title,
value: event.target.value
}
});
}
render() {
return <Container className='enum-select'>
<InputLabel shrink>
{this.props.title}
</InputLabel>
<Select
id={this.props.title}
name={this.props.title}
onChange={this.onChange}
value={this.state.value}
inputProps={{
id: 'action-method',
name: 'action'
}}
>
{this.props.items.map(item => {
return (
<MenuItem key={item.value} value={item.value}>{item.name}</MenuItem>
);
})}
</Select>
</Container>;
}
}
export default EnumInput;
EnumInput.propTypes = {
title: PropTypes.string.isRequired,
items: PropTypes.array.isRequired,
handleInputChange: PropTypes.func.isRequired,
};

Просмотреть файл

@ -1,98 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import { TextField } from '@material-ui/core';
class IntegerInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0n,
min: BigInt(this.props.min),
max: BigInt(this.props.max),
isBigInt: true,
error: undefined,
};
}
componentDidUpdate() {
const min = BigInt(this.props.min);
const max = BigInt(this.props.max);
if (
this.state.min !== min ||
this.state.max !== max
) {
this.setState({ min, max });
}
}
onChange = (e) => {
const { value } = e.target;
let isBigInt = true;
try {
BigInt(value);
} catch (error) {
isBigInt = false;
}
const error = this.getErrorMessage(value, isBigInt);
this.setState({ value, isBigInt, error });
this.props.handleInputChange(e);
this.props.updateValidationResult(error);
};
getErrorMessage = (value, isBigInt) => {
const { min, max, pow, item } = this.props;
if (value > max) {
return `${item.name} can only safely store up to ${pow} bits`;
}
if (value < min) {
if (min === 0n) {
return `${item.name} should be a positive`;
}
return `${item.name} can only safely store up to ${pow} bits`;
}
if (!isBigInt) {
return `${item.name} required as number`;
}
return '';
};
render() {
const { value, error } = this.state;
const { item: { name } } = this.props;
return <TextField
error={!!error}
helperText={error}
className='input-field'
id={name}
label={`${name}:`}
name={name}
onChange={this.onChange}
value={value}
/>;
}
}
export default IntegerInput;
IntegerInput.propTypes = {
item: PropTypes.any.isRequired,
handleInputChange: PropTypes.func.isRequired,
min: PropTypes.any.isRequired,
max: PropTypes.any.isRequired,
pow: PropTypes.number.isRequired,
updateValidationResult: PropTypes.func.isRequired,
};

Просмотреть файл

@ -1,37 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import { TextField } from '@material-ui/core';
class TextInput extends React.Component {
constructor(props) {
super(props);
this.state = { value: '' };
}
onChange = (event) => {
this.setState({ value: event.target.value });
this.props.handleInputChange(event);
}
render() {
return <TextField
className='input-field'
id={this.props.item.name}
label={`${this.props.item.name}:`}
name={this.props.item.name}
onChange={this.onChange}
value={this.state.value}
/>;
}
}
export default TextInput;
TextInput.propTypes = {
item: PropTypes.any.isRequired,
handleInputChange: PropTypes.func.isRequired,
};

Просмотреть файл

@ -1,21 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import { Container, FormHelperText, FormLabel } from '@material-ui/core';
class UnsupportedInput extends React.Component {
render() {
return <Container className='unsupported-input'>
<FormHelperText>{this.props.item.name}</FormHelperText>
<FormLabel >Unsupported input type</FormLabel>
</Container>;
}
}
export default UnsupportedInput;
UnsupportedInput.propTypes = {
item: PropTypes.any.isRequired,
};

Просмотреть файл

@ -1,20 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import AddressInput from './AddressInput';
import ArrayInput from './ArrayInput';
import BooleanInput from './BooleanInput';
import EnumInput from './EnumInput';
import IntegerInput from './IntegerInput';
import TextInput from './TextInput';
import UnsupportedInput from './UnsupportedInput';
export {
AddressInput,
ArrayInput,
BooleanInput,
EnumInput,
IntegerInput,
TextInput,
UnsupportedInput,
};

Просмотреть файл

@ -1,83 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Constants } from '../../constants';
import React from 'react';
import {
AddressInput,
ArrayInput,
BooleanInput,
EnumInput,
IntegerInput,
TextInput,
UnsupportedInput
} from '../executionSection/inputControlsCollection';
const { arrayTypeRegexp, intInputs, variableTypes } = Constants.executionSection;
export function createInputComponent(input, handleInputChange, updateValidationResult, index, extendedData) {
if (input.type.match(arrayTypeRegexp)) {
return <ArrayInput
key={index}
item={input}
handleInputChange={handleInputChange}
updateValidationResult={updateValidationResult}
/>;
}
if (input.type === variableTypes.string) {
return <TextInput
key={index}
item={input}
handleInputChange={handleInputChange}
/>;
}
if (input.type === variableTypes.address) {
return <AddressInput
key={index}
item={input}
handleInputChange={handleInputChange}
updateValidationResult={updateValidationResult}
/>;
}
if (input.type === variableTypes.bool) {
return <BooleanInput
key={index}
item={input}
handleInputChange={handleInputChange}
/>;
}
if (!!intInputs[input.type]) {
// enums section
const relatedEnum = extendedData.enumsInfo
.methods[extendedData.executedMethod] &&
extendedData.enumsInfo
.methods[extendedData.executedMethod][input.name];
if (relatedEnum) {
return <EnumInput
title={input.name}
items={relatedEnum}
handleInputChange={handleInputChange}
/>;
}
// end of enums section
const { min, max, pow } = intInputs[input.type];
return <IntegerInput
key={index}
item={input}
handleInputChange={handleInputChange}
min={min}
max={max}
pow={pow}
updateValidationResult={updateValidationResult}
/>;
}
return <UnsupportedInput
key={index}
item={input}
handleInputChange={handleInputChange}
/>;
}

Просмотреть файл

@ -1,73 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import React from 'react';
import {
ArrayProperty,
NotSupportedProperty,
SimpleProperty,
StructProperty,
} from '../stateSection/contractProperty';
// if it is array - it will fail with string key
// but mapping allows any type of keys
function IsMappingBehavior(property) {
if (property.inputs.length > 0
&& property.inputs[0].type.startsWith('uint')
&& property.inputs[0].name === '') {
let isMappingBehavior = true;
const someStringIndex = 'ind_1';
try {
property.method(someStringIndex).call();
} catch {
isMappingBehavior = false;
}
return isMappingBehavior;
}
return true;
}
export const GetStateComponent = (property, index) => {
const unsupportedComponents = property.outputs.find((output) => !!output.components);
if (unsupportedComponents) {
return (
<NotSupportedProperty
key={index}
property={property}
/>
);
}
if (property.inputs.length > 0) {
if (IsMappingBehavior(property)) {
return (
<NotSupportedProperty
key={index}
property={property}
/>
);
}
return (
<ArrayProperty
key={index}
property={property}
/>
);
}
if (property.outputs > 1 || property.outputs[0].name) {
return (
<StructProperty
key={index}
property={property}
/>
);
}
return (
<SimpleProperty
key={index}
property={property}
/>
);
};

Просмотреть файл

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import BaseViewComponent from './baseViewComponent/BaseViewComponent';
import ContractSelector from './contractSelector/ContractSelector';
import EventSection from './eventsSection/EventSection';
import ExecutionSection from './executionSection/ExecutionSection';
import Interaction from './interaction/Interaction';
import LabelWithIcon from './labelWithIcon/LabelWithIcon';
import Message from './message/Message';
import Metadata from './metadata/Metadata';
import StateSection from './stateSection/StateSection';
import TextFieldWithTooltip from './textFieldWithTooltip/TextFieldWithTooltip';
export {
BaseViewComponent,
ContractSelector,
EventSection,
ExecutionSection,
Interaction,
LabelWithIcon,
Message,
Metadata,
StateSection,
TextFieldWithTooltip
};

Просмотреть файл

@ -1,117 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Container } from '@material-ui/core';
import { deepEqual } from 'helpers';
import { DrizzleContext } from '@drizzle/react-plugin';
import PropTypes from 'prop-types';
import React from 'react';
import {
BaseViewComponent,
EventSection,
ExecutionSection,
StateSection
} from 'components';
import './interaction.less';
const getExecutableMethods = (contract) => {
const isExecutableAbiMethod = (abiEl) => (abiEl.type === 'function' && abiEl.constant === false);
const executableAbiMethods = contract.abi.filter(isExecutableAbiMethod).map(el => el.name);
return Object.keys(contract.methods).filter(m => executableAbiMethods.indexOf(m) !== -1);
};
class Interaction extends React.Component {
constructor(props) {
super(props);
this.state = {
methods: undefined,
contractName: undefined,
subscription: undefined,
events: []
};
}
componentDidMount() {
this.componentDidUpdate();
}
componentDidUpdate() {
const contract = this.props.drizzle.contractList[0];
if (!contract) {
return;
}
const { contractName } = contract;
const methods = Object.keys(contract);
const state = {
...this.state,
methods,
contractName
};
if (!deepEqual(this.state, state)) {
if (!!this.state.subscription) {
this.state.subscription.unsubscribe();
}
const subscription = contract.events.allEvents()
.on('data', (event) => this.setState({ events: [...this.state.events, event] }));
this.setState({ ...state, subscription });
}
}
render() {
const { events } = this.state;
return <BaseViewComponent
header='Interaction'
className='interaction component'
>
<Container className='container'>
<Container className='input'>
<ExecutionSection
actions={getExecutableMethods(this.props.drizzle.contractList[0])}
contractName={this.props.drizzle.contractList[0].contractName}
drizzle={this.props.drizzle}
render={this.renderExecutionSection}
/>
</Container>
<Container className='output'>
<StateSection
contract={this.props.drizzle.contractList[0]}
/>
<EventSection
events={events}
/>
</Container>
</Container>
</BaseViewComponent>;
}
}
const HOC = () => <DrizzleContext.Consumer>
{props => <Interaction {...props} />}
</DrizzleContext.Consumer>;
export default HOC;
Interaction.propTypes = {
drizzle: PropTypes.shape({
contractList: PropTypes.arrayOf(PropTypes.shape({
contractName: PropTypes.string.isRequired,
methods: PropTypes.object.isRequired,
events: PropTypes.shape({
allEvents: PropTypes.func.isRequired
}).isRequired,
options: PropTypes.shape({
isOnline: PropTypes.bool.isRequired
}).isRequired
})).isRequired
}).isRequired,
drizzleState: PropTypes.any
};

Просмотреть файл

@ -1,21 +0,0 @@
.interaction.component {
.container {
display: flex;
padding: 0;
text-align: left;
.input {
flex: 1;
margin-left: 12px;
margin-right: 6px;
padding: 0;
}
.output {
flex: 1;
margin-right: 12px;
margin-left: 6px;
padding: 0;
}
}
}

Просмотреть файл

@ -1,24 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Tooltip } from '@material-ui/core';
import './labelWithIcon.less';
class LabelWithIcon extends React.Component {
render() {
const { text, icon } = this.props;
return <Tooltip title='Copy' placement='top-end'>
<div className='label-with-icon'>
<span className='text'>{text}</span>
{icon}
</div>
</Tooltip>;
}
}
export default LabelWithIcon;
LabelWithIcon.propTypes = {
text: PropTypes.string.isRequired,
icon: PropTypes.node.isRequired
};

Просмотреть файл

@ -1,12 +0,0 @@
.label-with-icon {
display: flex;
align-items: center;
.text {
margin-right: 10px;
}
.MuiSvgIcon-root {
font-size: 1em;
}
}

Просмотреть файл

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Chip } from '@material-ui/core';
import PropTypes from 'prop-types';
import React from 'react';
class Message extends React.PureComponent {
render() {
const {
className = '',
message
} = this.props;
return <Chip
className={className}
color='secondary'
variant='outlined'
label={message}
/>;
}
}
export default Message;
Message.propTypes = {
className: PropTypes.string,
message: PropTypes.string.isRequired
};

Просмотреть файл

@ -1,241 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { DrizzleContext } from '@drizzle/react-plugin';
import { FileCopy } from '@material-ui/icons';
import PropTypes from 'prop-types';
import React from 'react';
import {
BaseViewComponent,
LabelWithIcon,
Message,
TextFieldWithTooltip
} from 'components';
import {
Container,
TextField
} from '@material-ui/core';
import { copy, deepEqual } from 'helpers';
import './metadata.less';
class Metadata extends React.Component {
constructor(props) {
super(props);
this.state = {
abi: undefined,
address: undefined,
bytecode: undefined,
events: undefined,
network: undefined,
networkName: undefined,
showBytecodeTooltip: false,
showABITooltip: false,
transaction: {
from: '',
},
};
}
componentDidMount() {
this.componentDidUpdate();
}
componentDidUpdate() {
const contract = this.props.drizzle.contractList[0];
if (!contract) {
return;
}
const abi = JSON.stringify(contract.abi);
const address = contract.address;
const bytecode = contract.options.data;
const events = this.getEvents();
const network = this.getNetwork();
const networkName = contract.options.networkName;
const state = {
...this.state,
abi,
address,
bytecode,
events,
network,
networkName
};
if (!deepEqual(this.state, state)) {
this.getTransactionInfo(state.network.transactionHash)
.then((transaction) => this.setState({ ...state, transaction }))
.catch(() => this.setState(state));
}
}
copyBytecodeToClipboard = () => {
copy(this.state.bytecode);
this.showBytecodeTooltip();
};
copyABIToClipboard = () => {
copy(this.state.abi);
this.showABITooltip();
}
getEvents = () => {
const { drizzle } = this.props;
const { events } = drizzle.contractList[0];
return Object.keys(events)
.find(key => key.match(/0x(\d|\S)+/g)) || '';
}
getNetwork = () => {
const { drizzle } = this.props;
const contract = drizzle.contractList[0];
const { networks } = contract.options;
const networkKey = Object.keys(networks)
.find((key) => networks[key].address === contract.address);
return Object.assign(networks[networkKey], { id: networkKey });
}
getTransactionInfo = (transactionHash) => {
const { drizzle } = this.props;
const { getTransaction } = drizzle.contractList[0].web3.eth;
return getTransaction(transactionHash);
}
showBytecodeTooltip = () => this.setState({ showBytecodeTooltip: true });
showABITooltip = () => this.setState({ showABITooltip: true });
hideBytecodeTooltip = () => this.setState({ showBytecodeTooltip: false });
hideABITooltip = () => this.setState({ showABITooltip: false });
render() {
const {
abi,
address,
events,
network,
bytecode,
transaction,
networkName,
showABITooltip,
showBytecodeTooltip
} = this.state;
if (!abi || !bytecode) {
return <BaseViewComponent header='Metadata' className='metadata component'>
<Message message='Contract not loaded (ABI or bytecode not defined)' />
</BaseViewComponent>;
}
const byteCodeLabel = <LabelWithIcon text='Bytecode:' icon={<FileCopy />} />;
const ABILabel = <LabelWithIcon text='ABI:' icon={<FileCopy />} />;
return <BaseViewComponent
header='Metadata'
className='metadata component'
>
<Container className='list'>
<TextField
disabled
fullWidth
id='deployed-location'
label='Deployed Location:'
value={networkName || network.id}
className='text field'
/>
<TextField
disabled
fullWidth
id='contract-address'
label='Contract Address:'
value={address}
className='text field'
/>
<TextField
disabled
fullWidth
id='creator-account'
label='Creator Account:'
value={transaction && transaction.from || ''}
className='text field'
/>
<TextFieldWithTooltip
open={showBytecodeTooltip}
title='Bytecode copied to clipboard!'
leaveDelay={900}
onClose={this.hideBytecodeTooltip}
disabled
fullWidth
id='bytecode'
label={byteCodeLabel}
onClick={this.copyBytecodeToClipboard}
value={bytecode}
className='text field'
/>
<TextFieldWithTooltip
open={showABITooltip}
title='ABI copied to clipboard!'
leaveDelay={900}
onClose={this.hideABITooltip}
disabled
fullWidth
id='abi'
label={ABILabel}
onClick={this.copyABIToClipboard}
value={abi}
className='text field'
/>
<TextField
disabled
fullWidth
id='tx-history'
label='TX History:'
value={network.transactionHash}
className='text field'
/>
<TextField
disabled
fullWidth
id='events'
label='Events:'
value={events}
className='text field'
/>
</Container>
</BaseViewComponent>;
}
}
const HOC = () => <DrizzleContext.Consumer>
{props => <Metadata {...props} />}
</DrizzleContext.Consumer>;
export default HOC;
Metadata.propTypes = {
drizzle: PropTypes.shape({
contractList: PropTypes.arrayOf(PropTypes.shape({
abi: PropTypes.array.isRequired,
address: PropTypes.string.isRequired,
events: PropTypes.object.isRequired,
options: PropTypes.shape({
data: PropTypes.string.isRequired,
networks: PropTypes.object.isRequired,
networkName: PropTypes.string,
}).isRequired,
web3: PropTypes.shape({
eth: PropTypes.shape({
getTransaction: PropTypes.func.isRequired,
}).isRequired,
}).isRequired,
})).isRequired
}).isRequired
};

Просмотреть файл

@ -1,12 +0,0 @@
.metadata.component {
.list {
.text.field {
margin-bottom: 12px;
margin-left: 12px;
}
}
.MuiContainer-root {
padding-left: 0;
}
}

Просмотреть файл

@ -1,57 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Box } from '@material-ui/core';
import { GetStateComponent } from '../factory/StateComponentFactory';
import PropTypes from 'prop-types';
import React from 'react';
class StateSection extends React.Component {
constructor(props) {
super(props);
this.state = {
stateObjects: this.getStateObjects(),
};
}
getStateObjects() {
const abi = this.props.contract.abi;
const methods = this.props.contract.methods;
return abi
.filter(element => element.constant)
.map((constant) => Object.assign(
constant,
{
method: methods[constant.name],
value: '',
enumsInfo: this.props.contract.options.enumsInfo,
}));
}
renderStateObjects() {
return this.state.stateObjects.map((stateObject, index) => {
return GetStateComponent(stateObject, index);
});
}
render() {
return <Box
className='state-section'>
{this.renderStateObjects()}
</Box>;
}
}
export default StateSection;
StateSection.propTypes = {
contract: PropTypes.shape({
abi: PropTypes.array.isRequired,
methods: PropTypes.object.isRequired,
options: PropTypes.shape({
enumsInfo: PropTypes.object.isRequired
}).isRequired
}).isRequired
};

Просмотреть файл

@ -1,119 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import ComplexField from './ComplexField';
import { deepEqual } from '../../../helpers';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import PropTypes from 'prop-types';
import React from 'react';
import {
Container,
ExpansionPanel,
ExpansionPanelDetails,
ExpansionPanelSummary,
TextField,
Typography,
} from '@material-ui/core';
import '../stateSection.less';
class ArrayProperty extends React.Component {
constructor(props) {
super(props);
this.state = {
property: {
name: '',
value: [],
outputs: [],
},
};
}
componentDidMount() {
this.getArrayPropertyInfo();
}
componentDidUpdate() {
this.getArrayPropertyInfo();
}
getArrayValues = async () => {
const valueArray = [];
try {
let counter = 0;
const maxDisplayedItems = 8000;
while (true) { // eslint-disable-line
if (counter > maxDisplayedItems) {
valueArray.push(`... more than ${maxDisplayedItems} items`);
break;
}
const elementValue = await this.props.property.method(counter).call();
valueArray.push(elementValue);
counter++;
}
return valueArray;
} catch {
return valueArray;
}
};
getArrayPropertyInfo = () => {
this.getArrayValues().then(result => {
if (!deepEqual(this.state.property.value, result)) {
this.setState({
property: {
name: this.props.property.name,
value: result,
outputs: this.props.property.outputs,
}
});
}
});
};
renderArrayElements = () => {
return this.state.property.value.map((value, index) => {
if (value instanceof Object) {
return <ComplexField
key={index}
fields={this.state.property.outputs}
values={value}
name={`[${index}] : `}
/>;
}
return (
<TextField
className='metadata text field'
disabled
fullWidth
key={index}
label={`[${index}] : `}
value={value}
/>
);
});
};
render() {
return <ExpansionPanel>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
>
<Typography>
{`${this.state.property.name} [ ] :`}
</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Container>{this.renderArrayElements()}</Container>
</ExpansionPanelDetails>
</ExpansionPanel>;
}
}
export default ArrayProperty;
ArrayProperty.propTypes = {
property: PropTypes.object.isRequired,
};

Просмотреть файл

@ -1,55 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import PropTypes from 'prop-types';
import React from 'react';
import {
Container,
ExpansionPanel,
ExpansionPanelDetails,
ExpansionPanelSummary,
TextField,
Typography,
} from '@material-ui/core';
import '../stateSection.less';
class ComplexField extends React.Component {
renderFields = () => {
return this.props.fields.map((field, index) => {
return (
<TextField
className='metadata text field'
disabled
fullWidth
key={index}
label={`${field.name}: `}
value={this.props.values[field.name]}
/>
);
});
};
render() {
return (
<ExpansionPanel>
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon />} href={''}>
<Typography>
{this.props.name}
</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Container>{this.renderFields()}</Container>
</ExpansionPanelDetails>
</ExpansionPanel>
);
}
}
export default ComplexField;
ComplexField.propTypes = {
name: PropTypes.string.isRequired,
values: PropTypes.object.isRequired,
fields: PropTypes.array.isRequired,
};

Просмотреть файл

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import { TextField } from '@material-ui/core';
class NotSupportedProperty extends React.Component {
render() {
const { property } = this.props;
return <TextField
className='metadata text field'
disabled
fullWidth
label={property.name}
value={'Sorry! Complex structures has not supported yet!'}
/>;
}
}
export default NotSupportedProperty;
NotSupportedProperty.propTypes = {
property: PropTypes.object.isRequired,
};

Просмотреть файл

@ -1,67 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import { TextField } from '@material-ui/core';
class SimpleProperty extends React.Component {
constructor(props) {
super(props);
this.state = {
property: {
name: '',
value: '',
},
};
}
componentDidMount() {
this.getPropertyInfo();
}
componentDidUpdate() {
this.getPropertyInfo();
}
getPropertyInfo = () => {
this.props.property.method().call().then(result => {
let newValue = result;
const relatedEnum = this.props.property.enumsInfo.fields[this.state.property.name];
if (relatedEnum !== undefined) {// replace uint value to enum named instance
const enumValue = relatedEnum.find(i => i.value === +result);
if (enumValue) {
newValue = enumValue.name;
}
}
if (newValue !== this.state.property.value || this.props.property.name !== this.state.property.name) {
this.setState({
property: {
name: this.props.property.name,
value: newValue
}
});
}
});
};
render() {
const { property } = this.state;
return <TextField
className='metadata text field'
disabled
fullWidth
label={property.name}
value={property.value}
/>;
}
}
export default SimpleProperty;
SimpleProperty.propTypes = {
property: PropTypes.object.isRequired,
};

Просмотреть файл

@ -1,58 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import ComplexField from './ComplexField';
import { deepEqual } from '../../../helpers';
import PropTypes from 'prop-types';
import React from 'react';
import '../stateSection.less';
class StructProperty extends React.Component {
constructor(props) {
super(props);
this.state = {
property: {
name: '',
value: {},
outputs: [],
},
};
}
componentDidMount() {
this.getStructPropertyInfo();
}
componentDidUpdate() {
this.getStructPropertyInfo();
}
getStructPropertyInfo = () => {
this.props.property.method().call().then(result => {
if (!deepEqual(this.state.property.value, result)) {
this.setState({
property: {
name: this.props.property.name,
value: result,
outputs: this.props.property.outputs
}
});
}
});
};
render() {
return <ComplexField
fields={this.state.property.outputs}
values={this.state.property.value}
name={`${this.state.property.name}:`}
/>;
}
}
export default StructProperty;
StructProperty.propTypes = {
property: PropTypes.object.isRequired,
};

Просмотреть файл

@ -1,11 +0,0 @@
import ArrayProperty from './ArrayProperty';
import NotSupportedProperty from './NotSupportedProperty';
import SimpleProperty from './SimpleProperty';
import StructProperty from './StructProperty';
export {
ArrayProperty,
NotSupportedProperty,
SimpleProperty,
StructProperty,
};

Просмотреть файл

@ -1,5 +0,0 @@
.state-section {
&.MuiContainer-root {
padding-right: 0;
}
}

Просмотреть файл

@ -1,63 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import PropTypes from 'prop-types';
import React from 'react';
import {
TextField,
Tooltip
} from '@material-ui/core';
class TextFieldWithTooltip extends React.PureComponent {
render() {
const {
id,
open,
title,
leaveDelay,
onClose,
onClick,
disabled = false,
className = '',
fullWidth = false,
label,
value,
placement = 'top'
} = this.props;
return <Tooltip
open={open}
title={title}
leaveDelay={leaveDelay}
placement={placement}
onClose={onClose}
>
<TextField
id={id}
disabled={disabled}
className={className}
fullWidth={fullWidth}
onClick={onClick}
label={label}
value={value}
/>
</Tooltip>;
}
}
export default TextFieldWithTooltip;
TextFieldWithTooltip.propTypes = {
open: PropTypes.bool.isRequired,
title: PropTypes.string.isRequired,
leaveDelay: PropTypes.number.isRequired,
onClose: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
className: PropTypes.string,
disabled: PropTypes.bool,
fullWidth: PropTypes.bool,
id: PropTypes.string,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
value: PropTypes.any,
placement: PropTypes.string
};

Просмотреть файл

@ -1,61 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { BigIntMath } from 'helpers';
const intInputs = Array(32)
.fill(0)
.map((_, index) => (index + 1) * 8)
.reduce((acc, pow) => {
const uintMax = BigIntMath.pow(2n, pow) - 1n;
const intMax = BigIntMath.pow(2n, pow - 1) - 1n;
const intMin = -intMax;
acc[`int${pow}`] = {
min: intMin,
max: intMax,
pow
};
acc[`uint${pow}`] = {
min: 0n,
max: uintMax,
pow
};
return acc;
}, {});
const getMessageInvalidAddress = (inputName) => {
return `The first characters of ${inputName} must be '0x'. `
+ 'Address should have letter from a to z, A to Z, digits. Length must be 42 characters.';
};
export class Constants {
static executionSection = {
variableTypes: {
address: 'address',
string: 'string',
bool: 'bool',
},
intInputs,
validationMessages: {
address: getMessageInvalidAddress,
},
validationRegexps: {
address: /^(0x)[a-zA-Z0-9]{40}$/g,
},
placeholder: {
address: '0x0000000000000000000000000000000000000000',
},
arrayTypeRegexp: /\w*\[\d*\]/g,
};
static regexps = {
providerTypes: {
azure: /blockchain.azure.com/i
}
};
}

Просмотреть файл

@ -1,47 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export const BigIntMath = {
// eval required because babel translate ** to Math.pow
/* eslint-disable no-eval */
pow: (value, pow) => BigInt(eval(`BigInt(${value}) ** BigInt(${pow})`)),
log2: (bigIntValue) => {
let count = 0;
let value = bigIntValue;
while (value > 0n) {
value /= 2n;
count += 1;
}
return count;
},
sqrt: (value) => {
if (value < 0n) {
throw Error('square root of negative numbers is not supported');
}
if (value < 2n) {
return value;
}
if (value === 4n) {
return 2n;
}
const newtonIteration = (n, x0) => {
/* eslint-disable no-bitwise */
const x1 = ((n / x0) + x0) >> 1n;
if (x0 === x1 || x0 === (x1 - 1n)) {
return x0;
}
return newtonIteration(n, x1);
};
return newtonIteration(value, 1n);
}
};

Просмотреть файл

@ -1,23 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export const copy = (text) => {
const span = document.createElement('span');
span.textContent = text;
span.style.whiteSpace = 'pre';
document.body.appendChild(span);
const selection = window.getSelection();
const range = document.createRange();
selection.removeAllRanges();
range.selectNode(span);
selection.addRange(range);
document.execCommand('copy');
selection.removeAllRanges();
document.body.removeChild(span);
};

Просмотреть файл

@ -1,41 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export const deepEqual = (first, second) => {
if (first === second) return true;
if (
first && second
&& typeof first === 'object' && typeof second === 'object'
) {
if (first.constructor !== second.constructor) return false;
if (Array.isArray(first) && Array.isArray(second)) {
if (first.length !== second.length) return false;
for (let i = 0; i < first.length; i++) {
if (!deepEqual(first[i], second[i])) return false;
}
return true;
}
const firstKeys = Object.keys(first);
const secondKeys = Object.keys(second);
if (firstKeys.length !== secondKeys.length) return false;
/* eslint-disable no-unused-vars */
for (const key of firstKeys) {
if (!second.hasOwnProperty(key)) return false;
}
for (const key of firstKeys) {
if (!deepEqual(first[key], second[key])) return false;
}
/* eslint-enable no-unused-vars */
return true;
}
return false;
};

Просмотреть файл

@ -1,12 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { BigIntMath } from './bigIntMath';
import { copy } from './copy';
import { deepEqual } from './deepEqual';
export {
BigIntMath,
copy,
deepEqual,
};

Просмотреть файл

@ -1,15 +0,0 @@
const normalize = (sourceUrl) => {
let url = sourceUrl.slice();
const protocol = url.match(/\:\/\//);
if (!protocol) {
url = `http://${url}`;
}
return url;
};
export const Url = {
normalize,
};

Просмотреть файл

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Client</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

Просмотреть файл

@ -1,56 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import App from './App';
import { contractEventNotifier } from 'middlewares';
import { DrizzleContext } from '@drizzle/react-plugin';
import { LocalStorage } from 'polyfills/localStorage';
import React from 'react';
import { render } from 'react-dom';
import { Drizzle, generateStore } from '@drizzle/store';
const storage = new LocalStorage();
window.ls = new Proxy(storage, {
set: (_, prop, value) => {
if (LocalStorage.prototype.hasOwnProperty(prop)) {
storage[prop] = value;
} else {
storage.setItem(prop, value);
}
return true;
},
get: (_, name) => {
if (LocalStorage.prototype.hasOwnProperty(name)) {
return storage[name];
}
if (storage.values.has(name)) {
return storage.getItem(name);
}
return undefined;
},
});
Drizzle.prototype.deleteAllContracts = function () {
Object.keys(this.contracts)
.forEach(contractName => this.deleteContract(contractName));
};
const options = { contracts: [] };
const store = generateStore({
drizzleOptions: options,
appMiddlewares: [contractEventNotifier]
});
const drizzle = new Drizzle(options, store);
render(
<DrizzleContext.Provider drizzle={drizzle}>
<App />
</DrizzleContext.Provider>,
document.getElementById('root')
);

Просмотреть файл

@ -1,28 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { EventActions } from '@drizzle/store';
import { Notifications } from 'services';
export const contractEventNotifier = () => next => action => {
let message;
// Workaround. Used to prevent drizzle fallback actions.
const showErrors = Boolean(window.showErrors) || false;
if (!showErrors) {
return next(action);
}
if (action.type === EventActions.EVENT_ERROR) {
message = action.event.returnValues._message;
} else if (!!action.error) {
message = action.error.message;
}
if (!!message) {
Notifications.showNotification({ message, type: Notifications.types.error, });
}
return next(action);
};

Просмотреть файл

@ -1,4 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export * from './drizzle';

Просмотреть файл

@ -1,36 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export class LocalStorage {
constructor() {
this.values = new Map();
}
getItem(key) {
const stringKey = String(key);
if (this.values.has(stringKey)) {
return String(this.values.get(stringKey));
}
return null;
}
setItem(key, value) {
this.values.set(String(key), String(value));
}
removeItem(key) {
this.values.delete(String(key));
}
clear() {
this.values.clear();
}
key(i) {
return Array.from(this.values.keys())[i];
}
get length() {
return this.values.size;
}
}

Просмотреть файл

@ -1,5 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export * from './ipc';
export * from './notifications';

Просмотреть файл

@ -1,57 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
const getCommandList = commands => commands
.trim()
.split(' ')
.map(command => command.trim());
const execute = (listeners, command, data) => {
if (listeners.has(command)) {
const handlers = listeners.get(command);
handlers.forEach(handler => handler(data));
}
};
const addToListeners = (listeners, commands, handler) => {
const commandsList = getCommandList(commands);
commandsList.forEach(command => {
if (!listeners.has(command)) {
listeners.set(command, []);
}
const handlers = listeners.get(command);
handlers.push(handler);
});
};
const removeFromListeners = (listeners, commands, handler) => {
const commandsList = getCommandList(commands);
commandsList.forEach(command => {
if (listeners.has(command)) {
if (!handler) {
listeners.delete(command);
return;
}
let handlers = listeners.get(command);
handlers = handlers.filter(element => element !== handler);
listeners.set(command, handlers);
}
});
};
const removeAllListers = (listeners, command) => listeners.delete(command);
export const Helpers = {
execute,
addToListeners,
removeFromListeners,
removeAllListers
};

Просмотреть файл

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Helpers } from './helpers';
// eslint-disable-next-line no-undef
const vscode = acquireVsCodeApi();
const listeners = new Map();
const onceListeners = new Map();
const on = (commands, handler) => Helpers.addToListeners(listeners, commands, handler);
const once = (commands, handler) => Helpers.addToListeners(onceListeners, commands, handler);
const off = (commands, handler) => {
Helpers.removeFromListeners(listeners, commands, handler);
Helpers.removeFromListeners(onceListeners, commands, handler);
};
const postMessage = (command, value) => vscode.postMessage({ command, value });
const messageReceived = (event) => {
const { command, value: data } = event.data;
Helpers.execute(listeners, command, data);
Helpers.execute(onceListeners, command, data);
Helpers.removeAllListers(onceListeners, command);
};
window.addEventListener('message', messageReceived, false);
export const IPC = {
on,
once,
off,
postMessage
};

Просмотреть файл

@ -1,19 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { IPC } from '../ipc';
const types = {
error: 'error',
info: 'info',
warning: 'warning',
};
const showNotification = ({ message, type }) => {
IPC.postMessage('notification', { message, type });
};
export const Notifications = {
showNotification,
types
};

Просмотреть файл

@ -1,8 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import SingleContractView from './singleContractView/SingleContractView';
export {
SingleContractView
};

Просмотреть файл

@ -1,45 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Container } from '@material-ui/core';
import { DrizzleContext } from '@drizzle/react-plugin';
import PropTypes from 'prop-types';
import React from 'react';
import {
Interaction,
Message,
Metadata,
} from 'components';
import './singleContractView.less';
class SingleContractView extends React.PureComponent {
render() {
const { drizzle } = this.props;
const contract = drizzle.contractList[0];
const { isOnline } = contract.options;
if (!contract) {
return <Message message='No contract'/>;
}
if (!isOnline) {
return <Message className='red-error' message='Connection error' />;
}
return <Container className='single contract view'>
<Interaction/>
<Metadata />
</Container>;
}
}
const HOC = () => <DrizzleContext.Consumer>
{ props => <SingleContractView {...props} /> }
</DrizzleContext.Consumer>;
export default HOC;
SingleContractView.propTypes = {
drizzle: PropTypes.any,
drizzleState: PropTypes.any
};

Просмотреть файл

@ -1,18 +0,0 @@
.single.contract.view {
& > * {
margin-top: 15px;
}
&:last-child {
margin-bottom: 15px;
}
}
.MuiChip-outlinedSecondary.red-error {
background: #f44336;
width: 94.7%;
color: #fff !important;
border-radius: 4px;
border: 1px solid #f44336 !important;
margin-top: 15px;
}

Просмотреть файл

@ -1,124 +0,0 @@
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = () => {
const ENV = process.env.NODE_ENV || 'development';
const EXT = !!process.env.EXT_ENV || false;
const PATH = EXT ? path.join(__dirname, '../resources/drizzle') : path.resolve(__dirname, 'dist');
const PUBLIC_PATH = EXT ? '{{root}}/resources/drizzle' : '/';
const config = {
target: 'web',
entry: {
app: './src/index.js'
},
output: {
path: PATH,
filename: '[name].js',
chunkFilename: '[name].chunk.js',
publicPath: PUBLIC_PATH
},
devServer: {
contentBase: PATH,
historyApiFallback: true
},
performance: {
hints: false
},
stats: {
colors: true,
chunks: false,
modules: false,
},
resolve: {
alias: {
components: path.join(__dirname, './src/components'),
constants: path.join(__dirname, './src/constants'),
helpers: path.join(__dirname, './src/helpers'),
polyfills: path.join(__dirname, './src/polyfills'),
services: path.join(__dirname, './src/services'),
views: path.join(__dirname, './src/views'),
middlewares: path.join(__dirname, './src/middlewares'),
}
},
module: {
rules: [
{
test: [/\.js$/, /\.jsx$/],
loader: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
loader: ['style-loader', 'css-loader']
},
{
test: /\.jpe?g$|\.gif$|\.png$|\.ttf$|\.eot$|\.svg$/,
use: 'file-loader?name=[name].[ext]?[hash]'
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?limit=10000&mimetype=application/fontwoff'
},
{
test: [/\.js$/, /\.jsx$/],
exclude: /node_modules/,
loader: 'eslint-loader'
},
{
test: /\.js$/,
loader: 'string-replace-loader',
options: {
search: 'localStorage',
replace: 'ls',
flags: 'g'
},
exclude: path.join(__dirname, 'src', 'index.js'),
include: [
path.resolve(__dirname, 'node_modules')
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
alwaysWriteToDisk: true,
template: './src/index.html',
inject: 'body',
hash: ENV !== 'development',
chunks: ['app'],
filename: 'index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].css',
allChunks: true,
}),
new webpack.optimize.AggressiveMergingPlugin(),
]
};
switch (ENV) {
case 'production':
config.devtool = false;
config.optimization = { minimize: true };
break;
case 'development':
config.watch = !EXT;
config.devtool = 'source-map';
break;
case 'test':
config.devtool = 'source-map';
delete config.entry;
break;
default:
break;
}
return config;
};

Просмотреть файл

@ -5,7 +5,7 @@
"publisher": "AzBlockchain",
"preview": false,
"icon": "images/blockchain-service-logo.png",
"version": "1.6.1",
"version": "1.6.2",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-azure-blockchain-ethereum"
@ -48,7 +48,6 @@
"onCommand:azureBlockchainService.generateEventPublishingWorkflows",
"onCommand:azureBlockchainService.generateDataPublishingWorkflows",
"onCommand:azureBlockchainService.connectProject",
"onCommand:azureBlockchainService.showSmartContractPage",
"onCommand:azureBlockchainService.copyRPCEndpointAddress",
"onCommand:azureBlockchainService.createProject",
"onCommand:azureBlockchainService.disconnectProject",
@ -160,11 +159,6 @@
"title": "Connect to network",
"category": "Blockchain"
},
{
"command": "azureBlockchainService.showSmartContractPage",
"title": "Show Smart Contract Interaction Page",
"category": "Blockchain"
},
{
"command": "azureBlockchainService.copyRPCEndpointAddress",
"title": "Copy RPC Endpoint Address",
@ -241,10 +235,6 @@
],
"menus": {
"commandPalette": [
{
"when": "false",
"command": "azureBlockchainService.showSmartContractPage"
},
{
"when": "false",
"command": "azureBlockchainService.refresh"
@ -392,11 +382,6 @@
"command": "azureBlockchainService.buildContracts",
"group": "8_buildContractGroup"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.showSmartContractPage",
"group": "8_buildContractGroup"
},
{
"when": "resourceLangId == json",
"command": "azureBlockchainService.deployContracts",
@ -541,7 +526,7 @@
"scripts": {
"package": "npx vsce package",
"publish": "npx vsce publish",
"vscode:prepublish": "npm i && npm run webpack:prod && cd ./drizzleUI && npm i && npm run build:ext",
"vscode:prepublish": "npm i && npm run webpack:prod",
"compile": "npm run clean && tsc -p ./",
"webpack:prod": "webpack --mode production",
"watch": "tsc -watch -p ./",

Просмотреть файл

@ -3,7 +3,6 @@
import { commands, ExtensionContext, Uri, window, workspace } from 'vscode';
import {
ContractCommands,
DebuggerCommands,
GanacheCommands,
InfuraCommands,
@ -104,7 +103,7 @@ export async function activate(context: ExtensionContext) {
const copyRPCEndpointAddress = commands.registerCommand('azureBlockchainService.copyRPCEndpointAddress',
async (viewItem: NetworkNodeView) => {
await tryExecute(() => TruffleCommands.writeRPCEndpointAddressToBuffer(viewItem));
});
});
const getPrivateKeyFromMnemonic = commands.registerCommand('azureBlockchainService.getPrivateKey', async () => {
await tryExecute(() => TruffleCommands.getPrivateKeyFromMnemonic());
});
@ -140,17 +139,12 @@ export async function activate(context: ExtensionContext) {
//#endregion
//#region contract commands
const showSmartContractPage = commands.registerCommand(
'azureBlockchainService.showSmartContractPage',
async (contractPath: Uri) => {
await tryExecute(() => ContractCommands.showSmartContractPage(context, contractPath));
});
const createNewBDMApplication = commands.registerCommand('azureBlockchainService.createNewBDMApplication',
async (viewItem: ProjectView) => {
await tryExecute(() => ServiceCommands.createNewBDMApplication(viewItem));
});
});
const deleteBDMApplication = commands.registerCommand('azureBlockchainService.deleteBDMApplication',
async (viewItem: NetworkNodeView) => await tryExecute(() => ServiceCommands.deleteBDMApplication(viewItem)));
async (viewItem: NetworkNodeView) => await tryExecute(() => ServiceCommands.deleteBDMApplication(viewItem)));
//#endregion
//#region open zeppelin commands
@ -199,7 +193,6 @@ export async function activate(context: ExtensionContext) {
const subscriptions = [
showWelcomePage,
showRequirementsPage,
showSmartContractPage,
refresh,
newSolidityProject,
buildContracts,

Просмотреть файл

@ -1,257 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { commands, ExtensionContext, Uri, window } from 'vscode';
import {
ContractCommands,
DebuggerCommands,
GanacheCommands,
InfuraCommands,
LogicAppCommands,
OpenZeppelinCommands,
ProjectCommands,
ServiceCommands,
TruffleCommands,
} from './commands';
import { Constants } from './Constants';
import { CommandContext, isWorkspaceOpen, openZeppelinHelper, required, setCommandContext } from './helpers';
import { CancellationEvent } from './Models';
import { Output } from './Output';
import { ChangelogPage, RequirementsPage, WelcomePage } from './pages';
import {
AdapterType,
ContractDB,
GanacheService,
InfuraServiceClient,
MnemonicRepository,
TreeManager,
TreeService,
} from './services';
import { Telemetry } from './TelemetryClient';
import { NetworkNodeView, ProjectView } from './ViewItems';
import { DebuggerConfiguration } from './debugAdapter/configuration/debuggerConfiguration';
export async function activate(context: ExtensionContext) {
if (process.env.CODE_TEST) {
return;
}
Constants.initialize(context);
DebuggerConfiguration.initialize(context);
await ContractDB.initialize(AdapterType.IN_MEMORY);
await InfuraServiceClient.initialize(context.globalState);
MnemonicRepository.initialize(context.globalState);
TreeManager.initialize(context.globalState);
TreeService.initialize('AzureBlockchain');
setCommandContext(CommandContext.Enabled, true);
setCommandContext(CommandContext.IsWorkspaceOpen, isWorkspaceOpen());
const welcomePage = new WelcomePage(context);
const requirementsPage = new RequirementsPage(context);
const changelogPage = new ChangelogPage(context);
await welcomePage.checkAndShow();
await changelogPage.checkAndShow();
//#region azureBlockchain extension commands
const refresh = commands.registerCommand('azureBlockchainService.refresh', (element) => {
TreeService.refresh(element);
});
const showWelcomePage = commands.registerCommand('azureBlockchainService.showWelcomePage', async () => {
return welcomePage.show();
});
const showRequirementsPage = commands.registerCommand('azureBlockchainService.showRequirementsPage',
async (checkShowOnStartup: boolean) => {
return checkShowOnStartup ? await requirementsPage.checkAndShow() : await requirementsPage.show();
});
//#endregion
//#region Ganache extension commands
const startGanacheServer = commands.registerCommand('azureBlockchainService.startGanacheServer',
async (viewItem?: ProjectView) => {
await tryExecute(() => GanacheCommands.startGanacheCmd(viewItem));
});
const stopGanacheServer = commands.registerCommand('azureBlockchainService.stopGanacheServer',
async (viewItem?: ProjectView) => {
await tryExecute(() => GanacheCommands.stopGanacheCmd(viewItem));
});
//#endregion
//#region truffle commands
const newSolidityProject = commands.registerCommand('truffle.newSolidityProject', async () => {
await tryExecute(() => ProjectCommands.newSolidityProject());
});
const buildContracts = commands.registerCommand('truffle.buildContracts', async () => {
await tryExecute(() => TruffleCommands.buildContracts());
});
const deployContracts = commands.registerCommand('truffle.deployContracts', async () => {
await tryExecute(() => TruffleCommands.deployContracts());
});
const copyByteCode = commands.registerCommand('contract.copyByteCode', async (uri: Uri) => {
await tryExecute(() => TruffleCommands.writeBytecodeToBuffer(uri));
});
const copyDeployedByteCode = commands.registerCommand('contract.copyDeployedByteCode', async (uri: Uri) => {
await tryExecute(() => TruffleCommands.writeDeployedBytecodeToBuffer(uri));
});
const copyABI = commands.registerCommand('contract.copyABI', async (uri: Uri) => {
await tryExecute(() => TruffleCommands.writeAbiToBuffer(uri));
});
const copyRPCEndpointAddress = commands.registerCommand('azureBlockchainService.copyRPCEndpointAddress',
async (viewItem: NetworkNodeView) => {
await tryExecute(() => TruffleCommands.writeRPCEndpointAddressToBuffer(viewItem));
});
const getPrivateKeyFromMnemonic = commands.registerCommand('azureBlockchainService.getPrivateKey', async () => {
await tryExecute(() => TruffleCommands.getPrivateKeyFromMnemonic());
});
//#endregion
//#region services with dialog
const createProject = commands.registerCommand('azureBlockchainService.createProject', async () => {
await tryExecute(() => ServiceCommands.createProject());
});
const connectProject = commands.registerCommand('azureBlockchainService.connectProject', async () => {
await tryExecute(() => ServiceCommands.connectProject());
});
const disconnectProject = commands.registerCommand('azureBlockchainService.disconnectProject',
async (viewItem: ProjectView) => {
await tryExecute(() => ServiceCommands.disconnectProject(viewItem));
});
const openAtAzurePortal = commands.registerCommand('azureBlockchainService.openAtAzurePortal',
async (viewItem: NetworkNodeView) => ServiceCommands.openAtAzurePortal(viewItem));
//#endregion
//#region Infura commands
const signInToInfuraAccount = commands.registerCommand('azureBlockchainService.signInToInfuraAccount', async () => {
await tryExecute(() => InfuraCommands.signIn());
});
const signOutOfInfuraAccount = commands.registerCommand('azureBlockchainService.signOutOfInfuraAccount', async () => {
await tryExecute(() => InfuraCommands.signOut());
});
const showProjectsFromInfuraAccount = commands.registerCommand(
'azureBlockchainService.showProjectsFromInfuraAccount',
async () => {
await tryExecute(() => InfuraCommands.showProjectsFromAccount());
});
//#endregion
//#region contract commands
const showSmartContractPage = commands.registerCommand(
'azureBlockchainService.showSmartContractPage',
async (contractPath: Uri) => {
await tryExecute(() => ContractCommands.showSmartContractPage(context, contractPath));
});
const createNewBDMApplication = commands.registerCommand('azureBlockchainService.createNewBDMApplication',
async (viewItem: ProjectView) => {
await tryExecute(() => ServiceCommands.createNewBDMApplication(viewItem));
});
const deleteBDMApplication = commands.registerCommand('azureBlockchainService.deleteBDMApplication',
async (viewItem: NetworkNodeView) => await tryExecute(() => ServiceCommands.deleteBDMApplication(viewItem)));
//#endregion
//#region open zeppelin commands
const openZeppelinAddCategory = commands.registerCommand('openZeppelin.addCategory', async () => {
await tryExecute(() => OpenZeppelinCommands.addCategory());
});
//#endregion
//#region logic app commands
const generateMicroservicesWorkflows = commands.registerCommand(
'azureBlockchainService.generateMicroservicesWorkflows',
async (filePath: Uri | undefined) => {
await tryExecute(async () => await LogicAppCommands.generateMicroservicesWorkflows(filePath));
});
const generateDataPublishingWorkflows = commands.registerCommand(
'azureBlockchainService.generateDataPublishingWorkflows',
async (filePath: Uri | undefined) => {
await tryExecute(async () => await LogicAppCommands.generateDataPublishingWorkflows(filePath));
});
const generateEventPublishingWorkflows = commands.registerCommand(
'azureBlockchainService.generateEventPublishingWorkflows',
async (filePath: Uri | undefined) => {
await tryExecute(async () => await LogicAppCommands.generateEventPublishingWorkflows(filePath));
});
const generateReportPublishingWorkflows = commands.registerCommand(
'azureBlockchainService.generateReportPublishingWorkflows',
async (filePath: Uri | undefined) => {
await tryExecute(async () => await LogicAppCommands.generateReportPublishingWorkflows(filePath));
});
//#endregion
//#region debugger commands
const startDebugger = commands.registerCommand('extension.truffle.debugTransaction', async () => {
await tryExecute(() => DebuggerCommands.startSolidityDebugger());
});
//#endregion
const subscriptions = [
showWelcomePage,
showRequirementsPage,
showSmartContractPage,
refresh,
newSolidityProject,
buildContracts,
deployContracts,
createNewBDMApplication,
createProject,
connectProject,
deleteBDMApplication,
disconnectProject,
copyByteCode,
copyDeployedByteCode,
copyABI,
copyRPCEndpointAddress,
startGanacheServer,
stopGanacheServer,
generateMicroservicesWorkflows,
generateDataPublishingWorkflows,
generateEventPublishingWorkflows,
generateReportPublishingWorkflows,
getPrivateKeyFromMnemonic,
startDebugger,
signInToInfuraAccount,
signOutOfInfuraAccount,
showProjectsFromInfuraAccount,
openZeppelinAddCategory,
openAtAzurePortal,
];
context.subscriptions.push(...subscriptions);
required.checkAllApps();
Telemetry.sendEvent(Constants.telemetryEvents.extensionActivated);
checkAndUpgradeOpenZeppelinAsync();
}
export async function deactivate(): Promise<void> {
// This method is called when your extension is deactivated
// To dispose of all extensions, vscode provides 5 sec.
// Therefore, please, call important dispose functions first and don't use await
// For more information see https://github.com/Microsoft/vscode/issues/47881
GanacheService.dispose();
ContractDB.dispose();
Telemetry.dispose();
TreeManager.dispose();
Output.dispose();
}
async function tryExecute(func: () => Promise<any>, errorMessage: string | null = null): Promise<void> {
try {
await func();
} catch (error) {
if (error instanceof CancellationEvent) {
return;
}
window.showErrorMessage(errorMessage || error.message);
}
}
async function checkAndUpgradeOpenZeppelinAsync(): Promise<void> {
if (await openZeppelinHelper.shouldUpgradeOpenZeppelinAsync()) {
await openZeppelinHelper.upgradeOpenZeppelinUserSettingsAsync();
await openZeppelinHelper.upgradeOpenZeppelinContractsAsync();
}
}

Просмотреть файл

@ -1,151 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as assert from 'assert';
import * as path from 'path';
import rewire = require('rewire');
import * as sinon from 'sinon';
import { commands, ExtensionContext, Uri, window } from 'vscode';
import { ContractCommands } from '../../src/commands';
import { Constants } from '../../src/Constants';
import { CancellationEvent } from '../../src/Models';
import * as contractUI from '../../src/pages/ContractUI';
import { ContractDB, ContractInstanceWithMetadata } from '../../src/services';
import { Contract } from '../../src/services/contract/Contract';
import { Network } from '../../src/services/contract/Network';
describe('ContractCommands tests', () => {
const fileUri = {
fsPath: path.join(__dirname, 'testData', 'TestContract.json'),
} as Uri;
const contractInstance = new ContractInstanceWithMetadata(
new Contract({
contractName: 'contractName',
networks: [{}],
updatedAt: '01-01-2019',
}),
{ id: 'testnetwork' } as Network,
null,
);
afterEach(() => {
sinon.restore();
});
it('showSmartContractPage throws error when contract does not exist and deploy can`t create contract', async () => {
// Arrange
sinon.stub(ContractDB, 'getContractInstances').returns(Promise.resolve([]));
sinon.stub(window, 'showWarningMessage').returns(Promise.resolve(undefined));
// Act
const action = async () => {
await ContractCommands
.showSmartContractPage({} as ExtensionContext, fileUri);
};
// Assert
await assert.rejects(
action,
Error,
Constants.errorMessageStrings.CompiledContractIsMissing);
});
it('showSmartContractPage throws cancellationEvent when contract does not exist and deploy can create contract, ' +
'but user click cancel button',
async () => {
// Arrange
sinon.stub(ContractDB, 'getContractInstances').returns(Promise.resolve([]));
sinon.stub(window, 'showWarningMessage')
.returns(Promise.resolve({ title: Constants.informationMessage.cancelButton }));
// Act
const action = async () => {
await ContractCommands
.showSmartContractPage({} as ExtensionContext, fileUri);
};
// Assert
await assert.rejects(action, CancellationEvent);
});
it('showSmartContractPage executes deploy contract when contract does not exist, deploy can create contract ' +
'and user click deploy button',
async () => {
// Arrange
sinon.stub(ContractDB, 'getContractInstances')
.onCall(0)
.returns(Promise.resolve([]))
.onCall(1)
.returns(Promise.resolve([
contractInstance,
]));
sinon.replace(
window,
'showWarningMessage',
sinon.stub().returns(Promise.resolve(Constants.informationMessage.deployButton)),
);
const executeCommandSpy = sinon.stub(commands, 'executeCommand').callsFake(() => Promise.resolve());
sinon.stub(contractUI, 'ContractUI').returns({
postMessage: () => undefined,
show: () => undefined,
});
// Act
await ContractCommands
.showSmartContractPage({} as ExtensionContext, fileUri);
// Assert
assert.strictEqual(executeCommandSpy.calledOnce, true, 'showSmartContract should execute deploy contract');
});
it('showSmartContractPage throws error when contract exist without networks and deploy can`t create contract',
async () => {
// Arrange
sinon.stub(ContractDB, 'getContractInstances')
.onCall(0)
.returns(Promise.resolve([]))
.onCall(1)
.returns(Promise.resolve([
contractInstance,
]));
sinon.replace(
window,
'showWarningMessage',
sinon.stub().returns(Promise.resolve(Constants.informationMessage.deployButton)),
);
sinon.stub(commands, 'executeCommand').callsFake(() => Promise.resolve());
// Act
const action = async () => {
await ContractCommands
.showSmartContractPage({} as ExtensionContext, fileUri);
};
// Assert
await assert.rejects(
action,
Error,
Constants.errorMessageStrings.CompiledContractIsMissing);
});
it('showSmartContractPage should show page for contract', async () => {
// Arrange
sinon.stub(ContractDB, 'getContractInstances')
.returns(Promise.resolve([
contractInstance,
]));
const contractCommandsRewire = rewire('../../src/commands/ContractCommands');
const showStub = sinon.stub().returns(() => undefined);
sinon.stub(contractUI, 'ContractUI').returns({
show: showStub,
});
// Act
await contractCommandsRewire.ContractCommands
.showSmartContractPage({} as ExtensionContext, fileUri);
// Assert
assert.strictEqual(showStub.calledOnce, true, 'showSmartContractPage should create drizzle page');
});
});