                (empty? (get-channel-ends channel-end-opts (get-or-make-channel client channel-id))))
        (add-channel-end-to-channel (new-channel-end channel-end-opts client) channel)))))
 
-
-(defn propose-transaction [client channel-id]
-  (let [channel (get-or-make-channel client channel-id)
-        req (client/new-transaction-proposal-request client)]
-    (when-not (channel/is-initialized channel)
-      (channel/initialize channel))
-    )
+(defrecord TransactionOpts [fcn args ;; chaincode-language chaincode-endorsement-policy
+                            proposal-wait-time])
+
+(defn propose-transaction
+  ([client channel-id chaincode-opts transaction-opts]
+   (propose-transaction client channel-id chaincode-opts transaction-opts nil))
+  ([client channel-id chaincode-opts {:keys [fcn args proposal-wait-time]} peers]
+   (let [channel (get-or-make-channel client channel-id)
+         req (client/new-transaction-proposal-request client)]
+     (when-not (channel/is-initialized channel)
+       (channel/initialize channel))
+     (request/set-chaincode-id req (get-or-make-chaincode chaincode-opts))
+     ;; Transaction options
+     ;; FIXME: what about chaincode-language, chaincode-endorsement-policy ?
+     (when fcn
+       (request/set-fcn req fcn))
+     (when args
+       (request/set-args req args))
+     (when proposal-wait-time
+       (request/set-proposal-wait-time req proposal-wait-time))
+     ;; Send Tx proposal to peers
+     (apply channel/send-transaction-proposal channel req peers))))
+
+(defn order-transaction
+  ([client channel-id proposal-responses]
+   (order-transaction client channel-id proposal-responses nil))
+  ([client channel-id proposal-responses orderers]
+   ;; Send Tx to orderers
+   (apply channel/send-transaction (get-or-make-channel client channel-id) proposal-responses orderers))
+  ;; (defonce resp (channel/send-transaction-proposal chan req))
+  ;; (defonce future1 (channel/send-transaction chan resp (client/get-user-context cli)))
+  ;; (.get future1)
   )
 
 ;;;

  (:import [org.hyperledger.fabric.sdk User Enrollment Peer Orderer]
           [org.bouncycastle.jcajce.provider.asymmetric.ec BCECPrivateKey]))

(defprotocol Marshall
  (marshall [obj]))

;;; FIXME: call marshal from generated code when return type matches
(extend-protocol Marshall
  java.util.Collection
  (marshall [obj]
    (if (instance? clojure.lang.IPersistentSet obj)
      obj
      (seq obj))))

(defonce client-lru-cache
  ;; FIXME: magic number
  (atom (cache/lru-cache-factory {} :threshold 64)))

#_
(comment
  (defonce cli
   (get-or-make-client "Org1MSP"
                "PeerAdmin"
                (-> (slurp "resources/creds/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-priv")
                    (keys/str->private-key))
