Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
54 changes: 39 additions & 15 deletions src/cloth/contracts.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -69,7 +75,28 @@
]
(str fncall const returns)))

;; (<function name>!)
#?(: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:
Expand All @@ -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#))
Expand Down
3 changes: 2 additions & 1 deletion test/cloth/Constructed.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pragma solidity ^0.4.1;
pragma solidity ^0.4.16;

contract Constructed {
string public status;

Expand Down
20 changes: 10 additions & 10 deletions test/cloth/SimpleToken.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.4.1;
pragma solidity ^0.4.16;
contract SimpleToken {
uint32 public circulation;
address public issuer;
Expand All @@ -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() {
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion test/cloth/core_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down