Merge branch 'develop' into fix/update-comment-only-if-hash-changes-385
This commit is contained in:
commit
da700edc08
|
@ -26,6 +26,7 @@ WORKDIR /root/
|
|||
RUN apt-get update
|
||||
RUN apt-get -y install xvfb
|
||||
RUN apt-get -y install wkhtmltopdf
|
||||
RUN apt-get -y install less
|
||||
|
||||
COPY --from=builder /usr/src/app/target/uberjar/commiteth.jar .
|
||||
COPY html2png.sh .
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE "public"."pull_requests" DROP COLUMN "title";
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE "public"."pull_requests" ADD COLUMN "title" character varying(256);
|
|
@ -0,0 +1,7 @@
|
|||
ALTER TABLE pull_requests DROP CONSTRAINT pull_requests_pkey;
|
||||
ALTER TABLE pull_requests DROP CONSTRAINT pull_requests_pr_id_key;
|
||||
ALTER TABLE pull_requests DROP CONSTRAINT pull_requests_fkey;
|
||||
|
||||
|
||||
ALTER TABLE pull_requests ADD CONSTRAINT pull_requests_pkey PRIMARY KEY (pr_id);
|
||||
ALTER TABLE pull_requests ADD CONSTRAINT pull_requests_pr_id_key UNIQUE (pr_id);
|
|
@ -0,0 +1,6 @@
|
|||
ALTER TABLE pull_requests DROP CONSTRAINT pull_requests_pkey;
|
||||
ALTER TABLE pull_requests DROP CONSTRAINT pull_requests_pr_id_key;
|
||||
|
||||
ALTER TABLE pull_requests ADD CONSTRAINT pull_requests_pkey PRIMARY KEY (pr_id, issue_id);
|
||||
ALTER TABLE pull_requests ADD CONSTRAINT pull_requests_pr_id_key UNIQUE (pr_id, issue_id);
|
||||
ALTER TABLE pull_requests ADD CONSTRAINT pull_requests_fkey FOREIGN KEY (issue_id) REFERENCES issues(issue_id);
|
|
@ -0,0 +1,78 @@
|
|||
-- restore the previous version of the view
|
||||
CREATE VIEW "public"."claims_view" AS SELECT i.title AS issue_title,
|
||||
i.issue_number,
|
||||
r.repo AS repo_name,
|
||||
r.owner AS repo_owner,
|
||||
COALESCE(u.name, u.login) AS user_name,
|
||||
u.avatar_url AS user_avatar_url,
|
||||
i.payout_receipt,
|
||||
p.updated,
|
||||
i.updated AS issue_updated,
|
||||
i.balance_eth,
|
||||
i.tokens,
|
||||
i.value_usd,
|
||||
p.state AS pr_state,
|
||||
i.is_open AS issue_open,
|
||||
(case when u.address IS NULL THEN false ELSE true END) AS user_has_address
|
||||
FROM issues i,
|
||||
users u,
|
||||
repositories r,
|
||||
pull_requests p
|
||||
WHERE r.repo_id = i.repo_id
|
||||
AND p.issue_id = i.issue_id
|
||||
AND p.user_id = u.id
|
||||
AND i.contract_address IS NOT NULL
|
||||
AND i.comment_id IS NOT NULL
|
||||
ORDER BY p.updated;
|
||||
|
||||
|
||||
CREATE OR REPLACE VIEW "public"."activity_feed_view" AS
|
||||
SELECT 'open-claim'::text AS type,
|
||||
claims_view.issue_title,
|
||||
claims_view.repo_name,
|
||||
claims_view.repo_owner,
|
||||
claims_view.issue_number,
|
||||
claims_view.user_name,
|
||||
claims_view.user_avatar_url,
|
||||
claims_view.balance_eth,
|
||||
claims_view.tokens,
|
||||
claims_view.value_usd,
|
||||
claims_view.user_has_address,
|
||||
claims_view.updated
|
||||
FROM claims_view
|
||||
WHERE claims_view.pr_state = 0
|
||||
AND claims_view.payout_receipt IS NULL
|
||||
AND claims_view.issue_open IS TRUE
|
||||
UNION
|
||||
SELECT 'claim-pending'::text AS type,
|
||||
claims_view.issue_title,
|
||||
claims_view.repo_name,
|
||||
claims_view.repo_owner,
|
||||
claims_view.issue_number,
|
||||
claims_view.user_name,
|
||||
claims_view.user_avatar_url,
|
||||
claims_view.balance_eth,
|
||||
claims_view.tokens,
|
||||
claims_view.value_usd,
|
||||
claims_view.user_has_address,
|
||||
claims_view.issue_updated AS updated
|
||||
FROM claims_view
|
||||
WHERE claims_view.pr_state = 1
|
||||
AND claims_view.payout_receipt IS NULL
|
||||
UNION
|
||||
SELECT 'claim-payout'::text AS type,
|
||||
claims_view.issue_title,
|
||||
claims_view.repo_name,
|
||||
claims_view.repo_owner,
|
||||
claims_view.issue_number,
|
||||
claims_view.user_name,
|
||||
claims_view.user_avatar_url,
|
||||
claims_view.balance_eth,
|
||||
claims_view.tokens,
|
||||
claims_view.value_usd,
|
||||
claims_view.user_has_address,
|
||||
claims_view.issue_updated AS updated
|
||||
FROM claims_view
|
||||
WHERE claims_view.pr_state = 1
|
||||
AND claims_view.payout_receipt IS NOT NULL
|
||||
ORDER BY 12 DESC;
|
|
@ -0,0 +1,95 @@
|
|||
DROP VIEW "public"."claims_view" CASCADE;
|
||||
|
||||
CREATE VIEW "public"."claims_view" AS SELECT i.title AS issue_title,
|
||||
i.issue_number,
|
||||
r.repo AS repo_name,
|
||||
r.owner AS repo_owner,
|
||||
p.title AS pr_title, -- added
|
||||
p.pr_number AS pr_number, -- added
|
||||
p.pr_id AS pr_id, -- added
|
||||
i.issue_id AS issue_id, -- added
|
||||
COALESCE(u.name, u.login) AS user_name,
|
||||
u.avatar_url AS user_avatar_url,
|
||||
i.payout_receipt,
|
||||
p.updated,
|
||||
i.updated AS issue_updated,
|
||||
i.balance_eth,
|
||||
i.tokens,
|
||||
i.value_usd,
|
||||
p.state AS pr_state,
|
||||
i.is_open AS issue_open,
|
||||
(case when u.address IS NULL THEN false ELSE true END) AS user_has_address
|
||||
FROM issues i,
|
||||
users u,
|
||||
repositories r,
|
||||
pull_requests p
|
||||
WHERE r.repo_id = i.repo_id
|
||||
AND p.issue_id = i.issue_id
|
||||
AND p.user_id = u.id
|
||||
AND i.contract_address IS NOT NULL
|
||||
AND i.comment_id IS NOT NULL
|
||||
ORDER BY p.updated;
|
||||
|
||||
|
||||
CREATE VIEW "public"."activity_feed_view" AS -- altered
|
||||
SELECT 'open-claim'::text AS type,
|
||||
claims_view.issue_title,
|
||||
claims_view.repo_name,
|
||||
claims_view.repo_owner,
|
||||
claims_view.pr_title, -- added
|
||||
claims_view.pr_number, -- added
|
||||
claims_view.pr_id, -- added
|
||||
claims_view.issue_number,
|
||||
claims_view.issue_id, -- added
|
||||
claims_view.user_name,
|
||||
claims_view.user_avatar_url,
|
||||
claims_view.balance_eth,
|
||||
claims_view.tokens,
|
||||
claims_view.value_usd,
|
||||
claims_view.user_has_address,
|
||||
claims_view.updated
|
||||
FROM claims_view
|
||||
WHERE claims_view.pr_state = 0
|
||||
AND claims_view.payout_receipt IS NULL
|
||||
AND claims_view.issue_open IS TRUE
|
||||
UNION
|
||||
SELECT 'claim-pending'::text AS type,
|
||||
claims_view.issue_title,
|
||||
claims_view.repo_name,
|
||||
claims_view.repo_owner,
|
||||
claims_view.pr_title, -- added
|
||||
claims_view.pr_number, -- added
|
||||
claims_view.pr_id, -- added
|
||||
claims_view.issue_number,
|
||||
claims_view.issue_id, -- added
|
||||
claims_view.user_name,
|
||||
claims_view.user_avatar_url,
|
||||
claims_view.balance_eth,
|
||||
claims_view.tokens,
|
||||
claims_view.value_usd,
|
||||
claims_view.user_has_address,
|
||||
claims_view.issue_updated AS updated
|
||||
FROM claims_view
|
||||
WHERE claims_view.pr_state = 1
|
||||
AND claims_view.payout_receipt IS NULL
|
||||
UNION
|
||||
SELECT 'claim-payout'::text AS type,
|
||||
claims_view.issue_title,
|
||||
claims_view.repo_name,
|
||||
claims_view.repo_owner,
|
||||
claims_view.pr_title, -- added
|
||||
claims_view.pr_number, -- added
|
||||
claims_view.pr_id, -- added
|
||||
claims_view.issue_number,
|
||||
claims_view.issue_id, -- added
|
||||
claims_view.user_name,
|
||||
claims_view.user_avatar_url,
|
||||
claims_view.balance_eth,
|
||||
claims_view.tokens,
|
||||
claims_view.value_usd,
|
||||
claims_view.user_has_address,
|
||||
claims_view.issue_updated AS updated
|
||||
FROM claims_view
|
||||
WHERE claims_view.pr_state = 1
|
||||
AND claims_view.payout_receipt IS NOT NULL
|
||||
ORDER BY 12 DESC;
|
Binary file not shown.
After Width: | Height: | Size: 266 B |
Binary file not shown.
After Width: | Height: | Size: 620 B |
|
@ -207,6 +207,7 @@ AND i.transaction_hash IS NOT NULL;
|
|||
INSERT INTO pull_requests (pr_id,
|
||||
repo_id,
|
||||
pr_number,
|
||||
title,
|
||||
issue_number,
|
||||
issue_id,
|
||||
commit_sha,
|
||||
|
@ -215,16 +216,18 @@ INSERT INTO pull_requests (pr_id,
|
|||
VALUES(:pr_id,
|
||||
:repo_id,
|
||||
:pr_number,
|
||||
:title,
|
||||
:issue_number,
|
||||
:issue_id,
|
||||
:commit_sha,
|
||||
:user_id,
|
||||
:state)
|
||||
ON CONFLICT (pr_id) DO UPDATE
|
||||
ON CONFLICT (pr_id,issue_id) DO UPDATE
|
||||
SET
|
||||
state = :state,
|
||||
issue_number = :issue_number,
|
||||
issue_id = :issue_id,
|
||||
title = :title,
|
||||
updated = timezone('utc'::text, now()),
|
||||
commit_sha = :commit_sha;
|
||||
|
||||
|
@ -573,7 +576,11 @@ SELECT
|
|||
issue_title,
|
||||
repo_name,
|
||||
repo_owner,
|
||||
pr_number,
|
||||
pr_title,
|
||||
pr_id,
|
||||
issue_number,
|
||||
issue_id,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
balance_eth,
|
||||
|
|
|
@ -21,34 +21,36 @@
|
|||
|
||||
(defn deploy-contract [owner owner-address repo issue-id issue-number]
|
||||
(if (empty? owner-address)
|
||||
(log/error "Unable to deploy bounty contract because"
|
||||
"repo owner has no Ethereum addres")
|
||||
(do
|
||||
(log/info "deploying contract to " owner-address)
|
||||
(if-let [transaction-hash (multisig/deploy-multisig owner-address)]
|
||||
(log/errorf "issue %s: Unable to deploy bounty contract because repo owner has no Ethereum addres" issue-id)
|
||||
(try
|
||||
(log/infof "issue %s: Deploying contract to %s" issue-id owner-address)
|
||||
(if-let [transaction-hash (multisig/deploy-multisig {:owner owner-address
|
||||
:internal-tx-id (str "contract-github-issue-" issue-id)})]
|
||||
(do
|
||||
(log/info "Contract deployed, transaction-hash:"
|
||||
transaction-hash)
|
||||
(log/infof "issue %s: Contract deployed, transaction-hash: %s" issue-id transaction-hash)
|
||||
(github/update-comment (to-map owner repo issue-number transaction-hash))
|
||||
(log/infof "issue %s: post-deploying-comment response: %s" issue-id resp)
|
||||
(issues/update-transaction-hash issue-id transaction-hash))
|
||||
(log/error "Failed to deploy contract to" owner-address)))))
|
||||
|
||||
(log/errorf "issue %s Failed to deploy contract to %s" issue-id owner-address))
|
||||
(catch Exception ex (log/errorf ex "issue %s: deploy-contract exception" issue-id)))))
|
||||
|
||||
(defn add-bounty-for-issue [repo repo-id issue]
|
||||
(let [{issue-id :id
|
||||
issue-number :number
|
||||
issue-title :title} issue
|
||||
created-issue (issues/create repo-id issue-id issue-number issue-title)
|
||||
{:keys [address owner]} (users/get-repo-owner repo-id)]
|
||||
(log/debug "Adding bounty for issue " repo issue-number "owner address: " address)
|
||||
(log/debug "issue %s: Adding bounty for issue %s/%s - owner address: %s"
|
||||
issue-id repo issue-number owner-address)
|
||||
(if (= 1 created-issue)
|
||||
(deploy-contract owner address repo issue-id issue-number)
|
||||
(log/debug "Issue already exists in DB, ignoring"))))
|
||||
(log/debug "issue %s: Issue already exists in DB, ignoring"))))
|
||||
|
||||
(defn maybe-add-bounty-for-issue [repo repo-id issue]
|
||||
(let [res (issues/get-issues-count repo-id)
|
||||
{count :count} res
|
||||
limit-reached? (> count max-issues-limit)
|
||||
_ (log/debug "*** get-issues-count" repo-id " " res " " count " " limit-reached?)]
|
||||
limit-reached? (> count max-issues-limit)]
|
||||
(log/debug "*** get-issues-count" repo-id " " res " " count " " limit-reached?)
|
||||
(if limit-reached?
|
||||
(log/debug "Total issues for repo limit reached " repo " " count)
|
||||
(add-bounty-for-issue repo repo-id issue))))
|
||||
|
@ -76,9 +78,7 @@
|
|||
(issues/get-issue-titles)]
|
||||
(let [gh-issue (github/get-issue owner repo issue-number)]
|
||||
(if-not (= title (:title gh-issue))
|
||||
(do
|
||||
(log/info "Updating changed title for issue" (:id gh-issue))
|
||||
(issues/update-issue-title (:id gh-issue) (:title gh-issue)))))))
|
||||
(issues/update-issue-title (:id gh-issue) (:title gh-issue))))))
|
||||
|
||||
(defn assert-keys [m ks]
|
||||
(doseq [k ks]
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
(:require [commiteth.db.core :refer [*db*] :as db]
|
||||
[clojure.java.jdbc :as jdbc]
|
||||
[commiteth.util.util :refer [to-db-map]]
|
||||
[clojure.set :refer [rename-keys]]))
|
||||
[clojure.set :refer [rename-keys]]
|
||||
[clojure.tools.logging :as log]))
|
||||
|
||||
(defn create
|
||||
"Creates issue"
|
||||
|
@ -32,6 +33,7 @@
|
|||
|
||||
(defn update-issue-title
|
||||
[issue-id title]
|
||||
(log/info "issue %s: Updating changed title \"%s\"" issue-id title)
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/update-issue-title con-db {:issue_id issue-id
|
||||
:title title})))
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
(defn update-repo-state
|
||||
[repo-id repo-state]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/update-repo-name con-db {:repo_id repo-id
|
||||
(db/update-repo-state con-db {:repo_id repo-id
|
||||
:repo_state repo-state})))
|
||||
(defn get-repo
|
||||
"Get a repo from DB given it's full name (owner/repo-name)"
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
(defn auto-gas-price? [] (env :auto-gas-price false))
|
||||
(defn offline-signing? [] (env :offline-signing true))
|
||||
|
||||
(def web3j-obj (atom nil))
|
||||
(def web3j-obj
|
||||
(delay (Web3j/build (HttpService. (eth-rpc-url)))))
|
||||
|
||||
(def creds-obj (atom nil))
|
||||
|
||||
(defn wallet-file-path []
|
||||
|
@ -37,10 +39,6 @@
|
|||
(defn wallet-password []
|
||||
(env :eth-password))
|
||||
|
||||
(defn create-web3j []
|
||||
(or @web3j-obj
|
||||
(swap! web3j-obj (constantly (Web3j/build (HttpService. (eth-rpc-url)))))))
|
||||
|
||||
(defn creds []
|
||||
(or @creds-obj
|
||||
(let [password (wallet-password)
|
||||
|
@ -53,27 +51,59 @@
|
|||
(throw (ex-info "Make sure you provided proper credentials in appropriate resources/config.edn"
|
||||
{:password password :file-path file-path}))))))
|
||||
|
||||
(defn get-signed-tx [gas-price gas-limit to data]
|
||||
"Create a sign a raw transaction.
|
||||
'From' argument is not needed as it's already
|
||||
encoded in credentials.
|
||||
(defn get-web3j-nonce [web3j-instance]
|
||||
(.. (.ethGetTransactionCount web3j-instance (env :eth-account) DefaultBlockParameterName/LATEST)
|
||||
sendAsync
|
||||
get
|
||||
getTransactionCount))
|
||||
|
||||
(defprotocol INonceTracker
|
||||
"The reason we need this is that we might send mutliple identical
|
||||
transactions (e.g. bounty contract deploy with same owner) shortly
|
||||
after another. In this case web3j's TX counting only increases once
|
||||
a transaction has been confirmed and so we send multiple identical
|
||||
transactions with the same nonce.
|
||||
|
||||
Web3j also provides a TransactionManager that increases nonces but it
|
||||
does not track nonces related to transactions and so as far as I understand
|
||||
it might cause transactions to be executed twice if they are retried.
|
||||
|
||||
https://github.com/web3j/web3j/blob/d19855475aa6620a7e93523bd9ede26ca50ed042/core/src/main/java/org/web3j/tx/RawTransactionManager.java"
|
||||
(get-nonce [this internal-tx-id]
|
||||
"Return the to be used nonce for an OpenBounty Ethereum
|
||||
transaction identified by `internal-tx-id`. As these IDs are stable
|
||||
we can use them to use consistent nonces for the same transaction."))
|
||||
|
||||
(defrecord NonceTracker [state]
|
||||
INonceTracker
|
||||
(get-nonce [this internal-tx-id]
|
||||
(let [prev-nonce (get @state internal-tx-id)
|
||||
web3j-nonce (get-web3j-nonce @web3j-obj)
|
||||
nonces (set (vals @state))
|
||||
nonce (if (seq nonces)
|
||||
(inc (apply max nonces))
|
||||
web3j-nonce)]
|
||||
(when prev-nonce
|
||||
(log/warnf "%s: tx will be retried (prev-nonce: %s, new-nonce: %s, web3j-nonce: %s)"
|
||||
internal-tx-id prev-nonce nonce web3j-nonce))
|
||||
;; TODO this is a memory leak since tracking state is never pruned
|
||||
;; Since we're not doing 1000s of transactions every day yet we can
|
||||
;; probably defer worrying about this until a bit later
|
||||
(swap! state assoc internal-tx-id nonce)
|
||||
nonce)))
|
||||
|
||||
(def nonce-tracker
|
||||
(->NonceTracker (atom {})))
|
||||
|
||||
(defn get-signed-tx [{:keys [gas-price gas-limit to data internal-tx-id]}]
|
||||
"Create a sign a raw transaction. 'From' argument is not needed as it's already encoded in credentials.
|
||||
See https://web3j.readthedocs.io/en/latest/transactions.html#offline-transaction-signing"
|
||||
(let [web3j (create-web3j)
|
||||
nonce (.. (.ethGetTransactionCount web3j
|
||||
(env :eth-account)
|
||||
DefaultBlockParameterName/LATEST)
|
||||
sendAsync
|
||||
get
|
||||
getTransactionCount)
|
||||
tx (RawTransaction/createTransaction
|
||||
nonce
|
||||
gas-price
|
||||
gas-limit
|
||||
to
|
||||
data)
|
||||
signed (TransactionEncoder/signMessage tx (creds))
|
||||
hex-string (Numeric/toHexString signed)]
|
||||
hex-string))
|
||||
(let [nonce (get-nonce nonce-tracker internal-tx-id)]
|
||||
(log/infof "%s: Signing nonce: %s, gas-price: %s, gas-limit: %s"
|
||||
internal-tx-id nonce gas-price gas-limit)
|
||||
(-> (RawTransaction/createTransaction (biginteger nonce) gas-price gas-limit to data)
|
||||
(TransactionEncoder/signMessage (creds))
|
||||
(Numeric/toHexString))))
|
||||
|
||||
(defn eth-gasstation-gas-price
|
||||
"Get max of average and average_calc from gas station and use that
|
||||
|
@ -122,19 +152,27 @@
|
|||
|
||||
nil)))))
|
||||
|
||||
(def req-id-tracker
|
||||
;; HACK to ensure previous random-number approach doesn't lead to
|
||||
;; unintended collisions
|
||||
(atom 0))
|
||||
|
||||
(defn eth-rpc
|
||||
[method params]
|
||||
(let [request-id (rand-int 4096)
|
||||
body (json/write-str {:jsonrpc "2.0"
|
||||
:method method
|
||||
:params params
|
||||
:id request-id})
|
||||
[{:keys [method params internal-tx-id]}]
|
||||
{:pre [(string? method) (some? params)]}
|
||||
(let [request-id (swap! req-id-tracker inc)
|
||||
body {:jsonrpc "2.0"
|
||||
:method method
|
||||
:params params
|
||||
:id request-id}
|
||||
options {:headers {"content-type" "application/json"}
|
||||
:body body}
|
||||
:body (json/write-str body)}
|
||||
response @(post (eth-rpc-url) options)
|
||||
result (safe-read-str (:body response))]
|
||||
(log/debug body "\n" result)
|
||||
|
||||
(when internal-tx-id
|
||||
(log/infof "%s: eth-rpc %s" internal-tx-id method))
|
||||
(log/debugf "%s: eth-rpc req(%s) body: %s" internal-tx-id request-id body)
|
||||
(log/debugf "%s: eth-rpc req(%s) result: %s" internal-tx-id request-id result)
|
||||
(cond
|
||||
;; Ignore any responses that have mismatching request ID
|
||||
(not= (:id result) request-id)
|
||||
|
@ -142,7 +180,10 @@
|
|||
|
||||
;; If request ID matches but contains error, throw
|
||||
(:error result)
|
||||
(throw (ex-info "Error submitting transaction via eth-rpc" (:error result)))
|
||||
(throw
|
||||
(ex-info (format "%s: Error submitting transaction via eth-rpc %s"
|
||||
(or internal-tx-id "(no-tx-id)") (:error result))
|
||||
(:error result)))
|
||||
|
||||
:else
|
||||
(:result result))))
|
||||
|
@ -179,9 +220,10 @@
|
|||
(defn estimate-gas
|
||||
[from to value & [params]]
|
||||
(let [geth-estimate (eth-rpc
|
||||
"eth_estimateGas" [(merge params {:from from
|
||||
:to to
|
||||
:value value})])
|
||||
{:method "eth_estimateGas"
|
||||
:params [(merge params {:from from
|
||||
:to to
|
||||
:value value})]})
|
||||
adjusted-gas (adjust-gas-estimate geth-estimate)]
|
||||
|
||||
(log/debug "estimated gas (geth):" geth-estimate)
|
||||
|
@ -204,7 +246,8 @@
|
|||
|
||||
(defn get-balance-hex
|
||||
[account]
|
||||
(eth-rpc "eth_getBalance" [account "latest"]))
|
||||
(eth-rpc {:method "eth_getBalance"
|
||||
:params [account "latest"]}))
|
||||
|
||||
(defn get-balance-wei
|
||||
[account]
|
||||
|
@ -223,7 +266,8 @@
|
|||
|
||||
(defn get-transaction-receipt
|
||||
[hash]
|
||||
(eth-rpc "eth_getTransactionReceipt" [hash]))
|
||||
(eth-rpc {:method "eth_getTransactionReceipt"
|
||||
:params [hash]}))
|
||||
|
||||
(defn format-call-params
|
||||
[method-id & params]
|
||||
|
@ -233,10 +277,12 @@
|
|||
(defn call
|
||||
[contract method-id & params]
|
||||
(let [data (apply format-call-params method-id params)]
|
||||
(eth-rpc "eth_call" [{:to contract :data data} "latest"])))
|
||||
(eth-rpc {:method "eth_call"
|
||||
:params [{:to contract :data data} "latest"]})))
|
||||
|
||||
(defn execute
|
||||
[from contract method-id gas-limit & params]
|
||||
[{:keys [from contract method-id gas-limit params internal-tx-id]}]
|
||||
{:pre [(string? method-id)]}
|
||||
(let [data (apply format-call-params method-id params)
|
||||
gas-price (gas-price)
|
||||
value (format "0x%x" 0)
|
||||
|
@ -247,21 +293,23 @@
|
|||
(merge {:gasPrice (integer->hex gas-price)})
|
||||
contract
|
||||
(merge {:to contract}))
|
||||
gas (if gas-limit gas-limit
|
||||
(estimate-gas from contract value params))
|
||||
gas (or gas-limit (estimate-gas from contract value params))
|
||||
params (if (offline-signing?)
|
||||
(get-signed-tx (biginteger gas-price)
|
||||
(hex->big-integer gas)
|
||||
contract
|
||||
data)
|
||||
(get-signed-tx {:internal-tx-id internal-tx-id
|
||||
:gas-price (biginteger gas-price)
|
||||
:gas-limit (hex->big-integer gas)
|
||||
:to contract
|
||||
:data data})
|
||||
(assoc params :gas gas))]
|
||||
(if (offline-signing?)
|
||||
(eth-rpc
|
||||
"eth_sendRawTransaction"
|
||||
[params])
|
||||
{:method "eth_sendRawTransaction"
|
||||
:params [params]
|
||||
:internal-tx-id internal-tx-id})
|
||||
(eth-rpc
|
||||
"personal_sendTransaction"
|
||||
[params (eth-password)]))))
|
||||
{:method "personal_sendTransaction"
|
||||
:params [params (eth-password)]
|
||||
:internal-tx-id internal-tx-id}))))
|
||||
|
||||
(defn hex-ch->num
|
||||
[ch]
|
||||
|
@ -322,7 +370,6 @@
|
|||
eth-core
|
||||
:start
|
||||
(do
|
||||
(swap! web3j-obj (constantly nil))
|
||||
(swap! creds-obj (constantly nil))
|
||||
(log/info "eth/core started"))
|
||||
:stop
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
(ns commiteth.eth.multisig-wallet
|
||||
(:require [commiteth.eth.core :as eth
|
||||
:refer [create-web3j creds]]
|
||||
(:require [commiteth.eth.core :as eth]
|
||||
[commiteth.config :refer [env]]
|
||||
[clojure.tools.logging :as log]
|
||||
[taoensso.tufte :as tufte :refer (defnp p profiled profile)]
|
||||
|
@ -36,24 +35,19 @@
|
|||
[]
|
||||
(env :tokenreg-base-format :status))
|
||||
|
||||
(defn create-new
|
||||
[owner1 owner2 required]
|
||||
(eth/execute (eth/eth-account)
|
||||
(factory-contract-addr)
|
||||
(:create method-ids)
|
||||
(eth/integer->hex 865000) ;; gas-limit
|
||||
0x40
|
||||
0x2
|
||||
required
|
||||
owner1
|
||||
owner2))
|
||||
|
||||
|
||||
(defn deploy-multisig
|
||||
"Deploy a new multisig contract to the blockchain with commiteth bot
|
||||
and given owner as owners."
|
||||
[owner]
|
||||
(create-new (eth/eth-account) owner 2))
|
||||
and given owner as owners.
|
||||
`internal-tx-id` is used to identify what issue this multisig is deployed
|
||||
for and manage nonces at a later point in time."
|
||||
[{:keys [owner internal-tx-id]}]
|
||||
{:pre [(string? owner) (string? internal-tx-id)]}
|
||||
(eth/execute {:internal-tx-id internal-tx-id
|
||||
:from (eth/eth-account)
|
||||
:contract (factory-contract-addr)
|
||||
:method-id (:create method-ids)
|
||||
:gas-limit (eth/integer->hex 865000)
|
||||
:params [0x40 0x2 2 (eth/eth-account) owner]}))
|
||||
|
||||
(defn find-event-in-tx-receipt [tx-receipt topic-id]
|
||||
(let [logs (:logs tx-receipt)
|
||||
|
@ -92,20 +86,18 @@
|
|||
|
||||
|
||||
(defn send-all
|
||||
[contract to]
|
||||
(log/debug "multisig/send-all " contract to)
|
||||
[{:keys [contract payout-address internal-tx-id]}]
|
||||
{:pre [(string? contract) (string? payout-address) (string? internal-tx-id)]}
|
||||
(log/debug "multisig/send-all " contract payout-address internal-tx-id)
|
||||
(let [params (eth/format-call-params
|
||||
(:withdraw-everything method-ids)
|
||||
to)]
|
||||
(eth/execute (eth/eth-account)
|
||||
contract
|
||||
(:submit-transaction method-ids)
|
||||
nil
|
||||
contract
|
||||
0
|
||||
"0x60" ;; TODO: document these
|
||||
"0x24" ;; or refactor out
|
||||
params)))
|
||||
payout-address)]
|
||||
(eth/execute {:internal-tx-id internal-tx-id
|
||||
:from (eth/eth-account)
|
||||
:contract contract
|
||||
:method-id (:submit-transaction method-ids)
|
||||
:gas-limit nil
|
||||
:params [contract 0 "0x60" "0x24" params]})))
|
||||
|
||||
|
||||
(defn get-token-address [token]
|
||||
|
@ -118,17 +110,17 @@
|
|||
(log/debug "multisig/watch-token" bounty-addr token)
|
||||
(let [token-address (get-token-address token)]
|
||||
(assert token-address)
|
||||
(eth/execute (eth/eth-account)
|
||||
bounty-addr
|
||||
(:watch method-ids)
|
||||
nil
|
||||
token-address)))
|
||||
|
||||
(eth/execute {:internal-tx-id (str "watch-token-" (System/currentTimeMillis) "-" bounty-addr)
|
||||
:from (eth/eth-account)
|
||||
:contract bounty-addr
|
||||
:method-id (:watch method-ids)
|
||||
:gas-limit nil
|
||||
:params [token-address]})))
|
||||
|
||||
(defn load-bounty-contract [addr]
|
||||
(MultiSigTokenWallet/load addr
|
||||
(create-web3j)
|
||||
(creds)
|
||||
@eth/web3j-obj
|
||||
(eth/creds)
|
||||
(eth/gas-price)
|
||||
(BigInteger/valueOf 500000)))
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
(ns commiteth.eth.token-registry
|
||||
(:require [commiteth.eth.core :as eth
|
||||
:refer [create-web3j creds]]
|
||||
(:require [commiteth.eth.core :as eth]
|
||||
[commiteth.config :refer [env]]
|
||||
[clojure.tools.logging :as log])
|
||||
(:import [org.web3j
|
||||
|
@ -23,8 +22,8 @@
|
|||
|
||||
(defn- load-tokenreg-contract [addr]
|
||||
(TokenReg/load addr
|
||||
(create-web3j)
|
||||
(creds)
|
||||
@eth/web3j-obj
|
||||
(eth/creds)
|
||||
(eth/gas-price)
|
||||
(BigInteger/valueOf 21000)))
|
||||
|
||||
|
@ -59,9 +58,8 @@
|
|||
(defn deploy-parity-tokenreg
|
||||
"Deploy an instance of parity token-registry to current network"
|
||||
[]
|
||||
(let [web3j (create-web3j)]
|
||||
(TokenReg/deploy web3j
|
||||
(creds)
|
||||
(eth/gas-price)
|
||||
(BigInteger/valueOf 4000000) ;; gas limit
|
||||
BigInteger/ZERO)))
|
||||
(TokenReg/deploy @eth/web3j-obj
|
||||
(eth/creds)
|
||||
(eth/gas-price)
|
||||
(BigInteger/valueOf 4000000) ;; gas limit
|
||||
BigInteger/ZERO))
|
||||
|
|
|
@ -341,6 +341,8 @@
|
|||
tokens
|
||||
winner-login)
|
||||
)]
|
||||
(when (and (= state :merged) (empty? winner-address))
|
||||
(log/warn "issue %s: Cannot sign pending bounty - winner has no payout address" issue-id))
|
||||
(when (= :paid state)
|
||||
(db-bounties/update-payout-receipt issue-id payout-receipt))
|
||||
|
||||
|
|
|
@ -115,18 +115,18 @@
|
|||
|
||||
|
||||
(defn handle-claim
|
||||
[issue user-id login name avatar_url owner repo repo-id pr-id pr-number head-sha merged? event-type]
|
||||
[issue user-id login name avatar_url owner repo repo-id pr-id pr-number pr-title head-sha merged? event-type]
|
||||
(users/create-user user-id login name nil avatar_url)
|
||||
(let [open-or-edit? (contains? #{:opened :edited} event-type)
|
||||
close? (= :closed event-type)
|
||||
pr-data {:repo_id repo-id
|
||||
:pr_id pr-id
|
||||
:pr_number pr-number
|
||||
:title pr-title
|
||||
:user_id user-id
|
||||
:issue_number (:issue_number issue)
|
||||
:issue_id (:issue_id issue)
|
||||
:state event-type}]
|
||||
|
||||
;; TODO: in the opened case if the submitting user has no
|
||||
;; Ethereum address stored, we could post a comment to the
|
||||
;; Github PR explaining that payout is not possible if the PR is
|
||||
|
@ -174,22 +174,24 @@
|
|||
pr-body :body
|
||||
pr-title :title} :pull_request}]
|
||||
(log/info "handle-pull-request-event" event-type owner repo repo-id login pr-body pr-title)
|
||||
(if-let [issue (some #(issues/get-issue repo-id %1) (extract-issue-number owner repo pr-body pr-title))]
|
||||
(if-not (:commit_sha issue) ; no PR has been merged yet referencing this issue
|
||||
(do
|
||||
(log/info "Referenced bounty issue found" owner repo (:issue_number issue))
|
||||
(handle-claim issue
|
||||
user-id
|
||||
login name
|
||||
avatar_url
|
||||
owner repo
|
||||
repo-id
|
||||
pr-id
|
||||
pr-number
|
||||
head-sha
|
||||
merged?
|
||||
event-type))
|
||||
(log/info "PR for issue already merged"))
|
||||
(if-let [issues (remove nil? (map #(issues/get-issue repo-id %1) (extract-issue-number owner repo pr-body pr-title)))]
|
||||
(doseq [issue issues]
|
||||
(if-not (:commit_sha issue) ; no PR has been merged yet referencing this issue
|
||||
(do
|
||||
(log/info "Referenced bounty issue found" owner repo (:issue_number issue))
|
||||
(handle-claim issue
|
||||
user-id
|
||||
login name
|
||||
avatar_url
|
||||
owner repo
|
||||
repo-id
|
||||
pr-id
|
||||
pr-number
|
||||
pr-title
|
||||
head-sha
|
||||
merged?
|
||||
event-type))
|
||||
(log/info "PR for issue already merged")))
|
||||
(when (= :edited event-type)
|
||||
; Remove PR if it does not reference any issue
|
||||
(pull-requests/remove pr-id))))
|
||||
|
@ -383,7 +385,7 @@
|
|||
(log/debug "webhook-app POST, headers" headers)
|
||||
(let [raw-payload (slurp body)
|
||||
payload (json/parse-string raw-payload true)]
|
||||
(log/info "webhook-app POST, payload:" (pr-str payload))
|
||||
(log/debug "webhook-app POST, payload:" (pr-str payload))
|
||||
(if (validate-secret-one-hook payload raw-payload (get headers "x-hub-signature"))
|
||||
(do
|
||||
(log/debug "Github secret validation OK app")
|
||||
|
|
|
@ -38,25 +38,26 @@
|
|||
(update-balances)))
|
||||
|
||||
)
|
||||
|
||||
(defn update-issue-contract-address
|
||||
"For each pending deployment: gets transaction receipt, updates db
|
||||
state (contract-address, comment-id) and posts github comment"
|
||||
[]
|
||||
(log/info "In update-issue-contract-address")
|
||||
(p :update-issue-contract-address
|
||||
<<<<<<< HEAD
|
||||
(doseq [{:keys [issue-id transaction-hash]} (issues/list-pending-deployments)]
|
||||
(log/info "pending deployment:" transaction-hash)
|
||||
(log/infof "issue %s: pending deployment: %s" issue-id transaction-hash)
|
||||
(try
|
||||
(when-let [receipt (eth/get-transaction-receipt transaction-hash)]
|
||||
(log/info "update-issue-contract-address: transaction receipt for issue #"
|
||||
issue-id ": " receipt)
|
||||
(log/infof "issue %s: update-issue-contract-address: tx receipt: %s" issue-id receipt)
|
||||
(if-let [contract-address (multisig/find-created-multisig-address receipt)]
|
||||
(let [{:keys [owner repo comment-id issue-number] :as issue}
|
||||
(issues/update-contract-address issue-id contract-address)
|
||||
balance-eth-str (eth/get-balance-eth contract-address 6)
|
||||
balance-eth (read-string balance-eth-str)
|
||||
tokens {}]
|
||||
(log/info "Updating comment")
|
||||
(log/infof "issue %s: Updating comment" issue-id)
|
||||
(github/update-comment (to-map issue-id
|
||||
owner
|
||||
repo
|
||||
|
@ -66,10 +67,9 @@
|
|||
balance-eth
|
||||
balance-eth-str
|
||||
tokens)))
|
||||
(log/error "Failed to find contract address in tx logs")))
|
||||
(log/errorf "issue %s: Failed to find contract address in tx logs" issue-id)))
|
||||
(catch Throwable ex
|
||||
(do (log/error "update-issue-contract-address exception:" ex)
|
||||
(clojure.stacktrace/print-stack-trace ex))))))
|
||||
(log/errorf ex "issue %s: update-issue-contract-address exception:" issue-id)))))
|
||||
(log/info "Exit update-issue-contract-address"))
|
||||
|
||||
|
||||
|
@ -79,26 +79,37 @@
|
|||
(p :deploy-pending-contracts
|
||||
(doseq [{:keys [issue-id issue-number owner owner-address repo]}
|
||||
(db-bounties/pending-contracts)]
|
||||
(log/debug "Trying to re-deploy failed bounty contract deployment, issue-id:" issue-id)
|
||||
(bounties/deploy-contract owner owner-address repo issue-id issue-number))))
|
||||
(log/infof "issue %s: Trying to re-deploy failed bounty contract deployment" issue-id)
|
||||
(try
|
||||
(bounties/deploy-contract owner owner-address repo issue-id issue-number)
|
||||
(doseq [{issue-id :issue_id
|
||||
issue-number :issue_number
|
||||
owner :owner
|
||||
owner-address :owner_address
|
||||
repo :repo} (db-bounties/pending-contracts)]
|
||||
(log/infof "issue %s: Trying to re-deploy failed bounty contract deployment" issue-id)
|
||||
(try
|
||||
(bounties/deploy-contract owner owner-address repo issue-id issue-number)
|
||||
(catch Throwable t
|
||||
(log/errorf t "issue %s: deploy-pending-contracts exception: %s" issue-id (ex-data t)))))))
|
||||
|
||||
(defn self-sign-bounty
|
||||
"Walks through all issues eligible for bounty payout and signs corresponding transaction"
|
||||
[]
|
||||
(log/info "In self-sign-bounty")
|
||||
(p :self-sign-bounty
|
||||
<<<<<<< HEAD
|
||||
(doseq [{:keys [contract-address winner-address issue-id winner-login] :as issue}
|
||||
(db-bounties/pending-bounties)]
|
||||
(try
|
||||
(let [value (eth/get-balance-hex contract-address)]
|
||||
(when-not (empty? winner-address)
|
||||
(let [execute-hash (multisig/send-all contract-address winner-address)]
|
||||
(log/info "Payout self-signed, called sign-all(" contract-address winner-address ") tx:" execute-hash)
|
||||
(log/infof "issue %s: Payout self-signed, called sign-all(%s) tx: %s" issue-id contract-address winner-address execute-hash)
|
||||
(db-bounties/update-execute-hash-and-winner-login issue-id execute-hash winner-login)))
|
||||
(github/update-comment issue))
|
||||
(catch Throwable ex
|
||||
(do (log/error "self-sign-bounty exception:" ex)
|
||||
(clojure.stacktrace/print-stack-trace ex))))))
|
||||
(log/error ex "issue %s: self-sign-bounty exception" issue-id)))))
|
||||
(log/info "Exit self-sign-bounty"))
|
||||
|
||||
(defn update-confirm-hash
|
||||
|
@ -107,13 +118,16 @@
|
|||
(log/info "In update-confirm-hash")
|
||||
(p :update-confirm-hash
|
||||
(doseq [{:keys [issue-id execute-hash]} (db-bounties/pending-payouts)]
|
||||
(log/info "pending payout:" execute-hash)
|
||||
(when-let [receipt (eth/get-transaction-receipt execute-hash)]
|
||||
(log/info "execution receipt for issue #" issue-id ": " receipt)
|
||||
(when-let [confirm-hash (multisig/find-confirmation-tx-id receipt)]
|
||||
(log/info "confirm hash:" confirm-hash)
|
||||
(db-bounties/update-confirm-hash issue-id confirm-hash)))))
|
||||
(log/info "Exit update-confirm-hash"))
|
||||
(log/infof "issue %s: pending payout: %s" issue-id execute-hash)
|
||||
(try
|
||||
(when-let [receipt (eth/get-transaction-receipt execute-hash)]
|
||||
(log/infof "issue %s: execution receipt for issue " issue-id receipt)
|
||||
(when-let [confirm-hash (multisig/find-confirmation-tx-id receipt)]
|
||||
(log/infof "issue %s: confirm hash:" issue-id confirm-hash)
|
||||
(db-bounties/update-confirm-hash issue-id confirm-hash)))
|
||||
(catch Throwable ex
|
||||
(log/errorf ex "issue %s: update-confirm-hash exception:" issue-id))) )
|
||||
(log/info "Exit update-confirm-hash")))
|
||||
|
||||
|
||||
(defn update-watch-hash
|
||||
|
@ -121,9 +135,13 @@
|
|||
[]
|
||||
(p :update-watch-hash
|
||||
(doseq [{:keys [issue-id watch-hash]} (db-bounties/pending-watch-calls)]
|
||||
(log/info "pending watch call" watch-hash)
|
||||
(when-let [receipt (eth/get-transaction-receipt watch-hash)]
|
||||
(db-bounties/update-watch-hash issue-id nil)))))
|
||||
(log/infof "issue %s: pending watch call %s" issue-id watch-hash)
|
||||
(try
|
||||
(when-let [receipt (eth/get-transaction-receipt watch-hash)]
|
||||
(db-bounties/update-watch-hash issue-id nil))
|
||||
(catch Throwable ex
|
||||
(log/errorf ex "issue %s: update-watch-hash exception:" issue-id))
|
||||
))))
|
||||
|
||||
|
||||
(defn older-than-3h?
|
||||
|
@ -141,7 +159,7 @@
|
|||
(p :update-payout-receipt
|
||||
(doseq [{:keys [payout-hash contract-address confirm-hash issue-id updated] :as issue}
|
||||
(db-bounties/confirmed-payouts)]
|
||||
(log/debug "confirmed payout:" payout-hash)
|
||||
(log/infof "issue %s: confirmed payout: %s" issue-id payout-hash)
|
||||
(try
|
||||
(if-let [receipt (eth/get-transaction-receipt payout-hash)]
|
||||
(let [contract-tokens (multisig/token-balances contract-address)
|
||||
|
@ -150,23 +168,21 @@
|
|||
(some #(> (second %) 0.0) contract-tokens)
|
||||
(> contract-eth-balance 0))
|
||||
(do
|
||||
(log/info "Contract still has funds")
|
||||
(log/infof "issue %s: Contract (%s) still has funds" issue-id contract-address)
|
||||
(when (multisig/is-confirmed? contract-address confirm-hash)
|
||||
(log/info "Detected bounty with funds and confirmed payout, calling executeTransaction")
|
||||
(log/infof "issue %s: Detected bounty with funds and confirmed payout, calling executeTransaction" issue-id)
|
||||
(let [execute-tx-hash (multisig/execute-tx contract-address confirm-hash)]
|
||||
(log/info "execute tx:" execute-tx-hash))))
|
||||
(log/infof "issue %s: execute tx: %s" issue-id execute-tx-hash))))
|
||||
|
||||
(do
|
||||
(log/info "Payout has succeeded, saving payout receipt for issue #" issue-id ": " receipt)
|
||||
(log/infof "issue %s: Payout has succeeded, payout receipt %s" issue-id receipt)
|
||||
(github/update-comment (assoc issue :payout-receipt receipt)))))
|
||||
(when (older-than-3h? updated)
|
||||
(log/info "Resetting payout hash for issue" issue-id "as it has not been mined in 3h")
|
||||
(log/warn "issue %s: Resetting payout hash for issue as it has not been mined in 3h" issue-id)
|
||||
(db-bounties/reset-payout-hash issue-id)))
|
||||
(catch Throwable ex
|
||||
(do (log/error "update-payout-receipt exception:" ex)
|
||||
(clojure.stacktrace/print-stack-trace ex))))))
|
||||
(log/info "Exit update-payout-receipt")
|
||||
)
|
||||
(catch Throwable ex
|
||||
(log/error ex "issue %s: update-payout-receipt exception" issue-id)))))
|
||||
(log/info "Exit update-payout-receipt"))
|
||||
|
||||
(defn abs
|
||||
"(abs n) is the absolute value of n"
|
||||
|
@ -179,26 +195,27 @@
|
|||
|
||||
|
||||
(defn update-bounty-token-balances
|
||||
"Helper function for updating internal ERC20 token balances to token multisig contract. Will be called periodically for all open bounty contracts."
|
||||
"Helper function for updating internal ERC20 token balances to token
|
||||
multisig contract. Will be called periodically for all open bounty
|
||||
contracts."
|
||||
[issue-id bounty-addr watch-hash]
|
||||
#_(log/info "In update-bounty-token-balances for issue" issue-id)
|
||||
(log/info "In update-bounty-token-balances for issue" issue-id)
|
||||
(doseq [[tla token-data] (token-data/as-map)]
|
||||
(try
|
||||
(let [balance (multisig/token-balance bounty-addr tla)]
|
||||
(when (> balance 0)
|
||||
(do
|
||||
(log/info "bounty at" bounty-addr "has" balance "of token" tla)
|
||||
(log/infof "bounty %s: has %s of token %s" bounty-addr balance tla)
|
||||
(let [internal-balance (multisig/token-balance-in-bounty bounty-addr tla)]
|
||||
(when (and (nil? watch-hash)
|
||||
(not= balance internal-balance))
|
||||
(log/info "balances not in sync, calling watch")
|
||||
(log/infof "bounty %s: balances not in sync, calling watch" bounty-addr)
|
||||
(let [hash (multisig/watch-token bounty-addr tla)]
|
||||
(db-bounties/update-watch-hash issue-id hash)))))))
|
||||
(catch Throwable ex
|
||||
(do (log/error "update-bounty-token-balances exception:" ex)
|
||||
(clojure.stacktrace/print-stack-trace ex)))))
|
||||
#_(log/info "Exit update-bounty-token-balances"))
|
||||
|
||||
(catch Throwable ex
|
||||
(log/error ex "bounty %s: update-bounty-token-balances exception" bounty-addr))))
|
||||
(log/info "Exit update-bounty-token-balances"))
|
||||
|
||||
|
||||
(defn update-contract-internal-balances
|
||||
"It is required in our current smart contract to manually update it's internal balance when some tokens have been added."
|
||||
|
@ -256,8 +273,7 @@
|
|||
(merge token-balances {:ETH current-balance-eth})))
|
||||
(github/update-comment issue))))
|
||||
(catch Throwable ex
|
||||
(do (log/error "update-balances exception:" ex)
|
||||
(clojure.stacktrace/print-stack-trace ex))))))
|
||||
(log/error ex "issue %s: update-balances exception" issue-id)))))
|
||||
(log/info "Exit update-balances"))
|
||||
|
||||
|
||||
|
@ -265,7 +281,7 @@
|
|||
(try
|
||||
(func)
|
||||
(catch Throwable t
|
||||
(log/error t))))
|
||||
(log/error t (.getMessage t) (ex-data t)))))
|
||||
|
||||
(defn run-tasks [tasks]
|
||||
(doall
|
||||
|
|
|
@ -4,51 +4,95 @@
|
|||
[commiteth.common :refer [human-time
|
||||
display-data-page
|
||||
items-per-page
|
||||
issue-url]]
|
||||
issue-url
|
||||
pull-request-url]]
|
||||
[commiteth.handlers :as handlers]
|
||||
[commiteth.db :as db]
|
||||
[commiteth.ui-model :as ui-model]
|
||||
[commiteth.subscriptions :as subs]
|
||||
[commiteth.util :as util]))
|
||||
|
||||
(defn display-bounty-claims [claims]
|
||||
[:div.bounty-claims-container.ph4.pv3
|
||||
(for [claim claims]
|
||||
(let [{:keys [avatar-url
|
||||
pr-title
|
||||
pr-id
|
||||
updated
|
||||
repo-owner
|
||||
repo-name
|
||||
pr-number]} claim]
|
||||
^{:key pr-id}
|
||||
[:div.bounty-claims-row.open-bounty-item-content.flex
|
||||
[:div.bounty-claims-icon.pl2
|
||||
[:div.ui.tiny.circular.image
|
||||
[:img {:src avatar-url}]]]
|
||||
[:span.bounty-claims-text.pt2.pl2
|
||||
[:a.fw5
|
||||
{:href (pull-request-url repo-owner repo-name pr-number)}
|
||||
(if pr-title
|
||||
pr-title
|
||||
(str "#" pr-number " by " repo-owner " in " repo-name))]
|
||||
[:span.time.pl2 (human-time updated)]]]))])
|
||||
|
||||
(defn blue-arrow-box [image-src]
|
||||
"generates the appropriate container for a blue arrow"
|
||||
[:span.blue-arrow-box.pa1
|
||||
[:img.blue-arrow-image.v-mid {:src image-src}]])
|
||||
|
||||
(defn bounty-item [bounty]
|
||||
(let [{avatar-url :repo_owner_avatar_url
|
||||
owner :repo-owner
|
||||
repo-name :repo-name
|
||||
issue-title :issue-title
|
||||
issue-number :issue-number
|
||||
updated :updated
|
||||
tokens :tokens
|
||||
balance-eth :balance-eth
|
||||
value-usd :value-usd
|
||||
claim-count :claim-count} bounty
|
||||
full-repo (str owner "/" repo-name)
|
||||
repo-url (str "https://github.com/" full-repo)
|
||||
repo-link [:a {:href repo-url} full-repo]
|
||||
issue-link [:a
|
||||
{:href (issue-url owner repo-name issue-number)}
|
||||
issue-title]]
|
||||
[:div.open-bounty-item
|
||||
[:div.open-bounty-item-content
|
||||
[:div.header issue-link]
|
||||
[:div.bounty-item-row
|
||||
[:div.time (human-time updated)]
|
||||
[:span.bounty-repo-label repo-link]]
|
||||
|
||||
[:div.footer-row
|
||||
[:div.balance-badge "ETH " balance-eth]
|
||||
(for [[tla balance] tokens]
|
||||
^{:key (random-uuid)}
|
||||
[:div.balance-badge.token
|
||||
(str (subs (str tla) 1) " " balance)])
|
||||
[:span.usd-value-label "Value "] [:span.usd-balance-label (str "$" value-usd)]
|
||||
(when (> claim-count 0)
|
||||
[:span.open-claims-label (str claim-count " open claim"
|
||||
(when (> claim-count 1) "s"))])]]
|
||||
[:div.open-bounty-item-icon
|
||||
[:div.ui.tiny.circular.image
|
||||
[:img {:src avatar-url}]]]]))
|
||||
(let [open-bounty-claims (rf/subscribe [::subs/open-bounty-claims])]
|
||||
(fn [bounty]
|
||||
(let [{avatar-url :repo_owner_avatar_url
|
||||
owner :repo-owner
|
||||
repo-name :repo-name
|
||||
issue-title :issue-title
|
||||
issue-number :issue-number
|
||||
issue-id :issue-id
|
||||
updated :updated
|
||||
tokens :tokens
|
||||
balance-eth :balance-eth
|
||||
value-usd :value-usd
|
||||
claim-count :claim-count
|
||||
claims :claims} bounty
|
||||
full-repo (str owner "/" repo-name)
|
||||
repo-url (str "https://github.com/" full-repo)
|
||||
repo-link [:a {:href repo-url} full-repo]
|
||||
issue-link [:a
|
||||
{:href (issue-url owner repo-name issue-number)}
|
||||
issue-title]
|
||||
open-claims-click #(rf/dispatch [::handlers/open-bounty-claim issue-id])
|
||||
close-claims-click #(rf/dispatch [::handlers/close-bounty-claim issue-id])
|
||||
matches-current-issue? (some #{issue-id} @open-bounty-claims)]
|
||||
[:div
|
||||
[:div.open-bounty-item.ph4
|
||||
[:div.open-bounty-item-content
|
||||
[:div.header issue-link]
|
||||
[:div.bounty-item-row
|
||||
[:div.time (human-time updated)]
|
||||
[:span.bounty-repo-label repo-link]]
|
||||
[:div.footer-row
|
||||
[:div.balance-badge "ETH " balance-eth]
|
||||
(for [[tla balance] tokens]
|
||||
^{:key (random-uuid)}
|
||||
[:div.balance-badge.token
|
||||
(str (subs (str tla) 1) " " balance)])
|
||||
[:span.usd-value-label "Value "] [:span.usd-balance-label (str "$" value-usd)]
|
||||
(when (> claim-count 0)
|
||||
[:span.open-claims-label
|
||||
{:on-click (if matches-current-issue?
|
||||
#(close-claims-click)
|
||||
#(open-claims-click))}
|
||||
(str claim-count " open claim"
|
||||
(when (> claim-count 1) "s"))
|
||||
(if matches-current-issue?
|
||||
[blue-arrow-box "blue-arrow-up.png"]
|
||||
[blue-arrow-box "blue-arrow-down.png"])])]]
|
||||
[:div.open-bounty-item-icon
|
||||
[:div.ui.tiny.circular.image
|
||||
[:img {:src avatar-url}]]]]
|
||||
(when matches-current-issue?
|
||||
[display-bounty-claims claims])]))))
|
||||
|
||||
(defn bounties-filter-tooltip-value-input-view [label tooltip-open? opts]
|
||||
[:div.open-bounties-filter-element-tooltip-value-input-container
|
||||
|
@ -210,7 +254,7 @@
|
|||
[:div
|
||||
(let [left (inc (* (dec page-number) items-per-page))
|
||||
right (dec (+ left item-count))]
|
||||
[:div.item-counts-label-and-sorting-container
|
||||
[:div.item-counts-label-and-sorting-container.ph4
|
||||
[:div.item-counts-label
|
||||
[:span (str "Showing " left "-" right " of " total-count)]]
|
||||
[bounties-sort-view]])
|
||||
|
@ -227,7 +271,7 @@
|
|||
[:div.ui.text.loader.view-loading-label "Loading"]]]
|
||||
[:div.ui.container.open-bounties-container
|
||||
{:ref #(reset! container-element %1)}
|
||||
[:div.open-bounties-header "Bounties"]
|
||||
[:div.open-bounties-filter-and-sort
|
||||
[:div.open-bounties-header.ph4.pt4 "Bounties"]
|
||||
[:div.open-bounties-filter-and-sort.ph4
|
||||
[bounty-filters-view]]
|
||||
[bounties-list @bounty-page-data container-element]]))))
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
(defn issue-url [owner repo number]
|
||||
(str "https://github.com/" owner "/" repo "/issues/" number))
|
||||
|
||||
(defn pull-request-url [owner repo number]
|
||||
(str "https://github.com/" owner "/" repo "/pull/" number))
|
||||
|
||||
(def items-per-page 15)
|
||||
|
||||
(defn draw-page-numbers [page-number page-count container-element]
|
||||
|
@ -132,7 +135,7 @@
|
|||
:else
|
||||
[:div
|
||||
[draw-items]
|
||||
[:div.page-nav-container
|
||||
[:div.page-nav-container.ph4.pb4
|
||||
[:div.page-direction-container
|
||||
[draw-rect :backward]
|
||||
[draw-rect :forward]]
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
(def default-db
|
||||
{:page :bounties
|
||||
:user nil
|
||||
:user-profile-loaded? false
|
||||
:user-profile-loaded? false
|
||||
:repos-loading? false
|
||||
:repos {}
|
||||
:activity-feed-loading? false
|
||||
|
@ -18,6 +18,7 @@
|
|||
::ui-model/bounty-filter-type|currency nil
|
||||
::ui-model/bounty-filter-type|date nil
|
||||
::ui-model/bounty-filter-type|owner nil}
|
||||
::open-bounty-claims #{}
|
||||
:owner-bounties {}
|
||||
:top-hunters []
|
||||
:activity-feed []})
|
||||
|
|
|
@ -484,6 +484,16 @@
|
|||
(.removeEventListener js/window "click" close-dropdown)
|
||||
(assoc db :user-dropdown-open? false)))
|
||||
|
||||
(reg-event-db
|
||||
::open-bounty-claim
|
||||
(fn [db [_ opening-issue-id]]
|
||||
(update db ::db/open-bounty-claims #(conj % opening-issue-id))))
|
||||
|
||||
(reg-event-db
|
||||
::close-bounty-claim
|
||||
(fn [db [_ closing-issue-id]]
|
||||
(update db ::db/open-bounty-claims #(disj % closing-issue-id))))
|
||||
|
||||
(reg-event-db
|
||||
::set-open-bounties-sorting-type
|
||||
(fn [db [_ sorting-type]]
|
||||
|
|
|
@ -53,17 +53,24 @@
|
|||
:open-bounties-page
|
||||
:<- [::filtered-and-sorted-open-bounties]
|
||||
:<- [:page-number]
|
||||
(fn [[open-bounties page-number] _]
|
||||
:<- [:activity-feed]
|
||||
(fn [[open-bounties page-number activity-feed] _]
|
||||
(let [total-count (count open-bounties)
|
||||
start (* (dec page-number) items-per-page)
|
||||
end (min total-count (+ items-per-page start))
|
||||
items (subvec open-bounties start end)]
|
||||
{:items items
|
||||
:item-count (count items)
|
||||
start (* (dec page-number) items-per-page)
|
||||
end (min total-count (+ items-per-page start))
|
||||
items (->> (subvec open-bounties start end)
|
||||
(map (fn [bounty]
|
||||
(let [matching-claims (filter
|
||||
(fn [claim]
|
||||
(= (:issue-id claim)
|
||||
(:issue-id bounty)))
|
||||
activity-feed)]
|
||||
(assoc bounty :claims matching-claims)))))]
|
||||
{:items items
|
||||
:item-count (count items)
|
||||
:total-count total-count
|
||||
:page-number page-number
|
||||
:page-count (Math/ceil (/ total-count items-per-page))})))
|
||||
|
||||
:page-count (Math/ceil (/ total-count items-per-page))})))
|
||||
|
||||
(reg-sub
|
||||
:owner-bounties
|
||||
|
@ -146,6 +153,11 @@
|
|||
(fn [db _]
|
||||
(:user-dropdown-open? db)))
|
||||
|
||||
(reg-sub
|
||||
::open-bounty-claims
|
||||
(fn [db _]
|
||||
(::db/open-bounty-claims db)))
|
||||
|
||||
(reg-sub
|
||||
::open-bounties-sorting-type
|
||||
(fn [db _]
|
||||
|
|
|
@ -415,7 +415,6 @@ label[for="input-hidden"] {
|
|||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
transform: translateY(-45px);
|
||||
padding: 24px;
|
||||
.open-bounties-header {
|
||||
font-family: "PostGrotesk-Medium";
|
||||
font-size: 21px;
|
||||
|
@ -714,6 +713,17 @@ label[for="input-hidden"] {
|
|||
padding: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.blue-arrow-box {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.blue-arrow-image {
|
||||
width: 13.5px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.open-bounty-item:first-child {
|
||||
|
@ -748,8 +758,6 @@ label[for="input-hidden"] {
|
|||
font-size: 15px;
|
||||
}
|
||||
|
||||
border-bottom: #eaecee 1px solid !important;
|
||||
|
||||
.open-bounty-item-content {
|
||||
width: 80%;
|
||||
padding: .7em 0 1em;
|
||||
|
@ -780,8 +788,28 @@ label[for="input-hidden"] {
|
|||
flex-direction: row !important;
|
||||
justify-content: space-between;
|
||||
width: 80%;
|
||||
|
||||
}
|
||||
|
||||
.bounty-claims-row {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.bounty-claims-container {
|
||||
background-color: #FBFBFB;
|
||||
}
|
||||
|
||||
.bounty-claims-icon {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.bounty-claims-text {
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #42505c;
|
||||
}
|
||||
}
|
||||
|
||||
.bounty-repo-label a {
|
||||
margin: auto;
|
||||
font-family: "PostGrotesk-Book";
|
||||
|
@ -804,6 +832,7 @@ label[for="input-hidden"] {
|
|||
padding-left: 15px;
|
||||
font-size: 15px;
|
||||
color: #57a7ed;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.activity-item-container {
|
||||
|
|
Loading…
Reference in New Issue