Merge pull request #422 from status-im/develop

Deploy week 16 2018
This commit is contained in:
Martin Klepsch 2018-04-18 16:45:50 +02:00 committed by GitHub
commit c4ab1047f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 478 additions and 159 deletions

View File

@ -0,0 +1 @@
ALTER TABLE "public"."pull_requests" DROP COLUMN "title";

View File

@ -0,0 +1 @@
ALTER TABLE "public"."pull_requests" ADD COLUMN "title" character varying(256);

View File

@ -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;

View File

@ -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

View File

@ -207,6 +207,7 @@ AND i.transaction_hash IS NOT NULL;
INSERT INTO pull_requests (pr_id, INSERT INTO pull_requests (pr_id,
repo_id, repo_id,
pr_number, pr_number,
title,
issue_number, issue_number,
issue_id, issue_id,
commit_sha, commit_sha,
@ -215,6 +216,7 @@ INSERT INTO pull_requests (pr_id,
VALUES(:pr_id, VALUES(:pr_id,
:repo_id, :repo_id,
:pr_number, :pr_number,
:title,
:issue_number, :issue_number,
:issue_id, :issue_id,
:commit_sha, :commit_sha,
@ -225,6 +227,7 @@ SET
state = :state, state = :state,
issue_number = :issue_number, issue_number = :issue_number,
issue_id = :issue_id, issue_id = :issue_id,
title = :title,
updated = timezone('utc'::text, now()), updated = timezone('utc'::text, now()),
commit_sha = :commit_sha; commit_sha = :commit_sha;
@ -592,7 +595,11 @@ SELECT
issue_title, issue_title,
repo_name, repo_name,
repo_owner, repo_owner,
pr_number,
pr_title,
pr_id,
issue_number, issue_number,
issue_id,
user_name, user_name,
user_avatar_url, user_avatar_url,
balance_eth, balance_eth,

View File

@ -23,9 +23,10 @@
(defn deploy-contract [owner owner-address repo issue-id issue-number] (defn deploy-contract [owner owner-address repo issue-id issue-number]
(if (empty? owner-address) (if (empty? owner-address)
(log/errorf "issue %s: Unable to deploy bounty contract because repo owner has no Ethereum addres" issue-id) (log/errorf "issue %s: Unable to deploy bounty contract because repo owner has no Ethereum addres" issue-id)
(do (try
(log/infof "issue %s: Deploying contract to %s" issue-id owner-address) (log/infof "issue %s: Deploying contract to %s" issue-id owner-address)
(if-let [transaction-hash (multisig/deploy-multisig owner-address)] (if-let [transaction-hash (multisig/deploy-multisig {:owner owner-address
:internal-tx-id (str "contract-github-issue-" issue-id)})]
(do (do
(log/infof "issue %s: Contract deployed, transaction-hash: %s" issue-id transaction-hash) (log/infof "issue %s: Contract deployed, transaction-hash: %s" issue-id transaction-hash)
(let [resp (github/post-deploying-comment owner (let [resp (github/post-deploying-comment owner
@ -36,7 +37,8 @@
(log/infof "issue %s: post-deploying-comment response: %s" issue-id resp) (log/infof "issue %s: post-deploying-comment response: %s" issue-id resp)
(issues/update-comment-id issue-id comment-id)) (issues/update-comment-id issue-id comment-id))
(issues/update-transaction-hash issue-id transaction-hash)) (issues/update-transaction-hash issue-id transaction-hash))
(log/errorf "issue %s Failed to deploy contract to %s" issue-id 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] (defn add-bounty-for-issue [repo repo-id issue]
(let [{issue-id :id (let [{issue-id :id

View File

@ -34,7 +34,7 @@
(defn update-repo-state (defn update-repo-state
[repo-id repo-state] [repo-id repo-state]
(jdbc/with-db-connection [con-db *db*] (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}))) :repo_state repo-state})))
(defn get-repo (defn get-repo
"Get a repo from DB given it's full name (owner/repo-name)" "Get a repo from DB given it's full name (owner/repo-name)"

View File

@ -28,7 +28,9 @@
(defn auto-gas-price? [] (env :auto-gas-price false)) (defn auto-gas-price? [] (env :auto-gas-price false))
(defn offline-signing? [] (env :offline-signing true)) (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)) (def creds-obj (atom nil))
(defn wallet-file-path [] (defn wallet-file-path []
@ -37,10 +39,6 @@
(defn wallet-password [] (defn wallet-password []
(env :eth-password)) (env :eth-password))
(defn create-web3j []
(or @web3j-obj
(swap! web3j-obj (constantly (Web3j/build (HttpService. (eth-rpc-url)))))))
(defn creds [] (defn creds []
(or @creds-obj (or @creds-obj
(let [password (wallet-password) (let [password (wallet-password)
@ -53,29 +51,59 @@
(throw (ex-info "Make sure you provided proper credentials in appropriate resources/config.edn" (throw (ex-info "Make sure you provided proper credentials in appropriate resources/config.edn"
{:password password :file-path file-path})))))) {:password password :file-path file-path}))))))
(defn get-signed-tx [gas-price gas-limit to data] (defn get-web3j-nonce [web3j-instance]
"Create a sign a raw transaction. (.. (.ethGetTransactionCount web3j-instance (env :eth-account) DefaultBlockParameterName/LATEST)
'From' argument is not needed as it's already sendAsync
encoded in credentials. 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" See https://web3j.readthedocs.io/en/latest/transactions.html#offline-transaction-signing"
(let [web3j (create-web3j) (let [nonce (get-nonce nonce-tracker internal-tx-id)]
nonce (.. (.ethGetTransactionCount web3j (log/infof "%s: Signing nonce: %s, gas-price: %s, gas-limit: %s"
(env :eth-account) internal-tx-id nonce gas-price gas-limit)
DefaultBlockParameterName/LATEST) (-> (RawTransaction/createTransaction (biginteger nonce) gas-price gas-limit to data)
sendAsync (TransactionEncoder/signMessage (creds))
get (Numeric/toHexString))))
getTransactionCount)
tx (RawTransaction/createTransaction
nonce
gas-price
gas-limit
to
data)
signed (TransactionEncoder/signMessage tx (creds))
hex-string (Numeric/toHexString signed)]
(log/infof "Signing TX: nonce: %s, gas-price: %s, gas-limit: %s, data: %s"
nonce gas-price gas-limit data)
hex-string))
(defn eth-gasstation-gas-price (defn eth-gasstation-gas-price
"Get max of average and average_calc from gas station and use that "Get max of average and average_calc from gas station and use that
@ -130,7 +158,8 @@
(atom 0)) (atom 0))
(defn eth-rpc (defn eth-rpc
[method params] [{:keys [method params internal-tx-id]}]
{:pre [(string? method) (some? params)]}
(let [request-id (swap! req-id-tracker inc) (let [request-id (swap! req-id-tracker inc)
body {:jsonrpc "2.0" body {:jsonrpc "2.0"
:method method :method method
@ -140,9 +169,10 @@
:body (json/write-str body)} :body (json/write-str body)}
response @(post (eth-rpc-url) options) response @(post (eth-rpc-url) options)
result (safe-read-str (:body response))] result (safe-read-str (:body response))]
(log/infof "eth-rpc req(%s) body: %s\neth-rpc req(%s) result: %s" (when internal-tx-id
request-id body request-id result) (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 (cond
;; Ignore any responses that have mismatching request ID ;; Ignore any responses that have mismatching request ID
(not= (:id result) request-id) (not= (:id result) request-id)
@ -150,7 +180,10 @@
;; If request ID matches but contains error, throw ;; If request ID matches but contains error, throw
(:error result) (: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 :else
(:result result)))) (:result result))))
@ -187,9 +220,10 @@
(defn estimate-gas (defn estimate-gas
[from to value & [params]] [from to value & [params]]
(let [geth-estimate (eth-rpc (let [geth-estimate (eth-rpc
"eth_estimateGas" [(merge params {:from from {:method "eth_estimateGas"
:to to :params [(merge params {:from from
:value value})]) :to to
:value value})]})
adjusted-gas (adjust-gas-estimate geth-estimate)] adjusted-gas (adjust-gas-estimate geth-estimate)]
(log/debug "estimated gas (geth):" geth-estimate) (log/debug "estimated gas (geth):" geth-estimate)
@ -212,7 +246,8 @@
(defn get-balance-hex (defn get-balance-hex
[account] [account]
(eth-rpc "eth_getBalance" [account "latest"])) (eth-rpc {:method "eth_getBalance"
:params [account "latest"]}))
(defn get-balance-wei (defn get-balance-wei
[account] [account]
@ -231,7 +266,8 @@
(defn get-transaction-receipt (defn get-transaction-receipt
[hash] [hash]
(eth-rpc "eth_getTransactionReceipt" [hash])) (eth-rpc {:method "eth_getTransactionReceipt"
:params [hash]}))
(defn format-call-params (defn format-call-params
[method-id & params] [method-id & params]
@ -241,10 +277,12 @@
(defn call (defn call
[contract method-id & params] [contract method-id & params]
(let [data (apply format-call-params 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 (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) (let [data (apply format-call-params method-id params)
gas-price (gas-price) gas-price (gas-price)
value (format "0x%x" 0) value (format "0x%x" 0)
@ -255,21 +293,23 @@
(merge {:gasPrice (integer->hex gas-price)}) (merge {:gasPrice (integer->hex gas-price)})
contract contract
(merge {:to contract})) (merge {:to contract}))
gas (if gas-limit gas-limit gas (or gas-limit (estimate-gas from contract value params))
(estimate-gas from contract value params))
params (if (offline-signing?) params (if (offline-signing?)
(get-signed-tx (biginteger gas-price) (get-signed-tx {:internal-tx-id internal-tx-id
(hex->big-integer gas) :gas-price (biginteger gas-price)
contract :gas-limit (hex->big-integer gas)
data) :to contract
:data data})
(assoc params :gas gas))] (assoc params :gas gas))]
(if (offline-signing?) (if (offline-signing?)
(eth-rpc (eth-rpc
"eth_sendRawTransaction" {:method "eth_sendRawTransaction"
[params]) :params [params]
:internal-tx-id internal-tx-id})
(eth-rpc (eth-rpc
"personal_sendTransaction" {:method "personal_sendTransaction"
[params (eth-password)])))) :params [params (eth-password)]
:internal-tx-id internal-tx-id}))))
(defn hex-ch->num (defn hex-ch->num
[ch] [ch]
@ -330,7 +370,6 @@
eth-core eth-core
:start :start
(do (do
(swap! web3j-obj (constantly nil))
(swap! creds-obj (constantly nil)) (swap! creds-obj (constantly nil))
(log/info "eth/core started")) (log/info "eth/core started"))
:stop :stop

View File

@ -1,6 +1,5 @@
(ns commiteth.eth.multisig-wallet (ns commiteth.eth.multisig-wallet
(:require [commiteth.eth.core :as eth (:require [commiteth.eth.core :as eth]
:refer [create-web3j creds]]
[commiteth.config :refer [env]] [commiteth.config :refer [env]]
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[taoensso.tufte :as tufte :refer (defnp p profiled profile)] [taoensso.tufte :as tufte :refer (defnp p profiled profile)]
@ -36,24 +35,19 @@
[] []
(env :tokenreg-base-format :status)) (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 (defn deploy-multisig
"Deploy a new multisig contract to the blockchain with commiteth bot "Deploy a new multisig contract to the blockchain with commiteth bot
and given owner as owners." and given owner as owners.
[owner] `internal-tx-id` is used to identify what issue this multisig is deployed
(create-new (eth/eth-account) owner 2)) 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] (defn find-event-in-tx-receipt [tx-receipt topic-id]
(let [logs (:logs tx-receipt) (let [logs (:logs tx-receipt)
@ -92,20 +86,18 @@
(defn send-all (defn send-all
[contract to] [{:keys [contract payout-address internal-tx-id]}]
(log/debug "multisig/send-all " contract to) {: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 (let [params (eth/format-call-params
(:withdraw-everything method-ids) (:withdraw-everything method-ids)
to)] payout-address)]
(eth/execute (eth/eth-account) (eth/execute {:internal-tx-id internal-tx-id
contract :from (eth/eth-account)
(:submit-transaction method-ids) :contract contract
nil :method-id (:submit-transaction method-ids)
contract :gas-limit nil
0 :params [contract 0 "0x60" "0x24" params]})))
"0x60" ;; TODO: document these
"0x24" ;; or refactor out
params)))
(defn get-token-address [token] (defn get-token-address [token]
@ -118,17 +110,17 @@
(log/debug "multisig/watch-token" bounty-addr token) (log/debug "multisig/watch-token" bounty-addr token)
(let [token-address (get-token-address token)] (let [token-address (get-token-address token)]
(assert token-address) (assert token-address)
(eth/execute (eth/eth-account) (eth/execute {:internal-tx-id (str "watch-token-" (System/currentTimeMillis) "-" bounty-addr)
bounty-addr :from (eth/eth-account)
(:watch method-ids) :contract bounty-addr
nil :method-id (:watch method-ids)
token-address))) :gas-limit nil
:params [token-address]})))
(defn load-bounty-contract [addr] (defn load-bounty-contract [addr]
(MultiSigTokenWallet/load addr (MultiSigTokenWallet/load addr
(create-web3j) @eth/web3j-obj
(creds) (eth/creds)
(eth/gas-price) (eth/gas-price)
(BigInteger/valueOf 500000))) (BigInteger/valueOf 500000)))

View File

@ -1,6 +1,5 @@
(ns commiteth.eth.token-registry (ns commiteth.eth.token-registry
(:require [commiteth.eth.core :as eth (:require [commiteth.eth.core :as eth]
:refer [create-web3j creds]]
[commiteth.config :refer [env]] [commiteth.config :refer [env]]
[clojure.tools.logging :as log]) [clojure.tools.logging :as log])
(:import [org.web3j (:import [org.web3j
@ -23,8 +22,8 @@
(defn- load-tokenreg-contract [addr] (defn- load-tokenreg-contract [addr]
(TokenReg/load addr (TokenReg/load addr
(create-web3j) @eth/web3j-obj
(creds) (eth/creds)
(eth/gas-price) (eth/gas-price)
(BigInteger/valueOf 21000))) (BigInteger/valueOf 21000)))
@ -59,9 +58,8 @@
(defn deploy-parity-tokenreg (defn deploy-parity-tokenreg
"Deploy an instance of parity token-registry to current network" "Deploy an instance of parity token-registry to current network"
[] []
(let [web3j (create-web3j)] (TokenReg/deploy @eth/web3j-obj
(TokenReg/deploy web3j (eth/creds)
(creds) (eth/gas-price)
(eth/gas-price) (BigInteger/valueOf 4000000) ;; gas limit
(BigInteger/valueOf 4000000) ;; gas limit BigInteger/ZERO))
BigInteger/ZERO)))

View File

@ -109,10 +109,14 @@
renames {:user_name :display-name renames {:user_name :display-name
:user_avatar_url :avatar-url :user_avatar_url :avatar-url
:issue_title :issue-title :issue_title :issue-title
:pr_title :pr-title
:pr_number :pr-number
:pr_id :pr-id
:type :item-type :type :item-type
:repo_name :repo-name :repo_name :repo-name
:repo_owner :repo-owner :repo_owner :repo-owner
:issue_number :issue-number :issue_number :issue-number
:issue_id :issue-id
:value_usd :value-usd :value_usd :value-usd
:claim_count :claim-count :claim_count :claim-count
:balance_eth :balance-eth :balance_eth :balance-eth

View File

@ -115,18 +115,18 @@
(defn handle-claim (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) (users/create-user user-id login name nil avatar_url)
(let [open-or-edit? (contains? #{:opened :edited} event-type) (let [open-or-edit? (contains? #{:opened :edited} event-type)
close? (= :closed event-type) close? (= :closed event-type)
pr-data {:repo_id repo-id pr-data {:repo_id repo-id
:pr_id pr-id :pr_id pr-id
:pr_number pr-number :pr_number pr-number
:title pr-title
:user_id user-id :user_id user-id
:issue_number (:issue_number issue) :issue_number (:issue_number issue)
:issue_id (:issue_id issue) :issue_id (:issue_id issue)
:state event-type}] :state event-type}]
;; TODO: in the opened case if the submitting user has no ;; TODO: in the opened case if the submitting user has no
;; Ethereum address stored, we could post a comment to the ;; Ethereum address stored, we could post a comment to the
;; Github PR explaining that payout is not possible if the PR is ;; Github PR explaining that payout is not possible if the PR is
@ -187,6 +187,7 @@
repo-id repo-id
pr-id pr-id
pr-number pr-number
pr-title
head-sha head-sha
merged? merged?
event-type)) event-type))

View File

@ -126,7 +126,9 @@
tokens tokens
winner-login winner-login
true)) true))
(let [execute-hash (multisig/send-all contract-address payout-address)] (let [execute-hash (multisig/send-all {:contract contract-address
:payout-address payout-address
:internal-tx-id (str "payout-github-issue-" issue-id)})]
(log/infof "issue %s: Payout self-signed, called sign-all(%s) tx: %s" issue-id contract-address payout-address execute-hash) (log/infof "issue %s: Payout self-signed, called sign-all(%s) tx: %s" issue-id contract-address payout-address execute-hash)
(db-bounties/update-execute-hash issue-id execute-hash) (db-bounties/update-execute-hash issue-id execute-hash)
(db-bounties/update-winner-login issue-id winner-login) (db-bounties/update-winner-login issue-id winner-login)

View File

@ -4,51 +4,95 @@
[commiteth.common :refer [human-time [commiteth.common :refer [human-time
display-data-page display-data-page
items-per-page items-per-page
issue-url]] issue-url
pull-request-url]]
[commiteth.handlers :as handlers] [commiteth.handlers :as handlers]
[commiteth.db :as db] [commiteth.db :as db]
[commiteth.ui-model :as ui-model] [commiteth.ui-model :as ui-model]
[commiteth.subscriptions :as subs] [commiteth.subscriptions :as subs]
[commiteth.util :as util])) [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] (defn bounty-item [bounty]
(let [{avatar-url :repo_owner_avatar_url (let [open-bounty-claims (rf/subscribe [::subs/open-bounty-claims])]
owner :repo-owner (fn [bounty]
repo-name :repo-name (let [{avatar-url :repo_owner_avatar_url
issue-title :issue-title owner :repo-owner
issue-number :issue-number repo-name :repo-name
updated :updated issue-title :issue-title
tokens :tokens issue-number :issue-number
balance-eth :balance-eth issue-id :issue-id
value-usd :value-usd updated :updated
claim-count :claim-count} bounty tokens :tokens
full-repo (str owner "/" repo-name) balance-eth :balance-eth
repo-url (str "https://github.com/" full-repo) value-usd :value-usd
repo-link [:a {:href repo-url} full-repo] claim-count :claim-count
issue-link [:a claims :claims} bounty
{:href (issue-url owner repo-name issue-number)} full-repo (str owner "/" repo-name)
issue-title]] repo-url (str "https://github.com/" full-repo)
[:div.open-bounty-item repo-link [:a {:href repo-url} full-repo]
[:div.open-bounty-item-content issue-link [:a
[:div.header issue-link] {:href (issue-url owner repo-name issue-number)}
[:div.bounty-item-row issue-title]
[:div.time (human-time updated)] open-claims-click #(rf/dispatch [::handlers/open-bounty-claim issue-id])
[:span.bounty-repo-label repo-link]] close-claims-click #(rf/dispatch [::handlers/close-bounty-claim issue-id])
matches-current-issue? (some #{issue-id} @open-bounty-claims)]
[:div.footer-row [:div
[:div.balance-badge "ETH " balance-eth] [:div.open-bounty-item.ph4
(for [[tla balance] tokens] [:div.open-bounty-item-content
^{:key (random-uuid)} [:div.header issue-link]
[:div.balance-badge.token [:div.bounty-item-row
(str (subs (str tla) 1) " " balance)]) [:div.time (human-time updated)]
[:span.usd-value-label "Value "] [:span.usd-balance-label (str "$" value-usd)] [:span.bounty-repo-label repo-link]]
(when (> claim-count 0) [:div.footer-row
[:span.open-claims-label (str claim-count " open claim" [:div.balance-badge "ETH " balance-eth]
(when (> claim-count 1) "s"))])]] (for [[tla balance] tokens]
[:div.open-bounty-item-icon ^{:key (random-uuid)}
[:div.ui.tiny.circular.image [:div.balance-badge.token
[:img {:src avatar-url}]]]])) (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] (defn bounties-filter-tooltip-value-input-view [label tooltip-open? opts]
[:div.open-bounties-filter-element-tooltip-value-input-container [:div.open-bounties-filter-element-tooltip-value-input-container
@ -210,7 +254,7 @@
[:div [:div
(let [left (inc (* (dec page-number) items-per-page)) (let [left (inc (* (dec page-number) items-per-page))
right (dec (+ left item-count))] right (dec (+ left item-count))]
[:div.item-counts-label-and-sorting-container [:div.item-counts-label-and-sorting-container.ph4
[:div.item-counts-label [:div.item-counts-label
[:span (str "Showing " left "-" right " of " total-count)]] [:span (str "Showing " left "-" right " of " total-count)]]
[bounties-sort-view]]) [bounties-sort-view]])
@ -227,7 +271,7 @@
[:div.ui.text.loader.view-loading-label "Loading"]]] [:div.ui.text.loader.view-loading-label "Loading"]]]
[:div.ui.container.open-bounties-container [:div.ui.container.open-bounties-container
{:ref #(reset! container-element %1)} {:ref #(reset! container-element %1)}
[:div.open-bounties-header "Bounties"] [:div.open-bounties-header.ph4.pt4 "Bounties"]
[:div.open-bounties-filter-and-sort [:div.open-bounties-filter-and-sort.ph4
[bounty-filters-view]] [bounty-filters-view]]
[bounties-list @bounty-page-data container-element]])))) [bounties-list @bounty-page-data container-element]]))))

View File

@ -48,6 +48,9 @@
(defn issue-url [owner repo number] (defn issue-url [owner repo number]
(str "https://github.com/" owner "/" repo "/issues/" 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) (def items-per-page 15)
(defn draw-page-numbers [page-number page-count container-element] (defn draw-page-numbers [page-number page-count container-element]
@ -132,7 +135,7 @@
:else :else
[:div [:div
[draw-items] [draw-items]
[:div.page-nav-container [:div.page-nav-container.ph4.pb4
[:div.page-direction-container [:div.page-direction-container
[draw-rect :backward] [draw-rect :backward]
[draw-rect :forward]] [draw-rect :forward]]

View File

@ -4,7 +4,7 @@
(def default-db (def default-db
{:page :bounties {:page :bounties
:user nil :user nil
:user-profile-loaded? false :user-profile-loaded? false
:repos-loading? false :repos-loading? false
:repos {} :repos {}
:activity-feed-loading? false :activity-feed-loading? false
@ -18,6 +18,7 @@
::ui-model/bounty-filter-type|currency nil ::ui-model/bounty-filter-type|currency nil
::ui-model/bounty-filter-type|date nil ::ui-model/bounty-filter-type|date nil
::ui-model/bounty-filter-type|owner nil} ::ui-model/bounty-filter-type|owner nil}
::open-bounty-claims #{}
:owner-bounties {} :owner-bounties {}
:top-hunters [] :top-hunters []
:activity-feed []}) :activity-feed []})

View File

@ -484,6 +484,16 @@
(.removeEventListener js/window "click" close-dropdown) (.removeEventListener js/window "click" close-dropdown)
(assoc db :user-dropdown-open? false))) (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 (reg-event-db
::set-open-bounties-sorting-type ::set-open-bounties-sorting-type
(fn [db [_ sorting-type]] (fn [db [_ sorting-type]]

View File

@ -53,17 +53,24 @@
:open-bounties-page :open-bounties-page
:<- [::filtered-and-sorted-open-bounties] :<- [::filtered-and-sorted-open-bounties]
:<- [:page-number] :<- [:page-number]
(fn [[open-bounties page-number] _] :<- [:activity-feed]
(fn [[open-bounties page-number activity-feed] _]
(let [total-count (count open-bounties) (let [total-count (count open-bounties)
start (* (dec page-number) items-per-page) start (* (dec page-number) items-per-page)
end (min total-count (+ items-per-page start)) end (min total-count (+ items-per-page start))
items (subvec open-bounties start end)] items (->> (subvec open-bounties start end)
{:items items (map (fn [bounty]
:item-count (count items) (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 :total-count total-count
:page-number page-number :page-number page-number
:page-count (Math/ceil (/ total-count items-per-page))}))) :page-count (Math/ceil (/ total-count items-per-page))})))
(reg-sub (reg-sub
:owner-bounties :owner-bounties
@ -146,6 +153,11 @@
(fn [db _] (fn [db _]
(:user-dropdown-open? db))) (:user-dropdown-open? db)))
(reg-sub
::open-bounty-claims
(fn [db _]
(::db/open-bounty-claims db)))
(reg-sub (reg-sub
::open-bounties-sorting-type ::open-bounties-sorting-type
(fn [db _] (fn [db _]

View File

@ -415,7 +415,6 @@ label[for="input-hidden"] {
background-color: #fff; background-color: #fff;
border-radius: 10px; border-radius: 10px;
transform: translateY(-45px); transform: translateY(-45px);
padding: 24px;
.open-bounties-header { .open-bounties-header {
font-family: "PostGrotesk-Medium"; font-family: "PostGrotesk-Medium";
font-size: 21px; font-size: 21px;
@ -714,6 +713,17 @@ label[for="input-hidden"] {
padding: 4px; padding: 4px;
text-align: center; text-align: center;
} }
.blue-arrow-box {
width: 24px;
height: 24px;
}
.blue-arrow-image {
width: 13.5px;
height: 6px;
}
} }
.open-bounty-item:first-child { .open-bounty-item:first-child {
@ -748,8 +758,6 @@ label[for="input-hidden"] {
font-size: 15px; font-size: 15px;
} }
border-bottom: #eaecee 1px solid !important;
.open-bounty-item-content { .open-bounty-item-content {
width: 80%; width: 80%;
padding: .7em 0 1em; padding: .7em 0 1em;
@ -780,8 +788,28 @@ label[for="input-hidden"] {
flex-direction: row !important; flex-direction: row !important;
justify-content: space-between; justify-content: space-between;
width: 80%; 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 { .bounty-repo-label a {
margin: auto; margin: auto;
font-family: "PostGrotesk-Book"; font-family: "PostGrotesk-Book";
@ -804,6 +832,7 @@ label[for="input-hidden"] {
padding-left: 15px; padding-left: 15px;
font-size: 15px; font-size: 15px;
color: #57a7ed; color: #57a7ed;
cursor: pointer;
} }
.activity-item-container { .activity-item-container {