Merge pull request #430 from rcullito/feature/bounty-revocation
bounty revocation
This commit is contained in:
commit
2ad1025b96
|
@ -103,9 +103,9 @@ lein less auto
|
|||
```
|
||||
|
||||
### Solidity compilation
|
||||
Invoke `build-contracts` Leiningen task to compile Solidity files into Java classes:
|
||||
Compile Solidity files into Java classes with:
|
||||
```
|
||||
lein build-contracts
|
||||
cd contracts && ./build.sh
|
||||
```
|
||||
|
||||
### Clojure app without REPL
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
This directory contains all the underlying smart contracts used by the OpenBounty platform.
|
||||
|
||||
- A script `contracts/build.sh` is part of this repository and can be used to
|
||||
compile the contracts and copy Java interfaces into `src/java/`.
|
||||
-- A script `build.sh` is part of this directory and can be used to
|
||||
-compile the contracts and copy Java interfaces into `src/java/`.
|
||||
|
||||
In order to run the script the following dependencies have to be met:
|
||||
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
#!/bin/bash -eu
|
||||
|
||||
SOLC=$(which solc)
|
||||
WEB3J=$(which web3j)
|
||||
function print_dependency_message
|
||||
{
|
||||
echo "error: " $1 "must already be installed!"
|
||||
}
|
||||
|
||||
SOLC=$(which solc) || print_dependency_message "solc"
|
||||
|
||||
WEB3J=$(which web3j) || print_dependency_message "web3"
|
||||
|
||||
rm -f resources/contracts/*.{abi,bin}
|
||||
|
||||
# compile contracts
|
||||
for f in contracts/{TokenReg,MultiSigTokenWallet*}.sol; do
|
||||
for f in {TokenReg,MultiSigTokenWallet*}.sol; do
|
||||
$SOLC $f --overwrite --bin --abi --optimize -o resources/contracts
|
||||
done
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 260 B |
Binary file not shown.
After Width: | Height: | Size: 493 B |
Binary file not shown.
After Width: | Height: | Size: 108 B |
Binary file not shown.
After Width: | Height: | Size: 155 B |
|
@ -313,11 +313,9 @@ SELECT
|
|||
i.issue_id AS issue_id,
|
||||
u.address AS payout_address,
|
||||
i.execute_hash AS execute_hash
|
||||
FROM issues i, pull_requests p, users u
|
||||
WHERE
|
||||
p.issue_id = i.issue_id
|
||||
AND p.repo_id = i.repo_id
|
||||
AND u.id = p.user_id
|
||||
FROM issues i, repositories r, users u
|
||||
WHERE i.repo_id = r.repo_id
|
||||
AND u.id = r.user_id
|
||||
AND i.confirm_hash IS NULL
|
||||
AND i.execute_hash IS NOT NULL;
|
||||
|
||||
|
@ -348,6 +346,29 @@ AND u.id = p.user_id
|
|||
AND i.payout_receipt IS NULL
|
||||
AND i.payout_hash IS NOT NULL;
|
||||
|
||||
-- :name confirmed-revocation-payouts :? :*
|
||||
-- :doc lists all recently confirmed bounty revocations
|
||||
SELECT
|
||||
i.contract_address AS contract_address,
|
||||
r.owner AS owner,
|
||||
r.repo AS repo,
|
||||
i.comment_id AS comment_id,
|
||||
i.issue_number AS issue_number,
|
||||
i.issue_id AS issue_id,
|
||||
i.balance_eth AS balance_eth,
|
||||
i.tokens AS tokens,
|
||||
i.value_usd AS value_usd,
|
||||
u.address AS payout_address,
|
||||
u.login AS payee_login,
|
||||
i.confirm_hash AS confirm_hash,
|
||||
i.payout_hash AS payout_hash,
|
||||
i.updated AS updated
|
||||
FROM issues i, users u, repositories r
|
||||
WHERE r.repo_id = i.repo_id
|
||||
AND u.id = r.user_id
|
||||
AND i.payout_receipt IS NULL
|
||||
AND i.payout_hash IS NOT NULL;
|
||||
|
||||
-- :name update-winner-login :! :n
|
||||
UPDATE issues
|
||||
SET winner_login = :winner_login
|
||||
|
@ -407,6 +428,12 @@ UPDATE issues
|
|||
SET is_open = :is_open
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name reset-bot-confirmation :! :n
|
||||
-- :doc updates issue's execute and confirm hash
|
||||
UPDATE issues
|
||||
SET execute_hash = NULL,
|
||||
confirm_hash = NULL
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name issue-exists :1
|
||||
-- :doc returns true if given issue exists
|
||||
|
@ -429,13 +456,17 @@ SELECT
|
|||
i.is_open AS is_open,
|
||||
i.winner_login AS winner_login,
|
||||
i.commit_sha AS commit_sha,
|
||||
u.address AS owner_address,
|
||||
i.contract_address AS contract_address,
|
||||
i.confirm_hash AS confirm_hash,
|
||||
i.title AS title,
|
||||
i.comment_id AS comment_id,
|
||||
i.repo_id AS repo_id,
|
||||
r.owner AS owner,
|
||||
r.repo AS repo
|
||||
FROM issues i, repositories r
|
||||
FROM issues i, repositories r, users u
|
||||
WHERE r.repo_id = i.repo_id
|
||||
AND r.user_id = u.id
|
||||
AND i.issue_id = :issue-id
|
||||
|
||||
|
||||
|
@ -480,6 +511,7 @@ SELECT
|
|||
i.balance_eth AS balance_eth,
|
||||
i.tokens AS tokens,
|
||||
i.value_usd AS value_usd,
|
||||
i.execute_hash AS execute_hash,
|
||||
i.confirm_hash AS confirm_hash,
|
||||
i.payout_hash AS payout_hash,
|
||||
i.payout_receipt AS payout_receipt,
|
||||
|
@ -489,6 +521,7 @@ SELECT
|
|||
r.owner AS repo_owner,
|
||||
r.owner_avatar_url AS repo_owner_avatar_url,
|
||||
o.address AS owner_address,
|
||||
o.login AS owner_login,
|
||||
u.address AS payout_address
|
||||
FROM users o, repositories r, issues i LEFT OUTER JOIN users u ON u.login = i.winner_login
|
||||
WHERE
|
||||
|
|
|
@ -125,9 +125,9 @@
|
|||
(let [open-claims (fn open-claims [bounty]
|
||||
(filter bnt/open? (:claims bounty)))]
|
||||
(if-let [merged-or-paid? (or (:winner_login bounty)
|
||||
(:payout_hash bounty))]
|
||||
(:payout_receipt bounty))]
|
||||
(cond
|
||||
(:payout_hash bounty) :paid
|
||||
(:payout_receipt bounty) :paid
|
||||
(nil? (:payout_address bounty)) :pending-contributor-address
|
||||
;; `confirm_hash` is set by us as soon as a PR is merged and the
|
||||
;; contributor address is known. Usually end users should not need
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/confirmed-payouts con-db)))
|
||||
|
||||
(defn confirmed-revocation-payouts
|
||||
[]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/confirmed-revocation-payouts con-db)))
|
||||
|
||||
(defn update-winner-login
|
||||
[issue-id login]
|
||||
|
@ -61,6 +65,20 @@
|
|||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/reset-payout-hash con-db {:issue_id issue-id})))
|
||||
|
||||
(def payout-receipt-keys
|
||||
[:issue_id
|
||||
:payout_hash
|
||||
:contract_address
|
||||
:repo
|
||||
:owner
|
||||
:comment_id
|
||||
:issue_number
|
||||
:balance_eth
|
||||
:tokens
|
||||
:confirm_hash
|
||||
:payee_login
|
||||
:updated])
|
||||
|
||||
(defn update-payout-receipt
|
||||
[issue-id payout-receipt]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
|
|
|
@ -95,6 +95,12 @@
|
|||
(db/update-issue-open con-db {:issue_id issue-id
|
||||
:is_open is-open})))
|
||||
|
||||
(defn reset-bot-confirmation
|
||||
"resets execute and confirm hash to null for given issue id"
|
||||
[issue-id]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/reset-bot-confirmation con-db {:issue_id issue-id})))
|
||||
|
||||
(defn is-bounty-issue?
|
||||
[issue-id]
|
||||
(let [res (jdbc/with-db-connection [con-db *db*]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
(ns commiteth.routes.services
|
||||
(:require [ring.util.http-response :refer :all]
|
||||
[compojure.api.sweet :refer :all]
|
||||
[compojure.api.exception :as ex]
|
||||
[schema.core :as s]
|
||||
[compojure.api.meta :refer [restructure-param]]
|
||||
[buddy.auth.accessrules :refer [restrict]]
|
||||
|
@ -10,8 +11,10 @@
|
|||
[commiteth.db.usage-metrics :as usage-metrics]
|
||||
[commiteth.db.repositories :as repositories]
|
||||
[commiteth.db.bounties :as bounties-db]
|
||||
[commiteth.db.issues :as issues]
|
||||
[commiteth.bounties :as bounties]
|
||||
[commiteth.eth.core :as eth]
|
||||
[commiteth.eth.tracker :as tracker]
|
||||
[commiteth.github.core :as github]
|
||||
[clojure.tools.logging :as log]
|
||||
[commiteth.config :refer [env]]
|
||||
|
@ -19,7 +22,8 @@
|
|||
eth-decimal->str]]
|
||||
[crypto.random :as random]
|
||||
[clojure.set :refer [rename-keys]]
|
||||
[clojure.string :as str]))
|
||||
[clojure.string :as str]
|
||||
[commiteth.eth.multisig-wallet :as multisig]))
|
||||
|
||||
(defn add-bounties-for-existing-issues? []
|
||||
(env :add-bounties-for-existing-issues false))
|
||||
|
@ -72,6 +76,7 @@
|
|||
:type :item-type
|
||||
:repo_name :repo-name
|
||||
:repo_owner :repo-owner
|
||||
:owner_login :owner-login
|
||||
:issue_number :issue-number
|
||||
:issue_id :issue-id
|
||||
:value_usd :value-usd
|
||||
|
@ -155,13 +160,28 @@
|
|||
(let [whitelist (env :user-whitelist #{})]
|
||||
(whitelist user)))
|
||||
|
||||
(defn execute-revocation [issue-id contract-address payout-address]
|
||||
(log/info (str "executing revocation for " issue-id "at" contract-address))
|
||||
(try
|
||||
(let [tx-info (multisig/send-all {:contract contract-address
|
||||
:payout-address payout-address
|
||||
:internal-tx-id [:execute issue-id]})]
|
||||
(tracker/track-tx! tx-info)
|
||||
{:execute-hash (:tx-hash tx-info)})
|
||||
(catch Throwable ex
|
||||
(log/errorf ex "error revoking funds for %s" issue-id))))
|
||||
|
||||
|
||||
(defapi service-routes
|
||||
(when (:dev env)
|
||||
{:swagger {:ui "/swagger-ui"
|
||||
:spec "/swagger.json"
|
||||
:data {:info {:version "0.1"
|
||||
:title "commitETH API"
|
||||
:description "commitETH API"}}}})
|
||||
{:swagger {:ui "/swagger-ui"
|
||||
:spec "/swagger.json"
|
||||
:data {:info {:version "0.1"
|
||||
:title "commitETH API"
|
||||
:description "commitETH API"}}}
|
||||
:exceptions {:handlers
|
||||
{::ex/request-parsing (ex/with-logging ex/request-parsing-handler :info)
|
||||
::ex/response-validation (ex/with-logging ex/response-validation-handler :error)}}})
|
||||
|
||||
(context "/api" []
|
||||
(GET "/top-hunters" []
|
||||
|
@ -191,11 +211,11 @@
|
|||
(POST "/" []
|
||||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
:body [body {:address s/Str
|
||||
:body [body {:address s/Str
|
||||
:is_hidden_in_hunters s/Bool}]
|
||||
:summary "Updates user's fields."
|
||||
|
||||
(let [user-id (:id user)
|
||||
(let [user-id (:id user)
|
||||
{:keys [address]} body]
|
||||
|
||||
(when-not (eth/valid-address? address)
|
||||
|
@ -226,9 +246,9 @@
|
|||
(log/debug "/bounty/X/payout" params)
|
||||
(let [{issue :issue
|
||||
payout-hash :payout-hash} params
|
||||
result (bounties-db/update-payout-hash
|
||||
(Integer/parseInt issue)
|
||||
payout-hash)]
|
||||
result (bounties-db/update-payout-hash
|
||||
(Integer/parseInt issue)
|
||||
payout-hash)]
|
||||
(log/debug "result" result)
|
||||
(if (= 1 result)
|
||||
(ok)
|
||||
|
@ -237,4 +257,22 @@
|
|||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
(log/debug "/user/bounties")
|
||||
(ok (user-bounties user))))))
|
||||
(ok (user-bounties user)))
|
||||
(POST "/revoke" {{issue-id :issue-id} :params}
|
||||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
(let [{contract-address :contract_address owner-address :owner_address} (issues/get-issue-by-id issue-id)]
|
||||
(do (log/infof "calling revoke-initiate for %s with %s %s" issue-id contract-address owner-address)
|
||||
(if-let [{:keys [execute-hash]} (execute-revocation issue-id contract-address owner-address)]
|
||||
(ok {:issue-id issue-id
|
||||
:execute-hash execute-hash
|
||||
:contract-address contract-address})
|
||||
(bad-request (str "Unable to withdraw funds from " contract-address))))))
|
||||
(POST "/remove-bot-confirmation" {{issue-id :issue-id} :params}
|
||||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
(do (log/infof "calling remove-bot-confirmation for %s " issue-id)
|
||||
;; if this resulted in updating a row, return success
|
||||
(if (pos? (issues/reset-bot-confirmation issue-id))
|
||||
(ok (str "Updated execute and confirm hash for " issue-id))
|
||||
(bad-request (str "Unable to update execute and confirm hash for " issue-id))))))))
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[commiteth.db.bounties :as db-bounties]
|
||||
[commiteth.bounties :as bounties]
|
||||
[commiteth.util.crypto-fiat-value :as fiat-util]
|
||||
[commiteth.util.util :refer [eth-decimal->str]]
|
||||
[commiteth.util.util :as util]
|
||||
[clojure.tools.logging :as log]
|
||||
[mount.core :as mount]
|
||||
[clj-time.core :as t]
|
||||
|
@ -129,7 +129,7 @@
|
|||
repo
|
||||
comment-id
|
||||
contract-address
|
||||
(eth-decimal->str balance-eth)
|
||||
(util/eth-decimal->str balance-eth)
|
||||
tokens
|
||||
winner-login
|
||||
true))
|
||||
|
@ -142,35 +142,39 @@
|
|||
repo
|
||||
comment-id
|
||||
contract-address
|
||||
(eth-decimal->str balance-eth)
|
||||
(util/eth-decimal->str balance-eth)
|
||||
tokens
|
||||
winner-login
|
||||
false))))
|
||||
(catch Throwable ex
|
||||
(log/error ex "issue %s: self-sign-bounty exception" issue-id)))))
|
||||
(log/errorf ex "issue %s: self-sign-bounty exception" issue-id)))))
|
||||
(log/info "Exit self-sign-bounty"))
|
||||
|
||||
(defn update-confirm-hash
|
||||
"Gets transaction receipt for each pending payout and updates DB confirm_hash with tranaction ID of commiteth bot account's confirmation."
|
||||
[]
|
||||
(log/info "In update-confirm-hash")
|
||||
(p :update-confirm-hash
|
||||
(doseq [{issue-id :issue_id
|
||||
execute-hash :execute_hash} (db-bounties/pending-payouts)]
|
||||
(log/infof "issue %s: pending payout: %s" issue-id execute-hash)
|
||||
[issue-id execute-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)
|
||||
(log/infof "issue %s: confirm hash:%s" issue-id confirm-hash)
|
||||
(tracker/untrack-tx! {:issue-id issue-id
|
||||
:tx-hash execute-hash
|
||||
:result confirm-hash
|
||||
:type :execute})))
|
||||
(catch Throwable ex
|
||||
(log/errorf ex "issue %s: update-confirm-hash exception:" issue-id)))))
|
||||
(log/info "Exit update-confirm-hash"))
|
||||
(log/errorf ex "issue %s: update-confirm-hash exception:" issue-id))))
|
||||
|
||||
(defn update-confirm-hashes
|
||||
"Gets transaction receipt for each pending payout and updates DB confirm_hash with tranaction ID of commiteth bot account's confirmation."
|
||||
[]
|
||||
(log/info "In update-confirm-hashes")
|
||||
(p :update-confirm-hash
|
||||
(doseq [{issue-id :issue_id
|
||||
execute-hash :execute_hash} (db-bounties/pending-payouts)]
|
||||
|
||||
(update-confirm-hash issue-id execute-hash)))
|
||||
(log/info "Exit update-confirm-hashes"))
|
||||
|
||||
(defn update-watch-hash
|
||||
"Sets watch-hash to NULL for bounties where watch tx has been mined. Used to avoid unneeded watch transactions in update-bounty-token-balances"
|
||||
|
@ -197,36 +201,33 @@
|
|||
(println "hour diff:" diff)
|
||||
(> diff 3)))
|
||||
|
||||
(defn update-payout-receipt
|
||||
"Gets transaction receipt for each confirmed payout and updates payout_hash"
|
||||
[]
|
||||
(log/info "In update-payout-receipt")
|
||||
(p :update-payout-receipt
|
||||
(doseq [{issue-id :issue_id
|
||||
payout-hash :payout_hash
|
||||
contract-address :contract_address
|
||||
repo :repo
|
||||
owner :owner
|
||||
comment-id :comment_id
|
||||
issue-number :issue_number
|
||||
balance-eth :balance_eth
|
||||
tokens :tokens
|
||||
confirm-id :confirm_hash
|
||||
payee-login :payee_login
|
||||
updated :updated} (db-bounties/confirmed-payouts)]
|
||||
(defn update-payout-receipt [bounty]
|
||||
{:pre [(util/contains-all-keys bounty db-bounties/payout-receipt-keys)]}
|
||||
(let [{issue-id :issue_id
|
||||
payout-hash :payout_hash
|
||||
contract-address :contract_address
|
||||
repo :repo
|
||||
owner :owner
|
||||
comment-id :comment_id
|
||||
issue-number :issue_number
|
||||
balance-eth :balance_eth
|
||||
tokens :tokens
|
||||
confirm-hash :confirm_hash
|
||||
payee-login :payee_login
|
||||
updated :updated} bounty]
|
||||
(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)
|
||||
(let [contract-tokens (multisig/token-balances contract-address)
|
||||
contract-eth-balance (eth/get-balance-wei contract-address)]
|
||||
(if (or
|
||||
(some #(> (second %) 0.0) contract-tokens)
|
||||
(> contract-eth-balance 0))
|
||||
(some #(> (second %) 0.0) contract-tokens)
|
||||
(> contract-eth-balance 0))
|
||||
(do
|
||||
(log/infof "issue %s: Contract (%s) still has funds" issue-id contract-address)
|
||||
(when (multisig/is-confirmed? contract-address confirm-id)
|
||||
(when (multisig/is-confirmed? contract-address confirm-hash)
|
||||
(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-id)]
|
||||
(let [execute-tx-hash (multisig/execute-tx contract-address confirm-hash)]
|
||||
(log/infof "issue %s: execute tx: %s" issue-id execute-tx-hash))))
|
||||
|
||||
(do
|
||||
|
@ -236,15 +237,33 @@
|
|||
repo
|
||||
comment-id
|
||||
contract-address
|
||||
(eth-decimal->str balance-eth)
|
||||
(util/eth-decimal->str balance-eth)
|
||||
tokens
|
||||
payee-login))))
|
||||
(when (older-than-3h? updated)
|
||||
(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
|
||||
(log/error ex "issue %s: update-payout-receipt exception" issue-id)))))
|
||||
(log/info "Exit update-payout-receipt"))
|
||||
(log/error ex "issue %s: update-payout-receipt exception" issue-id)))))
|
||||
|
||||
(defn update-payout-receipts
|
||||
"Gets transaction receipt for each confirmed payout and updates payout_hash"
|
||||
[]
|
||||
(log/info "In update-payout-receipts")
|
||||
(p :update-payout-receipts
|
||||
(doseq [bounty (db-bounties/confirmed-payouts)]
|
||||
(update-payout-receipt bounty))
|
||||
(log/info "Exit update-payout-receipts")))
|
||||
|
||||
(defn update-revoked-payout-receipts
|
||||
"Gets transaction receipt for each confirmed revocation and updates payout_hash"
|
||||
[]
|
||||
(log/info "In update-revoked-payout-receipts")
|
||||
(p :update-revoked-payout-receipts
|
||||
;; todo see if confirmed-payouts & confirmed-revocation-payouts can be combined
|
||||
(doseq [bounty (db-bounties/confirmed-revocation-payouts)]
|
||||
(update-payout-receipt bounty))
|
||||
(log/info "Exit update-revoked-payout-receipts")))
|
||||
|
||||
(defn abs
|
||||
"(abs n) is the absolute value of n"
|
||||
|
@ -412,8 +431,9 @@
|
|||
(run-tasks
|
||||
[deploy-pending-contracts
|
||||
update-issue-contract-address
|
||||
update-confirm-hash
|
||||
update-payout-receipt
|
||||
update-confirm-hashes
|
||||
update-payout-receipts
|
||||
update-revoked-payout-receipts
|
||||
update-watch-hash
|
||||
check-tx-receipts
|
||||
self-sign-bounty
|
||||
|
|
|
@ -14,3 +14,8 @@
|
|||
(->> (http/get url)
|
||||
(:body)
|
||||
(json/read-str)))
|
||||
|
||||
(defn contains-all-keys [m ks]
|
||||
{:pre [(map? m) [(vector? ks)]]}
|
||||
(every?
|
||||
#(contains? m %) ks))
|
||||
|
|
|
@ -5,6 +5,14 @@
|
|||
{:pre [(string? tla)]}
|
||||
(get {"ETH" "#57a7ed"} tla "#4360df"))
|
||||
|
||||
(defn pending-badge []
|
||||
"static component for pending badge"
|
||||
[:div.dib.ph2.pv1.relative
|
||||
{:style {:color "#CCAC00"}}
|
||||
[:div.absolute.top-0.left-0.right-0.bottom-0.o-30.br2
|
||||
{:style {:background-color "#FFD700"}}]
|
||||
[:span.pg-med "Refund pending"]])
|
||||
|
||||
(defn balance-badge
|
||||
[tla balance]
|
||||
{:pre [(keyword? tla)]}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
[commiteth.routes]
|
||||
[commiteth.handlers]
|
||||
[commiteth.subscriptions]
|
||||
[commiteth.interceptors]
|
||||
[commiteth.activity :refer [activity-page]]
|
||||
[commiteth.bounties :refer [bounties-page]]
|
||||
[commiteth.repos :refer [repos-page]]
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
::ui-model/bounty-filter-type|date nil
|
||||
::ui-model/bounty-filter-type|owner nil}
|
||||
::open-bounty-claims #{}
|
||||
::pending-revocations {}
|
||||
:owner-bounties {}
|
||||
:top-hunters []
|
||||
:activity-feed []})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
(ns commiteth.handlers
|
||||
(:require [commiteth.db :as db]
|
||||
[re-frame.core :refer [dispatch
|
||||
[re-frame.core :refer [debug
|
||||
dispatch
|
||||
reg-event-db
|
||||
reg-event-fx
|
||||
reg-fx
|
||||
|
@ -14,12 +15,16 @@
|
|||
:refer [reg-co-fx!]]
|
||||
[commiteth.ui-model :as ui-model]
|
||||
[commiteth.common :as common]
|
||||
[commiteth.routes :as routes]))
|
||||
[commiteth.routes :as routes]
|
||||
[commiteth.interceptors]))
|
||||
|
||||
|
||||
(rf-storage/reg-co-fx! :commiteth-sob {:fx :store
|
||||
:cofx :store})
|
||||
|
||||
;; https://github.com/Day8/re-frame/blob/master/docs/Debugging-Event-Handlers.md
|
||||
(def interceptors [(when ^boolean goog.DEBUG debug)])
|
||||
|
||||
(reg-fx
|
||||
:http
|
||||
(fn [{:keys [method url on-success on-error finally params]}]
|
||||
|
@ -110,6 +115,16 @@
|
|||
(fn [db _]
|
||||
(dissoc db :flash-message)))
|
||||
|
||||
(reg-event-db
|
||||
:set-revoke-modal
|
||||
(fn [db [_ bounty]]
|
||||
(assoc db :revoke-modal-bounty bounty)))
|
||||
|
||||
(reg-event-db
|
||||
:clear-revoke-modal
|
||||
(fn [db [_ bounty]]
|
||||
(dissoc db :revoke-modal-bounty bounty)))
|
||||
|
||||
(defn assoc-in-if-not-empty [m path val]
|
||||
(if (seq val)
|
||||
(assoc-in m path val)
|
||||
|
@ -217,6 +232,8 @@
|
|||
|
||||
(reg-event-db
|
||||
:set-owner-bounties
|
||||
[commiteth.interceptors/watch-confirm-hash
|
||||
commiteth.interceptors/watch-payout-receipt]
|
||||
(fn [db [_ issues]]
|
||||
(assoc db
|
||||
:owner-bounties issues
|
||||
|
@ -376,6 +393,7 @@
|
|||
|
||||
(reg-event-fx
|
||||
:save-payout-hash
|
||||
interceptors
|
||||
(fn [{:keys [db]} [_ issue-id payout-hash]]
|
||||
{:db db
|
||||
:http {:method POST
|
||||
|
@ -386,13 +404,15 @@
|
|||
|
||||
|
||||
(defn send-transaction-callback
|
||||
[issue-id]
|
||||
[issue-id pending-revocations]
|
||||
(fn [error payout-hash]
|
||||
(println "send-transaction-callback" error payout-hash)
|
||||
(when error
|
||||
(dispatch [:set-flash-message
|
||||
:error
|
||||
(str "Error sending transaction: " error)])
|
||||
(if (empty? pending-revocations)
|
||||
(dispatch [:set-flash-message
|
||||
:error
|
||||
(str "Error sending transaction: " error)])
|
||||
(dispatch [:remove-bot-confirmation issue-id]))
|
||||
(dispatch [:payout-confirm-failed issue-id]))
|
||||
(when payout-hash
|
||||
(dispatch [:save-payout-hash issue-id payout-hash]))))
|
||||
|
@ -405,14 +425,72 @@
|
|||
(defn strip-0x [x]
|
||||
(str/replace x #"^0x" ""))
|
||||
|
||||
(defn set-pending-revocation [location issue-id confirming-account]
|
||||
(assoc-in location [::db/pending-revocations issue-id]
|
||||
{:confirming-account confirming-account}))
|
||||
|
||||
(reg-event-fx
|
||||
:set-pending-revocation
|
||||
[interceptors (inject-cofx :store)]
|
||||
(fn [{:keys [db store]} [_ issue-id confirming-account]]
|
||||
{:db (set-pending-revocation db issue-id confirming-account)
|
||||
:store (set-pending-revocation store issue-id confirming-account)}))
|
||||
|
||||
(reg-event-fx
|
||||
:remove-pending-revocation
|
||||
[interceptors (inject-cofx :store)]
|
||||
(fn [{:keys [db store]} [_ issue-id]]
|
||||
{:db (dissoc-in db [::db/pending-revocations issue-id])
|
||||
:store (dissoc-in store [::db/pending-revocations issue-id])}))
|
||||
|
||||
(reg-event-fx
|
||||
:remove-bot-confirmation
|
||||
interceptors
|
||||
(fn [{:keys [db]} [_ issue-id]]
|
||||
{:http {:method POST
|
||||
:url "/api/user/remove-bot-confirmation"
|
||||
:params {:token (get-admin-token db)
|
||||
:issue-id issue-id}
|
||||
:on-success #(dispatch [:remove-pending-revocation issue-id])
|
||||
:on-error #(println "error removing bot confirmation for " issue-id)}}))
|
||||
|
||||
(reg-event-fx
|
||||
:revoke-bounty-success
|
||||
(fn [{:keys [db]} [_ {:keys [issue-id owner-address contract-address confirm-hash]}]]
|
||||
{:dispatch [:set-pending-revocation issue-id :commiteth]}))
|
||||
|
||||
(reg-event-fx
|
||||
:revoke-bounty-error
|
||||
interceptors
|
||||
(fn [{:keys [db]} [_ issue-id response]]
|
||||
{:dispatch [:set-flash-message
|
||||
:error (if (= 400 (:status response))
|
||||
(:response response)
|
||||
(str "Failed to initiate revocation for: " issue-id
|
||||
(:status-text response)))]}))
|
||||
|
||||
(reg-event-fx
|
||||
:revoke-bounty
|
||||
interceptors
|
||||
(fn [{:keys [db]} [_ issue-id]]
|
||||
{:http {:method POST
|
||||
:url "/api/user/revoke"
|
||||
:on-success #(dispatch [:revoke-bounty-success %])
|
||||
:on-error #(dispatch [:revoke-bounty-error %])
|
||||
:params {:token (get-admin-token db)
|
||||
:issue-id issue-id}}
|
||||
:dispatch [:clear-revoke-modal]}))
|
||||
|
||||
(reg-event-fx
|
||||
:confirm-payout
|
||||
interceptors
|
||||
(fn [{:keys [db]} [_ {issue-id :issue_id
|
||||
owner-address :owner_address
|
||||
contract-address :contract_address
|
||||
confirm-hash :confirm_hash} issue]]
|
||||
(println (:web3 db))
|
||||
(let [w3 (:web3 db)
|
||||
pending-revocations (::db/pending-revocations db)
|
||||
confirm-method-id (sig->method-id w3 "confirmTransaction(uint256)")
|
||||
confirm-id (strip-0x confirm-hash)
|
||||
data (str confirm-method-id
|
||||
|
@ -426,7 +504,7 @@
|
|||
(println "data:" data)
|
||||
(try
|
||||
(web3-eth/send-transaction! w3 payload
|
||||
(send-transaction-callback issue-id))
|
||||
(send-transaction-callback issue-id pending-revocations))
|
||||
{:db (assoc-in db [:owner-bounties issue-id :confirming?] true)}
|
||||
(catch js/Error e
|
||||
{:db (assoc-in db [:owner-bounties issue-id :confirm-failed?] true)
|
||||
|
@ -437,6 +515,7 @@
|
|||
|
||||
(reg-event-fx
|
||||
:payout-confirmed
|
||||
interceptors
|
||||
(fn [{:keys [db]} [_ issue-id]]
|
||||
{:dispatch [:load-owner-bounties]
|
||||
:db (-> db
|
||||
|
@ -490,6 +569,21 @@
|
|||
(.removeEventListener js/window "click" close-dropdown)
|
||||
(assoc db :user-dropdown-open? false)))
|
||||
|
||||
(defn close-three-dots []
|
||||
(dispatch [:three-dots-close]))
|
||||
|
||||
(reg-event-db
|
||||
:three-dots-open
|
||||
(fn [db [_ issue-id]]
|
||||
(.addEventListener js/window "click" close-three-dots)
|
||||
(update db ::db/unclaimed-options (fnil conj #{}) issue-id)))
|
||||
|
||||
(reg-event-db
|
||||
:three-dots-close
|
||||
(fn [db [_ issue-id]]
|
||||
(.removeEventListener js/window "click" close-three-dots)
|
||||
(assoc db ::db/unclaimed-options #{})))
|
||||
|
||||
(reg-event-db
|
||||
::open-bounty-claim
|
||||
(fn [db [_ opening-issue-id]]
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
(ns commiteth.interceptors
|
||||
(:require [commiteth.db :as db]
|
||||
[re-frame.core :as rf]
|
||||
[clojure.data :as data]))
|
||||
|
||||
(defn get-confirming-issue-id [owner pending-revocations]
|
||||
"returns the issue id for the current revocation matching the desired owner type"
|
||||
(some (fn [[issue-id revocation]]
|
||||
(when (= owner (:confirming-account revocation))
|
||||
issue-id))
|
||||
pending-revocations))
|
||||
|
||||
(defn dispatch-confirm-payout [bounty]
|
||||
"dispatches a bounty via reframe dispatch"
|
||||
(rf/dispatch [:confirm-payout {:issue_id (:issue-id bounty)
|
||||
:owner_address (:owner_address bounty)
|
||||
:contract_address (:contract_address bounty)
|
||||
:confirm_hash (:confirm_hash bounty)}]))
|
||||
|
||||
(defn dispatch-set-pending-revocation [bounty]
|
||||
"update the currently confirming account to owner"
|
||||
(rf/dispatch [:set-pending-revocation (:issue-id bounty) :owner]))
|
||||
|
||||
(defn dispatch-remove-pending-revocation [bounty]
|
||||
"dispatches a bounty via reframe dispatch"
|
||||
(rf/dispatch [:remove-pending-revocation (:issue-id bounty)]))
|
||||
|
||||
(def watch-confirm-hash
|
||||
"revocations move through 2 states, confirmation by commiteth and then the repo owner
|
||||
if a commiteth revocation is detected, check to see if its confirm hash is set, and, if it is
|
||||
dispatch a confirm payout event and update the confirming account to owner
|
||||
|
||||
*Warning* this inteceptor is only intended for use with the
|
||||
`:load-owner-bounties` event
|
||||
|
||||
More information on re-frame interceptors can be found here:
|
||||
https://github.com/Day8/re-frame/blob/master/docs/Interceptors.md"
|
||||
|
||||
(rf/->interceptor
|
||||
:id :watch-confirm-hash
|
||||
:after (fn confirm-hash-update-after
|
||||
[context]
|
||||
(println "watch confirm hash interceptor...")
|
||||
(let [pending-revocations (get-in context [:effects :db ::db/pending-revocations])
|
||||
updated-bounties (get-in context [:effects :db :owner-bounties])
|
||||
confirming-issue-id (get-confirming-issue-id :commiteth pending-revocations)]
|
||||
(when-let [revoking-bounty (get updated-bounties confirming-issue-id)]
|
||||
(if (:confirm_hash revoking-bounty)
|
||||
(do (dispatch-confirm-payout revoking-bounty)
|
||||
(dispatch-set-pending-revocation revoking-bounty))
|
||||
(println (str "currently revoking " confirming-issue-id " but confirm hash has not been set yet."))))
|
||||
;; interceptor must return context
|
||||
context))))
|
||||
|
||||
|
||||
(def watch-payout-receipt
|
||||
"examine pending revocations with their currently confirming account set to owner
|
||||
when one of them has its payout_receipt set, dispatch `remove-pending-revocation`
|
||||
|
||||
*Warning* this inteceptor is only intended for use with the
|
||||
`:load-owner-bounties` event
|
||||
|
||||
More information on re-frame interceptors can be found here:
|
||||
https://github.com/Day8/re-frame/blob/master/docs/Interceptors.md"
|
||||
|
||||
(rf/->interceptor
|
||||
:id :watch-payout-receipt
|
||||
:after (fn payout-receipt-update-after
|
||||
[context]
|
||||
(println "watch payout receipt interceptor...")
|
||||
(let [pending-revocations (get-in context [:effects :db ::db/pending-revocations])
|
||||
updated-bounties (get-in context [:effects :db :owner-bounties])
|
||||
confirming-issue-id (get-confirming-issue-id :owner pending-revocations)]
|
||||
(when-let [revoking-bounty (get updated-bounties confirming-issue-id)]
|
||||
(if (:payout_receipt revoking-bounty)
|
||||
(dispatch-remove-pending-revocation revoking-bounty)
|
||||
(println (str "currently revoking " confirming-issue-id " but payout receipt has not been set yet."))))
|
||||
;; interceptor must return context
|
||||
context))))
|
|
@ -6,6 +6,7 @@
|
|||
[commiteth.routes :as routes]
|
||||
[commiteth.model.bounty :as bnt]
|
||||
[commiteth.ui.balances :as ui-balances]
|
||||
[commiteth.config :as config]
|
||||
[commiteth.common :as common :refer [human-time]]))
|
||||
|
||||
(defn pr-url [{owner :repo_owner
|
||||
|
@ -13,6 +14,16 @@
|
|||
repo :repo_name}]
|
||||
(str "https://github.com/" owner "/" repo "/pull/" pr-number))
|
||||
|
||||
(defn etherscan-tx-url [tx-id]
|
||||
(str "https://"
|
||||
(when (config/on-testnet?) "ropsten.")
|
||||
"etherscan.io/tx/" tx-id))
|
||||
|
||||
(defn etherscan-address-url [address]
|
||||
(str "https://"
|
||||
(when (config/on-testnet?) "ropsten.")
|
||||
"etherscan.io/address/" address))
|
||||
|
||||
(def primary-button-button :button.f7.ttu.tracked.outline-0.bg-sob-blue.white.pv3.ph4.pg-med.br3.bn.pointer.shadow-7)
|
||||
(def primary-button-link :a.dib.tc.f7.ttu.tracked.bg-sob-blue.white.pv2.ph3.pg-med.br2.pointer.hover-white.shadow-7)
|
||||
|
||||
|
@ -57,6 +68,8 @@
|
|||
"Signed off"
|
||||
"Confirm Payment")])))
|
||||
|
||||
|
||||
|
||||
(defn confirm-row [bounty claim]
|
||||
(let [payout-address-available? (:payout_address bounty)]
|
||||
[:div
|
||||
|
@ -231,16 +244,80 @@
|
|||
[:div bottom]]])
|
||||
|
||||
(defn small-card-balances [bounty]
|
||||
[:div.f6
|
||||
[ui-balances/token-balances (bnt/crypto-balances bounty) :label]
|
||||
(let [pending-revocations (rf/subscribe [:pending-revocations])]
|
||||
(fn [bounty]
|
||||
[:div.f6.fl.w-80
|
||||
[ui-balances/token-balances (bnt/crypto-balances bounty) :label]
|
||||
[:div
|
||||
[ui-balances/usd-value-label (:value-usd bounty)]]
|
||||
(when (some #(= (:issue-id %)
|
||||
(:issue-id bounty)) @pending-revocations)
|
||||
[:div.pt1
|
||||
[ui-balances/pending-badge]])])))
|
||||
|
||||
(defn three-dots-box [image-src]
|
||||
"generates the appropriate container for menu dots"
|
||||
[:span.pt2.pointer
|
||||
[:img.o-50.pl3.pt2 {:src image-src}]])
|
||||
|
||||
(defn check-box [image-src]
|
||||
"generates the appropriate container for a blue arrow"
|
||||
[:span.pr2
|
||||
[:img.w1.v-mid.o-50 {:src image-src}]])
|
||||
|
||||
(defn three-dots [issue-id]
|
||||
[:div
|
||||
[:div
|
||||
[ui-balances/usd-value-label (:value-usd bounty)]]])
|
||||
{:on-click #(rf/dispatch [:three-dots-open issue-id])}
|
||||
[three-dots-box "ic-more-vert-black-24dp-1x.png"]]])
|
||||
|
||||
(defn revoke-modal []
|
||||
(let [bounty @(rf/subscribe [:revoke-modal-bounty])]
|
||||
(fn []
|
||||
(when bounty
|
||||
(let [owner-address (:owner_address bounty)]
|
||||
;; width requires a deliberate override of semantic.min.css
|
||||
[:div.ui.active.modal.br3 {:style {:top 100
|
||||
:width 650}}
|
||||
[:div.pa4
|
||||
[:h3.dark-gray "Are you sure you want to request a refund?"]
|
||||
[:p.silver "This will set your bounty"
|
||||
[:span.pg-med " value to $0."]
|
||||
" Don't worry, your issue will still be accessible to the community. You can check the status of your refund at the top of the dashboard."]
|
||||
[:div.bg-sob-tint.br3.pa3
|
||||
[:p.fw4 (:issue-title bounty)]
|
||||
[ui-balances/token-balances (bnt/crypto-balances bounty) :label]
|
||||
[:p [ui-balances/usd-value-label (:value-usd bounty)]]
|
||||
[:p.silver "To be refunded to: " owner-address]]
|
||||
[:div.pt3
|
||||
[primary-button-button
|
||||
{:on-click #(rf/dispatch [:revoke-bounty (:issue-id bounty)])}
|
||||
"REQUEST REFUND"]
|
||||
[:span.dark-gray.pointer.fw4.f7.ml3
|
||||
{:role "button"
|
||||
:on-click #(rf/dispatch [:clear-revoke-modal])}
|
||||
"CANCEL"]]]])))))
|
||||
|
||||
(defn revoke-dropdown [bounty]
|
||||
(let [menu (if (contains? @(rf/subscribe [:three-dots-open?]) (:issue-id bounty))
|
||||
[:div.ui.menu.revoke.transition {:tab-index -1}]
|
||||
[:div.ui.menu.transition.hidden])]
|
||||
[:div.fl.w-20
|
||||
(if (empty? @(rf/subscribe [:pending-revocations]))
|
||||
[three-dots (:issue-id bounty)])
|
||||
(into menu [[:div
|
||||
[:a.pa2
|
||||
|
||||
{:on-click #(rf/dispatch [:set-revoke-modal bounty])}
|
||||
"Revoke"]]])]))
|
||||
|
||||
(defn unclaimed-bounty [bounty]
|
||||
[:div.w-third-l.fl-l.pa2
|
||||
[square-card
|
||||
[bounty-title-link bounty {:show-date? true :max-length 60}]
|
||||
[small-card-balances bounty]]])
|
||||
[:div [small-card-balances bounty]
|
||||
(when (pos? (:value-usd bounty))
|
||||
[revoke-dropdown bounty])]]])
|
||||
|
||||
(defn paid-bounty [bounty]
|
||||
[:div.w-third-l.fl-l.pa2
|
||||
|
@ -248,7 +325,10 @@
|
|||
[:div
|
||||
[bounty-title-link bounty {:show-date? false :max-length 60}]
|
||||
[:div.f6.mt1.gray
|
||||
"Paid out to " [:span.pg-med.fw5 "@" (:winner_login bounty)]]]
|
||||
"Paid out to " [:span.pg-med.fw5 "@" (or (:winner_login bounty)
|
||||
;; use repo owner for revoked bounties
|
||||
;; where no winner login is set
|
||||
(:owner-login bounty))]]]
|
||||
[small-card-balances bounty]]])
|
||||
|
||||
(defn expandable-bounty-list [bounty-component bounties]
|
||||
|
@ -272,9 +352,27 @@
|
|||
(defn count-pill [n]
|
||||
[:span.v-top.ml3.ph3.pv1.bg-black-05.gray.br3.f7 n])
|
||||
|
||||
(defn salute [name]
|
||||
(defn pending-banner []
|
||||
(let [banner-info (rf/subscribe [:pending-revocations])]
|
||||
(fn pending-banner-render []
|
||||
(when @banner-info
|
||||
(into [:div]
|
||||
(for [revoking-bounty @banner-info]
|
||||
^{:key (:contract_address revoking-bounty)}
|
||||
[:div.relative.pa3.pr4.bg-sob-green.br3.nt1
|
||||
[:div
|
||||
(case (:confirming-account revoking-bounty)
|
||||
:commiteth [:p.v-mid [check-box "ic-check-circle-black-24dp-2x.png"]
|
||||
[:span.pg-med "Transaction sent."] " Your refund requires two confirmations. After the first one "
|
||||
[:a.sob-blue.pg-med {:href (etherscan-address-url (:contract_address revoking-bounty)) :target "_blank"} " completes "]
|
||||
"you'll be prompted to sign the second via metamask."]
|
||||
:owner [:p.v-mid [check-box "ic-check-circle-black-24dp-2x.png"]
|
||||
[:span.pg-med "Transaction sent."] " Once your metamask transaction is confirmed your revocation will be complete. Follow the final step "
|
||||
[:a.sob-blue.pg-med {:href (etherscan-address-url (:contract_address revoking-bounty)) :target "_blank"} " here. "]])]]))))))
|
||||
|
||||
(defn salute []
|
||||
(let [msg-info (rf/subscribe [:dashboard/banner-msg])]
|
||||
(fn salute-render [name]
|
||||
(fn salute-render []
|
||||
(when @msg-info
|
||||
[:div.relative.pa3.pr4.bg-sob-blue-o-20.br3.nt1
|
||||
[:div.f3.dark-gray.absolute.top-0.right-0.pa3.b.pointer
|
||||
|
@ -326,7 +424,8 @@
|
|||
user (rf/subscribe [:user])
|
||||
owner-bounties (rf/subscribe [:owner-bounties])
|
||||
bounty-stats-data (rf/subscribe [:owner-bounties-stats])
|
||||
owner-bounties-loading? (rf/subscribe [:get-in [:owner-bounties-loading?]])]
|
||||
owner-bounties-loading? (rf/subscribe [:get-in [:owner-bounties-loading?]])
|
||||
revoke-modal-bounty (rf/subscribe [:revoke-modal-bounty])]
|
||||
(fn manage-payouts-page-render []
|
||||
(cond
|
||||
(nil? @user)
|
||||
|
@ -348,13 +447,16 @@
|
|||
(get grouped :pending-contributor-address))]
|
||||
[:div.center.mw9.pa2.pa0-l
|
||||
[manage-bounties-title]
|
||||
[salute "Andy"]
|
||||
[salute]
|
||||
[pending-banner]
|
||||
[:div.dn-l.db-ns.mt4
|
||||
[bounty-stats-new @bounty-stats-data]]
|
||||
(when (nil? (common/web3))
|
||||
[:div.ui.warning.message
|
||||
[:i.warning.icon]
|
||||
"To sign off claims, please view Status Open Bounty in Status, Mist or Metamask"])
|
||||
(when @revoke-modal-bounty
|
||||
[revoke-modal])
|
||||
[manage-bounties-nav route-id]
|
||||
[:div.cf
|
||||
[:div.fr.w-third.pl4.mb3.dn.db-l
|
||||
|
|
|
@ -41,6 +41,11 @@
|
|||
(fn [db _]
|
||||
(:flash-message db)))
|
||||
|
||||
(reg-sub
|
||||
:revoke-modal-bounty
|
||||
(fn [db _]
|
||||
(:revoke-modal-bounty db)))
|
||||
|
||||
(reg-sub
|
||||
:open-bounties
|
||||
(fn [db _]
|
||||
|
@ -82,7 +87,10 @@
|
|||
;; special prefix or namespace for derived properties that
|
||||
;; are added to domain records like this
|
||||
;; e.g. `derived/paid?`
|
||||
[id (assoc bounty :paid? (boolean (:payout_hash bounty)))])
|
||||
[id (assoc bounty :paid? (boolean (and (:payout_receipt bounty)
|
||||
;; bounties with winner logins
|
||||
;; were not revoked
|
||||
(:winner_login bounty))))])
|
||||
(into {}))))
|
||||
|
||||
(reg-sub
|
||||
|
@ -179,6 +187,19 @@
|
|||
(fn [db _]
|
||||
(:user-dropdown-open? db)))
|
||||
|
||||
(reg-sub
|
||||
:three-dots-open?
|
||||
(fn [db _]
|
||||
(::db/unclaimed-options db)))
|
||||
|
||||
(reg-sub
|
||||
:pending-revocations
|
||||
(fn [db _]
|
||||
(map (fn [[issue-id revocations]]
|
||||
(merge revocations
|
||||
(get-in db [:owner-bounties issue-id])))
|
||||
(::db/pending-revocations db))))
|
||||
|
||||
(reg-sub
|
||||
::open-bounty-claims
|
||||
(fn [db _]
|
||||
|
|
|
@ -250,9 +250,29 @@ label[for="input-hidden"] {
|
|||
a {
|
||||
color: #fff;
|
||||
pointer: default;
|
||||
display:block;
|
||||
}
|
||||
}
|
||||
|
||||
.ui.menu.revoke.transition {
|
||||
font-family: "PostGrotesk-Book";
|
||||
font-size: 1em;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
padding: .5em;
|
||||
background-color: #fff;
|
||||
a {
|
||||
color: #8d99a4;;
|
||||
cursor: pointer;
|
||||
display:block;
|
||||
}
|
||||
min-width:80px;
|
||||
position:absolute;
|
||||
z-index: 800000;
|
||||
right: 22px;
|
||||
bottom:60px;
|
||||
}
|
||||
|
||||
.logout-link {
|
||||
color: #fff!important;
|
||||
}
|
||||
|
@ -1266,4 +1286,4 @@ body {
|
|||
}
|
||||
|
||||
|
||||
@import (inline) "tachyons-utils.css";
|
||||
@import (inline) "tachyons-utils.css";
|
||||
|
|
|
@ -29,6 +29,12 @@
|
|||
.hover-sob-sky:hover, .hover-sob-sky:focus { color: #f2f5f8; }
|
||||
.hover-bg-sob-sky:hover, .hover-bg-sob-sky:focus { background-color: #f2f5f8; }
|
||||
|
||||
.sob-green { color: #d1ead8; }
|
||||
.bg-sob-green { background-color: #d1ead8; }
|
||||
.b--sob-green { border-color: #d1ead8; }
|
||||
.hover-sob-green:hover, .hover-sob-green:focus { color: #d1ead8; }
|
||||
.hover-bg-sob-green:hover, .hover-bg-sob-green:focus { background-color: #d1ead8; }
|
||||
|
||||
.sob-tint { color: #f7f9fa; }
|
||||
.bg-sob-tint { background-color: #f7f9fa; }
|
||||
.b--sob-tint { border-color: #f7f9fa; }
|
||||
|
|
Loading…
Reference in New Issue