diff --git a/project.clj b/project.clj
index cd092e4..450b536 100644
--- a/project.clj
+++ b/project.clj
@@ -5,8 +5,11 @@
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.0"]
[org.web3j/core "4.8.4"]
- [org.web3j/web3j-evm "4.8.4"]]
+ [org.web3j/web3j-evm "4.8.4"]
+ [org.web3j/codegen "4.8.4"]
+ [org.web3j/web3j-sokt "0.2.1"]]
:repositories [["jcenter" {:url "https://jcenter.bintray.com/"}]
["releases" {:url "https://repo.clojars.org"
:creds :gpg}]]
- :repl-options {:init-ns web3clj.core})
+ :repl-options {:init-ns web3clj.core}
+ :java-source-paths ["src/java"])
diff --git a/resources/contracts/Inbox.sol b/resources/contracts/Inbox.sol
new file mode 100644
index 0000000..c089250
--- /dev/null
+++ b/resources/contracts/Inbox.sol
@@ -0,0 +1,22 @@
+// compiler version pinning
+pragma solidity ^0.4.17;
+
+// define a new contract
+contract Inbox {
+ // storage variable which is stored on the blockchain
+ string public message;
+
+ // constructor, invoked when we deploy contract to blockchain
+ function Inbox(string newMessage) public {
+ message = newMessage;
+ }
+
+ function setMessage(string newMessage) public {
+ message = newMessage;
+ }
+
+ // function-name() function-type return-type
+ function getMessage() public view returns (string) {
+ return message;
+ }
+}
\ No newline at end of file
diff --git a/src/java/web3clj/solc/Inbox.java b/src/java/web3clj/solc/Inbox.java
new file mode 100644
index 0000000..e2ff566
--- /dev/null
+++ b/src/java/web3clj/solc/Inbox.java
@@ -0,0 +1,118 @@
+package web3clj.solc;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collections;
+import org.web3j.abi.FunctionEncoder;
+import org.web3j.abi.TypeReference;
+import org.web3j.abi.datatypes.Function;
+import org.web3j.abi.datatypes.Type;
+import org.web3j.abi.datatypes.Utf8String;
+import org.web3j.crypto.Credentials;
+import org.web3j.protocol.Web3j;
+import org.web3j.protocol.core.RemoteCall;
+import org.web3j.protocol.core.RemoteFunctionCall;
+import org.web3j.protocol.core.methods.response.TransactionReceipt;
+import org.web3j.tx.Contract;
+import org.web3j.tx.TransactionManager;
+import org.web3j.tx.gas.ContractGasProvider;
+
+/**
+ *
Auto generated code.
+ *
Do not modify!
+ *
Please use the web3j command line tools,
+ * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the
+ * codegen module to update.
+ *
+ *
Generated with web3j version 4.8.4.
+ */
+@SuppressWarnings("rawtypes")
+public class Inbox extends Contract {
+ public static final String BINARY = "608060405234801561001057600080fd5b50604051610514380380610514833981018060405281019080805182019291905050508060009080519060200190610049929190610050565b50506100f5565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009157805160ff19168380011785556100bf565b828001600101855582156100bf579182015b828111156100be5782518255916020019190600101906100a3565b5b5090506100cc91906100d0565b5090565b6100f291905b808211156100ee5760008160009055506001016100d6565b5090565b90565b610410806101046000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063368b87721461005c578063ce6d41de146100c5578063e21f37ce14610155575b600080fd5b34801561006857600080fd5b506100c3600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101e5565b005b3480156100d157600080fd5b506100da6101ff565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561011a5780820151818401526020810190506100ff565b50505050905090810190601f1680156101475780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561016157600080fd5b5061016a6102a1565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101aa57808201518184015260208101905061018f565b50505050905090810190601f1680156101d75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101fb92919061033f565b5050565b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102975780601f1061026c57610100808354040283529160200191610297565b820191906000526020600020905b81548152906001019060200180831161027a57829003601f168201915b5050505050905090565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103375780601f1061030c57610100808354040283529160200191610337565b820191906000526020600020905b81548152906001019060200180831161031a57829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061038057805160ff19168380011785556103ae565b828001600101855582156103ae579182015b828111156103ad578251825591602001919060010190610392565b5b5090506103bb91906103bf565b5090565b6103e191905b808211156103dd5760008160009055506001016103c5565b5090565b905600a165627a7a723058201a8692b28f48880175c7f0e650864d1e809dd193a465ffb1ed74696155cbc0740029";
+
+ public static final String FUNC_SETMESSAGE = "setMessage";
+
+ public static final String FUNC_GETMESSAGE = "getMessage";
+
+ public static final String FUNC_MESSAGE = "message";
+
+ @Deprecated
+ protected Inbox(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
+ super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
+ }
+
+ protected Inbox(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
+ super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
+ }
+
+ @Deprecated
+ protected Inbox(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
+ super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
+ }
+
+ protected Inbox(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
+ super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
+ }
+
+ public RemoteFunctionCall setMessage(String newMessage) {
+ final Function function = new Function(
+ FUNC_SETMESSAGE,
+ Arrays.asList(new org.web3j.abi.datatypes.Utf8String(newMessage)),
+ Collections.>emptyList());
+ return executeRemoteCallTransaction(function);
+ }
+
+ public RemoteFunctionCall getMessage() {
+ final Function function = new Function(FUNC_GETMESSAGE,
+ Arrays.asList(),
+ Arrays.>asList(new TypeReference() {}));
+ return executeRemoteCallSingleValueReturn(function, String.class);
+ }
+
+ public RemoteFunctionCall message() {
+ final Function function = new Function(FUNC_MESSAGE,
+ Arrays.asList(),
+ Arrays.>asList(new TypeReference() {}));
+ return executeRemoteCallSingleValueReturn(function, String.class);
+ }
+
+ @Deprecated
+ public static Inbox load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
+ return new Inbox(contractAddress, web3j, credentials, gasPrice, gasLimit);
+ }
+
+ @Deprecated
+ public static Inbox load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
+ return new Inbox(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
+ }
+
+ public static Inbox load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
+ return new Inbox(contractAddress, web3j, credentials, contractGasProvider);
+ }
+
+ public static Inbox load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
+ return new Inbox(contractAddress, web3j, transactionManager, contractGasProvider);
+ }
+
+ public static RemoteCall deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider, String newMessage) {
+ String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Utf8String(newMessage)));
+ return deployRemoteCall(Inbox.class, web3j, credentials, contractGasProvider, BINARY, encodedConstructor);
+ }
+
+ public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider, String newMessage) {
+ String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Utf8String(newMessage)));
+ return deployRemoteCall(Inbox.class, web3j, transactionManager, contractGasProvider, BINARY, encodedConstructor);
+ }
+
+ @Deprecated
+ public static RemoteCall deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, String newMessage) {
+ String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Utf8String(newMessage)));
+ return deployRemoteCall(Inbox.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor);
+ }
+
+ @Deprecated
+ public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, String newMessage) {
+ String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Utf8String(newMessage)));
+ return deployRemoteCall(Inbox.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor);
+ }
+}
diff --git a/src/web3clj/solc.clj b/src/web3clj/solc.clj
new file mode 100644
index 0000000..49d1122
--- /dev/null
+++ b/src/web3clj/solc.clj
@@ -0,0 +1,119 @@
+(ns web3clj.solc
+ (:require
+ [clojure.java.io :as io]
+ [web3clj.core :as w3cljc]
+ [web3clj.crypto :as w3cljcrypto]
+ [web3clj.evm :as w3cljevm]
+ [web3clj.protocol :as w3cljproto]
+ [web3clj.utils :as wu]
+ [web3clj.utils.numeric :as wun])
+ (:import
+ org.web3j.codegen.SolidityFunctionWrapperGenerator
+ (org.web3j.crypto
+ Credentials
+ WalletUtils)
+ (org.web3j.protocol
+ Web3j
+ Web3jService)
+ org.web3j.protocol.http.HttpService
+ (org.web3j.sokt
+ MainKt
+ SolcArguments
+ SolcInstance
+ SolcOutput
+ SolidityFile)))
+
+
+(defn compile-contract
+ "Compile a given contract from a file and
+ return compiled information. Shape of returned
+ data :
+ {:abi
+ :bytecode
+ :ast }"
+ [file contract & {out :out solc-args :solc-args
+ :or {out "/tmp"
+ solc-args [SolcArguments/AST
+ SolcArguments/BIN
+ SolcArguments/ABI
+ SolcArguments/OVERWRITE
+ (.param (SolcArguments/OUTPUT_DIR)
+ (wu/func0 out))]}}]
+ (let [sol-file (SolidityFile. file)
+ solc (.getCompilerInstance sol-file ".web3j" false)
+ solc-args (into-array SolcArguments solc-args)]
+ (when (= 0 (.getExitCode (.execute solc solc-args)))
+ {:abi (slurp (str out "/" contract ".abi"))
+ :bytecode (slurp (str out "/" contract ".bin"))
+ :ast (slurp (str out "/" contract ".sol.ast"))})))
+
+
+(defn generate-solidity-wrappers
+ "Generate java wrappers from compiled solidity contract files.
+ Alternatively you can use the ~web3j generate solidity~ cli tool
+ Note: this does not seem to work with codegen 5.0.0, works with 4.8.4!"
+ [^String bytecode-file
+ ^String abi-file
+ ^String java-src-path
+ contract-name
+ package-name]
+ (let [gen-wrapper (SolidityFunctionWrapperGenerator. (io/file bytecode-file)
+ (io/file abi-file)
+ (io/file java-src-path)
+ contract-name
+ package-name
+ true ; useJavaNativeTypes
+ true ; useJavaPrimitiveTypes
+ 20)] ; address length in bytes, default is 20
+
+ (.generate gen-wrapper)))
+
+
+(defn create-compiled-contract
+ "Compile solidity contract and spit it out to
+ files
+ Returns: dictionary with :abi and :bytecode keys"
+ [file contract-name out-path]
+ (let [{:keys [abi bytecode] :as out} (compile-contract file contract-name :out out-path)
+ file-name (str out-path "/" contract-name)]
+ (io/make-parents file-name)
+ (spit (str file-name ".abi") abi)
+ (spit (str file-name ".bytecode") bytecode)
+ out))
+
+
+(comment
+ (let [{:keys [abi bytecode]} (create-compiled-contract "resources/contracts/Inbox.sol"
+ "Inbox"
+ "resources/out")]
+ (println "ABI is " abi))
+ (generate-solidity-wrappers "resources/out/Inbox.bytecode"
+ "resources/out/Inbox.abi"
+ "src/java"
+ "Inbox"
+ "web3clj.solc")
+ ;; At this point you will need to restart the repl for it
+ ;; to load this namespace.
+
+ (import web3clj.solc.Inbox)
+
+ ;; Deploy a smart contract
+ (def web3-ganache (Web3j/build (HttpService. "http://localhost:7545")))
+ ;; These credentials determine the author of the contract deployment
+ (def pr-key "8b246c57e1eb7ccec7705ce7889144292c37e02bec747da104b8981fdfb9fcfe")
+ (def local-wallet (Credentials/create pr-key))
+
+ (def inbox-contract (.send (Inbox/deploy web3-ganache
+ local-wallet
+ ;; this comes from the ganache settings
+ (wun/->big-integer 20000000000)
+ ;; eventually we required 302737 of gas
+ (wun/->big-integer 2000000)
+ "hello world")))
+
+ ;; get the message from contract
+ (def contract-message (.send (.getMessage inbox-contract)))
+
+ ;; update the message
+ ;; who decides the gas price and gas limit on these transactions?
+ (.send (.setMessage inbox-contract "bye for now")))
diff --git a/src/web3clj/utils.clj b/src/web3clj/utils.clj
index 802dc54..ca0dccf 100644
--- a/src/web3clj/utils.clj
+++ b/src/web3clj/utils.clj
@@ -1,6 +1,7 @@
(ns web3clj.utils
(:import [java.math BigDecimal]
- [org.web3j.utils Convert Convert$Unit]))
+ [org.web3j.utils Convert Convert$Unit]
+ (kotlin.jvm.functions Function0)))
(def WEI Convert$Unit/WEI)
(def KWEI Convert$Unit/KWEI)
@@ -54,3 +55,12 @@
(defn wei->kether [kether-amount] (<-wei kether-amount KETHER))
(defn wei->mether [mether-amount] (<-wei mether-amount METHER))
(defn wei->gether [gether-amount] (<-wei gether-amount GETHER))
+
+
+(defn func0
+ "A wrapper around the Function0 interface"
+ [s]
+ (reify Function0
+ (invoke
+ [this]
+ s)))