diff --git a/README.md b/README.md index c556149..7d3b9f5 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,10 @@ The `cloth.contracts` namespace allows you to compile solidity code and create c (defcontract simple-token "test/cloth/SimpleToken.sol") +;; The contract name argument must be the skewer-case version of the Solidity contract name. +;; In the example above, the use of `simple-token` means there's a contract named `SimpleToken` in SimpleToken.sol file. +;; Any solc compiler warnings are treated as an error; an optional `ignore-warnings?` argument to `defcontract` over-rides the default behavior. + ;; For simplicity sake the rest of the examples use Clojure @ syntax for dereferencing Promises ;; Deploy the solidity code to the blockchain @@ -199,6 +203,8 @@ testrpc -b 1 Run `lein test` or `lein test-refresh`. +Tests assume gasPrice is 1, so use `testrpc -g 1 -b 1` + ### Clojurescript tests We use [doo](https://github.com/bensu/doo) diff --git a/src/cloth/contracts.cljc b/src/cloth/contracts.cljc index c4a14ed..23d6585 100644 --- a/src/cloth/contracts.cljc +++ b/src/cloth/contracts.cljc @@ -16,11 +16,17 @@ #?(:clj - (defn compile-solidity [file] - (let [result (shell/sh "solc" "--combined-json" "abi,bin" file)] - (if (not (c/blank? (:err result))) - (throw (ex-info (:err result) {:solidity file :exit (:exit result)})) - (json/parse-string (:out result) true))))) + (defn compile-solidity + ([file ignore-warnings?] + (let [result (shell/sh "solc" "--combined-json" "abi,bin" file) + failed? (if ignore-warnings? + (not (zero? (:exit result))) + (not (c/blank? (:err result))))] + (if failed? + (throw (ex-info (:err result) {:solidity file :exit (:exit result)})) + (json/parse-string (:out result) true)))) + ([file] + (compile-solidity file false)))) #?(:clj (defn abi->args [f] @@ -69,7 +75,28 @@ ] (str fncall const returns))) -;; (!) +#?(:clj + (defn compile-contract [contract file ignore-warnings?] + "Compile a Solidity file and return a map of functions, constructor, events, and the name for a deploy function. +Tries to be compatible with older versions of solc that didn't include Solidity file name in the output of \"solc --combined-json=abi,bin\"." + (let [{:keys [contracts]} (compile-solidity file ignore-warnings?) + solidity-name (c/capitalize (c/camel (name contract))) ;; convert, e.g. "simple-token" -> "SimpleToken" + {json-abi :abi binary :bin} (or (contracts (keyword solidity-name)) ;; old version of solc + (contracts (keyword (str file ":" solidity-name))) ;; newer version of solc + (throw (ex-info (str "Contract \"" solidity-name "\" not found in file \"" file "\".") + {:clojure-name contract + :solidity-name solidity-name}))) + abi (json/parse-string json-abi true) + functions (filter #(= (:type %) "function") abi) + constructor (first (filter #(= (:type %) "constructor") abi)) + events (filter #(= (:type %) "event") abi) + deploy-name (symbol (str "deploy-" (c/kebab (name solidity-name)) "!"))] + {:functions functions + :constructor constructor + :events events + :binary binary + :deploy-name deploy-name}))) + #?(:clj (defmacro defcontract "Compiles solidity and creates a set of functions in the current namespace: @@ -82,16 +109,13 @@ (add-device!! \"0x0sdsfafs...\" {}) + Parameters: + contract - the name of the contract converted to Clojure-style skewer-case (e.g. \"SimpleToken\" becomes simple-token) + file - the Solidity file pathname (a string) + ignore-warnings? - optional, compiler warnings are OK but a compiler error will raise an exception " - [contract file] - (let [compiled (compile-solidity file) - contract-key (keyword (c/capitalize (c/camel (name contract)))) - binary (get-in compiled [:contracts contract-key :bin]) - abi (json/parse-string (get-in compiled [:contracts contract-key :abi]) true) - functions (filter #(= (:type %) "function") abi) - constructor (first (filter #(= (:type %) "constructor") abi)) - events (filter #(= (:type %) "event") abi) - deploy-name (symbol (str "deploy-" (c/kebab (name contract)) "!"))] + [contract file & [ignore-warnings?]] + (let [{:keys [functions constructor events binary deploy-name]} (compile-contract contract file ignore-warnings?)] `(do (defn ~deploy-name [ & args# ] (deploy-contract ~binary ~constructor args#)) diff --git a/test/cloth/Constructed.sol b/test/cloth/Constructed.sol index 43e9bee..126ed14 100644 --- a/test/cloth/Constructed.sol +++ b/test/cloth/Constructed.sol @@ -1,4 +1,5 @@ -pragma solidity ^0.4.1; +pragma solidity ^0.4.16; + contract Constructed { string public status; diff --git a/test/cloth/SimpleToken.sol b/test/cloth/SimpleToken.sol index f3db421..9ed6d71 100644 --- a/test/cloth/SimpleToken.sol +++ b/test/cloth/SimpleToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.1; +pragma solidity ^0.4.16; contract SimpleToken { uint32 public circulation; address public issuer; @@ -9,7 +9,7 @@ contract SimpleToken { bytes32 public ipfs; event Issued(address indexed recipient, uint32 amount); - event Message(address indexed shouter, string message); + event Message(address indexed shouter, string _message); event Transferred(address indexed sender, address indexed recipient, uint32 amount); function SimpleToken() { @@ -21,17 +21,17 @@ contract SimpleToken { modifier authorizedCustomer(address customer) { if (authorized[customer] != 0 ) _ ; } modifier unAuthorizedCustomer(address customer) { if (authorized[customer] == 0 ) _ ; } - function customer(address customer) constant + function customer(address _customer) constant returns(uint authorizedTime, uint32 balance) { - return (authorized[customer], balances[customer]); + return (authorized[_customer], balances[_customer]); } - function authorize(address customer) public + function authorize(address _customer) public onlyIssuer - unAuthorizedCustomer(customer) + unAuthorizedCustomer(_customer) returns(bool success) { - authorized[customer] = block.timestamp; - customers.push(customer); + authorized[_customer] = block.timestamp; + customers.push(_customer); return true; } @@ -45,9 +45,9 @@ contract SimpleToken { } function setMessage(string _message) - public returns(string message) { + public returns(string) { message = _message; - Message(msg.sender,message); + Message(msg.sender,_message); return message; } diff --git a/test/cloth/core_test.cljc b/test/cloth/core_test.cljc index 10f93cc..58008b2 100644 --- a/test/cloth/core_test.cljc +++ b/test/cloth/core_test.cljc @@ -158,7 +158,7 @@ (deftest sign-with-signer-proxy-test (let [keypair (keys/create-keypair)] (reset! core/global-signer keypair) - (c/defcontract proxy "test/cloth/Proxy.sol") + (c/defcontract proxy "test/cloth/Proxy.sol" :ignore-compiler-warnings) (let [recipient (:address (keys/create-keypair))] #?(:cljs