This commit is contained in:
Chris Segura 2019-12-02 14:27:41 -08:00
Родитель a40021cf4f
Коммит 2ec6e9d90b
58 изменённых файлов: 6997 добавлений и 28522 удалений

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

@ -1,9 +1,27 @@
# Change Log
All notable changes to the "azureblockchain" extension will be documented in this file.
All notable changes to the "azure blockchain" extension will be documented in this file.
## 0.1.12
- Deployments from external truffle boxes can now deploy to Azure Blockchain Service
- Additional context menu for deployed contract bytecode (transaction bytecode)
- Fix on Mocks category for OpenZeppelin to remove the dead link for not existent docs
- Better handling of OpenZeppelin when multiple categories are downloaded.
- Fix misspelling of OpenZeppelin on the welcome page.
## 0.1.11
- Fixed issue with Logic App generation JSON schema
- Added Contract UI support for contracts deployed on Azure Blockchain Service
- Updated deprecated Truffle NPM packages
- Bumped HD wallet provider to latest Truffle version
- Minimized output channel logs
- Fixed output channel issue which shows JavaScript object after contract migration
- Preview features added for token (TTI compliant) generation were added.
## 0.1.10
- Fixed Drizzle error handling issues

34263
NOTICE.txt

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -40,15 +40,15 @@
"style-loader": "^0.23.1",
"url-loader": "^2.1.0",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.6",
"webpack-plugin-replace": "^1.2.0"
"webpack-cli": "^3.3.6"
},
"dependencies": {
"@drizzle/react-components": "^1.5.1",
"@drizzle/react-plugin": "^1.5.1",
"@drizzle/store": "^1.5.1",
"@material-ui/core": "^4.2.1",
"@material-ui/icons": "^4.2.1",
"drizzle": "^1.4.0",
"drizzle-react": "^1.3.0",
"drizzle-react-components": "^1.4.0",
"@truffle/hdwallet-provider": "^1.0.23",
"react": "^16.10.2",
"react-dom": "^16.10.2"
}

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

@ -1,8 +1,10 @@
// 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';
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';
@ -83,11 +85,29 @@ class App extends React.Component {
drizzle.deleteAllContracts();
if (provider) {
const { WebsocketProvider } = drizzle.web3.providers;
const host = new URL(Url.normalize(provider.host));
host.protocol = 'ws';
let web3Provider;
drizzle.web3.setProvider(host.toString());
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;

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

@ -2,7 +2,7 @@
// Licensed under the MIT license.
import { createInputComponent } from '../factory/InputComponentFactory';
import { newContextComponents } from 'drizzle-react-components';
import { newContextComponents } from '@drizzle/react-components';
import PropTypes from 'prop-types';
import React from 'react';
import {

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

@ -3,7 +3,7 @@
import { Container } from '@material-ui/core';
import { deepEqual } from 'helpers';
import { DrizzleContext } from 'drizzle-react';
import { DrizzleContext } from '@drizzle/react-plugin';
import PropTypes from 'prop-types';
import React from 'react';
import {

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

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { DrizzleContext } from 'drizzle-react';
import { DrizzleContext } from '@drizzle/react-plugin';
import { FileCopy } from '@material-ui/icons';
import PropTypes from 'prop-types';
import React from 'react';

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

@ -52,4 +52,10 @@ export class Constants {
},
arrayTypeRegexp: /\w*\[\d*\]/g,
};
static regexps = {
providerTypes: {
azure: /blockchain.azure.com/i
}
};
}

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

@ -3,50 +3,16 @@
import App from './App';
import { contractEventNotifier } from 'middlewares';
import { DrizzleContext } from 'drizzle-react';
import { LocalStorage } from 'polyfills/localStorage';
import { newContextComponents } from 'drizzle-react-components';
import { DrizzleContext } from '@drizzle/react-plugin';
import React from 'react';
import { render } from 'react-dom';
import { Drizzle, generateStore } from 'drizzle';
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;
},
});
import { Drizzle, generateStore } from '@drizzle/store';
Drizzle.prototype.deleteAllContracts = function () {
Object.keys(this.contracts)
.forEach(contractName => this.deleteContract(contractName));
};
newContextComponents.ContractForm.prototype.handleInputChange = function (event) {
const value = event.target.type === 'checkbox'
? event.target.checked
: event.target.value;
this.setState({ [event.target.name]: value });
};
const options = { contracts: [] };
const store = generateStore({

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

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { EventActions } from 'drizzle';
import { EventActions } from '@drizzle/store';
import { Notifications } from 'services';
export const contractEventNotifier = () => next => action => {

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

@ -2,7 +2,7 @@
// Licensed under the MIT license.
import { Container } from '@material-ui/core';
import { DrizzleContext } from 'drizzle-react';
import { DrizzleContext } from '@drizzle/react-plugin';
import PropTypes from 'prop-types';
import React from 'react';
import {

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

@ -1,7 +1,6 @@
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ReplacePlugin = require('webpack-plugin-replace');
const webpack = require('webpack');
module.exports = () => {
@ -38,7 +37,6 @@ module.exports = () => {
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'),
@ -87,17 +85,6 @@ module.exports = () => {
filename: '[name].css',
allChunks: true,
}),
new ReplacePlugin({
exclude: path.join(__dirname, 'src', 'index.js'),
include: true,
patterns: [{
regex: /localStorage/g,
value: 'ls'
}],
values: {
ls: 'ls'
}
}),
new webpack.optimize.AggressiveMergingPlugin(),
]
};

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

@ -5,7 +5,7 @@
"publisher": "AzBlockchain",
"preview": true,
"icon": "images/blockchain-service-logo.png",
"version": "0.1.10",
"version": "0.1.12",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-azure-blockchain-ethereum"
@ -36,6 +36,7 @@
"onCommand:azureBlockchainService.showWelcomePage",
"onCommand:azureBlockchainService.refresh",
"onCommand:contract.copyByteCode",
"onCommand:contract.copyDeployedByteCode",
"onCommand:contract.copyABI",
"onCommand:truffle.createLocalUI",
"onCommand:truffle.newSolidityProject",
@ -54,6 +55,7 @@
"onCommand:openZeppelin.addCategory",
"onCommand:azureBlockchainService.signInToInfuraAccount",
"onCommand:azureBlockchainService.signOutOfInfuraAccount",
"onCommand:azureBlockchainService.generateToken",
"onDebug"
],
"contributes": {
@ -94,7 +96,12 @@
},
{
"command": "contract.copyByteCode",
"title": "Copy Contract Bytecode",
"title": "Copy Constructor Bytecode",
"category": "Azure Blockchain"
},
{
"command": "contract.copyDeployedByteCode",
"title": "Copy Transaction Bytecode",
"category": "Azure Blockchain"
},
{
@ -176,6 +183,11 @@
"command": "openZeppelin.addCategory",
"title": "Add contracts from OpenZeppelin",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.generateToken",
"title": "Generate token",
"category": "Azure Blockchain"
}
],
"breakpoints": [
@ -220,10 +232,18 @@
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateReportPublishingWorkflows"
},
{
"when": "false",
"command": "azureBlockchainService.generateToken"
},
{
"when": "false",
"command": "contract.copyByteCode"
},
{
"when": "false",
"command": "contract.copyDeployedByteCode"
},
{
"when": "false",
"command": "contract.copyABI"
@ -318,6 +338,11 @@
"command": "contract.copyByteCode",
"group": "9_copyFromContractGroup"
},
{
"when": "resourceLangId == json",
"command": "contract.copyDeployedByteCode",
"group": "9_copyFromContractGroup"
},
{
"when": "resourceLangId == json",
"command": "contract.copyABI",
@ -347,6 +372,11 @@
"when": "resourceLangId == solidity",
"command": "openZeppelin.addCategory",
"group": "8_buildContractGroup"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateToken",
"group": "8_buildContractGroup"
}
]
},
@ -453,7 +483,6 @@
"clean": "npx rimraf -- ./out/*"
},
"devDependencies": {
"@machinomy/types-ethereumjs-wallet": "^0.0.12",
"@types/astring": "^1.3.0",
"@types/estree": "^0.0.39",
"@types/download": "^6.2.4",
@ -492,6 +521,9 @@
"webpack-cli": "^3.3.6"
},
"dependencies": {
"@truffle/debug-utils": "1.0.19",
"@truffle/debugger": "5.0.35",
"@truffle/provider": "0.2.1",
"abi-decoder": "^2.0.1",
"acorn": "^6.1.1",
"acorn-walk": "^6.1.1",
@ -499,7 +531,6 @@
"azure-arm-resource": "^7.3.0",
"bip39": "^3.0.1",
"download": "^7.1.0",
"ethereumjs-wallet": "^0.6.3",
"fs-extra": "^7.0.1",
"hdkey": "^1.1.1",
"ms-rest": "^2.5.0",
@ -508,10 +539,6 @@
"request": "^2.88.0",
"request-promise": "^4.2.4",
"semver": "^6.0.0",
"truffle-contract": "4.0.30",
"truffle-debug-utils": "1.0.18",
"truffle-debugger": "5.0.25",
"truffle-provider": "0.1.15",
"uuid": "^3.3.2",
"vscode-azureextensionui": "^0.25.1",
"vscode-extension-telemetry": "^0.1.1"

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

@ -0,0 +1,37 @@
.hidden {
display: none;
}
label {
display: inline-block;
margin: 10px 0 10px 0;
}
.paragraph > div,
#numberOfSubTokens {
margin-left: 30px;
}
.textInputMargin {
margin-top: 10px;
}
.generateButton {
margin: 20px;
}
.errorNotification {
margin: 0;
color: red;
}
.notificationForRequiredFields {
border: 2px solid red;
border-radius: 7px;
width: fit-content;
padding-right: 10px;
}
#customDetails {
margin-top: 10px;
}

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

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="{{root}}/resources/tokenui/generateToken.css">
<script type="text/javascript" src="{{root}}/resources/welcome/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="{{root}}/resources/tokenui/generateToken.js"></script>
</head>
<body>
<header>
<div id="title-area">
<h1>Token generation</h1>
</div>
</header>
<div>
<div id="TokenGenerationForm">
<div class="inputForm paragraph">
<label>Please provide a name for your token.</label><br>
<div>
<input id="tokenName" type="text" placeholder="Token Name *">
<div id="tokenNameValidationTip" class="hidden">
<label class="errorNotification">Should be alphanumeric and from 1-255 characters.</label>
</div>
</div>
</div>
<div class="selectionForm paragraph">
<label>Please choose the token type.</label><br>
<div id="tokenType">
<label><input id="singleOrSolitary" type="radio" name="tokenType" value="false">Single/Solitary</label><br>
<label><input id="compositeOrHybrid" type="radio" name="tokenType" value="true">Composite/Hybrid</label><br>
<div id="selectionOfSubTokens" class="hidden">
<label>How many sub tokens?</label><br>
<div class="paragraph">
<select id="numberOfSubTokens">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select>
</div>
</div>
</div>
</div>
<div id="mainPropertiesAndBehaviorsToken">
<label>Please choose properties and behaviors for your token.</label>
</div>
<div id="propertiesAndBehaviorsSubTokens">
</div>
<div class="paragraph">
<label>Do you want to add custom details for your token (metadata such as SKU, issue date, place, etc)?</label><br>
<div>
<div id="addCustomDetails">
<label><input id="yesAddCustomDetails" type="radio" name="addCustomDetails" value="true">Yes</label>
<label><input id="noAddCustomDetails" type="radio" name="addCustomDetails" value="false">No</label>
</div>
<input class="textInputMargin hidden" id="customDetails" type="text" placeholder="Comma separated list">
<div id="customDetailsValidationTip" class="hidden">
<label class="errorNotification">Should be comma separated list of symbols. Should be alphanumeric and from 1-255 characters.</label>
</div>
</div>
</div>
<button class="generateButton" id="generateButton" type="submit">Generate</button>
</div>
</div>
</body>
</html>

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

@ -0,0 +1,363 @@
$(function() {
main();
});
function main() {
const vscode = acquireVsCodeApi();
$(document).ready(() => {
addPropertiesAndBehaviorsForMainToken();
vscode.postMessage({ command: 'documentReady'});
});
function addPropertiesAndBehaviorsForMainToken() {
const htmlQuestionsOfSubTokens = getHtmlPropertiesAndBehaviorsForToken();
$('#mainPropertiesAndBehaviorsToken')[0].innerHTML += htmlQuestionsOfSubTokens;
}
$('#yesAddCustomDetails').click(yesNoAddCustomDetails);
$('#noAddCustomDetails').click(yesNoAddCustomDetails);
$('#singleOrSolitary').click(showOrHideAdditionalOptionsForTokenType);
$('#compositeOrHybrid').click(showOrHideAdditionalOptionsForTokenType);
$('#numberOfSubTokens').change(addPropertiesAndBehaviorsForSubToken);
$('#generateButton').click(generateToken);
function generateToken() {
if (!validateTokenForm()) {
return null;
}
const tokenData = getTokenData();
const tokenExpression = generateTokenExpression(tokenData);
vscode.postMessage({ command: 'tokenExpression', value: JSON.stringify(tokenExpression)});
}
function getTokenData() {
const mainPropertiesAndBehaviors = getPropertiesAndBehaviorsForToken('#mainPropertiesAndBehaviorsToken');
const propertiesAndBehaviorsSubTokens = getPropertiesAndBehaviorsForSubTokens();
const additionalBehaviorsData = $('input[name=otherBehaviors]:checked').val() === 'true'
? $('input[id=customBehaviors]').val()
: null;
const customMetaData = $('input[name=addCustomDetails]:checked').val() === 'true'
? $('input[id=customDetails]').val()
: null;
return {
additionalBehaviorsData,
behaviors: mainPropertiesAndBehaviors,
composite: $('input[name=tokenType]:checked').val() === 'true',
customMetaData,
name: $('#tokenName')[0].value,
subTokens: propertiesAndBehaviorsSubTokens,
};
}
function isAllRequiredFieldsFilled() {
const tokenName = $('#tokenName')[0].value;
let isValid = true;
if (!tokenName) {
isValid = false;
}
const selectedTokenType = $('input[name=tokenType]:checked');
if(!selectedTokenType.length) {
$('#tokenType')[0].className = 'notificationForRequiredFields';
isValid = false;
} else {
$('#tokenType')[0].className = '';
}
const isComposite = selectedTokenType.val() === 'true';
if (isComposite) {
const elementWrapperForSubTokens = '.propertiesAndBehaviorsSubToken';
const countOfSubTokens = $(elementWrapperForSubTokens).length;
for (let i = 1; i <= countOfSubTokens; i++) {
const isSelectedAllBehaviors = checkSelectedAllBehaviors(elementWrapperForSubTokens, i);
if (!isSelectedAllBehaviors) {
isValid = false;
}
}
}
const elementWrapperForToken = '#mainPropertiesAndBehaviorsToken';
const isSelectedAllBehaviors = checkSelectedAllBehaviors(elementWrapperForToken);
if (!isSelectedAllBehaviors) {
isValid = false;
}
const isValidCustomDetails = checkCustomBehaviorsAndDetails('addCustomDetails', 'customDetails');
return isValid && isValidCustomDetails;
}
function checkSelectedAllBehaviors(elementWrapper, id = '') {
const numberOfBehaviors = $(`${elementWrapper} input`).length / 2;
const numberOfSelectedBehaviors = $(`${elementWrapper} input:checked`).length;
addNotificationForToken(elementWrapper, id);
return numberOfSelectedBehaviors !== numberOfBehaviors ? false : true;
}
function addNotificationForToken(elementWrapper, id) {
$(`${elementWrapper} input[name=baseTokenType${id}]`).parent().parent()[0].className =
$(`${elementWrapper} input[name=baseTokenType${id}]:checked`).length
? ''
: 'notificationForRequiredFields';
$(`${elementWrapper} input[name=additionalTokens${id}]`).parent().parent()[0].className =
$(`${elementWrapper} input[name=additionalTokens${id}]:checked`).length
? ''
: 'notificationForRequiredFields';
$(`${elementWrapper} input[name=destroyTokens${id}]`).parent().parent()[0].className =
$(`${elementWrapper} input[name=destroyTokens${id}]:checked`).length
? ''
: 'notificationForRequiredFields';
$(`${elementWrapper} input[name=transferOwnershipTokens${id}]`).parent().parent()[0].className =
$(`${elementWrapper} input[name=transferOwnershipTokens${id}]:checked`).length
? ''
: 'notificationForRequiredFields';
$(`${elementWrapper} input[name=delegateInteractions${id}]`).parent().parent()[0].className =
$(`${elementWrapper} input[name=delegateInteractions${id}]:checked`).length
? ''
: 'notificationForRequiredFields';
}
function checkCustomBehaviorsAndDetails(typeCustomProperty, typeDetails) {
const selectedCustomProperty = $(`input[name=${typeCustomProperty}]:checked`);
if(selectedCustomProperty.length) {
$('#addCustomDetails')[0].className = '';
const isYes = selectedCustomProperty.val() === 'true';
if (isYes && !$(`input#${typeDetails}`).val()) {
return false;
}
} else {
$('#addCustomDetails')[0].className = 'notificationForRequiredFields';
return false;
}
return true;
}
function getPropertiesAndBehaviorsForToken(elementWrapper, id = '') {
const isFungible = $(`${elementWrapper} input[name=baseTokenType${id}]:checked`).val();
const isMintable = $(`${elementWrapper} input[name=additionalTokens${id}]:checked`).val();
const isBurnable = $(`${elementWrapper} input[name=destroyTokens${id}]:checked`).val();
const isTransferable = $(`${elementWrapper} input[name=transferOwnershipTokens${id}]:checked`).val();
const isDelegate = $(`${elementWrapper} input[name=delegateInteractions${id}]:checked`).val();
return {
fungible: isFungible === 'true',
mintable: isMintable === 'true',
burnable: isBurnable === 'true',
transferable: isTransferable === 'true',
delegable: isDelegate === 'true',
};
}
function getPropertiesAndBehaviorsForSubTokens() {
let subTokensBehaviorsArray = [];
const elementWrapper = '.propertiesAndBehaviorsSubToken';
const countOfSubTokens = $(elementWrapper).length;
for (let i = 1; i <= countOfSubTokens; i++) {
subTokensBehaviorsArray.push(getPropertiesAndBehaviorsForToken(elementWrapper, i));
}
return subTokensBehaviorsArray;
}
function generateTokenExpression(tokenData) {
let template = getTokenString(tokenData.behaviors);
if (tokenData.customMetaData !== null) {
const customMetaData = tokenData.customMetaData.split(',').map(x => 'str' + x.trim()).join('+');
template = '[' + template.trim() + '+' + customMetaData + ']';
}
const subTokens = tokenData.subTokens;
if (subTokens && subTokens.length > 0) {
template = template.concat('(');
template = template.concat(subTokens.map(subToken => getTokenString(subToken)).join(','));
template = template.concat(')');
}
return `{"TokenName":"${tokenData.name}","Template":"${template}"}`;
}
function getTokenString(behaviors) {
const behaviorStrings = [];
let template = '';
template = behaviors.fungible ? template.concat('tF{') : template.concat('tN{');
behaviorStrings.push(behaviors.mintable ? 'm' : '~m');
behaviorStrings.push(behaviors.burnable ? 'b' : '~b');
behaviorStrings.push(behaviors.transferable ? 't' : '~t');
if (behaviors.delegable) {
behaviorStrings.push('g');
}
template = template.concat(behaviorStrings.join(',')).concat('}');
return template;
}
function yesNoAddCustomDetails() {
const isYes = $('#yesAddCustomDetails')[0];
if (isYes.checked) {
$('#customDetails')[0].style.display = 'block';
} else {
$('#customDetails')[0].style.display = 'none';
$('#customDetailsValidationTip')[0].style.display = 'none';
}
}
function showOrHideAdditionalOptionsForTokenType() {
const isCompositeOrHybrid = $('#compositeOrHybrid')[0];
if (isCompositeOrHybrid.checked) {
$('#selectionOfSubTokens')[0].style.display = 'block';
addPropertiesAndBehaviorsForSubToken();
} else {
$('#selectionOfSubTokens')[0].style.display = 'none';
removePropertiesAndBehaviorsForSubToken();
}
}
function addPropertiesAndBehaviorsForSubToken() {
let htmlOfPropertiesAndBehaviorsSubTokens = '';
const numbersOfSubTokens = $('#numberOfSubTokens')[0].value;
for(let i = 1; i <= numbersOfSubTokens; i++) {
htmlOfPropertiesAndBehaviorsSubTokens +=
'<div class="propertiesAndBehaviorsSubToken" style="margin: 20px 0 20px 0;">' +
'<label>Please choose properties and behaviors for your sub token #' + i + '</label>' +
getHtmlPropertiesAndBehaviorsForToken(i) +
'</div>';
}
$('#propertiesAndBehaviorsSubTokens')[0].innerHTML = htmlOfPropertiesAndBehaviorsSubTokens;
}
function removePropertiesAndBehaviorsForSubToken() {
$('#propertiesAndBehaviorsSubTokens')[0].innerHTML = '';
}
function validateCustomDetails() {
const isAddCustomDetailsSelected = $('input[name=addCustomDetails]:checked').val() === 'true';
const value = $('#customDetails')[0].value;
let isValid = true;
if (isAddCustomDetailsSelected) {
if (value) {
value.split(',').forEach(item => {
if (!isSymbols(item)) {
isValid = false;
}
});
isValid
? $('#customDetailsValidationTip')[0].style.display = 'none'
: $('#customDetailsValidationTip')[0].style.display = 'block';
return isValid;
}
$('#customDetailsValidationTip')[0].style.display = 'block'
return !isValid;
}
$('#customDetailsValidationTip')[0].style.display = 'none';
return isValid;
}
function getHtmlPropertiesAndBehaviorsForToken(index = '') {
return `<div class="paragraph">
<div class="paragraph">
<label>Please indicate the base type for your token.</label><br>
<div>
<label><input type="radio" name="baseTokenType${index}" value="true">My tokens have interchangable value (fungible)</label>
<label><input type="radio" name="baseTokenType${index}" value="false">Every token is unique (non-fungible)</label>
</div>
</div>
<div class="paragraph">
<label>Can you create additional tokens?</label><br>
<div>
<label><input type="radio" name="additionalTokens${index}" value="true">Yes(Mint)</label>
<label><input type="radio" name="additionalTokens${index}" value="false">No</label>
</div>
</div>
<div class="paragraph">
<label>Can you destroy tokens?</label><br>
<div>
<label><input type="radio" name="destroyTokens${index}" value="true">Yes(Burn)</label>
<label><input type="radio" name="destroyTokens${index}" value="false">No</label>
</div>
</div>
<div class="paragraph">
<label>Can you transfer ownership of the tokens?</label><br>
<div>
<label><input type="radio" name="transferOwnershipTokens${index}" value="true">Yes(Transferable)</label>
<label><input type="radio" name="transferOwnershipTokens${index}" value="false">No</label>
</div>
</div>
<div class="paragraph">
<label>Can you delegate interactions with the token to others?</label><br>
<div>
<label><input type="radio" class="delegateInteractions" name="delegateInteractions${index}" value="true">Yes(Delegate)</label>
<label><input type="radio" class="delegateInteractions" name="delegateInteractions${index}" value="false">No</label>
</div>
</div>
</div>`;
}
function isAlphaNumeric(str) {
var exp = /^[0-9a-zA-Z]{1,255}$/g;
return str && str.match(exp);
}
function isSymbols(str) {
var exp = /^[a-zA-Z]+$/g;
return str && str.match(exp);
}
function validateTokenName() {
const value = $('#tokenName').val();
if (!value) {
$('#tokenNameValidationTip')[0].style.display = 'block';
return false;
}
const flag = isAlphaNumeric(value);
flag
? $('#tokenNameValidationTip')[0].style.display = 'none'
: $('#tokenNameValidationTip')[0].style.display = 'block';
return flag;
}
function validateTokenForm() {
const isValidTokenName = validateTokenName();
const isValidCustomDetails = validateCustomDetails();
const isRequiredFieldsFilled = isAllRequiredFieldsFilled();
return isValidTokenName && isValidCustomDetails && isRequiredFieldsFilled;
}
}

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

@ -120,7 +120,7 @@
<a href="https://openzeppelin.com/">
<div class="specific-partner">
<div><img class="small-image-logo" src="{{root}}/images/OpenZLogo.png"></div>
<div>Open Zeppelin</div>
<div>OpenZeppelin</div>
</div>
</a>
</div>

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

@ -38,6 +38,12 @@ export class Constants {
treeManager: 'Service Tree Manager',
};
public static truffleConfigRequireNames = {
fs: 'fs',
fsPackageName: 'fs',
hdwalletProvider: 'HDWalletProvider',
};
public static defaultTruffleBox = 'Azure-Samples/Blockchain-Ethereum-Template';
public static defaultDebounceTimeout = 300;
@ -65,8 +71,8 @@ export class Constants {
},
[RequiredApps.git]: '2.10.0',
[RequiredApps.hdwalletProvider]: {
max: '1.0.7',
min: '1.0.6',
max: '2.0.0',
min: '1.0.17',
},
[RequiredApps.node]: '10.15.0',
[RequiredApps.npm]: '6.4.1',
@ -109,6 +115,12 @@ export class Constants {
title: 'Smart Contract UI',
viewType: 'contractUIPage',
},
generateToken: {
path: '',
showOnStartup: 'showOnStartupGenerateToken',
title: 'Generate Token',
viewType: 'generateTokenPage',
},
requirements: {
path: '',
showOnStartup: 'showOnStartupRequirementsPage',
@ -138,6 +150,7 @@ export class Constants {
public static contractProperties = {
abi: 'abi',
bytecode: 'bytecode',
deployedBytecode: 'deployedBytecode',
};
public static propertyLabels = {
@ -449,16 +462,20 @@ export class Constants {
consortiumNameValidating: 'Consortium name validating...',
contractNotDeployed: 'Contract not deployed yet.',
deployButton: 'Deploy',
deployFailed: 'Deploy failed',
deploySucceeded: 'Deploy succeeded',
detailsButton: 'Details',
generatedLogicApp: 'Generated the logic app!',
infuraAccountSuccessfullyCreated: 'Your Infura account successfully created. Please check you email for complete registration',
infuraSignInPrompt: 'Not signed in to Infura account, sign in first.',
installButton: 'Install',
invalidRequiredVersion: 'Required app is not installed or has an old version.',
memberNameValidating: 'Member name validating...',
newProjectCreationFinished: 'New project was created successfully',
newProjectCreationStarted: 'New project creation is started',
openButton: 'Open',
privateKeyWasCopiedToClipboard: 'Private key was copied to clipboard',
requiresDependency: 'This project deployment requires the truffle-hdwallet-provider.',
rpcEndpointCopiedToClipboard: 'RPCEndpointAddress copied to clipboard',
seeDetailsRequirementsPage: 'Please see details on the Requirements Page',
signInButton: 'Sign In',
@ -574,6 +591,8 @@ export class Constants {
this.webViewPages.contractUI.path = context.asAbsolutePath(path.join('resources', 'drizzle', 'index.html'));
this.webViewPages.welcome.path = context.asAbsolutePath(path.join('resources', 'welcome', 'index.html'));
this.webViewPages.requirements.path = context.asAbsolutePath(path.join('resources', 'welcome', 'prereqs.html'));
this.webViewPages.generateToken.path = context.asAbsolutePath(
path.join('resources', 'tokenui', 'generateToken.html'));
this.infuraFileResponse.path = context.asAbsolutePath(path.join('resources', 'codeFlowResult', 'index.html'));
this.infuraFileResponse.css = context.asAbsolutePath(path.join('resources', 'codeFlowResult', 'main.css'));

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

@ -2657,7 +2657,7 @@ $d.define(Nethereum.Generators.DataWorkflow.LogicApps.DataWorkflowFunctionOutput
return null;
};
$p.GetConnectionSection = function DataWorkflowFunctionOutputLogicAppTemplate_GetConnectionSection(subsciptionId) {
return String.Format(" \"$connections\": {{\r\n \"value\": {{\r\n \"blockchainethereum\": {{\r\n \"connectionId\": \"/subscriptions/{0}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{1}/providers/Microsoft.Web/locations/{2}/managedApis/blockchainethereum\"\r\n }},\r\n \"sql\": {{\r\n \"connectionId\": \"/subscriptions/{3}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{4}/providers/Microsoft.Web/locations/{5}/managedApis/sql\"\r\n }}\r\n }}\r\n }},",
return String.Format(" \"parameters\": {{\r\n \"$connections\": {{\r\n \"value\": {{\r\n \"blockchainethereum\": {{\r\n \"connectionId\": \"/subscriptions/{0}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{1}/providers/Microsoft.Web/locations/{2}/managedApis/blockchainethereum\"\r\n }},\r\n \"sql\": {{\r\n \"connectionId\": \"/subscriptions/{3}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{4}/providers/Microsoft.Web/locations/{5}/managedApis/sql\"\r\n }}\r\n }}\r\n }}\r\n }},",
[subsciptionId, subsciptionId, this._location, subsciptionId, subsciptionId, this._location]);
};
$p.GetLogicAppStart = function DataWorkflowFunctionOutputLogicAppTemplate_GetLogicAppStart() {
@ -3552,7 +3552,7 @@ $d.define(Nethereum.Generators.MessagingWorkflow.LogicApps.MessagingWorkflowEven
return null;
};
$p.GetConnectionSection = function MessagingWorkflowEventLogicAppTemplate_GetConnectionSection(subscriptionId) {
return String.Format(" \"$connections\": {{\r\n \"value\": {{\r\n \"blockchainethereum\": {{\r\n \"connectionId\": \"/subscriptions/{0}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{1}/providers/Microsoft.Web/locations/{2}/managedApis/blockchainethereum\"\r\n }},\r\n {3}\r\n }}\r\n }},",
return String.Format(" \"parameters\": {{\r\n \"$connections\": {{\r\n \"value\": {{\r\n \"blockchainethereum\": {{\r\n \"connectionId\": \"/subscriptions/{0}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{1}/providers/Microsoft.Web/locations/{2}/managedApis/blockchainethereum\"\r\n }},\r\n {3}\r\n }}\r\n }}\r\n }},",
[subscriptionId, subscriptionId, this._location, this.GetConnection(subscriptionId)]);
};
$p.GetConnection = function MessagingWorkflowEventLogicAppTemplate_GetConnection(subscriptionId) {
@ -3594,10 +3594,12 @@ $d.define(Nethereum.Generators.MessagingWorkflow.LogicApps.MessagingWorkflowEven
var beginActions = String.Format("\"actions\": {{\r\n \"Publish_Event\": {{\r\n \"inputs\": {{\r\n \"body\": [\r\n {{\r\n \"data\": \"@triggerBody()\",\r\n \"eventTime\": \"@{{utcNow()}}\",\r\n \"eventType\": \"{0}\",\r\n \"id\": \"@{{guid()}}\",\r\n \"subject\": \"{1}\"\r\n }}\r\n ]",
[this.get_Model().get_EventABI().get_Name(), this._topicName]);
var paths = "/eventGrid/api/events";
var eventProperties = String.Concat(System.Linq.Enumerable.Select(Nethereum.Generators.Model.ParameterABI,
String, this.get_Model().get_EventABI().get_InputParameters(), $d.delegate(function(p) {
return "'\\\"" + p.get_Name() + "\\\": ',triggerBody()?['" + p.get_Name() + "'],',','\\n',";
})));
var endActions = String.Format(",\r\n \"host\": {{\r\n \"connection\": {{\r\n \"name\": \"@parameters('$connections')['azureeventgridpublish']['connectionId']\"\r\n }}\r\n }},\r\n \"method\": \"post\",\r\n \"path\": \"{0}\"\r\n }},\r\n \"runAfter\": {{}},\r\n \"type\": \"ApiConnection\"\r\n }}\r\n }}",
[paths]);
var endActions = ",\r\n \"host\": {\r\n \"connection\": {\r\n \"name\": \"@parameters('$connections')['azureeventgridpublish']['connectionId']\"\r\n }\r\n },\r\n \"method\": \"post\",\r\n \"path\": \"/eventGrid/api/events\"\r\n },\r\n \"runAfter\": {},\r\n \"type\": \"ApiConnection\"\r\n }\r\n }";
return String.Concat$5(beginActions, endActions);
};
$p.GetServiceBusActions = function MessagingWorkflowEventLogicAppTemplate_GetServiceBusActions() {
@ -4073,7 +4075,7 @@ $d.define(Nethereum.Generators.ReportingWorkflow.LogicApps.ReportingWorkflowEven
return null;
};
$p.GetConnectionSection = function ReportingWorkflowEventsOutputLogicAppTemplate_GetConnectionSection(subsciptionId) {
return String.Format(" \"$connections\": {{\r\n \"value\": {{\r\n \"blockchainethereum\": {{\r\n \"connectionId\": \"/subscriptions/{0}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{1}/providers/Microsoft.Web/locations/{2}/managedApis/blockchainethereum\"\r\n }},\r\n \"powerbi\": {{\r\n \"connectionId\": \"/subscriptions/{3}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{4}/providers/Microsoft.Web/locations/{5}/managedApis/powerbi\"\r\n }}\r\n }}\r\n }},",
return String.Format(" \"parameters\": {{\r\n \"$connections\": {{\r\n \"value\": {{\r\n \"blockchainethereum\": {{\r\n \"connectionId\": \"/subscriptions/{0}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{1}/providers/Microsoft.Web/locations/{2}/managedApis/blockchainethereum\"\r\n }},\r\n \"powerbi\": {{\r\n \"connectionId\": \"/subscriptions/{3}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{4}/providers/Microsoft.Web/locations/{5}/managedApis/powerbi\"\r\n }}\r\n }}\r\n }}\r\n }},",
[subsciptionId, subsciptionId, this._location, subsciptionId, subsciptionId, this._location]);
};
$p.GetLogicAppStart = function ReportingWorkflowEventsOutputLogicAppTemplate_GetLogicAppStart() {
@ -5040,7 +5042,7 @@ $d.define(Nethereum.Generators.ServiceWorkflow.FlowApps.ServiceWorkflowFunctionO
};
$p.GetExecuteFunctionActionForLogicApp = function ServiceWorkflowFunctionOutputFlowAppTemplate_GetExecuteFunctionActionForLogicApp() {
return " \"actions\": {\r\n \"Call smart contract function\": {\r\n \"runAfter\": { },\r\n \"type\": \"ApiConnection\",\r\n \"inputs\": {\r\n \"body\": {\r\n" + this.GetEthereumConnectorParameters() + "\r\n },\r\n \"host\": {\r\n \"connection\": {\r\n \"name\": \"@parameters('$connections')['shared_blockchainethereum']['connectionId']\"\r\n }\r\n },\r\n \"method\": \"post\",\r\n \"path\": \"/contract/functions/@{encodeURIComponent(encodeURIComponent('" + this.get_Model().get_FunctionABI().get_Name() + "'))}/" + (this.get_Model().get_FunctionABI().get_Constant() ? "query" : "execute") + "\",\r\n \"queries\": {\r\n \"abi\": \"" + this._escapedAbi + "\",\r\n\r\n \"contractAddress\": \"" + this._contractAddress + "\"\r\n }\r\n }\r\n },";
return " \"actions\": {\r\n \"Call smart contract function\": {\r\n \"runAfter\": { },\r\n \"type\": \"ApiConnection\",\r\n \"inputs\": {\r\n \"body\": {\r\n" + this.GetEthereumConnectorParameters() + "\r\n },\r\n \"host\": { \"parameters\": {{\r\n \"connection\": {\r\n \"name\": \"@parameters('$connections')['shared_blockchainethereum']['connectionId']\"\r\n }\r\n }\r\n },\r\n \"method\": \"post\",\r\n \"path\": \"/contract/functions/@{encodeURIComponent(encodeURIComponent('" + this.get_Model().get_FunctionABI().get_Name() + "'))}/" + (this.get_Model().get_FunctionABI().get_Constant() ? "query" : "execute") + "\",\r\n \"queries\": {\r\n \"abi\": \"" + this._escapedAbi + "\",\r\n\r\n \"contractAddress\": \"" + this._contractAddress + "\"\r\n }\r\n }\r\n },";
};
$p.GetEthereumConnectorParameters = function ServiceWorkflowFunctionOutputFlowAppTemplate_GetEthereumConnectorParameters() {
return String.Join$1(String, ",\r\n", System.Linq.Enumerable.Select(Nethereum.Generators.Model.ParameterABI,
@ -5436,7 +5438,7 @@ $d.define(Nethereum.Generators.ServiceWorkflow.LogicApps.ServiceWorkflowFunction
return null;
};
$p.GetConnections = function ServiceWorkflowFunctionOutputLogicAppTemplate_GetConnections() {
return String.Format("{{ \"$connections\": {{\r\n \"value\": {{\r\n \"blockchainethereum\": {{\r\n \"connectionId\": \"/subscriptions/{0}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{1}/providers/Microsoft.Web/locations/{2}/managedApis/blockchainethereum\"\r\n }}\r\n }}\r\n }},",
return String.Format("{{ \"parameters\": {{\r\n \"$connections\": {{\r\n \"value\": {{\r\n \"blockchainethereum\": {{\r\n \"connectionId\": \"/subscriptions/{0}/resourceGroups/\",\r\n \"connectionName\": \"\",\r\n \"id\": \"/subscriptions/{1}/providers/Microsoft.Web/locations/{2}/managedApis/blockchainethereum\"\r\n }}\r\n }}\r\n }}\r\n }},",
[this._subsciptionId, this._subsciptionId, this._location]);
};
$p.GetLogicAppStart = function ServiceWorkflowFunctionOutputLogicAppTemplate_GetLogicAppStart() {

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Constants } from '../../Constants';
import { Constants, RequiredApps } from '../../Constants';
import { saveTextInFile, showInputBox, showQuickPick, TruffleConfiguration } from '../../helpers';
import { MnemonicRepository } from '../../services/MnemonicRepository'; // Should be full path since cycle dependencies
import { NetworkNode } from './NetworkNode';
@ -14,7 +14,9 @@ export abstract class MnemonicNetworkNode extends NetworkNode {
const targetURL = await this.getRPCAddress();
const mnemonic = await this.getMnemonic();
await config.importPackage('fs', 'fs');
const { fs, fsPackageName, hdwalletProvider } = Constants.truffleConfigRequireNames;
await config.importPackage(fs, fsPackageName);
await config.importPackage(hdwalletProvider, RequiredApps.hdwalletProvider);
network.options.provider = {
mnemonic: mnemonic.path,

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

@ -4,6 +4,7 @@
import { commands, ExtensionContext, Uri, window } from 'vscode';
import { Constants } from '../Constants';
import { CancellationEvent } from '../Models';
import { GenerateToken } from '../pages';
import { ContractUI } from '../pages/ContractUI';
import { ContractDB, ContractService } from '../services';
import { Telemetry } from '../TelemetryClient';
@ -47,4 +48,13 @@ export namespace ContractCommands {
await contractPage.show();
Telemetry.sendEvent('ContractCommands.showSmartContract.commandFinished');
}
export async function generateToken(context: ExtensionContext): Promise<void> {
Telemetry.sendEvent('ContractCommands.generateToken.commandStarted');
const contractPage = new GenerateToken(context);
await contractPage.show();
Telemetry.sendEvent('ContractCommands.generateToken.commandFinished');
}
}

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

@ -26,15 +26,15 @@ export namespace OpenZeppelinCommands {
Constants.outputChannel.azureBlockchain,
Constants.openZeppelin.categoryWillDownloaded(category.name),
);
const assetStatuses = OpenZeppelinService.getAssetsStatus(fullAssetWithDependencies);
const assetsStatuses = OpenZeppelinService.getAssetsStatus(fullAssetWithDependencies);
Output.outputLine(
Constants.outputChannel.azureBlockchain,
Constants.openZeppelin.fileNow(assetStatuses.existing.length),
Constants.openZeppelin.fileNow(assetsStatuses.existing.length),
);
if (assetStatuses.existing.length > 0) {
if (assetsStatuses.existing.length > 0) {
const answer = await window.showInformationMessage(
Constants.openZeppelin.alreadyExisted(assetStatuses.existing),
Constants.openZeppelin.alreadyExisted(assetsStatuses.existing),
Constants.openZeppelin.replaceButtonTitle,
Constants.openZeppelin.skipButtonTitle,
);
@ -47,7 +47,7 @@ export namespace OpenZeppelinCommands {
);
await downloadFileSetWithProgress(fullAssetWithDependencies, true);
} else {
await downloadFileSetWithProgress(assetStatuses.missing, false);
await downloadFileSetWithProgress(assetsStatuses.missing, false);
}
} else {
await downloadFileSetWithProgress(fullAssetWithDependencies, false);
@ -119,13 +119,17 @@ async function selectCategory(categories: IOZContractCategory[]): Promise<IOZCon
}
async function openDocumentationUrl(category: IOZContractCategory): Promise<void> {
const documentationUrl = OpenZeppelinService.getCategoryApiDocumentationUrl(category);
if (!documentationUrl) {
return;
}
const answer = await window.showInformationMessage(
Constants.openZeppelin.exploreDownloadedContractsInfo,
Constants.openZeppelin.moreDetailsButtonTitle,
Constants.openZeppelin.cancelButtonTitle);
if (answer === Constants.openZeppelin.moreDetailsButtonTitle) {
const documentationUrl = OpenZeppelinService.getCategoryApiDocumentationUrl(category);
open(documentationUrl);
}
}

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

@ -14,7 +14,6 @@ import {
TruffleConfiguration,
} from '../helpers';
import { CancellationEvent } from '../Models';
import { Output } from '../Output';
import { Telemetry } from '../TelemetryClient';
interface IProjectDestination {
@ -96,7 +95,6 @@ async function createProjectFromTruffleBox(projectPath: string): Promise<void> {
async function createProject(projectPath: string, truffleBoxName: string): Promise<void> {
try {
Output.show();
Telemetry.sendEvent('ProjectCommands.createProject.unbox', { truffleBoxName });
await outputCommandHelper.executeCommand(projectPath, 'npx', RequiredApps.truffle, 'unbox', truffleBoxName);

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

@ -56,7 +56,6 @@ export namespace TruffleCommands {
await required.installTruffle(required.Scope.locally);
}
Output.show();
await outputCommandHelper.executeCommand(getWorkspaceRoot(), 'npx', RequiredApps.truffle, 'compile');
});
Telemetry.sendEvent('TruffleCommands.buildContracts.commandFinished');
@ -90,18 +89,6 @@ export namespace TruffleCommands {
{ url: Telemetry.obfuscate(command.description || '') },
);
// this code should be below showQuickPick because it takes time and it affects on responsiveness
if (!await required.checkAppsSilent(RequiredApps.truffle)) {
Telemetry.sendEvent('TruffleCommands.deployContracts.installTruffle');
await required.installTruffle(required.Scope.locally);
}
if (await required.isHdWalletProviderRequired()
&& !(await required.checkHdWalletProviderVersion())) {
Telemetry.sendEvent('TruffleCommands.deployContracts.installTruffleHdWalletProvider');
await required.installTruffleHdWalletProvider();
}
await validateOpenZeppelinContracts();
await command.cmd();
@ -125,6 +112,14 @@ export namespace TruffleCommands {
Telemetry.sendEvent('TruffleCommands.writeBytecodeToBuffer.commandFinished');
}
export async function writeDeployedBytecodeToBuffer(uri: Uri): Promise<void> {
Telemetry.sendEvent('TruffleCommands.writeBytecodeToBuffer.commandStarted');
const contract = await readCompiledContract(uri);
await vscodeEnvironment.writeToClipboard(contract[Constants.contractProperties.deployedBytecode]);
Telemetry.sendEvent('TruffleCommands.writeBytecodeToBuffer.commandFinished');
}
export async function writeRPCEndpointAddressToBuffer(projectView: ProjectView): Promise<void> {
Telemetry.sendEvent('TruffleCommands.writeRPCEndpointAddressToBuffer.commandStarted');
const rpcEndpointAddress = await projectView.extensionItem.getRPCAddress();
@ -190,6 +185,28 @@ function removeDuplicateNetworks(deployDestinations: IDeployDestinationItem[]):
});
}
async function installRequiredDependencies(): Promise<void> {
if (!await required.checkAppsSilent(RequiredApps.truffle)) {
Telemetry.sendEvent('TruffleCommands.installRequiredDependencies.installTruffle');
await required.installTruffle(required.Scope.locally);
}
if (await required.isHdWalletProviderRequired() && !(await required.checkHdWalletProviderVersion())) {
if (!await required.isDefaultProject()) {
const { cancelButton, installButton, requiresDependency } = Constants.informationMessage;
const answer = await window.showInformationMessage(requiresDependency, installButton, cancelButton);
if (answer !== installButton) {
return;
}
}
Telemetry.sendEvent('TruffleCommands.installRequiredDependencies.installTruffleHdWalletProvider');
await required.installTruffleHdWalletProvider();
}
}
async function validateOpenZeppelinContracts(): Promise<void> {
const validatedContracts = await OpenZeppelinService.validateContracts();
validatedContracts.forEach((ozContract: OZContractValidated) => {
@ -386,11 +403,19 @@ async function deployToNetwork(networkName: string, truffleConfigPath: string):
const workspaceRoot = path.dirname(truffleConfigPath);
await fs.ensureDir(workspaceRoot);
await outputCommandHelper.executeCommand(
workspaceRoot,
'npx',
RequiredApps.truffle, 'migrate', '--reset', '--network', networkName,
);
try {
await installRequiredDependencies();
await outputCommandHelper.executeCommand(
workspaceRoot,
'npx',
RequiredApps.truffle, 'migrate', '--reset', '--network', networkName,
);
Output.outputLine(Constants.outputChannel.azureBlockchain, Constants.informationMessage.deploySucceeded);
} catch (error) {
Output.outputLine(Constants.outputChannel.azureBlockchain, Constants.informationMessage.deployFailed);
throw error;
}
await ContractDB.updateContracts();
});

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

@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as truffleDebugUtils from '@truffle/debug-utils';
import * as truffleDebugger from '@truffle/debugger';
import { EventEmitter } from 'events';
import { relative as pathRelative } from 'path';
import * as truffleDebugUtils from 'truffle-debug-utils';
import * as truffleDebugger from 'truffle-debugger';
import {
filterContractsWithAddress,
prepareContracts,

8
src/debugAdapter/types/@truffle/debug-utils.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
declare module '@truffle/debug-utils' {
function nativize(variables: any): any;
export { nativize };
}

31
src/debugAdapter/types/@truffle/debugger.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
declare module '@truffle/debugger' {
interface Selectors {
solidity: any,
evm: any,
controller: any,
trace: any,
}
const selectors: Selectors;
interface Session {
removeAllBreakpoints: () => Promise<void>,
view: (selectors: any) => any,
addBreakpoint: (breakPoint: any) => {},
variables: () => Promise<any>,
continueUntilBreakpoint: () => Promise<void>,
stepNext: () => Promise<void>,
stepInto: () => Promise<void>,
stepOut: () => Promise<void>,
}
interface Debugger {
connect: () => Session,
}
function forTx(txHash: string, debugOptions: any): Promise<Debugger>;
export { selectors, Selectors, forTx, Session };
}

13
src/debugAdapter/types/@truffle/provider.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
declare module '@truffle/provider' {
interface IProviderOptions {
provider?: any,
host?: string,
port?: number,
websockets?: boolean,
}
function create(networkOptions: IProviderOptions): any;
export { create };
}

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

@ -1,4 +1,7 @@
import * as truffleProvider from 'truffle-provider';
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as truffleProvider from '@truffle/provider';
import * as web3 from 'web3';
import { ConfigurationReader } from './configurationReader';

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

@ -87,6 +87,9 @@ export async function activate(context: ExtensionContext) {
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));
});
@ -132,6 +135,11 @@ export async function activate(context: ExtensionContext) {
async (contractPath: Uri) => {
await tryExecute(() => ContractCommands.showSmartContractPage(context, contractPath));
});
const generateToken = commands.registerCommand(
'azureBlockchainService.generateToken',
async () => {
await tryExecute(() => ContractCommands.generateToken(context));
});
//#endregion
//#region open zeppelin commands
@ -173,6 +181,7 @@ export async function activate(context: ExtensionContext) {
showWelcomePage,
showRequirementsPage,
showSmartContractPage,
generateToken,
refresh,
newSolidityProject,
buildContracts,
@ -181,6 +190,7 @@ export async function activate(context: ExtensionContext) {
connectProject,
disconnectProject,
copyByteCode,
copyDeployedByteCode,
copyABI,
copyRPCEndpointAddress,
startGanacheServer,

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

@ -37,7 +37,6 @@ export async function executeCommand(workingDirectory: string | undefined, comma
);
if (result.code !== 0) {
Output.show();
Telemetry.sendException(new Error('commands.executeCommand.resultWithIncorrectCode'));
throw new Error(Constants.executeCommandMessage.failedToRunCommand(commands.concat(' ', ...args.join(' '))));
}

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

@ -4,7 +4,7 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import * as semver from 'semver';
import { commands, window } from 'vscode';
import { commands, ProgressLocation, window } from 'vscode';
import { Constants, RequiredApps } from '../Constants';
import { Output } from '../Output';
import { Telemetry } from '../TelemetryClient';
@ -252,22 +252,32 @@ export namespace required {
);
const truffleConfigPath = await TruffleConfiguration.getTruffleConfigUri();
const config = new TruffleConfiguration.TruffleConfig(truffleConfigPath);
await config.importPackage('HDWalletProvider', RequiredApps.hdwalletProvider);
await config.importPackage(Constants.truffleConfigRequireNames.hdwalletProvider, RequiredApps.hdwalletProvider);
} catch (error) {
Telemetry.sendException(error);
}
}
export async function isHdWalletProviderRequired(): Promise<boolean> {
try {
const truffleConfigPath = TruffleConfiguration.getTruffleConfigUri();
const config = new TruffleConfiguration.TruffleConfig(truffleConfigPath);
return config.isHdWalletProviderDeclared();
} catch (error) {
Telemetry.sendException(error);
Output.outputLine(Constants.outputChannel.requirements, error.message);
}
return false;
}
export async function isDefaultProject(): Promise<boolean> {
try {
// File might not exist in some truffle-box
const data = await fs.readFile(path.join(getWorkspaceRoot()!, 'package.json'), 'utf-8');
const packagesData = JSON.parse(data);
if (packagesData.name === 'blockchain-ethereum-template') {
const truffleConfigPath = TruffleConfiguration.getTruffleConfigUri();
const config = new TruffleConfiguration.TruffleConfig(truffleConfigPath);
return config.isHdWalletProviderDeclared();
}
return packagesData.name === 'blockchain-ethereum-template';
} catch (error) {
Telemetry.sendException(error);
Output.outputLine(Constants.outputChannel.requirements, error.message);
@ -299,8 +309,6 @@ export namespace required {
packageVersion: string | { min: string, max: string },
scope?: Scope,
): Promise<void> {
Output.show();
const versionString = typeof packageVersion === 'string' ?
`^${packageVersion}` :
`>=${packageVersion.min} <${packageVersion.max}`;
@ -313,7 +321,12 @@ export namespace required {
throw error;
}
await executeCommand(workspaceRoot, 'npm', 'i', scope ? '' : '-g', ` ${packageName}@"${versionString}"`);
await window.withProgress({
location: ProgressLocation.Window,
title: `Installing ${packageName}`,
}, async () => {
await executeCommand(workspaceRoot, 'npm', 'i', scope ? '' : '-g', ` ${packageName}@"${versionString}"`);
});
}
async function getVersion(program: string, command: string, matcher: RegExp): Promise<string> {

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

@ -11,6 +11,7 @@ import * as ESTree from 'estree';
import * as fs from 'fs-extra';
import * as path from 'path';
import { Constants } from '../Constants';
import { MnemonicRepository } from '../services';
import { Telemetry } from '../TelemetryClient';
import { getWorkspaceRoot } from './workspace';
@ -246,12 +247,18 @@ export namespace TruffleConfiguration {
}
function isHdWalletProviderDeclaration(nodeType: string, node: ESTree.Node): boolean {
if (nodeType !== 'NewExpression') {
return false;
if (nodeType === 'NewExpression') {
node = node as ESTree.NewExpression;
node = node.callee as ESTree.Identifier;
return node.name === Constants.truffleConfigRequireNames.hdwalletProvider;
}
node = node as ESTree.NewExpression;
node = node.callee as ESTree.Identifier;
return node.name === 'HDWalletProvider';
if (nodeType === 'VariablePattern') {
node = node as ESTree.Identifier;
return node.name === Constants.truffleConfigRequireNames.hdwalletProvider;
}
return false;
}
function getModuleExportsObjectExpression(ast: ESTree.Node): ESTree.ObjectExpression | void {
@ -301,7 +308,8 @@ export namespace TruffleConfiguration {
function isHDWalletProvider(nodeType: string, node: ESTree.Node): boolean {
if (nodeType === 'NewExpression') {
node = node as ESTree.NewExpression;
if (node.callee.type === 'Identifier' && node.callee.name === 'HDWalletProvider') {
if (node.callee.type === 'Identifier'
&& node.callee.name === Constants.truffleConfigRequireNames.hdwalletProvider) {
return true;
}
}
@ -448,14 +456,22 @@ export namespace TruffleConfiguration {
return obj;
}
function isMnemonicNode(node: ESTree.Literal | ESTree.NewExpression): boolean {
return node && node.type === 'Literal' && typeof node.value === 'string';
}
function astToHDWalletProvider(node: ESTree.NewExpression): IProvider {
const provider: IProvider = {
raw: generate(node),
};
const mnemonicNode = node.arguments[0];
if (mnemonicNode && mnemonicNode.type === 'Literal') {
provider.mnemonic = '' + mnemonicNode.value;
const mnemonicNode = node.arguments[0] as ESTree.NewExpression & ESTree.Literal;
const mnemonicFilePathNode = mnemonicNode && mnemonicNode.arguments && mnemonicNode.arguments[0] as ESTree.Literal;
if (isMnemonicNode(mnemonicNode)) {
provider.mnemonic = mnemonicNode.value as string;
} else if (isMnemonicNode(mnemonicFilePathNode)) {
provider.mnemonic = MnemonicRepository.getMnemonic(mnemonicFilePathNode.value as string);
}
const urlNode = node.arguments[1];
@ -477,7 +493,7 @@ export namespace TruffleConfiguration {
generateLiteral(provider.url || ''),
],
callee: {
name: 'HDWalletProvider',
name: Constants.truffleConfigRequireNames.hdwalletProvider,
type: 'Identifier',
},
type: 'NewExpression',

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

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { ExtensionContext } from 'vscode';
import { Constants } from '../Constants';
import { BasicWebView, IWebViewConfig } from './BasicWebView';
export class GenerateToken extends BasicWebView {
protected readonly config: IWebViewConfig;
constructor(context: ExtensionContext) {
super(context);
this.config = Object.assign({}, Constants.webViewPages.generateToken);
}
protected async setShowOnStartupFlagAtFirstTime(): Promise<boolean> {
return false;
}
protected async receiveMessage(message: {[key: string]: any}): Promise<void> {
await super.receiveMessage(message);
if (!this.panel) {
return;
}
if (message.command === 'tokenExpression') {
// TODO: change this to process tokenExpression
// @ts-ignore
const tokenExpression = message.value;
}
}
}

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

@ -1,10 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { GenerateToken } from './GenerateToken';
import { RequirementsPage } from './Requirements';
import { WelcomePage } from './Welcome';
export {
GenerateToken,
RequirementsPage,
WelcomePage,
};

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

@ -3,14 +3,10 @@
import * as requestPromise from 'request-promise';
import { Constants } from '../Constants';
import { Output } from '../Output';
import { Telemetry } from '../TelemetryClient';
export namespace HttpService {
export async function sendRPCRequest(
host: string,
methodName: string,
): Promise<{ result?: any } | undefined> {
export async function sendRPCRequest(host: string, methodName: string): Promise<{ result?: any } | undefined> {
const address = hasProtocol(host) ? host : `${Constants.networkProtocols.http}${host}`;
return requestPromise.post(
address,
@ -23,15 +19,7 @@ export namespace HttpService {
},
json: true,
})
.then((result) => {
const message = `HttpService.sendRPCRequest has done with result: ${result}`;
Output.outputLine(Constants.outputChannel.azureBlockchain, message);
return result;
})
.catch((errorMessage) => {
const message = `HttpService.sendRPCRequest has done with error: ${errorMessage}`;
Output.outputLine(Constants.outputChannel.azureBlockchain, message);
.catch((_errorMessage) => {
Telemetry.sendException(new Error(`HttpService.sendRPCRequest has done with error for method: ${methodName}`));
return undefined;

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

@ -1,4 +1,4 @@
import * as fs from 'fs';
import * as fs from 'fs-extra';
import { Memento } from 'vscode';
import { Constants } from '../Constants';

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

@ -72,7 +72,7 @@ export namespace NetworkService {
return {
network: { id: networkId || '*', name: truffleNetwork.name },
provider: host ? { host } : null,
provider: host ? { host, options: { mnemonic: options.provider && options.provider.mnemonic } } : null,
};
}

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

@ -14,6 +14,7 @@ export interface ProviderOptions {
timeout?: number;
headers?: HttpHeader[];
withCredentials?: boolean;
mnemonic?: string;
}
// tslint:disable-next-line:interface-name

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

@ -92,7 +92,6 @@ export namespace GanacheService {
addAllListeners(output, port, process);
await waitGanacheStarted(port, Constants.ganacheRetryAttempts);
ganacheProcess.pid = await findPid(port);
output.show();
} catch (error) {
Telemetry.sendException(error);
await stopGanacheProcess(ganacheProcess, true);

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

@ -28,7 +28,7 @@ export namespace OpenZeppelinMigrationsService {
'};',
].join('\n');
saveMigrationContent(migrationContent);
return saveMigrationContent(migrationContent);
}
}
}
@ -62,16 +62,17 @@ function generateLibrariesDeployerSection(items: IOZAsset[]): string {
async function generateLinkingSection(items: IOZAsset[]): Promise<string[]> {
const librariesLinkingSection: string[] = [];
items
.filter((asset: IOZAsset) => asset.type === OZAssetType.contract)
.forEach(async (asset: IOZAsset) => {
librariesLinkingSection.push(
...contractToLibraryLinkingSection(
asset,
await OpenZeppelinService.getReferencesToLibraries(asset),
),
);
});
const contracts = items.filter((asset) => asset.type === OZAssetType.contract);
for (const contract of contracts) {
librariesLinkingSection.push(
...contractToLibraryLinkingSection(
contract,
await OpenZeppelinService.getReferencesToLibraries(contract),
),
);
}
return librariesLinkingSection;
}
@ -88,16 +89,16 @@ async function saveMigrationContent(content: string): Promise<void> {
const truffleConfig = new TruffleConfiguration.TruffleConfig(truffleConfigPath);
const configuration = truffleConfig.getConfiguration();
const migrationFilePath = configuration.migrations_directory;
const filePath = path.join(
getWorkspaceRoot()!,
migrationFilePath,
migrationFilename,
);
Output.outputLine(
Constants.outputChannel.azureBlockchain,
`New migration for OpenZeppelin contracts was stored to file ${migrationFilename}`,
);
const filePath = path.join(
getWorkspaceRoot()!,
migrationFilePath,
migrationFilename,
);
fs.writeFileSync(filePath, content, { encoding: 'utf8' });
return fs.writeFile(filePath, content, { encoding: 'utf8' });
}

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

@ -14,6 +14,7 @@ import * as _metadata from './manifest.json';
const metadata = _metadata as IOZMetadata;
const projectFileName: string = 'project.json';
const categoryWithoutDocumentation = 'mocks';
export interface IOZMetadata {
contentVersion: string;
@ -98,8 +99,8 @@ export namespace OpenZeppelinService {
return result;
}
contractDependencies.dependencies.forEach(async (id: string) => {
const dependency = metadata.assets.find((asset: IOZAsset) => asset.id === id)!;
for (const dependencyId of contractDependencies.dependencies) {
const dependency = metadata.assets.find((asset: IOZAsset) => asset.id === dependencyId)!;
if (dependency.type === OZAssetType.library) {
if (!result.includes(dependency)) {
result.push(dependency);
@ -111,7 +112,7 @@ export namespace OpenZeppelinService {
}
});
}
});
}
return result;
}
@ -145,19 +146,23 @@ export namespace OpenZeppelinService {
export function getAssetsStatus(assets: IOZAsset[]): { existing: IOZAsset[], missing: IOZAsset[] } {
const openZeppelinSubfolder = getContractFolderPath();
const statuses = assets.map((asset) => {
if (fs.existsSync(path.join(openZeppelinSubfolder, path.dirname(asset.name)))) {
return {asset, exists: true};
}
return {asset, exists: false};
const assetsStatuses = assets.map((asset) => {
const assetPath = getAssetFullPath(openZeppelinSubfolder, asset);
return { asset, exists: fs.existsSync(assetPath) };
});
return {
existing: statuses.filter((status) => status.exists === true).map((status) => status.asset),
missing: statuses.filter((status) => status.exists === false).map((status) => status.asset),
existing: assetsStatuses.filter((status) => status.exists === true).map((status) => status.asset),
missing: assetsStatuses.filter((status) => status.exists === false).map((status) => status.asset),
};
}
export function getCategoryApiDocumentationUrl(category: IOZContractCategory) {
if (category.id === categoryWithoutDocumentation) {
return undefined;
}
const baseUrl = appendSlashIfNotExists(metadata.apiDocumentationBaseUri);
return url.resolve(baseUrl, category.id);
}
@ -216,23 +221,23 @@ function isFileExists(filePath: string) {
async function downloadFile(asset: IOZAsset, overwrite: boolean = false, openZeppelinSubfolder: string)
: Promise<IDownloadingResult> {
const fileUrl = new URL(path.join(metadata.targetPoint, asset.name), metadata.baseUri).toString();
const destinationPath = path.join(openZeppelinSubfolder, path.dirname(asset.name));
const destinationFile = path.join(destinationPath, path.basename(asset.name));
const fileUrl = new URL(getAssetFullPath(metadata.targetPoint, asset), metadata.baseUri).toString();
const destinationFilePath = getAssetFullPath(openZeppelinSubfolder, asset);
const destinationDirPath = path.dirname(destinationFilePath);
if (fs.existsSync(destinationFile)) {
if (fs.existsSync(destinationFilePath)) {
if (overwrite) {
await fs.chmod(destinationFile, 0o222); // reset r/o flag, this allows to overwrite
await fs.chmod(destinationFilePath, 0o222); // reset r/o flag, this allows to overwrite
} else {
Output.outputLine(Constants.outputChannel.azureBlockchain, `${fileUrl} - Skipped`);
return { state: PromiseState.fileExisted, asset };
}
}
return download(fileUrl, destinationPath, { filename: path.basename(asset.name) })
return download(fileUrl, destinationDirPath, { filename: path.basename(destinationFilePath) })
.then(async () => {
Output.outputLine(Constants.outputChannel.azureBlockchain, `${fileUrl} - OK`);
await fs.chmod(destinationFile, 0o444);
await fs.chmod(destinationFilePath, 0o444);
return { state: PromiseState.fulfilled, asset };
})
.catch(() => {
@ -258,8 +263,7 @@ function getOzContractsFromProjectMetadata(
openZeppelinSubfolder: string,
userProjectMetadata: IProjectMetadata) {
return Object.values(userProjectMetadata.openZeppelin.assets)
// map assetName to contractPath
.map((asset) => path.join(openZeppelinSubfolder, asset.name));
.map((asset) => getAssetFullPath(openZeppelinSubfolder, asset));
}
function getOriginalHash(
@ -273,3 +277,7 @@ function getOriginalHash(
}
return '';
}
function getAssetFullPath(baseDir: string, asset: IOZAsset) {
return path.join(baseDir, asset.name);
}

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

@ -478,7 +478,7 @@ describe('AzureBlockchainServiceClient', () => {
pipelineMock.restore();
});
function assertRequestFailed(error: any, callbackFunctionSpy: sinon.SinonSpy): void {
function assertRequestFailed(error: any, callbackFunctionSpy: sinon.SinonSpy<[Error | null, any?], void>): void {
assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once');
assert.strictEqual(
showErrorMessageMock.calledOnceWithExactly(error.message),
@ -490,7 +490,10 @@ describe('AzureBlockchainServiceClient', () => {
'callbackFunction should called once with correct arguments');
}
function assertResponseNotSuccess(callbackFunctionSpy: sinon.SinonSpy, pipelineCallbackSpy: sinon.SinonSpy): void {
function assertResponseNotSuccess(
callbackFunctionSpy: sinon.SinonSpy<[Error | null, any?], void>,
pipelineCallbackSpy: sinon.SinonSpy)
: void {
assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once');
assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not called');
assert.strictEqual(callbackFunctionSpy.calledOnce, true, 'callbackFunction should called once');
@ -506,7 +509,7 @@ describe('AzureBlockchainServiceClient', () => {
}
function assertResponseSuccess(
callbackFunctionSpy: sinon.SinonSpy,
callbackFunctionSpy: sinon.SinonSpy<[Error | null, any?], void>,
pipelineCallbackSpy: sinon.SinonSpy,
parsedResult: any)
: void {

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

@ -2,7 +2,7 @@
// Licensed under the MIT license.
import * as assert from 'assert';
import * as fs from 'fs';
import * as fs from 'fs-extra';
import * as os from 'os';
import * as sinon from 'sinon';
import uuid = require('uuid');

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

@ -4,7 +4,7 @@
import * as assert from 'assert';
import * as sinon from 'sinon';
import * as uuid from 'uuid';
import { window } from 'vscode';
import { CancellationToken, Progress, ProgressOptions, window } from 'vscode';
import { TruffleCommands } from '../../src/commands/TruffleCommands';
import * as helpers from '../../src/helpers';
import * as commands from '../../src/helpers/command';
@ -18,7 +18,8 @@ describe('BuildContracts Command', () => {
let installTruffle: sinon.SinonExpectation;
let commandContextMock: sinon.SinonMock;
let executeCommandMock: sinon.SinonExpectation;
let withProgressStub: sinon.SinonStub<any[], any>;
let withProgressStub: sinon.SinonStub<[
ProgressOptions, (progress: Progress<any>, token: CancellationToken) => any], any>;
beforeEach(() => {
requiredMock = sinon.mock(helpers.required);

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

@ -48,11 +48,12 @@ describe('TruffleCommands', () => {
let showQuickPickMock: any;
let showInputBoxMock: any;
let showSaveDialogMock: sinon.SinonExpectation;
let showInformationMessageMock: any;
let ganacheServiceMock: sinon.SinonMock;
let startGanacheServerMock: sinon.SinonExpectation;
let getItemsMock: sinon.SinonStub<[(boolean | undefined)?], IExtensionItem[]>;
let getItemsMock: sinon.SinonStub<[], IExtensionItem[]>;
let loadStateMock: sinon.SinonStub<[], IExtensionItem[]>;
let servicesItems: Service[];
@ -93,7 +94,7 @@ describe('TruffleCommands', () => {
showInputBoxMock = sinon.stub(vscode.window, 'showInputBox');
showSaveDialogMock = windowMock.expects('showSaveDialog');
sinon.stub(vscode.window, 'showErrorMessage');
sinon.stub(vscode.window, 'showInformationMessage');
showInformationMessageMock = sinon.stub(vscode.window, 'showInformationMessage');
ganacheServiceMock = sinon.mock(GanacheService);
startGanacheServerMock = ganacheServiceMock.expects('startGanacheServer');
@ -159,7 +160,9 @@ describe('TruffleCommands', () => {
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
isHdWalletProviderRequiredMock.returns(true);
checkHdWalletProviderVersionMock.returns(false);
executeCommandMock.returns(uuid.v4());
showInformationMessageMock.returns(Constants.informationMessage.installButton);
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.development);
@ -231,6 +234,7 @@ describe('TruffleCommands', () => {
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.returns(uuid.v4());
isHdWalletProviderRequiredMock.returns(false);
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.development);

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

@ -22,7 +22,7 @@ import { ProjectView } from '../../src/ViewItems';
describe('Integration tests GanacheCommands', () => {
const defaultPort = 8545;
let getItemsMock: sinon.SinonStub<[(boolean | undefined)?], IExtensionItem[]>;
let getItemsMock: sinon.SinonStub<[], IExtensionItem[]>;
let serviceItems: Service[];
let loadStateMock: sinon.SinonStub<[], IExtensionItem[]>;
let projectView: ProjectView;

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

@ -26,7 +26,7 @@ import { TestConstants } from '../TestConstants';
describe('Unit tests GanacheCommands', () => {
let checkAppsStub: sinon.SinonStub<RequiredApps[], Promise<boolean>>;
let getItemsMock: sinon.SinonStub<[(boolean | undefined)?], IExtensionItem[]>;
let getItemsMock: sinon.SinonStub<[], IExtensionItem[]>;
let testServiceItems: Service[];
let loadStateMock: sinon.SinonStub<[], IExtensionItem[]>;
let projectView: ProjectView;

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

@ -6,7 +6,16 @@ import * as path from 'path';
import rewire = require('rewire');
import * as sinon from 'sinon';
import uuid = require('uuid');
import { window } from 'vscode';
import {
CancellationToken,
MessageItem,
MessageOptions,
Progress,
ProgressOptions,
QuickPickItem,
QuickPickOptions,
window,
} from 'vscode';
import { Constants } from '../../src/Constants';
import { TruffleConfiguration } from '../../src/helpers/truffleConfig';
import * as workspace from '../../src/helpers/workspace';
@ -23,18 +32,21 @@ import {
describe('OpenZeppelinCommands tests', () => {
let testCategories: IOZContractCategory[];
let getCategoriesStub: sinon.SinonStub<any[], any> | sinon.SinonStub<unknown[], unknown>;
let collectAssetsWithDependenciesStub: sinon.SinonStub<any[], any> | sinon.SinonStub<unknown[], unknown>;
let downloadFilesStub: sinon.SinonStub<any[], any> | sinon.SinonStub<unknown[], unknown>;
let getCategoriesStub: sinon.SinonStub<[], IOZContractCategory[]>;
let collectAssetsWithDependenciesStub: sinon.SinonStub<[(string[] | undefined)?], IOZAsset[]>;
let downloadFilesStub: sinon.SinonStub<[IOZAsset[], (boolean | undefined)?], Promise<IDownloadingResult[]>>;
let addAssetsToProjectJsonStub: sinon.SinonStub<[IOZAsset[]], Promise<void>>;
let getAssetsStatusStub: sinon.SinonStub<any>;
let generateMigrationsStub: sinon.SinonStub<[IOZAsset[]], Promise<void>>;
let getCategoryApiDocumentationUrlStub: sinon.SinonStub<any>;
let withProgressStub: sinon.SinonStub<any[], any>;
let showQuickPickStub: sinon.SinonStub<any[], any>;
let showInformationMessageStub: sinon.SinonStub<any[], any>;
let showErrorMessageStub: sinon.SinonStub<any[], any>;
let withProgressStub: sinon.SinonStub<[ProgressOptions,
(progress: Progress<any>, token: CancellationToken) => any], any>;
let showQuickPickStub: sinon.SinonStub<
[QuickPickItem[] | Thenable<QuickPickItem[]>, (QuickPickOptions | undefined)?, (CancellationToken | undefined)?],
any>;
let showInformationMessageStub: sinon.SinonStub<[string, MessageOptions, ...MessageItem[]], any>;
let showErrorMessageStub: sinon.SinonStub<[string, MessageOptions, ...MessageItem[]], any>;
let selectedCategory: IOZContractCategory;
let testAssets: IOZAsset[];
@ -63,7 +75,8 @@ describe('OpenZeppelinCommands tests', () => {
getAssetsStatusStub = sinon.stub(OpenZeppelinService, 'getAssetsStatus');
downloadFilesStub = sinon.stub(OpenZeppelinService, 'downloadFiles');
generateMigrationsStub = sinon.stub(OpenZeppelinMigrationsService, 'generateMigrations');
getCategoryApiDocumentationUrlStub = sinon.stub(OpenZeppelinService, 'getCategoryApiDocumentationUrl');
getCategoryApiDocumentationUrlStub = sinon.stub(OpenZeppelinService, 'getCategoryApiDocumentationUrl')
.returns('testUrl');
getAssetsStatusStub.returns({ existing: [], missing: testAssets });
@ -86,7 +99,7 @@ describe('OpenZeppelinCommands tests', () => {
state: PromiseState.fulfilled,
});
});
downloadFilesStub.returns(testDownloadingResult);
downloadFilesStub.resolves(testDownloadingResult);
openStub = sinon.stub().resolves();
const openZeppelinCommandsRewire = rewire('../../src/commands/OpenZeppelinCommands');
@ -227,11 +240,11 @@ describe('OpenZeppelinCommands tests', () => {
it('should show error message if some files failed on downloading and allow to retry', async () => {
// Arrange
const rejectedAssets = [
{ asset: {}, state: PromiseState.rejected },
{ asset: {}, state: PromiseState.rejected },
{ asset: {} as IOZAsset, state: PromiseState.rejected },
{ asset: {} as IOZAsset, state: PromiseState.rejected },
];
downloadFilesStub.returns([
{ asset: {}, state: PromiseState.fulfilled },
downloadFilesStub.resolves([
{ asset: {} as IOZAsset, state: PromiseState.fulfilled },
...rejectedAssets,
]);
showErrorMessageStub
@ -267,6 +280,22 @@ describe('OpenZeppelinCommands tests', () => {
`open should be called with ${testDocumentationUrl}`);
assert.strictEqual(openStub.calledAfter(downloadFilesStub), true, 'open should be called after downloadFiles');
});
it('should not ask and open category documentation if it doesn\'t exist', async () => {
// Arrange
getCategoryApiDocumentationUrlStub.returns(undefined);
generateMigrationsStub.resolves();
// Act
await openZeppelinCommands.addCategory();
// Assert
assert.strictEqual(
showInformationMessageStub.calledWith(Constants.openZeppelin.exploreDownloadedContractsInfo),
false,
'showInformationMessageStub should not be called with explore contracts info message');
assert.strictEqual(openStub.called, false, 'open should not be called');
});
});
function getTestCategories(): IOZContractCategory[] {

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

@ -5,7 +5,7 @@ import * as assert from 'assert';
import * as fs from 'fs-extra';
import rewire = require('rewire');
import * as sinon from 'sinon';
import * as vscode from 'vscode';
import { CancellationToken, Progress, ProgressOptions, window, workspace } from 'vscode';
import { Constants, RequiredApps } from '../../src/Constants';
import * as helpers from '../../src/helpers';
import { CancellationEvent } from '../../src/Models';
@ -23,7 +23,8 @@ describe('ProjectCommands', () => {
let gitInitMock: sinon.SinonStub<any[], any>;
let requiredMock: sinon.SinonMock;
let checkRequiredAppsMock: sinon.SinonExpectation;
let withProgressStub: sinon.SinonStub<any[], any>;
let withProgressStub: sinon.SinonStub<
[ProgressOptions, (progress: Progress<any>, token: CancellationToken) => any], any>;
beforeEach(() => {
helpersMock = sinon.mock(helpers);
@ -36,7 +37,7 @@ describe('ProjectCommands', () => {
requiredMock = sinon.mock(helpers.required);
checkRequiredAppsMock = requiredMock.expects('checkRequiredApps');
withProgressStub = sinon.stub(vscode.window, 'withProgress');
withProgressStub = sinon.stub(window, 'withProgress');
withProgressStub.callsFake(async (...args: any[]) => {
return args[1]();
});
@ -108,7 +109,7 @@ describe('ProjectCommands', () => {
ensureDirMock = fsMock.expects('ensureDir');
readdirMock = fsMock.expects('readdir');
windowMock = sinon.mock(vscode.window);
windowMock = sinon.mock(window);
showErrorMessageMock = windowMock.expects('showErrorMessage');
});
@ -204,10 +205,11 @@ describe('ProjectCommands', () => {
});
describe('createNewEmptyProject', () => {
let withProgressStub: sinon.SinonStub<any[], any>;
let withProgressStub: sinon.SinonStub<
[ProgressOptions, (progress: Progress<any>, token: CancellationToken) => any], any>;
beforeEach(() => {
withProgressStub = sinon.stub(vscode.window, 'withProgress');
withProgressStub = sinon.stub(window, 'withProgress');
});
afterEach(() => {
@ -252,8 +254,6 @@ describe('ProjectCommands', () => {
});
describe('createProject', () => {
let outputMock: sinon.SinonMock;
let showMock: sinon.SinonExpectation;
let outputCommandHelperMock: sinon.SinonMock;
let executeCommandMock: sinon.SinonExpectation;
let workspaceMock: sinon.SinonMock;
@ -262,11 +262,9 @@ describe('ProjectCommands', () => {
let emptyDirSyncMock: sinon.SinonExpectation;
beforeEach(() => {
outputMock = sinon.mock(Output);
showMock = outputMock.expects('show');
outputCommandHelperMock = sinon.mock(helpers.outputCommandHelper);
executeCommandMock = outputCommandHelperMock.expects('executeCommand');
workspaceMock = sinon.mock(vscode.workspace);
workspaceMock = sinon.mock(workspace);
updateWorkspaceFoldersMock = workspaceMock.expects('updateWorkspaceFolders');
fsMock = sinon.mock(fs);
emptyDirSyncMock = fsMock.expects('emptyDirSync');
@ -279,7 +277,7 @@ describe('ProjectCommands', () => {
it('Method createProject run command for create new project and project was created successfully. ' +
'Workspace was updated to certain workspace.', async () => {
// Arrange
sinon.stub(vscode.workspace, 'workspaceFolders').value(['1']);
sinon.stub(workspace, 'workspaceFolders').value(['1']);
const projectCommandsRewire = rewire('../../src/commands/ProjectCommands');
const createProject = projectCommandsRewire.__get__('createProject');
@ -287,7 +285,6 @@ describe('ProjectCommands', () => {
await createProject(projectPath, truffleBoxName);
// Assert
assert.strictEqual(showMock.calledOnce, true, 'show should be called once');
assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once');
assert.strictEqual(
executeCommandMock.args[0][0],
@ -328,7 +325,7 @@ describe('ProjectCommands', () => {
it('Method createProject run command for create new project and project was created successfully. ' +
'Workspace was not updated to certain workspace.', async () => {
// Arrange
sinon.stub(vscode.workspace, 'workspaceFolders').value(undefined);
sinon.stub(workspace, 'workspaceFolders').value(undefined);
const projectCommandsRewire = rewire('../../src/commands/ProjectCommands');
const createProject = projectCommandsRewire.__get__('createProject');
@ -336,7 +333,6 @@ describe('ProjectCommands', () => {
await createProject(projectPath, truffleBoxName);
// Assert
assert.strictEqual(showMock.calledOnce, true, 'show should be called once');
assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once');
assert.strictEqual(
executeCommandMock.args[0][0],
@ -390,7 +386,6 @@ describe('ProjectCommands', () => {
action,
Error,
Constants.errorMessageStrings.NewProjectCreationFailed);
assert.strictEqual(showMock.calledOnce, true, 'show should be called once');
assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once');
assert.strictEqual(
executeCommandMock.args[0][0],
@ -460,11 +455,11 @@ describe('ProjectCommands', () => {
let outputCommandHelperMock: sinon.SinonMock;
let executeCommandMock: sinon.SinonExpectation;
let outputMock: sinon.SinonMock;
let showMock: sinon.SinonExpectation;
let workspaceMock: sinon.SinonMock;
let updateWorkspaceFoldersMock: sinon.SinonExpectation;
let emptyDirSyncMock: sinon.SinonExpectation;
let withProgressStub: sinon.SinonStub<any[], any>;
let withProgressStub: sinon.SinonStub<
[ProgressOptions, (progress: Progress<any>, token: CancellationToken) => any], any>;
beforeEach(() => {
helpersMock = sinon.mock(helpers);
@ -486,15 +481,14 @@ describe('ProjectCommands', () => {
readdirMock = fsMock.expects('readdir');
emptyDirSyncMock = fsMock.expects('emptyDirSync');
windowMock = sinon.mock(vscode.window);
windowMock = sinon.mock(window);
showErrorMessageMock = windowMock.expects('showErrorMessage');
workspaceMock = sinon.mock(vscode.workspace);
workspaceMock = sinon.mock(workspace);
updateWorkspaceFoldersMock = workspaceMock.expects('updateWorkspaceFolders');
outputMock = sinon.mock(Output);
showMock = outputMock.expects('show');
withProgressStub = sinon.stub(vscode.window, 'withProgress');
withProgressStub = sinon.stub(window, 'withProgress');
withProgressStub.callsFake(async (...args: any[]) => {
return args[1]();
});
@ -559,7 +553,7 @@ describe('ProjectCommands', () => {
it('Method chooseNewProjectDir returns projectPath which we selected at first time.', async () => {
// Arrange
checkRequiredAppsMock.returns(true);
sinon.stub(vscode.workspace, 'workspaceFolders').value(['1']);
sinon.stub(workspace, 'workspaceFolders').value(['1']);
readdirMock.returns([]);
showQuickPickMock.returns({
cmd: () => undefined,
@ -591,7 +585,7 @@ describe('ProjectCommands', () => {
// Arrange
checkRequiredAppsMock.returns(true);
readdirMock.returns([]);
sinon.stub(vscode.workspace, 'workspaceFolders').value(['1']);
sinon.stub(workspace, 'workspaceFolders').value(['1']);
const projectCommandsRewire = rewire('../../src/commands/ProjectCommands');
const createNewEmptyProject = projectCommandsRewire.__get__('createNewEmptyProject');
@ -619,7 +613,6 @@ describe('ProjectCommands', () => {
assert.strictEqual(readdirMock.calledOnce, true, 'readdir should be called once');
assert.strictEqual(readdirMock.args[0][0], firstProjectPath, 'readdir should be called with correct arguments');
assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not be called');
assert.strictEqual(showMock.calledOnce, true, 'show should be called once');
assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once');
assert.strictEqual(
executeCommandMock.args[0][0],
@ -694,7 +687,6 @@ describe('ProjectCommands', () => {
assert.strictEqual(readdirMock.calledOnce, true, 'readdir should be called once');
assert.strictEqual(readdirMock.args[0][0], firstProjectPath, 'readdir should be called with correct arguments');
assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not be called');
assert.strictEqual(showMock.calledOnce, true, 'show should be called once');
assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once');
assert.strictEqual(
executeCommandMock.args[0][0],
@ -729,7 +721,7 @@ describe('ProjectCommands', () => {
// Arrange
checkRequiredAppsMock.returns(true);
readdirMock.returns([]);
sinon.stub(vscode.workspace, 'workspaceFolders').value(['1']);
sinon.stub(workspace, 'workspaceFolders').value(['1']);
const projectCommandsRewire = rewire('../../src/commands/ProjectCommands');
projectCommandsRewire.__set__('getTruffleBoxName', sinon.mock().returns(truffleBoxName));
@ -760,7 +752,6 @@ describe('ProjectCommands', () => {
assert.strictEqual(readdirMock.calledOnce, true, 'readdir should be called once');
assert.strictEqual(readdirMock.args[0][0], firstProjectPath, 'readdir should be called with correct arguments');
assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not be called');
assert.strictEqual(showMock.calledOnce, true, 'show should be called once');
assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once');
assert.strictEqual(
executeCommandMock.args[0][0],
@ -838,7 +829,6 @@ describe('ProjectCommands', () => {
assert.strictEqual(readdirMock.calledOnce, true, 'readdir should be called once');
assert.strictEqual(readdirMock.args[0][0], firstProjectPath, 'readdir should be called with correct arguments');
assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not be called');
assert.strictEqual(showMock.calledOnce, true, 'show should be called once');
assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once');
assert.strictEqual(
executeCommandMock.args[0][0],

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

@ -8,7 +8,7 @@ import * as uuid from 'uuid';
import * as vscode from 'vscode';
import { Constants } from '../../../src/Constants';
import { ItemType } from '../../../src/Models';
import { AzureBlockchainProject, AzureBlockchainService, Project } from '../../../src/Models/TreeItems';
import { AzureBlockchainProject, AzureBlockchainService, Project, Service, ServiceTypes } from '../../../src/Models/TreeItems';
import { ConsortiumResourceExplorer } from '../../../src/resourceExplorers';
import { GanacheService, TreeManager } from '../../../src/services';
import { AzureAccountHelper } from '../../testHelpers/AzureAccountHelper';
@ -42,7 +42,7 @@ describe('Service Commands', () => {
describe('connectProject returns project', () => {
let selectedDestination: any;
let getItemStub: sinon.SinonStub<any[], any> | sinon.SinonStub<unknown[], {}>;
let getItemStub: sinon.SinonStub<[ServiceTypes], Service>;
let addChildStub: sinon.SinonStub<any, any>;
let showQuickPickMock: sinon.SinonStub<any[], any>;
let showInputBoxMock: sinon.SinonExpectation;

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

@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as truffleDebugUtils from '@truffle/debug-utils';
import * as truffleDebugger from '@truffle/debugger';
import * as assert from 'assert';
import * as sinon from 'sinon';
import * as truffleDebugUtils from 'truffle-debug-utils';
import * as truffleDebugger from 'truffle-debugger';
import * as contractsPrepareHelpers from '../../src/debugAdapter/contracts/contractsPrepareHelpers';
import { IContractModel } from '../../src/debugAdapter/models/IContractModel';
import RuntimeInterface from '../../src/debugAdapter/runtimeInterface';

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

@ -1,6 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as truffleProvider from '@truffle/provider';
import * as assert from 'assert';
import * as sinon from 'sinon';
import * as truffleProvider from 'truffle-provider';
import { ConfigurationReader } from '../../src/debugAdapter/configurationReader';
import { Web3Wrapper } from '../../src/debugAdapter/web3Wrapper';

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

@ -6,7 +6,8 @@ contract Base {
address public Responder;
}
contract HelloBlockchain is Base {
contract HelloBlockchain is Base
{
enum StateType { Request, Respond }
enum SwitcherEnum { On, Off }
@ -15,19 +16,23 @@ contract HelloBlockchain is Base {
string public RequestMessage;
string public ResponseMessage;
constructor(string memory message) public {
constructor(string memory message) public
{
Requestor = msg.sender;
RequestMessage = message;
State = StateType.Request;
}
function SendRequest(string memory requestMessage, StateType state) public {
function SendRequest(string memory requestMessage, StateType state) public
{
RequestMessage = requestMessage;
State = state;
}
function SendResponse(StateType state, SwitcherEnum flag) public {
if (flag == SwitcherEnum.On) {
function SendResponse(StateType state, SwitcherEnum flag) public
{
if (flag == SwitcherEnum.On)
{
Responder = msg.sender;
}
@ -35,7 +40,8 @@ contract HelloBlockchain is Base {
}
function SwitcheToOff(uint completed) public {
if(completed > 0) {
if(completed > 0)
{
Flag = SwitcherEnum.Off;
}
}

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

@ -4,6 +4,8 @@
import * as fs from 'fs-extra';
import * as path from 'path';
export const referenceMnemonic = 'some menmonic some menmonic some menmonic some menmonic some menmonic some menmonic';
export const referenceCfgContent = 'const HDWalletProvider = require("truffle-hdwallet-provider");'
+ 'module.exports = {'
+ ' networks: {'
@ -88,6 +90,7 @@ export const referenceConfiguration = {
network_id: '*',
port: 123,
provider: {
mnemonic: referenceMnemonic,
raw: 'new HDWalletProvider(fs.readFileSync(\"path\", \"encoding\"), \"url\")',
url: 'url',
},

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

@ -54,12 +54,16 @@ describe('TruffleConfiguration helper', () => {
describe('class TruffleConfig', () => {
const configPathStub = path.normalize('w:/temp/truffle-config.js');
let readFileStub: any;
let writeFileStub: any;
let readFileStub: sinon.SinonStub<any, any>;
let writeFileStub: sinon.SinonStub<any, any>;
before(() => {
readFileStub = sinon.stub(fs, 'readFileSync').returns(testData.referenceCfgContent);
readFileStub = sinon.stub(fs, 'readFileSync');
writeFileStub = sinon.stub(fs, 'writeFileSync');
readFileStub.withArgs(configPathStub).returns(testData.referenceCfgContent);
readFileStub.withArgs('path').returns(testData.referenceMnemonic);
readFileStub.withArgs('path', 'encoding').returns(testData.referenceMnemonic);
});
after(() => {
@ -186,6 +190,14 @@ describe('class TruffleConfig', () => {
describe('getConfiguration() in class TruffleConfig', () => {
const configPathStub = path.normalize('w:/temp/truffle-config.js');
let readFileStub: sinon.SinonStub<any, any>;
beforeEach(() => {
readFileStub = sinon.stub(fs, 'readFileSync');
readFileStub.withArgs('path').returns(testData.referenceMnemonic);
readFileStub.withArgs('path, encoding').returns(testData.referenceMnemonic);
});
afterEach(() => {
sinon.restore();
@ -194,7 +206,7 @@ describe('getConfiguration() in class TruffleConfig', () => {
it('getConfiguration returns default configuration',
async () => {
// Arrange
sinon.stub(fs, 'readFileSync').returns('');
readFileStub.returns('');
const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub);
// Act
@ -219,7 +231,7 @@ describe('getConfiguration() in class TruffleConfig', () => {
it('getConfiguration returns configuration without required fields',
async () => {
// Arrange
sinon.stub(fs, 'readFileSync').returns(testData.referenceCfgContent);
readFileStub.returns(testData.referenceCfgContent);
const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub);
// Act
@ -247,7 +259,7 @@ describe('getConfiguration() in class TruffleConfig', () => {
it('getConfiguration returns configuration with required fields',
async () => {
// Arrange
sinon.stub(fs, 'readFileSync').returns(testData.referenceCfgContentWithDirectories);
readFileStub.returns(testData.referenceCfgContentWithDirectories);
const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub);
// Act

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

@ -61,4 +61,4 @@ const config = {
__dirname: false,
}
};
module.exports = config;
module.exports = config;