/** * @license * * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @fileoverview Define generation methods for custom blocks. * @author samelh@google.com (Sam El-Husseini) */ // More on generating code: // https://developers.google.com/blockly/guides/create-custom-blocks/generating-code import Blockly from "blockly"; export const Solidity = new Blockly.Generator("Solidity"); Solidity.LABEL_GROUP_STATE = "state"; Solidity.LABEL_GROUP_PARAMETER = "parameter"; Solidity.LABEL_GROUP_VARIABLE = "variable"; Solidity.LABEL_GROUP_METHOD = "method"; Solidity.UNDEFINED_NAME = "__UNDEFINED__"; const objectUtils = Blockly.utils.object; const stringUtils = Blockly.utils.string; Solidity.isInitialized = false; //incomplete list Solidity.addReservedWords( "after,alias,apply,auto,byte,case,copyof,default," + "define,final,implements,in,inline,let,macro,match," + "mutable,null,of,partial,promise,reference,relocatable,sealed,", "sizeof,static,supports,switch,typedef,typeof,var", "function,true,false,bool,int8,int,uint8,", "uint,address,bytes1,contract,constructor,uint8,", "override,virtual,indexed,anonymous,immutable,constant,payable,view,pure,", "public,private,external,internal,abi,bytes,block,gasleft,msg,tx,assert,require,", "revert,blockhash,keccak256,sha256,ripemd160,ecrecover,addmod,mulmod,this,", "super,selfdestruct,type" ); Solidity.ORDER_ATOMIC = 0; // 0 "" ... Solidity.ORDER_NEW = 1.1; // new Solidity.ORDER_MEMBER = 1.2; // . [] Solidity.ORDER_FUNCTION_CALL = 2; // () Solidity.ORDER_INCREMENT = 3; // ++ Solidity.ORDER_DECREMENT = 3; // -- Solidity.ORDER_BITWISE_NOT = 4.1; // ~ Solidity.ORDER_UNARY_PLUS = 4.2; // + Solidity.ORDER_UNARY_NEGATION = 4.3; // - Solidity.ORDER_LOGICAL_NOT = 4.4; // ! Solidity.ORDER_TYPEOF = 4.5; // typeof Solidity.ORDER_VOID = 4.6; // void Solidity.ORDER_DELETE = 4.7; // delete Solidity.ORDER_DIVISION = 5.1; // / Solidity.ORDER_MULTIPLICATION = 5.2; // * Solidity.ORDER_MODULUS = 5.3; // % Solidity.ORDER_SUBTRACTION = 6.1; // - Solidity.ORDER_ADDITION = 6.2; // + Solidity.ORDER_BITWISE_SHIFT = 7; // << >> >>> Solidity.ORDER_RELATIONAL = 8; // < <= > >= Solidity.ORDER_IN = 8; // in Solidity.ORDER_INSTANCEOF = 8; // instanceof Solidity.ORDER_EQUALITY = 9; // == != === !== Solidity.ORDER_BITWISE_AND = 10; // & Solidity.ORDER_BITWISE_XOR = 11; // ^ Solidity.ORDER_BITWISE_OR = 12; // | Solidity.ORDER_LOGICAL_AND = 13; // && Solidity.ORDER_LOGICAL_OR = 14; // || Solidity.ORDER_CONDITIONAL = 15; // ?: Solidity.ORDER_ASSIGNMENT = 16; // = += -= *= /= %= <<= >>= ... Solidity.ORDER_COMMA = 17; // , Solidity.ORDER_NONE = 99; // (...) /** * List of outer-inner pairings that do NOT require parentheses. * @type {!Array<!Array<number>>} */ Solidity.ORDER_OVERRIDES = [ // (foo()).bar -> foo().bar // (foo())[0] -> foo()[0] [Solidity.ORDER_FUNCTION_CALL, Solidity.ORDER_MEMBER], // (foo())() -> foo()() [Solidity.ORDER_FUNCTION_CALL, Solidity.ORDER_FUNCTION_CALL], // (foo.bar).baz -> foo.bar.baz // (foo.bar)[0] -> foo.bar[0] // (foo[0]).bar -> foo[0].bar // (foo[0])[1] -> foo[0][1] [Solidity.ORDER_MEMBER, Solidity.ORDER_MEMBER], // (foo.bar)() -> foo.bar() // (foo[0])() -> foo[0]() [Solidity.ORDER_MEMBER, Solidity.ORDER_FUNCTION_CALL], // !(!foo) -> !!foo [Solidity.ORDER_LOGICAL_NOT, Solidity.ORDER_LOGICAL_NOT], // a * (b * c) -> a * b * c [Solidity.ORDER_MULTIPLICATION, Solidity.ORDER_MULTIPLICATION], // a + (b + c) -> a + b + c [Solidity.ORDER_ADDITION, Solidity.ORDER_ADDITION], // a && (b && c) -> a && b && c [Solidity.ORDER_LOGICAL_AND, Solidity.ORDER_LOGICAL_AND], // a || (b || c) -> a || b || c [Solidity.ORDER_LOGICAL_OR, Solidity.ORDER_LOGICAL_OR], ]; /** * Whether the init method has been called. * @type {?boolean} */ Solidity.isInitialized = false; /** * Initialise the database of variable names. * @param {!Workspace} workspace Workspace to generate code from. */ Solidity.init = function (workspace) { // Call Blockly.Generator's init. Object.getPrototypeOf(this).init.call(this); if (!this.nameDB_) { this.nameDB_ = new Blockly.Names(this.RESERVED_WORDS_); } else { this.nameDB_.reset(); } this.nameDB_.setVariableMap(workspace.getVariableMap()); this.nameDB_.populateVariables(workspace); this.nameDB_.populateProcedures(workspace); this.isInitialized = true; }; /** * Prepend the generated code with the variable definitions. * @param {string} code Generated code. * @return {string} Completed code. */ Solidity.finish = function (code) { // Convert the definitions dictionary into a list. const definitions = objectUtils.values(this.definitions_); // Call Blockly.Generator's finish. code = Object.getPrototypeOf(this).finish.call(this, code); this.isInitialized = false; this.nameDB_.reset(); return definitions.join("\n\n") + "\n\n\n" + code; }; /** * Naked values are top-level blocks with outputs that aren't plugged into * anything. A trailing semicolon is needed to make this legal. * @param {string} line Line of generated code. * @return {string} Legal line of code. */ Solidity.scrubNakedValue = function (line) { return line + ";\n"; }; /** * Encode a string as a properly escaped Solidity string, complete with * quotes. * @param {string} string Text to encode. * @return {string} Solidity string. * @protected */ Solidity.quote_ = function (string) { // Can't use goog.string.quote since Google's style guide recommends // JS string literals use single quotes. string = string .replace(/\\/g, "\\\\") .replace(/\n/g, "\\\n") .replace(/'/g, "\\'"); return "'" + string + "'"; }; /** * Encode a string as a properly escaped multiline Solidity string, complete * with quotes. * @param {string} string Text to encode. * @return {string} Solidity string. * @protected */ Solidity.multiline_quote_ = function (string) { // Can't use goog.string.quote since Google's style guide recommends // JS string literals use single quotes. const lines = string.split(/\n/g).map(this.quote_); return lines.join(" + '\\n' +\n"); }; /** * Common tasks for generating Solidity from blocks. * Handles comments for the specified block and any connected value blocks. * Calls any statements following this block. * @param {!Block} block The current block. * @param {string} code The Solidity code created for this block. * @param {boolean=} opt_thisOnly True to generate code for only this statement. * @return {string} Solidity code with comments and subsequent blocks added. * @protected */ Solidity.scrub_ = function (block, code, opt_thisOnly) { let commentCode = ""; // Only collect comments for blocks that aren't inline. if (!block.outputConnection || !block.outputConnection.targetConnection) { // Collect comment for this block. let comment = block.getCommentText(); if (comment) { comment = stringUtils.wrap(comment, this.COMMENT_WRAP - 3); commentCode += this.prefixLines(comment + "\n", "/// "); } /// Collect comments for all value arguments. /// Don't collect comments for nested statements. for (let i = 0; i < block.inputList.length; i++) { if (block.inputList[i].type === Blockly.inputTypes.VALUE) { const childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { comment = this.allNestedComments(childBlock); if (comment) { commentCode += this.prefixLines(comment, "/// "); } } } } } const nextBlock = block.nextConnection && block.nextConnection.targetBlock(); const nextCode = opt_thisOnly ? "" : this.blockToCode(nextBlock); return commentCode + code + nextCode; }; /** * Gets a property and adjusts the value while taking into account indexing. * @param {!Block} block The block. * @param {string} atId The property ID of the element to get. * @param {number=} opt_delta Value to add. * @param {boolean=} opt_negate Whether to negate the value. * @param {number=} opt_order The highest order acting on this value. * @return {string|number} */ Solidity.getAdjusted = function ( block, atId, opt_delta, opt_negate, opt_order ) { let delta = opt_delta || 0; let order = opt_order || this.ORDER_NONE; if (block.workspace.options.oneBasedIndex) { delta--; } const defaultAtIndex = block.workspace.options.oneBasedIndex ? "1" : "0"; let innerOrder; let outerOrder = order; if (delta > 0) { outerOrder = this.ORDER_ADDITION; innerOrder = this.ORDER_ADDITION; } else if (delta < 0) { outerOrder = this.ORDER_SUBTRACTION; innerOrder = this.ORDER_SUBTRACTION; } else if (opt_negate) { outerOrder = this.ORDER_UNARY_NEGATION; innerOrder = this.ORDER_UNARY_NEGATION; } let at = this.valueToCode(block, atId, outerOrder) || defaultAtIndex; if (stringUtils.isNumber(at)) { // If the index is a naked number, adjust it right now. at = Number(at) + delta; if (opt_negate) { at = -at; } } else { // If the index is dynamic, adjust it in code. if (delta > 0) { at = at + " + " + delta; } else if (delta < 0) { at = at + " - " + -delta; } if (opt_negate) { if (delta) { at = "-(" + at + ")"; } else { at = "-" + at; } } innerOrder = Math.floor(innerOrder); order = Math.floor(order); if (innerOrder && order >= innerOrder) { at = "(" + at + ")"; } } return at; }; Solidity.updateWorkspaceNameFields = function (workspace) { var blocks = workspace.getAllBlocks(); for (var i = 0; i < blocks.length; ++i) { var nameField = blocks[i].getVariableNameSelectField ? blocks[i].getVariableNameSelectField() : null; var group = blocks[i].getVariableLabelGroup ? blocks[i].getVariableLabelGroup() : null; if (!!nameField && !!group) { var vars = Solidity.getVariablesInScope(blocks[i], group); var options = vars.map(function (v) { return [Solidity.getVariableName(v), v.id_]; }); var selectedOption = nameField.getValue(); console.log("selected option => ", selectedOption); if (options.length != 0) { var wasUndefined = nameField.menuGenerator_[0][1] == Solidity.UNDEFINED_NAME; nameField.menuGenerator_ = options; if (wasUndefined) { nameField.setValue(options[0][1]); } else { nameField.setValue(selectedOption); // The text input does not redraw/update itself after we call "setValue", // so we set the text manually. // nameField.setText( // options.filter(function (o) { return o[1] == selectedOption })[0][0] // ); } } } } }; Solidity.updateWorkspaceTypes = function ( workspace, nameFieldName, valueFieldName ) { var blocks = workspace.getAllBlocks(); var vars = workspace.getAllVariables(); for (var i = 0; i < blocks.length; ++i) { var stateNameField = blocks[i].getField(nameFieldName); if (!stateNameField) { continue; } var variableId = blocks[i].getFieldValue(nameFieldName); var variable = workspace.getVariableById(variableId); if (!variable) { return; } if ( blocks[i].inputList[0] && blocks[i].inputList[0].name == valueFieldName ) { switch (variable.type) { case "TYPE_BOOL": blocks[i].inputList[0].setCheck("Boolean"); break; case "TYPE_INT": blocks[i].inputList[0].setCheck("Number"); break; case "TYPE_UINT": blocks[i].inputList[0].setCheck("Number"); break; case 'TYPE_STRING': blocks[i].inputList[0].setCheck("String"); break; case 'TYPE_ADDRESS': blocks[i].inputList[0].setCheck("String"); break; default: } } // TODO: update the output type } }; Solidity.updateWorkspaceStateTypes = function (workspace) { Solidity.updateWorkspaceTypes(workspace, "STATE_NAME", "STATE_VALUE"); }; Solidity.updateWorkspaceParameterTypes = function (workspace) { Solidity.updateWorkspaceTypes(workspace, "PARAM_NAME", "PARAM_VALUE"); }; Solidity.createVariable = function (workspace, group, type, name, scope, id) { var variable = workspace.createVariable(name, type, id); variable.group = group; variable.scope = scope; Solidity.setVariableName(variable, name); return variable; }; Solidity.getVariableById = function (workspace, id) { return workspace.getVariableById(id); }; Solidity.getVariableByName = function (workspace, name) { return Solidity.getAllVariables(workspace).filter(function (v) { return Solidity.getVariableName(v) == name; })[0]; }; Solidity.getVariableByNameAndScope = function (name, scope, group = null) { return Solidity.getVariablesInScope(scope, group).filter(function (v) { return Solidity.getVariableName(v) == name; })[0]; }; Solidity.deleteVariableById = function (workspace, id) { Solidity.deleteVariableByName( workspace, Solidity.getVariableById(workspace, id).name ); }; Solidity.deleteVariableByName = function (workspace, name) { return workspace.deleteVariable(name); }; Solidity.variableIsInScope = function (variable, scope) { while (!!scope && scope.id != variable.scope.id) { var type = scope.type; do { scope = scope.getParent(); } while (scope && type == scope.type); } return !!scope; }; Solidity.setVariableName = function (variable, name) { variable.name = '_scope("' + variable.scope.id + '")_' + name; }; Solidity.getVariableName = function (variable) { return variable.name.replace('_scope("' + variable.scope.id + '")_', ""); }; Solidity.getAllVariables = function (workspace) { return workspace.getAllVariables(); }; Solidity.getVariablesInScope = function (block, group = null) { return Solidity.getAllVariables(block.workspace) .filter(function (v) { return Solidity.variableIsInScope(v, block); }) .filter(function (v) { return !group || v.group == group; }); }; Solidity["contract"] = function (block) { var states = Solidity.statementToCode(block, "STATES"); if (states.length > 0) { states += "\n"; } var ctor = Solidity.statementToCode(block, "CTOR"); var methods = Solidity.statementToCode(block, "METHODS"); // trim newline before ultimate closing curly brace if (methods.length > 0) { methods = methods.slice(0, -2); } else if (ctor.length > 0) { ctor = ctor.slice(0, -2); } var code = "//SPDX-License-Identifier: MIT\n\n" + "pragma solidity ^0.8.17;\n\n" + "contract " + block.getFieldValue("NAME") + " {\n\n" + states + ctor + methods + "}\n"; return code; }; Solidity["contract_state"] = function (block) { var name = block.getFieldValue("NAME"); var value = Solidity.valueToCode(block, "VALUE", Solidity.ORDER_ASSIGNMENT); var type = block.getFieldValue("TYPE"); var types = { TYPE_BOOL: "bool", TYPE_INT: "int", TYPE_UINT: "uint", TYPE_STRING: "string", TYPE_ADDRESS: "address", }; var defaultValue = { TYPE_BOOL: "false", TYPE_INT: "0", TYPE_UINT: "0", TYPE_STRING: '""', TYPE_ADDRESS: "0x0000000000000000000000000000000000000000", }; if (value === "") { value = defaultValue[type]; } return types[type] + " " + name + " = " + value + ";\n"; }; Solidity["contract_state_get"] = function (block) { var variableId = block.getFieldValue("STATE_NAME"); var variable = block.workspace.getVariableById(variableId); if (!variable) { return ""; } return [Solidity.getVariableName(variable), Solidity.ORDER_ATOMIC]; }; Solidity["contract_state_set"] = function (block) { // Variable setter. var argument0 = Solidity.valueToCode(block, "STATE_VALUE", Solidity.ORDER_ASSIGNMENT) || "0"; var variableId = block.getFieldValue("STATE_NAME"); var variable = block.workspace.getVariableById(variableId); if (!variable) { return ""; } return Solidity.getVariableName(variable) + " = " + argument0 + ";\n"; }; Solidity["controls_if"] = function (block) { // If/elseif/else condition. var n = 0; var code = "", branchCode, conditionCode; do { conditionCode = Solidity.valueToCode(block, "IF" + n, Solidity.ORDER_NONE) || "false"; branchCode = Solidity.statementToCode(block, "DO" + n); code += (n > 0 ? " else " : "") + "if (" + conditionCode + ") {\n" + branchCode + "}"; ++n; } while (block.getInput("IF" + n)); if (block.getInput("ELSE")) { branchCode = Solidity.statementToCode(block, "ELSE"); code += " else {\n" + branchCode + "}"; } return code + "\n"; }; Solidity["controls_ifelse"] = Solidity["controls_if"]; Solidity["logic_compare"] = function (block) { // Comparison operator. var OPERATORS = { EQ: "==", NEQ: "!=", LT: "<", LTE: "<=", GT: ">", GTE: ">=", }; var operator = OPERATORS[block.getFieldValue("OP")]; var order = operator == "==" || operator == "!=" ? Solidity.ORDER_EQUALITY : Solidity.ORDER_RELATIONAL; var argument0 = Solidity.valueToCode(block, "A", order) || "0"; var argument1 = Solidity.valueToCode(block, "B", order) || "0"; var code = argument0 + " " + operator + " " + argument1; return [code, order]; }; Solidity["logic_operation"] = function (block) { // Operations 'and', 'or'. var operator = block.getFieldValue("OP") == "AND" ? "&&" : "||"; var order = operator == "&&" ? Solidity.ORDER_LOGICAL_AND : Solidity.ORDER_LOGICAL_OR; var argument0 = Solidity.valueToCode(block, "A", order); var argument1 = Solidity.valueToCode(block, "B", order); if (!argument0 && !argument1) { // If there are no arguments, then the return value is false. argument0 = "false"; argument1 = "false"; } else { // Single missing arguments have no effect on the return value. var defaultArgument = operator == "&&" ? "true" : "false"; if (!argument0) { argument0 = defaultArgument; } if (!argument1) { argument1 = defaultArgument; } } var code = argument0 + " " + operator + " " + argument1; return [code, order]; }; Solidity["logic_negate"] = function (block) { // Negation. var order = Solidity.ORDER_LOGICAL_NOT; var argument0 = Solidity.valueToCode(block, "BOOL", order) || "true"; var code = "!" + argument0; return [code, order]; }; Solidity["logic_boolean"] = function (block) { // Boolean values true and false. var code = block.getFieldValue("BOOL") == "TRUE" ? "true" : "false"; return [code, Solidity.ORDER_ATOMIC]; }; Solidity["logic_null"] = function (block) { // Null data type. return ["null", Solidity.ORDER_ATOMIC]; }; Solidity["logic_ternary"] = function (block) { // Ternary operator. var value_if = Solidity.valueToCode(block, "IF", Solidity.ORDER_CONDITIONAL) || "false"; var value_then = Solidity.valueToCode(block, "THEN", Solidity.ORDER_CONDITIONAL) || "null"; var value_else = Solidity.valueToCode(block, "ELSE", Solidity.ORDER_CONDITIONAL) || "null"; var code = value_if + " ? " + value_then + " : " + value_else; return [code, Solidity.ORDER_CONDITIONAL]; }; Solidity["math_number"] = function (block) { // Numeric value. var code = parseFloat(block.getFieldValue("NUM")); return [code, Solidity.ORDER_ATOMIC]; }; Solidity["math_arithmetic"] = function (block) { // Basic arithmetic operators, and power. var OPERATORS = { ADD: [" + ", Solidity.ORDER_ADDITION], MINUS: [" - ", Solidity.ORDER_SUBTRACTION], MULTIPLY: [" * ", Solidity.ORDER_MULTIPLICATION], DIVIDE: [" / ", Solidity.ORDER_DIVISION], POWER: [" ** ", Solidity.ORDER_EXPONENTATION], }; var tuple = OPERATORS[block.getFieldValue("OP")]; var operator = tuple[0]; var order = tuple[1]; var argument0 = Solidity.valueToCode(block, "A", order) || "0"; var argument1 = Solidity.valueToCode(block, "B", order) || "0"; var code = argument0 + operator + argument1; return [code, order]; }; Solidity["math_single"] = function (block) { // Math operators with single operand. var operator = block.getFieldValue("OP"); var code; var arg; if (operator == "NEG") { // Negation is a special case given its different operator precedence. arg = Solidity.valueToCode(block, "NUM", Solidity.ORDER_UNARY_NEGATION) || "0"; if (arg[0] == "-") { // --3 is not legal in JS. arg = " " + arg; } code = "-" + arg; return [code, Solidity.ORDER_UNARY_NEGATION]; } if (operator == "SIN" || operator == "COS" || operator == "TAN") { arg = Solidity.valueToCode(block, "NUM", Solidity.ORDER_DIVISION) || "0"; } else { arg = Solidity.valueToCode(block, "NUM", Solidity.ORDER_NONE) || "0"; } // First, handle cases which generate values that don't need parentheses // wrapping the code. switch (operator) { case "ABS": code = "Math.abs(" + arg + ")"; break; case "ROOT": code = "Math.sqrt(" + arg + ")"; break; case "LN": code = "Math.log(" + arg + ")"; break; case "EXP": code = "Math.exp(" + arg + ")"; break; case "POW10": code = "Math.pow(10," + arg + ")"; break; case "ROUND": code = "Math.round(" + arg + ")"; break; case "ROUNDUP": code = "Math.ceil(" + arg + ")"; break; case "ROUNDDOWN": code = "Math.floor(" + arg + ")"; break; case "SIN": code = "Math.sin(" + arg + " / 180 * Math.PI)"; break; case "COS": code = "Math.cos(" + arg + " / 180 * Math.PI)"; break; case "TAN": code = "Math.tan(" + arg + " / 180 * Math.PI)"; break; } if (code) { return [code, Solidity.ORDER_FUNCTION_CALL]; } // Second, handle cases which generate values that may need parentheses // wrapping the code. switch (operator) { case "LOG10": code = "Math.log(" + arg + ") / Math.log(10)"; break; case "ASIN": code = "Math.asin(" + arg + ") / Math.PI * 180"; break; case "ACOS": code = "Math.acos(" + arg + ") / Math.PI * 180"; break; case "ATAN": code = "Math.atan(" + arg + ") / Math.PI * 180"; break; default: throw "Unknown math operator: " + operator; } return [code, Solidity.ORDER_DIVISION]; }; Solidity["math_constant"] = function (block) { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. var CONSTANTS = { PI: ["Math.PI", Solidity.ORDER_MEMBER], E: ["Math.E", Solidity.ORDER_MEMBER], GOLDEN_RATIO: ["(1 + Math.sqrt(5)) / 2", Solidity.ORDER_DIVISION], SQRT2: ["Math.SQRT2", Solidity.ORDER_MEMBER], SQRT1_2: ["Math.SQRT1_2", Solidity.ORDER_MEMBER], INFINITY: ["Infinity", Solidity.ORDER_ATOMIC], }; return CONSTANTS[block.getFieldValue("CONSTANT")]; }; Solidity["math_number_property"] = function (block) { // Check if a number is even, odd, prime, whole, positive, or negative // or if it is divisible by certain number. Returns true or false. var number_to_check = Solidity.valueToCode(block, "NUMBER_TO_CHECK", Solidity.ORDER_MODULUS) || "0"; var dropdown_property = block.getFieldValue("PROPERTY"); var code; if (dropdown_property == "PRIME") { // Prime is a special case as it is not a one-liner test. var functionName = Solidity.provideFunction_("mathIsPrime", [ "function " + Solidity.FUNCTION_NAME_PLACEHOLDER_ + "(n) {", " // https://en.wikipedia.org/wiki/Primality_test#Naive_methods", " if (n == 2 || n == 3) {", " return true;", " }", " // False if n is NaN, negative, is 1, or not whole.", " // And false if n is divisible by 2 or 3.", " if (isNaN(n) || n <= 1 || n % 1 != 0 || n % 2 == 0 ||" + " n % 3 == 0) {", " return false;", " }", " // Check all the numbers of form 6k +/- 1, up to sqrt(n).", " for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {", " if (n % (x - 1) == 0 || n % (x + 1) == 0) {", " return false;", " }", " }", " return true;", "}", ]); code = functionName + "(" + number_to_check + ")"; return [code, Solidity.ORDER_FUNCTION_CALL]; } switch (dropdown_property) { case "EVEN": code = number_to_check + " % 2 == 0"; break; case "ODD": code = number_to_check + " % 2 == 1"; break; case "WHOLE": code = number_to_check + " % 1 == 0"; break; case "POSITIVE": code = number_to_check + " > 0"; break; case "NEGATIVE": code = number_to_check + " < 0"; break; case "DIVISIBLE_BY": var divisor = Solidity.valueToCode(block, "DIVISOR", Solidity.ORDER_MODULUS) || "0"; code = number_to_check + " % " + divisor + " == 0"; break; } return [code, Solidity.ORDER_EQUALITY]; }; Solidity["math_change"] = function (block) { // Add to a variable in place. var argument0 = Solidity.valueToCode(block, "DELTA", Solidity.ORDER_ADDITION) || "0"; var varName = Solidity.variableDB_.getName( block.getFieldValue("VAR"), Blockly.Variables.NAME_TYPE ); return ( varName + " = (typeof " + varName + " == 'number' ? " + varName + " : 0) + " + argument0 + ";\n" ); }; // Rounding functions have a single operand. Solidity["math_round"] = Solidity["math_single"]; // Trigonometry functions have a single operand. Solidity["math_trig"] = Solidity["math_single"]; Solidity["math_on_list"] = function (block) { // Math functions for lists. var func = block.getFieldValue("OP"); var list, code; switch (func) { case "SUM": list = Solidity.valueToCode(block, "LIST", Solidity.ORDER_MEMBER) || "[]"; code = list + ".reduce(function(x, y) {return x + y;})"; break; case "MIN": list = Solidity.valueToCode(block, "LIST", Solidity.ORDER_COMMA) || "[]"; code = "Math.min.apply(null, " + list + ")"; break; case "MAX": list = Solidity.valueToCode(block, "LIST", Solidity.ORDER_COMMA) || "[]"; code = "Math.max.apply(null, " + list + ")"; break; case "AVERAGE": // mathMean([null,null,1,3]) == 2.0. var functionName = Solidity.provideFunction_("mathMean", [ "function " + Solidity.FUNCTION_NAME_PLACEHOLDER_ + "(myList) {", " return myList.reduce(function(x, y) {return x + y;}) / " + "myList.length;", "}", ]); list = Solidity.valueToCode(block, "LIST", Solidity.ORDER_NONE) || "[]"; code = functionName + "(" + list + ")"; break; case "MEDIAN": // mathMedian([null,null,1,3]) == 2.0. var functionName = Solidity.provideFunction_("mathMedian", [ "function " + Solidity.FUNCTION_NAME_PLACEHOLDER_ + "(myList) {", " var localList = myList.filter(function (x) " + "{return typeof x == 'number';});", " if (!localList.length) return null;", " localList.sort(function(a, b) {return b - a;});", " if (localList.length % 2 == 0) {", " return (localList[localList.length / 2 - 1] + " + "localList[localList.length / 2]) / 2;", " } else {", " return localList[(localList.length - 1) / 2];", " }", "}", ]); list = Solidity.valueToCode(block, "LIST", Solidity.ORDER_NONE) || "[]"; code = functionName + "(" + list + ")"; break; case "MODE": // As a list of numbers can contain more than one mode, // the returned result is provided as an array. // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. var functionName = Solidity.provideFunction_("mathModes", [ "function " + Solidity.FUNCTION_NAME_PLACEHOLDER_ + "(values) {", " var modes = [];", " var counts = [];", " var maxCount = 0;", " for (var i = 0; i < values.length; i++) {", " var value = values[i];", " var found = false;", " var thisCount;", " for (var j = 0; j < counts.length; j++) {", " if (counts[j][0] === value) {", " thisCount = ++counts[j][1];", " found = true;", " break;", " }", " }", " if (!found) {", " counts.push([value, 1]);", " thisCount = 1;", " }", " maxCount = Math.max(thisCount, maxCount);", " }", " for (var j = 0; j < counts.length; j++) {", " if (counts[j][1] == maxCount) {", " modes.push(counts[j][0]);", " }", " }", " return modes;", "}", ]); list = Solidity.valueToCode(block, "LIST", Solidity.ORDER_NONE) || "[]"; code = functionName + "(" + list + ")"; break; case "STD_DEV": var functionName = Solidity.provideFunction_("mathStandardDeviation", [ "function " + Solidity.FUNCTION_NAME_PLACEHOLDER_ + "(numbers) {", " var n = numbers.length;", " if (!n) return null;", " var mean = numbers.reduce(function(x, y) {return x + y;}) / n;", " var variance = 0;", " for (var j = 0; j < n; j++) {", " variance += Math.pow(numbers[j] - mean, 2);", " }", " variance = variance / n;", " return Math.sqrt(variance);", "}", ]); list = Solidity.valueToCode(block, "LIST", Solidity.ORDER_NONE) || "[]"; code = functionName + "(" + list + ")"; break; case "RANDOM": var functionName = Solidity.provideFunction_("mathRandomList", [ "function " + Solidity.FUNCTION_NAME_PLACEHOLDER_ + "(list) {", " var x = Math.floor(Math.random() * list.length);", " return list[x];", "}", ]); list = Solidity.valueToCode(block, "LIST", Solidity.ORDER_NONE) || "[]"; code = functionName + "(" + list + ")"; break; default: throw "Unknown operator: " + func; } return [code, Solidity.ORDER_FUNCTION_CALL]; }; Solidity["math_modulo"] = function (block) { // Remainder computation. var argument0 = Solidity.valueToCode(block, "DIVIDEND", Solidity.ORDER_MODULUS) || "0"; var argument1 = Solidity.valueToCode(block, "DIVISOR", Solidity.ORDER_MODULUS) || "0"; var code = argument0 + " % " + argument1; return [code, Solidity.ORDER_MODULUS]; }; Solidity["math_constrain"] = function (block) { // Constrain a number between two limits. var argument0 = Solidity.valueToCode(block, "VALUE", Solidity.ORDER_COMMA) || "0"; var argument1 = Solidity.valueToCode(block, "LOW", Solidity.ORDER_COMMA) || "0"; var argument2 = Solidity.valueToCode(block, "HIGH", Solidity.ORDER_COMMA) || "Infinity"; var code = "Math.min(Math.max(" + argument0 + ", " + argument1 + "), " + argument2 + ")"; return [code, Solidity.ORDER_FUNCTION_CALL]; }; Solidity["math_random_int"] = function (block) { // Random integer between [X] and [Y]. var argument0 = Solidity.valueToCode(block, "FROM", Solidity.ORDER_COMMA) || "0"; var argument1 = Solidity.valueToCode(block, "TO", Solidity.ORDER_COMMA) || "0"; var functionName = Solidity.provideFunction_("mathRandomInt", [ "function " + Solidity.FUNCTION_NAME_PLACEHOLDER_ + "(a, b) {", " if (a > b) {", " // Swap a and b to ensure a is smaller.", " var c = a;", " a = b;", " b = c;", " }", " return Math.floor(Math.random() * (b - a + 1) + a);", "}", ]); var code = functionName + "(" + argument0 + ", " + argument1 + ")"; return [code, Solidity.ORDER_FUNCTION_CALL]; }; Solidity["math_random_float"] = function (block) { // Random fraction between 0 and 1. return ["Math.random()", Solidity.ORDER_FUNCTION_CALL]; }; Solidity["contract_method"] = function (block) { var params = Solidity.statementToCode(block, "PARAMS").trim(); var branch = Solidity.statementToCode(block, "STACK"); var code = "function " + block.getFieldValue("NAME") + "(" + params + ") {\n" + branch + "\n}\n\n"; return code; }; Solidity["contract_ctor"] = function (block) { var parent = block.getSurroundParent(); if (!parent) { return ""; } var params = Solidity.statementToCode(block, "PARAMS").trim(); var branch = Solidity.statementToCode(block, "STACK"); var code = "constructor " + "(" + params + ") {\n" + branch + "}\n\n"; return code; }; Solidity["contract_method_parameter"] = function (block) { var name = block.getFieldValue("NAME"); var nextBlock = block.getNextBlock(); var sep = nextBlock && nextBlock.type == block.type ? ", " : ""; var types = { TYPE_BOOL: "bool", TYPE_INT: "int", TYPE_UINT: "uint", TYPE_STRING: "string", TYPE_ADDRESS: "address", }; return types[block.getFieldValue("TYPE")] + " " + name + sep; }; Solidity["contract_method_parameter_get"] = function (block) { var variableId = block.getFieldValue("PARAM_NAME"); var variable = block.workspace.getVariableById(variableId); if (!variable) { return ""; } return [Solidity.getVariableName(variable), Solidity.ORDER_ATOMIC]; }; Solidity["contract_intrinsic_sha3"] = function (block) { var argument0 = Solidity.valueToCode(block, "VALUE", Solidity.ORDER_ASSIGNMENT) || "0"; return ["sha3(" + argument0 + ")", Solidity.ORDER_ATOMIC]; }; Solidity["contract_method_call"] = function (block) { var variableId = block.getFieldValue("METHOD_NAME"); var variable = block.workspace.getVariableById(variableId); if (!variable) { return ""; } return Solidity.getVariableName(variable) + "();\n"; }; Solidity["variables_get"] = function (block) { // Variable getter. var code = Solidity.variableDB_.getName( block.getFieldValue("VAR"), Blockly.Variables.NAME_TYPE ); return [code, Solidity.ORDER_ATOMIC]; }; Solidity["variables_set"] = function (block) { // Variable setter. var argument0 = Solidity.valueToCode(block, "VALUE", Solidity.ORDER_ASSIGNMENT) || "0"; var varName = Solidity.variableDB_.getName( block.getFieldValue("VAR"), Blockly.Variables.NAME_TYPE ); return varName + " = " + argument0 + ";\n"; }; // Blockly.JavaScript["test_react_field"] = function (block) { // return "console.log('custom block');\n" // } // Blockly.JavaScript["test_react_date_field"] = function (block) { // return "console.log(" + block.getField("DATE").getText() + ");\n" // } // // Solidity["contract"] = function (block) { // // var states = Solidity.statementToCode(block, "STATES"); // // if (states.length > 0) { // // states += "\n"; // // } // // var ctor = Solidity.statementToCode(block, "CTOR"); // // var methods = Solidity.statementToCode(block, "METHODS"); // // // trim newline before ultimate closing curly brace // // if (methods.length > 0) { // // methods = methods.slice(0, -2); // // } else if (ctor.length > 0) { // // ctor = ctor.slice(0, -2); // // } // // var code = // // "//SPDX-License-Identifier: MIT\n\n" + // // "pragma solidity ^0.8.17;\n\n" + // // "contract " + // // block.getFieldValue("NAME") + // // " {\n\n" + // // states + // // ctor + // // methods + // // "}\n"; // // return code; // // }; // /** Global variables **/ // // Transaction variables - Subcategory // Blockly.JavaScript["transaction_constiables"] = function (block) { // const dropdown_transaction = block.getFieldValue("transaction") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "transaction", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // // Block variables - Subcategory // Blockly.JavaScript["block_constiables"] = function (block) { // const dropdown_transaction = block.getFieldValue("block") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "block", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // /* State variables */ // // Keywords - Subcategory // Blockly.JavaScript["keywords"] = function (block) { // const dropdown_transaction = block.getFieldValue("keywords") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "keywords", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["visibility"] = function (block) { // const dropdown_transaction = block.getFieldValue("visibility") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "visibility", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // // Value Types - Subcategory // Blockly.JavaScript["uint"] = function (block) { // const dropdown_transaction = block.getFieldValue("uint") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "uint", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["int"] = function (block) { // const dropdown_transaction = block.getFieldValue("int") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "int", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["bytes"] = function (block) { // const dropdown_transaction = block.getFieldValue("bytes") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "bytes", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["string"] = function (block) { // const dropdown_transaction = block.getFieldValue("string") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "string", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["boolean"] = function (block) { // const dropdown_transaction = block.getFieldValue("bool") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "bool", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["bool_value"] = function (block) { // const dropdown_transaction = block.getFieldValue("bool_value") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "bool_value", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["address"] = function (block) { // const dropdown_transaction = block.getFieldValue("address") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "address", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["uintStateVariable"] = function (block) { // const dropdown_uint = block.getFieldValue("uint") // const dropdown_visibility = block.getFieldValue("visibility") // const text_constiable_name = block.getFieldValue("variable_name") // const text_name = block.getFieldValue("NAME") // const code = `${dropdown_uint} ${dropdown_visibility} ${text_constiable_name} = ${text_name} \n` // return [code, Blockly.JavaScript.ORDER_NONE] // } // // Reference Types - Subcategory // Blockly.JavaScript["mapping"] = function (block) { // const key = // Blockly.JavaScript.valueToCode(block, "KEY", Blockly.JavaScript.ORDER_ATOMIC) || null // const value = // Blockly.JavaScript.valueToCode(block, "VALUE", Blockly.JavaScript.ORDER_ATOMIC) || null // const code = `mapping(${key} => ${value})` // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["fixed_sized_arrays"] = function (block) { // const key = // Blockly.JavaScript.valueToCode(block, "KEY", Blockly.JavaScript.ORDER_ATOMIC) || null // const size = // Blockly.JavaScript.valueToCode(block, "SIZE", Blockly.JavaScript.ORDER_ATOMIC) || null // const code = `${key}[${size}]` // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["dynamic_arrays"] = function (block) { // const key = // Blockly.JavaScript.valueToCode(block, "KEY", Blockly.JavaScript.ORDER_ATOMIC) || null // const code = `${key}[]` // return [code, Blockly.JavaScript.ORDER_NONE] // } // /* Functions Category*/ // // Normal Functions - Subcategory // Blockly.JavaScript["function_name"] = function (block) { // const function_name = // block.getFieldValue("function_name") || "" // const code = `function ${function_name}()` // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["function_visibility"] = function (block) { // const dropdown_transaction = block.getFieldValue("function_visibility") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "function_visibility", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["payable"] = function (block) { // const function_name = // block.getFieldValue("payable") || "" // const code = function_name // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["function_state_mutability"] = function (block) { // const dropdown_transaction = block.getFieldValue("state_mutability") // const value_transaction = // Blockly.JavaScript.valueToCode(block, "state_mutability", Blockly.JavaScript.ORDER_ATOMIC) || // dropdown_transaction // const code = value_transaction // return [code, Blockly.JavaScript.ORDER_NONE] // } // // Special Functions - Subcategory // // Local Variables Category // // Handling Errors // Blockly.JavaScript["assert"] = function (block) { // const value_name = // Blockly.JavaScript.valueToCode(block, "assert", Blockly.JavaScript.ORDER_ATOMIC) || // "assert" // const code = value_name // return [code, Blockly.JavaScript.ORDER_NONE] // } // Blockly.JavaScript["require"] = function (block) { // const value_require = // Blockly.JavaScript.valueToCode(block, "require", Blockly.JavaScript.ORDER_ATOMIC) || // "require" // const code = value_require // return [code, Blockly.JavaScript.ORDER_ATOMIC] // } // Blockly.JavaScript["revert"] = function (block) { // const value_revert = // Blockly.JavaScript.valueToCode(block, "revert", Blockly.JavaScript.ORDER_ATOMIC) || // "revert" // const code = value_revert // return [code, Blockly.JavaScript.ORDER_ATOMIC] // }