diff --git a/README.rst b/README.rst index bc2e4a2a06..bfc57f60ac 100644 --- a/README.rst +++ b/README.rst @@ -44,6 +44,8 @@ Features - Ethereum wallet support - Auto-generation of Java smart contract wrappers to create, deploy, transact with and call smart contracts from native Java code + - from solc output; or + - from Truffle `.json` contract files. - Reactive-functional API for working with filters - Support for Parity's `Personal `__, and Geth's @@ -251,7 +253,24 @@ Now you can create and deploy your smart contract: GAS_PRICE, GAS_LIMIT, , ..., ).send(); // constructor params -Or use an existing contract: +Alternatively, if you use `Truffle `_, you can make use of its `.json` output files: + +.. code-block:: bash + + # Inside your Truffle project + $ truffle compile + $ truffle deploy + +Then generate the wrapper code using web3j's `Command line tools`_: + +.. code-block:: bash + + $ cd /path/to/your/web3j/java/project + $ web3j truffle generate /path/to/.json -o /path/to/src/main/java -p com.your.organisation.name + +Whether using `Truffle` or `solc` directly, either way you get a ready-to-use Java wrapper for your contract. + +So, to use an existing contract: .. code-block:: java diff --git a/codegen/src/main/java/org/web3j/codegen/FunctionWrapperGenerator.java b/codegen/src/main/java/org/web3j/codegen/FunctionWrapperGenerator.java new file mode 100644 index 0000000000..4314f2e2a6 --- /dev/null +++ b/codegen/src/main/java/org/web3j/codegen/FunctionWrapperGenerator.java @@ -0,0 +1,69 @@ +package org.web3j.codegen; + +import java.io.File; + +import static org.web3j.codegen.Console.exitError; + +/** + * Abstract base class for the concrete function wrapper generators. + */ +abstract class FunctionWrapperGenerator { + + static final String JAVA_TYPES_ARG = "--javaTypes"; + static final String SOLIDITY_TYPES_ARG = "--solidityTypes"; + + final File destinationDirLocation; + final String basePackageName; + final boolean useJavaNativeTypes; + + FunctionWrapperGenerator( + String destinationDirLocation, + String basePackageName, + boolean useJavaNativeTypes) { + + this.destinationDirLocation = new File(destinationDirLocation); + this.basePackageName = basePackageName; + this.useJavaNativeTypes = useJavaNativeTypes; + } + + static boolean useJavaNativeTypes(String argVal, String usageString) { + boolean useJavaNativeTypes = true; + if (SOLIDITY_TYPES_ARG.equals(argVal)) { + useJavaNativeTypes = false; + } else if (JAVA_TYPES_ARG.equals(argVal)) { + useJavaNativeTypes = true; + } else { + exitError(usageString); + } + return useJavaNativeTypes; + } + + static String parsePositionalArg(String[] args, int idx) { + if (args != null && args.length > idx) { + return args[idx]; + } else { + return ""; + } + } + + static String parseParameterArgument(String[] args, String... parameters) { + for (String parameter : parameters) { + for (int i = 0; i < args.length; i++) { + if (args[i].equals(parameter) + && i + 1 < args.length) { + String parameterValue = args[i + 1]; + if (!parameterValue.startsWith("-")) { + return parameterValue; + } + } + } + } + return ""; + } + + static String getFileNameNoExtension(String fileName) { + String[] splitName = fileName.split("\\.(?=[^.]*$)"); + return splitName[0]; + } + +} diff --git a/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapper.java b/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapper.java index 9ddb649f58..98bcd5fc23 100644 --- a/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapper.java +++ b/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapper.java @@ -5,7 +5,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import javax.lang.model.element.Modifier; @@ -81,10 +83,21 @@ public SolidityFunctionWrapper(boolean useNativeJavaTypes) { this.useNativeJavaTypes = useNativeJavaTypes; } + @SuppressWarnings("unchecked") public void generateJavaFiles( String contractName, String bin, String abi, String destinationDir, String basePackageName) throws IOException, ClassNotFoundException { + generateJavaFiles(contractName, bin, + loadContractDefinition(abi), + destinationDir, basePackageName, + null); + } + + void generateJavaFiles( + String contractName, String bin, List abi, String destinationDir, + String basePackageName, Map addresses) + throws IOException, ClassNotFoundException { String className = Strings.capitaliseFirstLetter(contractName); TypeSpec.Builder classBuilder = createClassBuilder(className, bin); @@ -92,13 +105,52 @@ public void generateJavaFiles( classBuilder.addMethod(buildConstructor(Credentials.class, CREDENTIALS)); classBuilder.addMethod(buildConstructor(TransactionManager.class, TRANSACTION_MANAGER)); classBuilder.addMethods( - buildFunctionDefinitions(className, classBuilder, loadContractDefinition(abi))); + buildFunctionDefinitions(className, classBuilder, abi)); classBuilder.addMethod(buildLoad(className, Credentials.class, CREDENTIALS)); classBuilder.addMethod(buildLoad(className, TransactionManager.class, TRANSACTION_MANAGER)); + addAddressesSupport(classBuilder, addresses); + write(basePackageName, classBuilder.build(), destinationDir); } + private void addAddressesSupport(TypeSpec.Builder classBuilder, Map addresses) { + if (addresses != null) { + + ClassName stringType = ClassName.get(String.class); + ClassName mapType = ClassName.get(HashMap.class); + TypeName mapStringString = ParameterizedTypeName.get(mapType, stringType, stringType); + FieldSpec addressesStaticField = FieldSpec + .builder(mapStringString, "_addresses", + Modifier.PROTECTED, Modifier.STATIC, Modifier.FINAL) + .build(); + classBuilder.addField(addressesStaticField); + + final CodeBlock.Builder staticInit = CodeBlock.builder(); + staticInit.addStatement("_addresses = new HashMap<>()"); + addresses.forEach((k, v) -> + staticInit.addStatement(String.format("_addresses.put(\"%1s\", \"%2s\")", k, v)) + ); + classBuilder.addStaticBlock(staticInit.build()); + + // See org.web3j.tx.Contract#getStaticDeployedAddress(String) + MethodSpec getAddress = MethodSpec + .methodBuilder("getStaticDeployedAddress") + .addModifiers(Modifier.PROTECTED) + .returns(stringType) + .addParameter(stringType, "networkId") + .addCode( + CodeBlock + .builder() + .addStatement("return _addresses.get(networkId)") + .build()) + .build(); + classBuilder.addMethod(getAddress); + + } + } + + private TypeSpec.Builder createClassBuilder(String className, String binary) { String javadoc = CODEGEN_WARNING + getWeb3jVersion(); diff --git a/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapperGenerator.java b/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapperGenerator.java index cd0ec1d95d..7716578ecc 100644 --- a/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapperGenerator.java +++ b/codegen/src/main/java/org/web3j/codegen/SolidityFunctionWrapperGenerator.java @@ -18,7 +18,7 @@ /** * Java wrapper source code generator for Solidity ABI format. */ -public class SolidityFunctionWrapperGenerator { +public class SolidityFunctionWrapperGenerator extends FunctionWrapperGenerator { private static final String USAGE = "solidity generate " + "[--javaTypes|--solidityTypes] " @@ -26,14 +26,8 @@ public class SolidityFunctionWrapperGenerator { + "-p|--package " + "-o|--output "; - static final String JAVA_TYPES_ARG = "--javaTypes"; - static final String SOLIDITY_TYPES_ARG = "--solidityTypes"; - - private String binaryFileLocation; - private String absFileLocation; - private File destinationDirLocation; - private String basePackageName; - private boolean useJavaNativeTypes; + private final String binaryFileLocation; + private final String absFileLocation; private SolidityFunctionWrapperGenerator( String binaryFileLocation, @@ -42,11 +36,9 @@ private SolidityFunctionWrapperGenerator( String basePackageName, boolean useJavaNativeTypes) { + super(destinationDirLocation, basePackageName, useJavaNativeTypes); this.binaryFileLocation = binaryFileLocation; this.absFileLocation = absFileLocation; - this.destinationDirLocation = new File(destinationDirLocation); - this.basePackageName = basePackageName; - this.useJavaNativeTypes = useJavaNativeTypes; } public static void run(String[] args) throws Exception { @@ -72,14 +64,7 @@ public static void main(String[] args) throws Exception { exitError(USAGE); } - boolean useJavaNativeTypes = true; - if (fullArgs[0].equals(SOLIDITY_TYPES_ARG)) { - useJavaNativeTypes = false; - } else if (fullArgs[0].equals(JAVA_TYPES_ARG)) { - useJavaNativeTypes = true; - } else { - exitError(USAGE); - } + boolean useJavaNativeTypes = useJavaNativeTypes(fullArgs[0], USAGE); String binaryFileLocation = parsePositionalArg(fullArgs, 1); String absFileLocation = parsePositionalArg(fullArgs, 2); @@ -102,27 +87,11 @@ public static void main(String[] args) throws Exception { .generate(); } - private static String parsePositionalArg(String[] args, int idx) { - if (args != null && args.length > idx) { - return args[idx]; - } else { - return ""; - } - } - - private static String parseParameterArgument(String[] args, String... parameters) { - for (String parameter : parameters) { - for (int i = 0; i < args.length; i++) { - if (args[i].equals(parameter) - && i + 1 < args.length) { - String parameterValue = args[i + 1]; - if (!parameterValue.startsWith("-")) { - return parameterValue; - } - } - } - } - return ""; + static List loadContractDefinition(File absFile) + throws IOException { + ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(); + AbiDefinition[] abiDefinition = objectMapper.readValue(absFile, AbiDefinition[].class); + return Arrays.asList(abiDefinition); } private void generate() throws IOException, ClassNotFoundException { @@ -157,15 +126,5 @@ private void generate() throws IOException, ClassNotFoundException { } } - private static List loadContractDefinition(File absFile) - throws IOException { - ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(); - AbiDefinition[] abiDefinition = objectMapper.readValue(absFile, AbiDefinition[].class); - return Arrays.asList(abiDefinition); - } - static String getFileNameNoExtension(String fileName) { - String[] splitName = fileName.split("\\.(?=[^.]*$)"); - return splitName[0]; - } } \ No newline at end of file diff --git a/codegen/src/main/java/org/web3j/codegen/TruffleJsonFunctionWrapperGenerator.java b/codegen/src/main/java/org/web3j/codegen/TruffleJsonFunctionWrapperGenerator.java new file mode 100644 index 0000000000..545b289f2f --- /dev/null +++ b/codegen/src/main/java/org/web3j/codegen/TruffleJsonFunctionWrapperGenerator.java @@ -0,0 +1,363 @@ +package org.web3j.codegen; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.web3j.protocol.ObjectMapperFactory; +import org.web3j.protocol.core.methods.response.AbiDefinition; +import org.web3j.utils.Strings; + +import static org.web3j.codegen.Console.exitError; +import static org.web3j.utils.Collection.tail; + +/** + * Java wrapper source code generator for Truffle JSON format. Truffle embeds the Solidity ABI + * formatted JSON in its own format. That format also gives access to the binary code. It also + * contains information about deployment addresses. This should make integration with Truffle + * easier. + */ +public class TruffleJsonFunctionWrapperGenerator extends FunctionWrapperGenerator { + + private static final String USAGE = "truffle generate " + + "[--javaTypes|--solidityTypes] " + + ".json " + + "-p|--package " + + "-o|--output "; + + + private String jsonFileLocation; + + private TruffleJsonFunctionWrapperGenerator( + String jsonFileLocation, + String destinationDirLocation, + String basePackageName, + boolean useJavaNativeTypes) { + + super(destinationDirLocation, basePackageName, useJavaNativeTypes); + this.jsonFileLocation = jsonFileLocation; + } + + public static void run(String[] args) throws Exception { + if (args.length < 1 || !"generate".equals(args[0])) { + exitError(USAGE); + } else { + main(tail(args)); + } + } + + public static void main(String[] args) throws Exception { + + String[] fullArgs; + if (args.length == 5) { + fullArgs = new String[args.length + 1]; + fullArgs[0] = JAVA_TYPES_ARG; + System.arraycopy(args, 0, fullArgs, 1, args.length); + } else { + fullArgs = args; + } + + if (fullArgs.length != 6) { + exitError(USAGE); + } + + boolean useJavaNativeTypes = useJavaNativeTypes(fullArgs[0], USAGE); + + String jsonFileLocation = parsePositionalArg(fullArgs, 1); + String destinationDirLocation = parseParameterArgument(fullArgs, "-o", "--outputDir"); + String basePackageName = parseParameterArgument(fullArgs, "-p", "--package"); + + if (Strings.isEmpty(jsonFileLocation) + || Strings.isEmpty(destinationDirLocation) + || Strings.isEmpty(basePackageName)) { + exitError(USAGE); + } + + new TruffleJsonFunctionWrapperGenerator( + jsonFileLocation, + destinationDirLocation, + basePackageName, + useJavaNativeTypes) + .generate(); + } + + static Contract loadContractDefinition(File jsonFile) + throws IOException { + ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(); + return objectMapper.readValue(jsonFile, Contract.class); + } + + @SuppressWarnings("unchecked") + private void generate() throws IOException, ClassNotFoundException { + + File truffleJsonFile = new File(jsonFileLocation); + if (!truffleJsonFile.exists() || !truffleJsonFile.canRead()) { + exitError("Invalid input json file specified: " + jsonFileLocation); + } + + String fileName = truffleJsonFile.getName(); + String contractName = getFileNameNoExtension(fileName); + + Contract c = loadContractDefinition(truffleJsonFile); + if (c == null) { + exitError("Unable to parse input json file"); + } else { + String className = Strings.capitaliseFirstLetter(contractName); + System.out.printf("Generating " + basePackageName + "." + className + " ... "); + Map addresses; + if (c.networks != null && !c.networks.isEmpty()) { + addresses = c.networks.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, e -> e.getValue().getAddress() + )); + } else { + addresses = Collections.EMPTY_MAP; + } + new SolidityFunctionWrapper(useJavaNativeTypes) + .generateJavaFiles(contractName, + c.getBytecode(), + c.getAbi(), + destinationDirLocation.toString(), + basePackageName, + addresses); + System.out.println("File written to " + destinationDirLocation.toString() + "\n"); + } + } + + /** + * Truffle Contract

Describes a contract exported by and consumable by Truffle, which may + * include information about deployed instances on networks.

+ */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonPropertyOrder({ + "contractName", + "abi", + "bytecode", + "deployedBytecode", + "sourceMap", + "deployedSourceMap", + "source", + "sourcePath", + "ast", + "compiler", + "networks", + "schemaVersion", + "updatedAt" + }) + public static class Contract { + + @JsonProperty("contractName") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "^[a-zA-Z_][a-zA-Z0-9_]*$") + public String contractName; + @JsonProperty(value = "abi", required = true) + public List abi; + @JsonProperty("bytecode") + @JsonFormat(shape = JsonFormat.Shape.STRING, + pattern = "^0x0$|^0x([a-fA-F0-9]{2}|__.{38})+$") + public String bytecode; + @JsonProperty("deployedBytecode") + @JsonFormat(shape = JsonFormat.Shape.STRING, + pattern = "^0x0$|^0x([a-fA-F0-9]{2}|__.{38})+$") + public String deployedBytecode; + @JsonProperty("sourceMap") + public String sourceMap; + @JsonProperty("deployedSourceMap") + public String deployedSourceMap; + @JsonProperty("source") + public String source; + @JsonProperty("sourcePath") + public String sourcePath; + @JsonProperty("ast") + public JsonNode ast; + @JsonProperty("compiler") + public Compiler compiler; + @JsonProperty("networks") + public Map networks; + @JsonProperty("schemaVersion") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "[0-9]+\\.[0-9]+\\.[0-9]+") + public String schemaVersion; + @JsonProperty("updatedAt") + @JsonFormat(shape = JsonFormat.Shape.STRING, + pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "GMT") + public Date updatedAt; + + /** + * No args constructor for use in serialization. + */ + public Contract() { + } + + /** + * Default constructor. + */ + public Contract(String contractName, List abi, String bytecode, + String deployedBytecode, + String sourceMap, String deployedSourceMap, String source, String sourcePath, + JsonNode ast, + Compiler compiler, Map networks, String schemaVersion, + Date updatedAt) { + super(); + this.contractName = contractName; + this.abi = abi; + this.bytecode = bytecode; + this.deployedBytecode = deployedBytecode; + this.sourceMap = sourceMap; + this.deployedSourceMap = deployedSourceMap; + this.source = source; + this.sourcePath = sourcePath; + this.ast = ast; + this.compiler = compiler; + this.networks = networks; + this.schemaVersion = schemaVersion; + this.updatedAt = updatedAt; + } + + public String getContractName() { + return contractName; + } + + public List getAbi() { + return abi; + } + + public String getBytecode() { + return bytecode; + } + + public NetworkInfo getNetwork(String networkId) { + return networks.get(networkId); + } + + public String getAddress(String networkId) { + NetworkInfo network = getNetwork(networkId); + return network == null ? null : network.getAddress(); + } + + /** + * Convenience method to get the deployed address of the contract. + * + * @param network the contract's address on this Ethereum network + * @return the contract's address or null if there isn't one known. + */ + public String getAddress(Network network) { + return getAddress(Long.toString(network.id)); + } + + /* + * c.f., org.web3j.tx.ChainId + */ + enum Network { + olympic(0), mainnet(1), morden(2), ropsten(3), rinkeby(4), kovan(42); + + public final long id; + + Network(long id) { + this.id = id; + } + } + + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonPropertyOrder({ + "name", + "version" + }) + public static class Compiler { + + @JsonProperty("name") + public String name; + @JsonProperty("version") + public String version; + @JsonIgnore + private Map additionalProperties = new HashMap(); + + /** + * No args constructor for use in serialization. + */ + public Compiler() { + } + + /** + * Default constructor. + */ + public Compiler(String name, String version) { + super(); + this.name = name; + this.version = version; + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, JsonNode value) { + this.additionalProperties.put(name, value); + } + + public Compiler withAdditionalProperty(String name, JsonNode value) { + this.additionalProperties.put(name, value); + return this; + } + + } + + + // For now we just ignore "events" + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonPropertyOrder({ + "events", + "links", + "address" + }) + public static class NetworkInfo { + + @JsonProperty("events") + public Map events; + @JsonProperty("links") + public Map links; + @JsonProperty("address") + public String address; + + /** + * No args constructor for use in serialization. + */ + public NetworkInfo() { + } + + public NetworkInfo(Map events, Map links, + String address) { + super(); + this.events = events; + this.links = links; + this.address = address; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + } + +} \ No newline at end of file diff --git a/codegen/src/test/java/org/web3j/codegen/ContractJsonParseTest.java b/codegen/src/test/java/org/web3j/codegen/ContractJsonParseTest.java new file mode 100644 index 0000000000..e345c78496 --- /dev/null +++ b/codegen/src/test/java/org/web3j/codegen/ContractJsonParseTest.java @@ -0,0 +1,68 @@ +package org.web3j.codegen; + +import java.io.File; +import java.net.URL; + +import org.junit.Before; +import org.junit.Test; + +import org.web3j.protocol.core.methods.response.AbiDefinition; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.web3j.codegen.TruffleJsonFunctionWrapperGenerator.Contract; +import static org.web3j.codegen.TruffleJsonFunctionWrapperGenerator.loadContractDefinition; + +/** + * Test that we can parse Truffle Contract from JSON file. + */ +public class ContractJsonParseTest { + + static final String BUILD_CONTRACTS = "build" + File.separator + "contracts"; + private String contractBaseDir; + + static String jsonFileLocation(String baseDir, + String contractName, String inputFileName) { + return baseDir + File.separator + contractName + File.separator + BUILD_CONTRACTS + + File.separator + inputFileName + ".json"; + } + + @SuppressWarnings("SameParameterValue") + static Contract parseContractJson(String baseDir, + String contractName, String inputFileName) + throws Exception { + + String fileLocation = jsonFileLocation(baseDir, contractName, inputFileName); + return loadContractDefinition(new File(fileLocation)); + } + + @Before + public void setUp() throws Exception { + URL url = SolidityFunctionWrapperGeneratorTest.class.getClass().getResource("/truffle"); + contractBaseDir = url.getPath(); + } + + @Test + public void testParseMetaCoin() throws Exception { + Contract mc = parseContractJson(contractBaseDir, "MetaCoin", "MetaCoin"); + + assertEquals("Unexpected contract name", "MetaCoin", mc.getContractName()); + } + + @Test + public void testParseConvertLib() throws Exception { + Contract mc = parseContractJson(contractBaseDir, "MetaCoin", "ConvertLib"); + + assertEquals("Unexpected contract name", "ConvertLib", mc.getContractName()); + assertEquals("Unexpected number of functions", 1, mc.abi.size()); + AbiDefinition abi = mc.abi.get(0); + assertEquals("Unexpected function name", "convert", abi.getName()); + assertTrue("Expected function to be 'constant'", abi.isConstant()); + assertFalse("Expected function to not be 'payable'", abi.isPayable()); + assertEquals("Expected abi to represent a function", "function", abi.getType()); + assertEquals("Expected the 'pure' for the state mutability setting", "pure", + abi.getStateMutability()); + + } +} \ No newline at end of file diff --git a/codegen/src/test/java/org/web3j/codegen/SolidityFunctionWrapperGeneratorTest.java b/codegen/src/test/java/org/web3j/codegen/SolidityFunctionWrapperGeneratorTest.java index d899c903cb..966a192ea9 100644 --- a/codegen/src/test/java/org/web3j/codegen/SolidityFunctionWrapperGeneratorTest.java +++ b/codegen/src/test/java/org/web3j/codegen/SolidityFunctionWrapperGeneratorTest.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.net.URL; import java.util.Arrays; -import java.util.Collections; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; diff --git a/codegen/src/test/java/org/web3j/codegen/TruffleJsonFunctionWrapperGeneratorTest.java b/codegen/src/test/java/org/web3j/codegen/TruffleJsonFunctionWrapperGeneratorTest.java new file mode 100644 index 0000000000..008d36dd67 --- /dev/null +++ b/codegen/src/test/java/org/web3j/codegen/TruffleJsonFunctionWrapperGeneratorTest.java @@ -0,0 +1,101 @@ +package org.web3j.codegen; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Arrays; +import java.util.Collections; + +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +import org.junit.Before; +import org.junit.Test; + +import org.web3j.TempFileProvider; +import org.web3j.utils.Strings; + +import static org.junit.Assert.assertTrue; +import static org.web3j.codegen.FunctionWrapperGenerator.JAVA_TYPES_ARG; +import static org.web3j.codegen.FunctionWrapperGenerator.SOLIDITY_TYPES_ARG; + +public class TruffleJsonFunctionWrapperGeneratorTest extends TempFileProvider { + + private static final String PackageName = "org.web3j.unittests.truffle.java"; + + private String contractBaseDir; + + private static void verifyGeneratedCode(String sourceFile) throws IOException { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + + try (StandardJavaFileManager fileManager = + compiler.getStandardFileManager(diagnostics, null, null)) { + Iterable compilationUnits = fileManager + .getJavaFileObjectsFromStrings(Collections.singletonList(sourceFile)); + JavaCompiler.CompilationTask task = compiler.getTask( + null, fileManager, diagnostics, null, null, compilationUnits); + assertTrue("Generated contract contains compile time error", task.call()); + } + } + + @Before + public void setUp() throws Exception { + super.setUp(); + + URL url = SolidityFunctionWrapperGeneratorTest.class.getClass().getResource("/truffle"); + contractBaseDir = url.getPath(); + } + + @Test + public void testLibGeneration() throws Exception { + testCodeGenerationJvmTypes("MetaCoin", "ConvertLib"); + testCodeGenerationSolidtyTypes("MetaCoin", "ConvertLib"); + } + + @Test + public void testContractGeneration() throws Exception { + testCodeGenerationJvmTypes("MetaCoin", "MetaCoin"); + testCodeGenerationSolidtyTypes("MetaCoin", "MetaCoin"); + } + + @SuppressWarnings("SameParameterValue") + private void testCodeGenerationJvmTypes( + String contractName, String inputFileName) throws Exception { + + testCodeGeneration( + contractName, inputFileName, PackageName, JAVA_TYPES_ARG); + + } + + @SuppressWarnings("SameParameterValue") + private void testCodeGenerationSolidtyTypes( + String contractName, String inputFileName) throws Exception { + + testCodeGeneration( + contractName, inputFileName, PackageName, SOLIDITY_TYPES_ARG); + + } + + private void testCodeGeneration( + String contractName, String inputFileName, String packageName, String types) + throws Exception { + + TruffleJsonFunctionWrapperGenerator.main(Arrays.asList( + types, + ContractJsonParseTest + .jsonFileLocation(contractBaseDir, contractName, inputFileName), + "-p", packageName, + "-o", tempDirPath + ).toArray(new String[0])); + + verifyGeneratedCode(tempDirPath + File.separator + + packageName.replace('.', File.separatorChar) + File.separator + + Strings.capitaliseFirstLetter(inputFileName) + ".java"); + } + + +} \ No newline at end of file diff --git a/codegen/src/test/resources/truffle/MetaCoin/ConvertLib.sol b/codegen/src/test/resources/truffle/MetaCoin/ConvertLib.sol new file mode 100644 index 0000000000..99f004dfe2 --- /dev/null +++ b/codegen/src/test/resources/truffle/MetaCoin/ConvertLib.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.4.2; + +library ConvertLib{ + function convert(uint amount,uint conversionRate) pure public returns (uint convertedAmount) + { + return amount * conversionRate; + } +} diff --git a/codegen/src/test/resources/truffle/MetaCoin/MetaCoin.sol b/codegen/src/test/resources/truffle/MetaCoin/MetaCoin.sol new file mode 100644 index 0000000000..2658d0466f --- /dev/null +++ b/codegen/src/test/resources/truffle/MetaCoin/MetaCoin.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.4.2; + +import "./ConvertLib.sol"; + +// This is just a simple example of a coin-like contract. +// It is not standards compatible and cannot be expected to talk to other +// coin/token contracts. If you want to create a standards-compliant +// token, see: https://github.com/ConsenSys/Tokens. Cheers! + +contract MetaCoin { + mapping (address => uint) balances; + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + + function MetaCoin() public { + balances[tx.origin] = 10000; + } + + function sendCoin(address receiver, uint amount) public returns(bool sufficient) { + if (balances[msg.sender] < amount) return false; + balances[msg.sender] -= amount; + balances[receiver] += amount; + Transfer(msg.sender, receiver, amount); + return true; + } + + function getBalanceInEth(address addr) view public returns(uint){ + return ConvertLib.convert(getBalance(addr),2); + } + + function getBalance(address addr) view public returns(uint) { + return balances[addr]; + } +} diff --git a/codegen/src/test/resources/truffle/MetaCoin/build/contracts/ConvertLib.json b/codegen/src/test/resources/truffle/MetaCoin/build/contracts/ConvertLib.json new file mode 100644 index 0000000000..a54254200b --- /dev/null +++ b/codegen/src/test/resources/truffle/MetaCoin/build/contracts/ConvertLib.json @@ -0,0 +1,281 @@ +{ + "contractName": "ConvertLib", + "abi": [ + { + "constant": true, + "inputs": [ + { + "name": "amount", + "type": "uint256" + }, + { + "name": "conversionRate", + "type": "uint256" + } + ], + "name": "convert", + "outputs": [ + { + "name": "convertedAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x6060604052341561000f57600080fd5b60b08061001d6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806396e4ee3d146044575b600080fd5b606160048080359060200190919080359060200190919050506077565b6040518082815260200191505060405180910390f35b60008183029050929150505600a165627a7a72305820fc2416f7929b800f6d4af1a1e6d2a8d7d8ac81998f8c072a99314c7bf5c2a5f80029", + "deployedBytecode": "0x606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806396e4ee3d146044575b600080fd5b606160048080359060200190919080359060200190919050506077565b6040518082815260200191505060405180910390f35b60008183029050929150505600a165627a7a72305820fc2416f7929b800f6d4af1a1e6d2a8d7d8ac81998f8c072a99314c7bf5c2a5f80029", + "sourceMap": "25:155:0:-;;;;;;;;;;;;;;;;;", + "deployedSourceMap": "25:155:0:-;;;;;;;;;;;;;;;;;;;;;;;;46:132;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;117:20;160:14;151:6;:23;144:30;;46:132;;;;:::o", + "source": "pragma solidity ^0.4.2;\n\nlibrary ConvertLib{\n\tfunction convert(uint amount,uint conversionRate) pure public returns (uint convertedAmount)\n\t{\n\t\treturn amount * conversionRate;\n\t}\n}\n", + "sourcePath": "/Users/ezra/Developer/blockchain/truffle_webpack/contracts/ConvertLib.sol", + "ast": { + "attributes": { + "absolutePath": "/Users/ezra/Developer/blockchain/truffle_webpack/contracts/ConvertLib.sol", + "exportedSymbols": { + "ConvertLib": [ + 16 + ] + } + }, + "children": [ + { + "attributes": { + "literals": [ + "solidity", + "^", + "0.4", + ".2" + ] + }, + "id": 1, + "name": "PragmaDirective", + "src": "0:23:0" + }, + { + "attributes": { + "baseContracts": [ + null + ], + "contractDependencies": [ + null + ], + "contractKind": "library", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": [ + 16 + ], + "name": "ConvertLib", + "scope": 17 + }, + "children": [ + { + "attributes": { + "constant": true, + "implemented": true, + "isConstructor": false, + "modifiers": [ + null + ], + "name": "convert", + "payable": false, + "scope": 16, + "stateMutability": "pure", + "superFunction": null, + "visibility": "public" + }, + "children": [ + { + "children": [ + { + "attributes": { + "constant": false, + "name": "amount", + "scope": 15, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "uint", + "type": "uint256" + }, + "id": 2, + "name": "ElementaryTypeName", + "src": "63:4:0" + } + ], + "id": 3, + "name": "VariableDeclaration", + "src": "63:11:0" + }, + { + "attributes": { + "constant": false, + "name": "conversionRate", + "scope": 15, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "uint", + "type": "uint256" + }, + "id": 4, + "name": "ElementaryTypeName", + "src": "75:4:0" + } + ], + "id": 5, + "name": "VariableDeclaration", + "src": "75:19:0" + } + ], + "id": 6, + "name": "ParameterList", + "src": "62:33:0" + }, + { + "children": [ + { + "attributes": { + "constant": false, + "name": "convertedAmount", + "scope": 15, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "uint", + "type": "uint256" + }, + "id": 7, + "name": "ElementaryTypeName", + "src": "117:4:0" + } + ], + "id": 8, + "name": "VariableDeclaration", + "src": "117:20:0" + } + ], + "id": 9, + "name": "ParameterList", + "src": "116:22:0" + }, + { + "children": [ + { + "attributes": { + "functionReturnParameters": 9 + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "operator": "*", + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 3, + "type": "uint256", + "value": "amount" + }, + "id": 10, + "name": "Identifier", + "src": "151:6:0" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 5, + "type": "uint256", + "value": "conversionRate" + }, + "id": 11, + "name": "Identifier", + "src": "160:14:0" + } + ], + "id": 12, + "name": "BinaryOperation", + "src": "151:23:0" + } + ], + "id": 13, + "name": "Return", + "src": "144:30:0" + } + ], + "id": 14, + "name": "Block", + "src": "140:38:0" + } + ], + "id": 15, + "name": "FunctionDefinition", + "src": "46:132:0" + } + ], + "id": 16, + "name": "ContractDefinition", + "src": "25:155:0" + } + ], + "id": 17, + "name": "SourceUnit", + "src": "0:181:0" + }, + "compiler": { + "name": "solc", + "version": "0.4.18+commit.9cf6e910.Emscripten.clang" + }, + "networks": { + "4": { + "events": {}, + "links": {}, + "address": "0x14d00a701a2f0d4d65eebaf191f4f60bbaa1da36" + }, + "4447": { + "events": {}, + "links": {}, + "address": "0xcfeb869f69431e42cdb54a4f4f105c19c080a601" + } + }, + "schemaVersion": "1.0.1", + "updatedAt": "2017-11-07T20:44:02.301Z" +} \ No newline at end of file diff --git a/codegen/src/test/resources/truffle/MetaCoin/build/contracts/MetaCoin.json b/codegen/src/test/resources/truffle/MetaCoin/build/contracts/MetaCoin.json new file mode 100644 index 0000000000..8bed99e71e --- /dev/null +++ b/codegen/src/test/resources/truffle/MetaCoin/build/contracts/MetaCoin.json @@ -0,0 +1,1431 @@ +{ + "contractName": "MetaCoin", + "abi": [ + { + "constant": true, + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "name": "getBalanceInEth", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "receiver", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "sendCoin", + "outputs": [ + { + "name": "sufficient", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "addr", + "type": "address" + } + ], + "name": "getBalance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } + ], + "bytecode": "0x6060604052341561000f57600080fd5b6127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506103c5806100636000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680637bd703e81461005c57806390b98a11146100a9578063f8b2cb4f14610103575b600080fd5b341561006757600080fd5b610093600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610150565b6040518082815260200191505060405180910390f35b34156100b457600080fd5b6100e9600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101f8565b604051808215151515815260200191505060405180910390f35b341561010e57600080fd5b61013a600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610351565b6040518082815260200191505060405180910390f35b600073__ConvertLib____________________________6396e4ee3d61017584610351565b60026000604051602001526040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808381526020018281526020019250505060206040518083038186803b15156101d657600080fd5b6102c65a03f415156101e757600080fd5b505050604051805190509050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610249576000905061034b565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509190505600a165627a7a72305820f791829bf0c920a6d43123cac9b9894757ddc838c17efbad6d56773d6e3dcf4c0029", + "deployedBytecode": "0x606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680637bd703e81461005c57806390b98a11146100a9578063f8b2cb4f14610103575b600080fd5b341561006757600080fd5b610093600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610150565b6040518082815260200191505060405180910390f35b34156100b457600080fd5b6100e9600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101f8565b604051808215151515815260200191505060405180910390f35b341561010e57600080fd5b61013a600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610351565b6040518082815260200191505060405180910390f35b600073__ConvertLib____________________________6396e4ee3d61017584610351565b60026000604051602001526040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808381526020018281526020019250505060206040518083038186803b15156101d657600080fd5b6102c65a03f415156101e757600080fd5b505050604051805190509050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610249576000905061034b565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509190505600a165627a7a72305820f791829bf0c920a6d43123cac9b9894757ddc838c17efbad6d56773d6e3dcf4c0029", + "sourceMap": "315:675:1:-;;;452:62;;;;;;;;505:5;483:8;:19;492:9;483:19;;;;;;;;;;;;;;;:27;;;;315:675;;;;;;", + "deployedSourceMap": "315:675:1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;779:117;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;517:259;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;899:89;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;779:117;838:4;854:10;:18;873:16;884:4;873:10;:16::i;:::-;890:1;854:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;847:45;;779:117;;;:::o;517:259::-;581:15;629:6;606:8;:20;615:10;606:20;;;;;;;;;;;;;;;;:29;602:47;;;644:5;637:12;;;;602:47;677:6;653:8;:20;662:10;653:20;;;;;;;;;;;;;;;;:30;;;;;;;;;;;709:6;687:8;:18;696:8;687:18;;;;;;;;;;;;;;;;:28;;;;;;;;;;;740:8;719:38;;728:10;719:38;;;750:6;719:38;;;;;;;;;;;;;;;;;;768:4;761:11;;517:259;;;;;:::o;899:89::-;953:4;970:8;:14;979:4;970:14;;;;;;;;;;;;;;;;963:21;;899:89;;;:::o", + "source": "pragma solidity ^0.4.2;\n\nimport \"./ConvertLib.sol\";\n\n// This is just a simple example of a coin-like contract.\n// It is not standards compatible and cannot be expected to talk to other\n// coin/token contracts. If you want to create a standards-compliant\n// token, see: https://github.com/ConsenSys/Tokens. Cheers!\n\ncontract MetaCoin {\n\tmapping (address => uint) balances;\n\n\tevent Transfer(address indexed _from, address indexed _to, uint256 _value);\n\n\tfunction MetaCoin() public {\n\t\tbalances[tx.origin] = 10000;\n\t}\n\n\tfunction sendCoin(address receiver, uint amount) public returns(bool sufficient) {\n\t\tif (balances[msg.sender] < amount) return false;\n\t\tbalances[msg.sender] -= amount;\n\t\tbalances[receiver] += amount;\n\t\tTransfer(msg.sender, receiver, amount);\n\t\treturn true;\n\t}\n\n\tfunction getBalanceInEth(address addr) view public returns(uint){\n\t\treturn ConvertLib.convert(getBalance(addr),2);\n\t}\n\n\tfunction getBalance(address addr) view public returns(uint) {\n\t\treturn balances[addr];\n\t}\n}\n", + "sourcePath": "/Users/ezra/Developer/blockchain/truffle_webpack/contracts/MetaCoin.sol", + "ast": { + "attributes": { + "absolutePath": "/Users/ezra/Developer/blockchain/truffle_webpack/contracts/MetaCoin.sol", + "exportedSymbols": { + "MetaCoin": [ + 112 + ] + } + }, + "children": [ + { + "attributes": { + "literals": [ + "solidity", + "^", + "0.4", + ".2" + ] + }, + "id": 18, + "name": "PragmaDirective", + "src": "0:23:1" + }, + { + "attributes": { + "SourceUnit": 17, + "absolutePath": "/Users/ezra/Developer/blockchain/truffle_webpack/contracts/ConvertLib.sol", + "file": "./ConvertLib.sol", + "scope": 113, + "symbolAliases": [ + null + ], + "unitAlias": "" + }, + "id": 19, + "name": "ImportDirective", + "src": "25:26:1" + }, + { + "attributes": { + "baseContracts": [ + null + ], + "contractDependencies": [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": [ + 112 + ], + "name": "MetaCoin", + "scope": 113 + }, + "children": [ + { + "attributes": { + "constant": false, + "name": "balances", + "scope": 112, + "stateVariable": true, + "storageLocation": "default", + "type": "mapping(address => uint256)", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "type": "mapping(address => uint256)" + }, + "children": [ + { + "attributes": { + "name": "address", + "type": "address" + }, + "id": 20, + "name": "ElementaryTypeName", + "src": "345:7:1" + }, + { + "attributes": { + "name": "uint", + "type": "uint256" + }, + "id": 21, + "name": "ElementaryTypeName", + "src": "356:4:1" + } + ], + "id": 22, + "name": "Mapping", + "src": "336:25:1" + } + ], + "id": 23, + "name": "VariableDeclaration", + "src": "336:34:1" + }, + { + "attributes": { + "anonymous": false, + "name": "Transfer" + }, + "children": [ + { + "children": [ + { + "attributes": { + "constant": false, + "indexed": true, + "name": "_from", + "scope": 31, + "stateVariable": false, + "storageLocation": "default", + "type": "address", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "address", + "type": "address" + }, + "id": 24, + "name": "ElementaryTypeName", + "src": "389:7:1" + } + ], + "id": 25, + "name": "VariableDeclaration", + "src": "389:21:1" + }, + { + "attributes": { + "constant": false, + "indexed": true, + "name": "_to", + "scope": 31, + "stateVariable": false, + "storageLocation": "default", + "type": "address", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "address", + "type": "address" + }, + "id": 26, + "name": "ElementaryTypeName", + "src": "412:7:1" + } + ], + "id": 27, + "name": "VariableDeclaration", + "src": "412:19:1" + }, + { + "attributes": { + "constant": false, + "indexed": false, + "name": "_value", + "scope": 31, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "uint256", + "type": "uint256" + }, + "id": 28, + "name": "ElementaryTypeName", + "src": "433:7:1" + } + ], + "id": 29, + "name": "VariableDeclaration", + "src": "433:14:1" + } + ], + "id": 30, + "name": "ParameterList", + "src": "388:60:1" + } + ], + "id": 31, + "name": "EventDefinition", + "src": "374:75:1" + }, + { + "attributes": { + "constant": false, + "implemented": true, + "isConstructor": true, + "modifiers": [ + null + ], + "name": "MetaCoin", + "payable": false, + "scope": 112, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": [ + { + "attributes": { + "parameters": [ + null + ] + }, + "children": [], + "id": 32, + "name": "ParameterList", + "src": "469:2:1" + }, + { + "attributes": { + "parameters": [ + null + ] + }, + "children": [], + "id": 33, + "name": "ParameterList", + "src": "479:0:1" + }, + { + "children": [ + { + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "operator": "=", + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 23, + "type": "mapping(address => uint256)", + "value": "balances" + }, + "id": 34, + "name": "Identifier", + "src": "483:8:1" + }, + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "member_name": "origin", + "referencedDeclaration": null, + "type": "address" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 191, + "type": "tx", + "value": "tx" + }, + "id": 35, + "name": "Identifier", + "src": "492:2:1" + } + ], + "id": 36, + "name": "MemberAccess", + "src": "492:9:1" + } + ], + "id": 37, + "name": "IndexAccess", + "src": "483:19:1" + }, + { + "attributes": { + "argumentTypes": null, + "hexvalue": "3130303030", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "number", + "type": "int_const 10000", + "value": "10000" + }, + "id": 38, + "name": "Literal", + "src": "505:5:1" + } + ], + "id": 39, + "name": "Assignment", + "src": "483:27:1" + } + ], + "id": 40, + "name": "ExpressionStatement", + "src": "483:27:1" + } + ], + "id": 41, + "name": "Block", + "src": "479:35:1" + } + ], + "id": 42, + "name": "FunctionDefinition", + "src": "452:62:1" + }, + { + "attributes": { + "constant": false, + "implemented": true, + "isConstructor": false, + "modifiers": [ + null + ], + "name": "sendCoin", + "payable": false, + "scope": 112, + "stateMutability": "nonpayable", + "superFunction": null, + "visibility": "public" + }, + "children": [ + { + "children": [ + { + "attributes": { + "constant": false, + "name": "receiver", + "scope": 83, + "stateVariable": false, + "storageLocation": "default", + "type": "address", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "address", + "type": "address" + }, + "id": 43, + "name": "ElementaryTypeName", + "src": "535:7:1" + } + ], + "id": 44, + "name": "VariableDeclaration", + "src": "535:16:1" + }, + { + "attributes": { + "constant": false, + "name": "amount", + "scope": 83, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "uint", + "type": "uint256" + }, + "id": 45, + "name": "ElementaryTypeName", + "src": "553:4:1" + } + ], + "id": 46, + "name": "VariableDeclaration", + "src": "553:11:1" + } + ], + "id": 47, + "name": "ParameterList", + "src": "534:31:1" + }, + { + "children": [ + { + "attributes": { + "constant": false, + "name": "sufficient", + "scope": 83, + "stateVariable": false, + "storageLocation": "default", + "type": "bool", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "bool", + "type": "bool" + }, + "id": 48, + "name": "ElementaryTypeName", + "src": "581:4:1" + } + ], + "id": 49, + "name": "VariableDeclaration", + "src": "581:15:1" + } + ], + "id": 50, + "name": "ParameterList", + "src": "580:17:1" + }, + { + "children": [ + { + "attributes": { + "falseBody": null + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "operator": "<", + "type": "bool" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 23, + "type": "mapping(address => uint256)", + "value": "balances" + }, + "id": 51, + "name": "Identifier", + "src": "606:8:1" + }, + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "member_name": "sender", + "referencedDeclaration": null, + "type": "address" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 181, + "type": "msg", + "value": "msg" + }, + "id": 52, + "name": "Identifier", + "src": "615:3:1" + } + ], + "id": 53, + "name": "MemberAccess", + "src": "615:10:1" + } + ], + "id": 54, + "name": "IndexAccess", + "src": "606:20:1" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 46, + "type": "uint256", + "value": "amount" + }, + "id": 55, + "name": "Identifier", + "src": "629:6:1" + } + ], + "id": 56, + "name": "BinaryOperation", + "src": "606:29:1" + }, + { + "attributes": { + "functionReturnParameters": 50 + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "hexvalue": "66616c7365", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "bool", + "type": "bool", + "value": "false" + }, + "id": 57, + "name": "Literal", + "src": "644:5:1" + } + ], + "id": 58, + "name": "Return", + "src": "637:12:1" + } + ], + "id": 59, + "name": "IfStatement", + "src": "602:47:1" + }, + { + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "operator": "-=", + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 23, + "type": "mapping(address => uint256)", + "value": "balances" + }, + "id": 60, + "name": "Identifier", + "src": "653:8:1" + }, + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "member_name": "sender", + "referencedDeclaration": null, + "type": "address" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 181, + "type": "msg", + "value": "msg" + }, + "id": 61, + "name": "Identifier", + "src": "662:3:1" + } + ], + "id": 62, + "name": "MemberAccess", + "src": "662:10:1" + } + ], + "id": 63, + "name": "IndexAccess", + "src": "653:20:1" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 46, + "type": "uint256", + "value": "amount" + }, + "id": 64, + "name": "Identifier", + "src": "677:6:1" + } + ], + "id": 65, + "name": "Assignment", + "src": "653:30:1" + } + ], + "id": 66, + "name": "ExpressionStatement", + "src": "653:30:1" + }, + { + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "operator": "+=", + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 23, + "type": "mapping(address => uint256)", + "value": "balances" + }, + "id": 67, + "name": "Identifier", + "src": "687:8:1" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 44, + "type": "address", + "value": "receiver" + }, + "id": 68, + "name": "Identifier", + "src": "696:8:1" + } + ], + "id": 69, + "name": "IndexAccess", + "src": "687:18:1" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 46, + "type": "uint256", + "value": "amount" + }, + "id": 70, + "name": "Identifier", + "src": "709:6:1" + } + ], + "id": 71, + "name": "Assignment", + "src": "687:28:1" + } + ], + "id": 72, + "name": "ExpressionStatement", + "src": "687:28:1" + }, + { + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "isStructConstructorCall": false, + "lValueRequested": false, + "names": [ + null + ], + "type": "tuple()", + "type_conversion": false + }, + "children": [ + { + "attributes": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 31, + "type": "function (address,address,uint256)", + "value": "Transfer" + }, + "id": 73, + "name": "Identifier", + "src": "719:8:1" + }, + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "member_name": "sender", + "referencedDeclaration": null, + "type": "address" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 181, + "type": "msg", + "value": "msg" + }, + "id": 74, + "name": "Identifier", + "src": "728:3:1" + } + ], + "id": 75, + "name": "MemberAccess", + "src": "728:10:1" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 44, + "type": "address", + "value": "receiver" + }, + "id": 76, + "name": "Identifier", + "src": "740:8:1" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 46, + "type": "uint256", + "value": "amount" + }, + "id": 77, + "name": "Identifier", + "src": "750:6:1" + } + ], + "id": 78, + "name": "FunctionCall", + "src": "719:38:1" + } + ], + "id": 79, + "name": "ExpressionStatement", + "src": "719:38:1" + }, + { + "attributes": { + "functionReturnParameters": 50 + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "hexvalue": "74727565", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "bool", + "type": "bool", + "value": "true" + }, + "id": 80, + "name": "Literal", + "src": "768:4:1" + } + ], + "id": 81, + "name": "Return", + "src": "761:11:1" + } + ], + "id": 82, + "name": "Block", + "src": "598:178:1" + } + ], + "id": 83, + "name": "FunctionDefinition", + "src": "517:259:1" + }, + { + "attributes": { + "constant": true, + "implemented": true, + "isConstructor": false, + "modifiers": [ + null + ], + "name": "getBalanceInEth", + "payable": false, + "scope": 112, + "stateMutability": "view", + "superFunction": null, + "visibility": "public" + }, + "children": [ + { + "children": [ + { + "attributes": { + "constant": false, + "name": "addr", + "scope": 99, + "stateVariable": false, + "storageLocation": "default", + "type": "address", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "address", + "type": "address" + }, + "id": 84, + "name": "ElementaryTypeName", + "src": "804:7:1" + } + ], + "id": 85, + "name": "VariableDeclaration", + "src": "804:12:1" + } + ], + "id": 86, + "name": "ParameterList", + "src": "803:14:1" + }, + { + "children": [ + { + "attributes": { + "constant": false, + "name": "", + "scope": 99, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "uint", + "type": "uint256" + }, + "id": 87, + "name": "ElementaryTypeName", + "src": "838:4:1" + } + ], + "id": 88, + "name": "VariableDeclaration", + "src": "838:4:1" + } + ], + "id": 89, + "name": "ParameterList", + "src": "837:6:1" + }, + { + "children": [ + { + "attributes": { + "functionReturnParameters": 89 + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "isStructConstructorCall": false, + "lValueRequested": false, + "names": [ + null + ], + "type": "uint256", + "type_conversion": false + }, + "children": [ + { + "attributes": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + } + ], + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "member_name": "convert", + "referencedDeclaration": 15, + "type": "function (uint256,uint256) pure returns (uint256)" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 16, + "type": "type(library ConvertLib)", + "value": "ConvertLib" + }, + "id": 90, + "name": "Identifier", + "src": "854:10:1" + } + ], + "id": 91, + "name": "MemberAccess", + "src": "854:18:1" + }, + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": false, + "isPure": false, + "isStructConstructorCall": false, + "lValueRequested": false, + "names": [ + null + ], + "type": "uint256", + "type_conversion": false + }, + "children": [ + { + "attributes": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + } + ], + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 111, + "type": "function (address) view returns (uint256)", + "value": "getBalance" + }, + "id": 92, + "name": "Identifier", + "src": "873:10:1" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 85, + "type": "address", + "value": "addr" + }, + "id": 93, + "name": "Identifier", + "src": "884:4:1" + } + ], + "id": 94, + "name": "FunctionCall", + "src": "873:16:1" + }, + { + "attributes": { + "argumentTypes": null, + "hexvalue": "32", + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "subdenomination": null, + "token": "number", + "type": "int_const 2", + "value": "2" + }, + "id": 95, + "name": "Literal", + "src": "890:1:1" + } + ], + "id": 96, + "name": "FunctionCall", + "src": "854:38:1" + } + ], + "id": 97, + "name": "Return", + "src": "847:45:1" + } + ], + "id": 98, + "name": "Block", + "src": "843:53:1" + } + ], + "id": 99, + "name": "FunctionDefinition", + "src": "779:117:1" + }, + { + "attributes": { + "constant": true, + "implemented": true, + "isConstructor": false, + "modifiers": [ + null + ], + "name": "getBalance", + "payable": false, + "scope": 112, + "stateMutability": "view", + "superFunction": null, + "visibility": "public" + }, + "children": [ + { + "children": [ + { + "attributes": { + "constant": false, + "name": "addr", + "scope": 111, + "stateVariable": false, + "storageLocation": "default", + "type": "address", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "address", + "type": "address" + }, + "id": 100, + "name": "ElementaryTypeName", + "src": "919:7:1" + } + ], + "id": 101, + "name": "VariableDeclaration", + "src": "919:12:1" + } + ], + "id": 102, + "name": "ParameterList", + "src": "918:14:1" + }, + { + "children": [ + { + "attributes": { + "constant": false, + "name": "", + "scope": 111, + "stateVariable": false, + "storageLocation": "default", + "type": "uint256", + "value": null, + "visibility": "internal" + }, + "children": [ + { + "attributes": { + "name": "uint", + "type": "uint256" + }, + "id": 103, + "name": "ElementaryTypeName", + "src": "953:4:1" + } + ], + "id": 104, + "name": "VariableDeclaration", + "src": "953:4:1" + } + ], + "id": 105, + "name": "ParameterList", + "src": "952:6:1" + }, + { + "children": [ + { + "attributes": { + "functionReturnParameters": 105 + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "type": "uint256" + }, + "children": [ + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 23, + "type": "mapping(address => uint256)", + "value": "balances" + }, + "id": 106, + "name": "Identifier", + "src": "970:8:1" + }, + { + "attributes": { + "argumentTypes": null, + "overloadedDeclarations": [ + null + ], + "referencedDeclaration": 101, + "type": "address", + "value": "addr" + }, + "id": 107, + "name": "Identifier", + "src": "979:4:1" + } + ], + "id": 108, + "name": "IndexAccess", + "src": "970:14:1" + } + ], + "id": 109, + "name": "Return", + "src": "963:21:1" + } + ], + "id": 110, + "name": "Block", + "src": "959:29:1" + } + ], + "id": 111, + "name": "FunctionDefinition", + "src": "899:89:1" + } + ], + "id": 112, + "name": "ContractDefinition", + "src": "315:675:1" + } + ], + "id": 113, + "name": "SourceUnit", + "src": "0:991:1" + }, + "compiler": { + "name": "solc", + "version": "0.4.18+commit.9cf6e910.Emscripten.clang" + }, + "networks": { + "4": { + "events": {}, + "links": { + "ConvertLib": "0x14d00a701a2f0d4d65eebaf191f4f60bbaa1da36" + }, + "address": "0xaea9d31a4aeda9e510f7d85559261c16ea0b6b8b" + }, + "4447": { + "events": {}, + "links": { + "ConvertLib": "0xcfeb869f69431e42cdb54a4f4f105c19c080a601" + }, + "address": "0x254dffcd3277c0b1660f6d42efbb754edababc2b" + } + }, + "schemaVersion": "1.0.1", + "updatedAt": "2017-11-07T20:44:02.307Z" +} \ No newline at end of file diff --git a/console/src/main/java/org/web3j/console/Runner.java b/console/src/main/java/org/web3j/console/Runner.java index 5a3f95119d..f52c83bf8c 100644 --- a/console/src/main/java/org/web3j/console/Runner.java +++ b/console/src/main/java/org/web3j/console/Runner.java @@ -2,6 +2,7 @@ import org.web3j.codegen.Console; import org.web3j.codegen.SolidityFunctionWrapperGenerator; +import org.web3j.codegen.TruffleJsonFunctionWrapperGenerator; import org.web3j.utils.Version; import static org.web3j.utils.Collection.tail; @@ -36,6 +37,9 @@ public static void main(String[] args) throws Exception { case "solidity": SolidityFunctionWrapperGenerator.run(tail(args)); break; + case "truffle": + TruffleJsonFunctionWrapperGenerator.run(tail(args)); + break; case "version": Console.exitSuccess("Version: " + Version.getVersion() + "\n" + "Build timestamp: " + Version.getTimestamp()); diff --git a/core/src/main/java/org/web3j/protocol/core/methods/response/AbiDefinition.java b/core/src/main/java/org/web3j/protocol/core/methods/response/AbiDefinition.java index ecdab20767..91d7e44c53 100644 --- a/core/src/main/java/org/web3j/protocol/core/methods/response/AbiDefinition.java +++ b/core/src/main/java/org/web3j/protocol/core/methods/response/AbiDefinition.java @@ -16,19 +16,44 @@ public class AbiDefinition { private String type; private boolean payable; + /** + * The stateMutability function modifier. + *

this does not factor into the #hashCode() or #equals() logic + * since multiple functions with the same signature that only differ in mutability are not + * allowed in Solidity.

+ *

+ * Valid values are: + *

    + *
  • pure
  • + *
  • view
  • + *
  • nonpayable
  • + *
  • payable
  • + *
+ *

+ */ + private String stateMutability; + public AbiDefinition() { } public AbiDefinition(boolean constant, List inputs, String name, List outputs, String type, boolean payable) { + this(constant, inputs, name, outputs, type, payable, null); + } + + public AbiDefinition(boolean constant, List inputs, String name, + List outputs, String type, boolean payable, + String stateMutability) { this.constant = constant; this.inputs = inputs; this.name = name; this.outputs = outputs; this.type = type; this.payable = payable; + this.stateMutability = stateMutability; } + public boolean isConstant() { return constant; } @@ -77,6 +102,14 @@ public void setPayable(boolean payable) { this.payable = payable; } + public String getStateMutability() { + return stateMutability; + } + + public void setStateMutability(String stateMutability) { + this.stateMutability = stateMutability; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -106,6 +139,11 @@ public boolean equals(Object o) { ? !getOutputs().equals(that.getOutputs()) : that.getOutputs() != null) { return false; } + if (getStateMutability() != null + ? !getStateMutability().equals(that.getStateMutability()) + : that.getStateMutability() != null) { + return false; + } return getType() != null ? getType().equals(that.getType()) : that.getType() == null; } @@ -118,6 +156,7 @@ public int hashCode() { result = 31 * result + (getOutputs() != null ? getOutputs().hashCode() : 0); result = 31 * result + (getType() != null ? getType().hashCode() : 0); result = 31 * result + (isPayable() ? 1 : 0); + result = 31 * result + (getStateMutability() != null ? getStateMutability().hashCode() : 0); return result; } @@ -172,6 +211,7 @@ public boolean equals(Object o) { if (isIndexed() != namedType.isIndexed()) { return false; } + if (getName() != null ? !getName().equals(namedType.getName()) : namedType.getName() != null) { return false; diff --git a/core/src/main/java/org/web3j/tx/Contract.java b/core/src/main/java/org/web3j/tx/Contract.java index a0e76aa368..0386efb62b 100644 --- a/core/src/main/java/org/web3j/tx/Contract.java +++ b/core/src/main/java/org/web3j/tx/Contract.java @@ -4,7 +4,9 @@ import java.lang.reflect.Constructor; import java.math.BigInteger; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import org.web3j.abi.EventEncoder; @@ -42,6 +44,7 @@ public abstract class Contract extends ManagedTransaction { private final BigInteger gasPrice; private final BigInteger gasLimit; private TransactionReceipt transactionReceipt; + private Map deployedAddresses; protected Contract(String contractBinary, String contractAddress, Web3j web3j, TransactionManager transactionManager, @@ -379,4 +382,31 @@ protected List extractEventParameters( return values; } + + /** + * Subclasses should implement this method to return pre-existing addresses for deployed + * contracts. + * + * @param networkId the network id, for example "1" for the main-net, "3" for ropsten, etc. + * @return the deployed address of the contract, if known, and null otherwise. + */ + protected String getStaticDeployedAddress(String networkId) { + return null; + } + + public final void setDeployedAddress(String networkId, String address) { + if (deployedAddresses == null) { + deployedAddresses = new HashMap<>(); + } + deployedAddresses.put(networkId, address); + } + + public final String getDeployedAddress(String networkId) { + String addr = null; + if (deployedAddresses != null) { + addr = deployedAddresses.get(networkId); + } + return addr == null ? getStaticDeployedAddress(networkId) : addr; + } + } diff --git a/core/src/test/java/org/web3j/tx/ContractTest.java b/core/src/test/java/org/web3j/tx/ContractTest.java index b6eb52115b..585b4c6a23 100644 --- a/core/src/test/java/org/web3j/tx/ContractTest.java +++ b/core/src/test/java/org/web3j/tx/ContractTest.java @@ -37,6 +37,7 @@ import org.web3j.utils.Async; import org.web3j.utils.Numeric; +import static junit.framework.TestCase.assertNotNull; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertFalse; @@ -256,6 +257,14 @@ public void testInvalidTransactionResponse() throws Throwable { testErrorScenario(); } + @Test + public void testSetGetAddresses() throws Exception { + assertNull(contract.getDeployedAddress("1")); + contract.setDeployedAddress("1", "0x000000000000add0e00000000000"); + assertNotNull(contract.getDeployedAddress("1")); + contract.setDeployedAddress("2", "0x000000000000add0e00000000000"); + assertNotNull(contract.getDeployedAddress("2")); + } @Test(expected = RuntimeException.class) @SuppressWarnings("unchecked") diff --git a/docs/source/index.rst b/docs/source/index.rst index 582b9bf189..104ec84f6f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -23,6 +23,8 @@ Features - Ethereum wallet support - Auto-generation of Java smart contract wrappers to create, deploy, transact with and call smart contracts from native Java code + - from solc output; or + - from Truffle `.json` contract files. - Reactive-functional API for working with filters - Support for Parity's `Personal `__, and Geth's diff --git a/docs/source/smart_contracts.rst b/docs/source/smart_contracts.rst index f4f23b9c5a..93e34af7b0 100644 --- a/docs/source/smart_contracts.rst +++ b/docs/source/smart_contracts.rst @@ -160,7 +160,7 @@ In versions prior to 3.x of web3j, the generated smart contract wrappers used na types. From web3j 3.x onwards, Java types are created by default. You can create Solidity types using the *--solidityTypes* command line argument. -You can also generate the wrappers by calling then Java class directly: +You can also generate the wrappers by calling the Java class directly: .. code-block:: bash @@ -180,6 +180,19 @@ The smart contract wrappers support all common operations for working with smart Any method calls that requires an underlying JSON-RPC call to take place will return a Future to avoid blocking. +web3j also supports the generation of Java smart contract function wrappers directly from `Truffle `_ via the :doc:`command_line` utility. + +.. code-block:: bash + + $ web3j truffle generate [--javaTypes|--solidityTypes] /path/to/.json -o /path/to/src/main/java -p com.your.organisation.name + +And this also can be invoked by calling the Java class: + +.. code-block:: bash + + org.web3j.codegen.TruffleJsonFunctionWrapperGenerator /path/to/.json -o /path/to/src/main/java -p com.your.organisation.name + +A wrapper generated this way ia "enhanced" to expose the per-network deployed address of the contract. These addresses are from the truffle deployment at the time the wrapper is generared. .. _construction-and-deployment: diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ca78035ef0..27768f1bba 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 02d3a96e43..590f0e81da 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,3 @@ -#Mon Nov 06 17:09:51 GMT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 27309d9231..cccdd3d517 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 832fdb6079..f9553162f1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -49,7 +49,6 @@ goto fail @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/utils/src/main/java/org/web3j/utils/Strings.java b/utils/src/main/java/org/web3j/utils/Strings.java index 37a1ada2c6..bcba0a84fe 100644 --- a/utils/src/main/java/org/web3j/utils/Strings.java +++ b/utils/src/main/java/org/web3j/utils/Strings.java @@ -10,18 +10,12 @@ public class Strings { private Strings() {} public static String toCsv(List src) { + // return src == null ? null : String.join(", ", src.toArray(new String[0])); return join(src, ", "); } public static String join(List src, String delimiter) { - String result = ""; - for (int i = 0; i < src.size(); i++) { - result += src.get(i); - if (i + 1 < src.size()) { - result += delimiter; - } - } - return result; + return src == null ? null : String.join(delimiter, src.toArray(new String[0])); } public static String capitaliseFirstLetter(String string) { @@ -47,4 +41,8 @@ public static String zeros(int n) { public static String repeat(char value, int n) { return new String(new char[n]).replace("\0", String.valueOf(value)); } + + public static boolean isEmpty(String s) { + return s == null || s.length() == 0; + } } diff --git a/utils/src/test/java/org/web3j/utils/StringsTest.java b/utils/src/test/java/org/web3j/utils/StringsTest.java index 92ff7917ef..1ace414d87 100644 --- a/utils/src/test/java/org/web3j/utils/StringsTest.java +++ b/utils/src/test/java/org/web3j/utils/StringsTest.java @@ -6,8 +6,12 @@ import org.junit.Test; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.web3j.utils.Strings.capitaliseFirstLetter; +import static org.web3j.utils.Strings.isEmpty; import static org.web3j.utils.Strings.join; import static org.web3j.utils.Strings.lowercaseFirstLetter; import static org.web3j.utils.Strings.repeat; @@ -27,6 +31,8 @@ public void testToCsv() { @Test public void testJoin() { assertThat(join(Arrays.asList("a", "b"), "|"), is("a|b")); + assertNull(join(null, "|")); + assertThat(join(Collections.singletonList("a"), "|"), is("a")); } @Test @@ -58,4 +64,13 @@ public void testZeros() { assertThat(zeros(0), is("")); assertThat(zeros(3), is("000")); } + + @SuppressWarnings("ConstantConditions") + @Test + public void testEmptyString() { + assertTrue(isEmpty(null)); + assertTrue(isEmpty("")); + assertFalse(isEmpty("hello world")); + } + }