Merge pull request #457 from status-im/develop
Prod deployment 2018/05/29
This commit is contained in:
commit
bccd8d19b2
|
@ -103,9 +103,9 @@ lein less auto
|
||||||
```
|
```
|
||||||
|
|
||||||
### Solidity compilation
|
### 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
|
### Clojure app without REPL
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
This directory contains all the underlying smart contracts used by the OpenBounty platform.
|
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
|
-- A script `build.sh` is part of this directory and can be used to
|
||||||
compile the contracts and copy Java interfaces into `src/java/`.
|
-compile the contracts and copy Java interfaces into `src/java/`.
|
||||||
|
|
||||||
In order to run the script the following dependencies have to be met:
|
In order to run the script the following dependencies have to be met:
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
#!/bin/bash -eu
|
#!/bin/bash -eu
|
||||||
|
|
||||||
SOLC=$(which solc)
|
function print_dependency_message
|
||||||
WEB3J=$(which web3j)
|
{
|
||||||
|
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}
|
rm -f resources/contracts/*.{abi,bin}
|
||||||
|
|
||||||
# compile contracts
|
# 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
|
$SOLC $f --overwrite --bin --abi --optimize -o resources/contracts
|
||||||
done
|
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 |
|
@ -296,7 +296,7 @@ SELECT
|
||||||
i.tokens AS tokens,
|
i.tokens AS tokens,
|
||||||
i.value_usd AS value_usd,
|
i.value_usd AS value_usd,
|
||||||
u.login AS winner_login,
|
u.login AS winner_login,
|
||||||
u.address AS payout_address
|
u.address AS winner_address
|
||||||
FROM issues i, pull_requests p, users u, repositories r
|
FROM issues i, pull_requests p, users u, repositories r
|
||||||
WHERE
|
WHERE
|
||||||
p.issue_id = i.issue_id
|
p.issue_id = i.issue_id
|
||||||
|
@ -313,11 +313,9 @@ SELECT
|
||||||
i.issue_id AS issue_id,
|
i.issue_id AS issue_id,
|
||||||
u.address AS payout_address,
|
u.address AS payout_address,
|
||||||
i.execute_hash AS execute_hash
|
i.execute_hash AS execute_hash
|
||||||
FROM issues i, pull_requests p, users u
|
FROM issues i, repositories r, users u
|
||||||
WHERE
|
WHERE i.repo_id = r.repo_id
|
||||||
p.issue_id = i.issue_id
|
AND u.id = r.user_id
|
||||||
AND p.repo_id = i.repo_id
|
|
||||||
AND u.id = p.user_id
|
|
||||||
AND i.confirm_hash IS NULL
|
AND i.confirm_hash IS NULL
|
||||||
AND i.execute_hash IS NOT 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_receipt IS NULL
|
||||||
AND i.payout_hash IS NOT 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
|
-- :name update-winner-login :! :n
|
||||||
UPDATE issues
|
UPDATE issues
|
||||||
SET winner_login = :winner_login
|
SET winner_login = :winner_login
|
||||||
|
@ -384,29 +405,18 @@ SET payout_receipt = :payout_receipt::jsonb,
|
||||||
updated = timezone('utc'::text, now())
|
updated = timezone('utc'::text, now())
|
||||||
WHERE issue_id = :issue_id;
|
WHERE issue_id = :issue_id;
|
||||||
|
|
||||||
|
|
||||||
-- :name update-token-balances :! :n
|
|
||||||
-- :doc updates issue with given token balances
|
|
||||||
UPDATE issues
|
|
||||||
SET tokens = :token_balances::jsonb,
|
|
||||||
updated = timezone('utc'::text, now())
|
|
||||||
WHERE contract_address = :contract_address;
|
|
||||||
|
|
||||||
|
|
||||||
-- :name update-usd-value :! :n
|
|
||||||
-- :doc updates issue with given USD value
|
|
||||||
UPDATE issues
|
|
||||||
SET value_usd = :usd_value,
|
|
||||||
value_usd_updated = timezone('utc'::text, now())
|
|
||||||
WHERE contract_address = :contract_address;
|
|
||||||
|
|
||||||
|
|
||||||
-- :name update-issue-open :! :n
|
-- :name update-issue-open :! :n
|
||||||
-- :doc updates issue's open status
|
-- :doc updates issue's open status
|
||||||
UPDATE issues
|
UPDATE issues
|
||||||
SET is_open = :is_open
|
SET is_open = :is_open
|
||||||
WHERE issue_id = :issue_id;
|
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
|
-- :name issue-exists :1
|
||||||
-- :doc returns true if given issue exists
|
-- :doc returns true if given issue exists
|
||||||
|
@ -428,14 +438,29 @@ SELECT
|
||||||
i.issue_number AS issue_number,
|
i.issue_number AS issue_number,
|
||||||
i.is_open AS is_open,
|
i.is_open AS is_open,
|
||||||
i.winner_login AS winner_login,
|
i.winner_login AS winner_login,
|
||||||
|
i.transaction_hash AS transaction_hash,
|
||||||
|
i.contract_address AS contract_address,
|
||||||
|
i.confirm_hash AS confirm_hash,
|
||||||
|
i.execute_hash AS execute_hash,
|
||||||
|
i.payout_hash AS payout_hash,
|
||||||
|
i.watch_hash AS watch_hash,
|
||||||
|
i.payout_receipt AS payout_receipt,
|
||||||
i.commit_sha AS commit_sha,
|
i.commit_sha AS commit_sha,
|
||||||
|
u.address AS owner_address,
|
||||||
|
u.login AS owner_login,
|
||||||
|
i.contract_address AS contract_address,
|
||||||
|
i.confirm_hash AS confirm_hash,
|
||||||
i.title AS title,
|
i.title AS title,
|
||||||
i.comment_id AS comment_id,
|
i.comment_id AS comment_id,
|
||||||
|
i.balance_eth AS balance_eth,
|
||||||
|
i.tokens AS tokens,
|
||||||
|
i.value_usd AS value_usd,
|
||||||
i.repo_id AS repo_id,
|
i.repo_id AS repo_id,
|
||||||
r.owner AS owner,
|
r.owner AS owner,
|
||||||
r.repo AS repo
|
r.repo AS repo
|
||||||
FROM issues i, repositories r
|
FROM issues i, repositories r, users u
|
||||||
WHERE r.repo_id = i.repo_id
|
WHERE r.repo_id = i.repo_id
|
||||||
|
AND r.user_id = u.id
|
||||||
AND i.issue_id = :issue-id
|
AND i.issue_id = :issue-id
|
||||||
|
|
||||||
|
|
||||||
|
@ -480,6 +505,7 @@ SELECT
|
||||||
i.balance_eth AS balance_eth,
|
i.balance_eth AS balance_eth,
|
||||||
i.tokens AS tokens,
|
i.tokens AS tokens,
|
||||||
i.value_usd AS value_usd,
|
i.value_usd AS value_usd,
|
||||||
|
i.execute_hash AS execute_hash,
|
||||||
i.confirm_hash AS confirm_hash,
|
i.confirm_hash AS confirm_hash,
|
||||||
i.payout_hash AS payout_hash,
|
i.payout_hash AS payout_hash,
|
||||||
i.payout_receipt AS payout_receipt,
|
i.payout_receipt AS payout_receipt,
|
||||||
|
@ -489,6 +515,7 @@ SELECT
|
||||||
r.owner AS repo_owner,
|
r.owner AS repo_owner,
|
||||||
r.owner_avatar_url AS repo_owner_avatar_url,
|
r.owner_avatar_url AS repo_owner_avatar_url,
|
||||||
o.address AS owner_address,
|
o.address AS owner_address,
|
||||||
|
o.login AS owner_login,
|
||||||
u.address AS payout_address
|
u.address AS payout_address
|
||||||
FROM users o, repositories r, issues i LEFT OUTER JOIN users u ON u.login = i.winner_login
|
FROM users o, repositories r, issues i LEFT OUTER JOIN users u ON u.login = i.winner_login
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -568,10 +595,12 @@ AND r.repo_id = i.repo_id
|
||||||
AND r.owner = :owner
|
AND r.owner = :owner
|
||||||
AND r.repo = :repo;
|
AND r.repo = :repo;
|
||||||
|
|
||||||
-- :name update-eth-balance :! :n
|
-- :name update-balances :! :n
|
||||||
-- :doc updates balance of a wallet attached to a given issue
|
-- :doc updates balance of a wallet attached to a given issue
|
||||||
UPDATE issues
|
UPDATE issues
|
||||||
SET balance_eth = :balance_eth,
|
SET balance_eth = :balance_eth,
|
||||||
|
tokens = :token_balances::jsonb,
|
||||||
|
value_usd = :usd_value,
|
||||||
updated = timezone('utc'::text, now())
|
updated = timezone('utc'::text, now())
|
||||||
WHERE contract_address = :contract_address;
|
WHERE contract_address = :contract_address;
|
||||||
|
|
||||||
|
@ -625,8 +654,8 @@ SELECT
|
||||||
pr_id,
|
pr_id,
|
||||||
issue_number,
|
issue_number,
|
||||||
issue_id,
|
issue_id,
|
||||||
user_name,
|
user_name as display_name,
|
||||||
user_avatar_url,
|
user_avatar_url as avatar_url,
|
||||||
balance_eth,
|
balance_eth,
|
||||||
tokens,
|
tokens,
|
||||||
value_usd,
|
value_usd,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
(ns commiteth.bounties
|
(ns commiteth.bounties
|
||||||
(:require [commiteth.db.issues :as issues]
|
(:require [commiteth.db.issues :as issues]
|
||||||
|
[commiteth.db.bounties :as db-bounties]
|
||||||
[commiteth.db.users :as users]
|
[commiteth.db.users :as users]
|
||||||
[commiteth.db.repositories :as repos]
|
[commiteth.db.repositories :as repos]
|
||||||
[commiteth.db.comment-images :as comment-images]
|
|
||||||
[commiteth.eth.core :as eth]
|
[commiteth.eth.core :as eth]
|
||||||
[commiteth.eth.tracker :as tracker]
|
[commiteth.eth.tracker :as tracker]
|
||||||
[commiteth.github.core :as github]
|
[commiteth.github.core :as github]
|
||||||
|
[commiteth.util.util :refer [to-map]]
|
||||||
[commiteth.eth.multisig-wallet :as multisig]
|
[commiteth.eth.multisig-wallet :as multisig]
|
||||||
[commiteth.model.bounty :as bnt]
|
[commiteth.model.bounty :as bnt]
|
||||||
[commiteth.util.png-rendering :as png-rendering]
|
|
||||||
[clojure.tools.logging :as log]))
|
[clojure.tools.logging :as log]))
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,58 @@
|
||||||
(let [labels (:labels issue)]
|
(let [labels (:labels issue)]
|
||||||
(some #(= label-name (:name %)) labels)))
|
(some #(= label-name (:name %)) labels)))
|
||||||
|
|
||||||
(defn deploy-contract [owner-address issue-id]
|
(def last-states (atom {}))
|
||||||
|
|
||||||
|
(defn transition [{:keys [issue-id tx-info] :as bounty} state]
|
||||||
|
(let [bounty-not= (fn [current db]
|
||||||
|
(some #(not= (%1 current) (%1 db))
|
||||||
|
(disj (set (keys current)) :tx-info)))
|
||||||
|
bounty-from-db (issues/get-issue-by-id issue-id)
|
||||||
|
bounty (and (or
|
||||||
|
(and (= state :pending-contributor-address)
|
||||||
|
(not= state (get @last-states issue-id)))
|
||||||
|
(bounty-not= bounty bounty-from-db))
|
||||||
|
(merge bounty-from-db bounty))]
|
||||||
|
(when bounty
|
||||||
|
(case state
|
||||||
|
:deploying
|
||||||
|
(tracker/track-tx! tx-info)
|
||||||
|
|
||||||
|
:opened
|
||||||
|
(do
|
||||||
|
(tracker/untrack-tx! {:issue-id (:issue-id bounty)
|
||||||
|
:tx-hash (:transaction-hash bounty)
|
||||||
|
:result (:contract-address bounty)
|
||||||
|
:type :deploy})
|
||||||
|
(github/update-bounty-comment-image bounty))
|
||||||
|
|
||||||
|
:pending-sob-confirmation
|
||||||
|
(tracker/track-tx! tx-info)
|
||||||
|
|
||||||
|
:pending-maintainer-confirmation
|
||||||
|
(tracker/untrack-tx! tx-info)
|
||||||
|
|
||||||
|
:paid
|
||||||
|
(db-bounties/update-payout-receipt issue-id (:payout-receipt bounty))
|
||||||
|
|
||||||
|
:watch-set
|
||||||
|
(tracker/track-tx! tx-info)
|
||||||
|
|
||||||
|
:watch-reset
|
||||||
|
(tracker/untrack-tx! tx-info)
|
||||||
|
|
||||||
|
:update-balances
|
||||||
|
(do
|
||||||
|
(github/update-bounty-comment-image bounty)
|
||||||
|
(issues/update-balances (:contract-address bounty)
|
||||||
|
(:balance-eth bounty)
|
||||||
|
(:tokens bounty)
|
||||||
|
(:value-usd bounty)))
|
||||||
|
nil)
|
||||||
|
(github/update-comment bounty state)))
|
||||||
|
(swap! last-states assoc issue-id state))
|
||||||
|
|
||||||
|
(defn deploy-contract [owner-address issue-id]
|
||||||
(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)
|
||||||
(try
|
(try
|
||||||
|
@ -31,23 +82,36 @@
|
||||||
:internal-tx-id [:deploy issue-id]})]
|
:internal-tx-id [:deploy issue-id]})]
|
||||||
(do
|
(do
|
||||||
(log/infof "issue %s: Contract deployed, transaction-hash: %s" issue-id (:tx-hash tx-info))
|
(log/infof "issue %s: Contract deployed, transaction-hash: %s" issue-id (:tx-hash tx-info))
|
||||||
(github/post-deploying-comment issue-id
|
(transition {:issue-id issue-id
|
||||||
(:tx-hash tx-info))
|
:transaction-hash (:tx-hash tx-info)
|
||||||
(tracker/track-tx! tx-info))
|
:tx-info tx-info} :deploying))
|
||||||
(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)))))
|
(catch Exception ex (log/errorf ex "issue %s: deploy-contract exception" issue-id)))))
|
||||||
|
|
||||||
|
(defn execute-payout [issue-id contract-address payout-address]
|
||||||
|
(if (empty? payout-address)
|
||||||
|
(do
|
||||||
|
(log/warn "issue %s: Cannot sign pending bounty - winner has no payout address" issue-id)
|
||||||
|
(transition {:issue-id issue-id} :pending-contributor-address))
|
||||||
|
(let [tx-info (multisig/send-all {:contract contract-address
|
||||||
|
:payout-address payout-address
|
||||||
|
:internal-tx-id [:execute issue-id]})]
|
||||||
|
(log/infof "issue %s: Payout self-signed, called sign-all(%s) tx: %s" issue-id contract-address payout-address (:tx-hash tx-info))
|
||||||
|
(transition {:execute-hash (:tx-hash tx-info)
|
||||||
|
:issue-id issue-id
|
||||||
|
:tx-info tx-info} :pending-sob-confirmation)
|
||||||
|
tx-info)))
|
||||||
|
|
||||||
(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
|
||||||
issue-number :number
|
issue-number :number
|
||||||
issue-title :title} issue
|
issue-title :title} issue
|
||||||
created-issue (issues/create repo-id issue-id issue-number issue-title)
|
created-issue (issues/create repo-id issue-id issue-number issue-title)
|
||||||
{owner-address :address
|
{:keys [address owner]} (users/get-repo-owner repo-id)]
|
||||||
owner :owner} (users/get-repo-owner repo-id)]
|
|
||||||
(log/debug "issue %s: Adding bounty for issue %s/%s - owner address: %s"
|
(log/debug "issue %s: Adding bounty for issue %s/%s - owner address: %s"
|
||||||
issue-id repo issue-number owner-address)
|
issue-id repo issue-number address)
|
||||||
(if (= 1 created-issue)
|
(if (= 1 created-issue)
|
||||||
(deploy-contract owner-address issue-id)
|
(deploy-contract address issue-id)
|
||||||
(log/debug "issue %s: 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]
|
(defn maybe-add-bounty-for-issue [repo repo-id issue]
|
||||||
|
@ -63,9 +127,8 @@
|
||||||
;; We have a max-limit to ensure people can't add more issues and
|
;; We have a max-limit to ensure people can't add more issues and
|
||||||
;; drain bot account until we have economic design in place
|
;; drain bot account until we have economic design in place
|
||||||
(defn add-bounties-for-existing-issues [full-name]
|
(defn add-bounties-for-existing-issues [full-name]
|
||||||
(let [{repo-id :repo_id
|
(let [{:keys [repo-id
|
||||||
owner :owner
|
owner repo] } (repos/get-repo full-name)
|
||||||
repo :repo} (repos/get-repo full-name)
|
|
||||||
issues (github/get-issues owner repo)
|
issues (github/get-issues owner repo)
|
||||||
bounty-issues (filter has-bounty-label? issues)
|
bounty-issues (filter has-bounty-label? issues)
|
||||||
max-bounties (take max-issues-limit bounty-issues)]
|
max-bounties (take max-issues-limit bounty-issues)]
|
||||||
|
@ -75,30 +138,13 @@
|
||||||
(map (partial maybe-add-bounty-for-issue repo repo-id) max-bounties))))
|
(map (partial maybe-add-bounty-for-issue repo repo-id) max-bounties))))
|
||||||
|
|
||||||
|
|
||||||
(defn update-bounty-comment-image [issue-id owner repo issue-number contract-address eth-balance eth-balance-str tokens]
|
|
||||||
(let [hash (github/github-comment-hash owner repo issue-number eth-balance)
|
|
||||||
issue-url (str owner "/" repo "/issues/" (str issue-number))
|
|
||||||
png-data (png-rendering/gen-comment-image
|
|
||||||
contract-address
|
|
||||||
eth-balance-str
|
|
||||||
tokens
|
|
||||||
issue-url)]
|
|
||||||
(log/debug "update-bounty-comment-image" issue-id owner repo issue-number)
|
|
||||||
(log/debug contract-address eth-balance-str)
|
|
||||||
(log/debug "hash" hash)
|
|
||||||
|
|
||||||
(if png-data
|
|
||||||
(comment-images/save-image! issue-id hash png-data)
|
|
||||||
(log/error "Failed ot generate PNG"))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn update-bounty-issue-titles
|
(defn update-bounty-issue-titles
|
||||||
"Update stored titles for bounty issues if changed on Github side"
|
"Update stored titles for bounty issues if changed on Github side"
|
||||||
[]
|
[]
|
||||||
(log/debug "update-bounty-issue-titles")
|
(log/debug "update-bounty-issue-titles")
|
||||||
(for [{:keys [title issue_number repo owner]}
|
(for [{:keys [title issue-number repo owner]}
|
||||||
(issues/get-issue-titles)]
|
(issues/get-issue-titles)]
|
||||||
(let [gh-issue (github/get-issue owner repo issue_number)]
|
(let [gh-issue (github/get-issue owner repo issue-number)]
|
||||||
(if-not (= title (:title gh-issue))
|
(if-not (= title (:title gh-issue))
|
||||||
(issues/update-issue-title (:id gh-issue) (:title gh-issue))))))
|
(issues/update-issue-title (:id gh-issue) (:title gh-issue))))))
|
||||||
|
|
||||||
|
@ -118,29 +164,31 @@
|
||||||
- :pending-contributor-address
|
- :pending-contributor-address
|
||||||
- :pending-maintainer-confirmation"
|
- :pending-maintainer-confirmation"
|
||||||
[bounty]
|
[bounty]
|
||||||
(assert-keys bounty [:winner_login :payout_address :confirm_hash :payout_hash
|
(assert-keys bounty [:winner-login :payout-address :confirm-hash :payout-hash
|
||||||
:claims :tokens :contract_address])
|
:claims :tokens :contract-address])
|
||||||
;; Some bounties have been paid out manually, the payout hash
|
;; Some bounties have been paid out manually, the payout hash
|
||||||
;; was set properly but winner_login was not
|
;; was set properly but winner-login was not
|
||||||
(let [open-claims (fn open-claims [bounty]
|
(let [open-claims (fn open-claims [bounty]
|
||||||
(filter bnt/open? (:claims bounty)))]
|
(filter bnt/open? (:claims bounty)))]
|
||||||
(if-let [merged-or-paid? (or (:winner_login bounty)
|
(if-let [merged-or-paid? (or (:winner-login bounty)
|
||||||
(:payout_hash bounty))]
|
(:payout-receipt bounty))]
|
||||||
(cond
|
(cond
|
||||||
(:payout_hash bounty) :paid
|
(:payout-receipt bounty) :paid
|
||||||
(nil? (:payout_address bounty)) :pending-contributor-address
|
(nil? (:payout-address bounty)) :pending-contributor-address
|
||||||
;; `confirm_hash` is set by us as soon as a PR is merged and the
|
;; `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
|
;; contributor address is known. Usually end users should not need
|
||||||
;; to be aware of this step.
|
;; to be aware of this step.
|
||||||
(nil? (:confirm_hash bounty)) :pending-sob-confirmation
|
(nil? (:confirm-hash bounty)) :pending-sob-confirmation
|
||||||
;; `payout_hash` is set when the bounty issuer signs the payout
|
;; `payout-hash` is set when the bounty issuer signs the payout
|
||||||
(nil? (:payout_hash bounty)) :pending-maintainer-confirmation
|
(nil? (:payout-hash bounty)) :pending-maintainer-confirmation
|
||||||
:else :merged)
|
:else :merged)
|
||||||
(cond ; not yet merged
|
(cond ; not yet merged
|
||||||
(< 1 (count (open-claims bounty))) :multiple-claims
|
(< 1 (count (open-claims bounty))) :multiple-claims
|
||||||
(= 1 (count (open-claims bounty))) :claimed
|
(= 1 (count (open-claims bounty))) :claimed
|
||||||
(seq (:tokens bounty)) :funded
|
(seq (:tokens bounty)) :funded
|
||||||
(:contract_address bounty) :opened))))
|
(:contract-address bounty) :opened))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(def user 97496)
|
(def user 97496)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
(ns commiteth.db.bounties
|
(ns commiteth.db.bounties
|
||||||
(:require [commiteth.db.core :refer [*db*] :as db]
|
(:require [commiteth.db.core :refer [*db*] :as db]
|
||||||
|
[commiteth.util.util :refer [to-db-map]]
|
||||||
|
[clojure.tools.logging :as log]
|
||||||
[clojure.java.jdbc :as jdbc]
|
[clojure.java.jdbc :as jdbc]
|
||||||
[clojure.set :refer [rename-keys]]))
|
[clojure.set :refer [rename-keys]]))
|
||||||
|
|
||||||
|
@ -40,6 +42,10 @@
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
(jdbc/with-db-connection [con-db *db*]
|
||||||
(db/confirmed-payouts con-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
|
(defn update-winner-login
|
||||||
[issue-id login]
|
[issue-id login]
|
||||||
|
@ -54,23 +60,37 @@
|
||||||
(defn update-payout-hash
|
(defn update-payout-hash
|
||||||
[issue-id payout-hash]
|
[issue-id payout-hash]
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
(jdbc/with-db-connection [con-db *db*]
|
||||||
(db/update-payout-hash con-db {:issue_id issue-id :payout_hash payout-hash})))
|
(db/update-payout-hash con-db (to-db-map issue-id payout-hash))))
|
||||||
|
|
||||||
(defn reset-payout-hash
|
(defn reset-payout-hash
|
||||||
[issue-id]
|
[issue-id]
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
(jdbc/with-db-connection [con-db *db*]
|
||||||
(db/reset-payout-hash con-db {:issue_id issue-id})))
|
(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
|
(defn update-payout-receipt
|
||||||
[issue-id payout-receipt]
|
[issue-id payout-receipt]
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
(jdbc/with-db-connection [con-db *db*]
|
||||||
(db/update-payout-receipt con-db {:issue_id issue-id
|
(db/update-payout-receipt con-db (to-db-map issue-id payout-receipt))))
|
||||||
:payout_receipt payout-receipt})))
|
|
||||||
|
|
||||||
(defn get-bounty
|
(defn get-bounty
|
||||||
[owner repo issue-number]
|
[owner repo issue-number]
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
(jdbc/with-db-connection [con-db *db*]
|
||||||
(db/get-bounty con-db {:owner owner :repo repo :issue_number issue-number})))
|
(log/info "get-bounty params:" (to-db-map owner repo issue-number))
|
||||||
|
(db/get-bounty con-db (to-db-map owner repo issue-number))))
|
||||||
|
|
||||||
(defn open-bounty-contracts
|
(defn open-bounty-contracts
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
[mount.core :refer [defstate]]
|
[mount.core :refer [defstate]]
|
||||||
[migratus.core :as migratus]
|
[migratus.core :as migratus]
|
||||||
[mpg.core :as mpg]
|
[mpg.core :as mpg]
|
||||||
|
[hugsql.core]
|
||||||
[clojure.string :as str])
|
[clojure.string :as str])
|
||||||
(:import org.postgresql.util.PGobject
|
(:import org.postgresql.util.PGobject
|
||||||
java.sql.Array
|
java.sql.Array
|
||||||
|
@ -92,3 +93,30 @@
|
||||||
|
|
||||||
(defn update! [& args]
|
(defn update! [& args]
|
||||||
(apply jdbc/update! *db* args))
|
(apply jdbc/update! *db* args))
|
||||||
|
|
||||||
|
(defn convert-keys-to-lisp-case [res]
|
||||||
|
(->> res
|
||||||
|
(map #(vector (keyword (str/replace (name (first %1)) "_" "-"))
|
||||||
|
(second %1)))
|
||||||
|
(into {})))
|
||||||
|
|
||||||
|
(defn result-one-sql->lisp
|
||||||
|
[this result options]
|
||||||
|
(convert-keys-to-lisp-case (first result)))
|
||||||
|
|
||||||
|
(defn result-many-sql->lisp
|
||||||
|
[this result options]
|
||||||
|
(map convert-keys-to-lisp-case result))
|
||||||
|
|
||||||
|
(defmethod hugsql.core/hugsql-result-fn :1 [sym]
|
||||||
|
'commiteth.db.core/result-one-sql->lisp)
|
||||||
|
|
||||||
|
(defmethod hugsql.core/hugsql-result-fn :one [sym]
|
||||||
|
'commiteth.db.core/result-one-sql->lisp)
|
||||||
|
|
||||||
|
(defmethod hugsql.core/hugsql-result-fn :* [sym]
|
||||||
|
'commiteth.db.core/result-many-sql->lisp)
|
||||||
|
|
||||||
|
(defmethod hugsql.core/hugsql-result-fn :many [sym]
|
||||||
|
'commiteth.db.core/result-many-sql->lisp)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns commiteth.db.issues
|
(ns commiteth.db.issues
|
||||||
(:require [commiteth.db.core :refer [*db*] :as db]
|
(:require [commiteth.db.core :refer [*db*] :as db]
|
||||||
[clojure.java.jdbc :as jdbc]
|
[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]))
|
[clojure.tools.logging :as log]))
|
||||||
|
|
||||||
|
@ -71,23 +72,13 @@
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
(jdbc/with-db-connection [con-db *db*]
|
||||||
(db/list-pending-deployments con-db)))
|
(db/list-pending-deployments con-db)))
|
||||||
|
|
||||||
(defn update-eth-balance
|
(defn update-balances
|
||||||
[contract-address balance-eth]
|
[contract-address balance-eth token-balances usd-value]
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
(jdbc/with-db-connection [con-db *db*]
|
||||||
(db/update-eth-balance con-db {:contract_address contract-address
|
(db/update-balances con-db (to-db-map contract-address
|
||||||
:balance_eth balance-eth})))
|
balance-eth
|
||||||
|
token-balances
|
||||||
(defn update-token-balances
|
usd-value))))
|
||||||
[contract-address balances]
|
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
|
||||||
(db/update-token-balances con-db {:contract_address contract-address
|
|
||||||
:token_balances balances})))
|
|
||||||
|
|
||||||
(defn update-usd-value
|
|
||||||
[contract-address usd-value]
|
|
||||||
(jdbc/with-db-connection [con-db *db*]
|
|
||||||
(db/update-usd-value con-db {:contract_address contract-address
|
|
||||||
:usd_value usd-value})))
|
|
||||||
|
|
||||||
(defn update-open-status
|
(defn update-open-status
|
||||||
[issue-id is-open]
|
[issue-id is-open]
|
||||||
|
@ -95,6 +86,12 @@
|
||||||
(db/update-issue-open con-db {:issue_id issue-id
|
(db/update-issue-open con-db {:issue_id issue-id
|
||||||
:is_open is-open})))
|
: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?
|
(defn is-bounty-issue?
|
||||||
[issue-id]
|
[issue-id]
|
||||||
(let [res (jdbc/with-db-connection [con-db *db*]
|
(let [res (jdbc/with-db-connection [con-db *db*]
|
||||||
|
|
|
@ -48,7 +48,10 @@
|
||||||
(try
|
(try
|
||||||
(eth-gasstation-gas-price)
|
(eth-gasstation-gas-price)
|
||||||
(catch Throwable t
|
(catch Throwable t
|
||||||
(log/error "Failed to get gas price with ethgasstation API" t)
|
(let [cause (-> t
|
||||||
|
Throwable->map
|
||||||
|
:cause)]
|
||||||
|
(log/error "Failed to get gas price with ethgasstation API" cause))
|
||||||
(gas-price-from-config)))
|
(gas-price-from-config)))
|
||||||
(gas-price-from-config)))
|
(gas-price-from-config)))
|
||||||
|
|
||||||
|
@ -82,10 +85,10 @@
|
||||||
response @(post (eth-rpc-url) options)
|
response @(post (eth-rpc-url) options)
|
||||||
result (safe-read-str (:body response))]
|
result (safe-read-str (:body response))]
|
||||||
(when internal-tx-id
|
(when internal-tx-id
|
||||||
(log/infof "%s: eth-rpc %s" tx-id-str method))
|
(log/debugf "%s: eth-rpc %s" tx-id-str method))
|
||||||
(log/debugf "%s: eth-rpc req(%s) body: %s" tx-id-str request-id body)
|
(log/debugf "%s: eth-rpc req(%s) body: %s" tx-id-str request-id body)
|
||||||
(if tx-id-str
|
(if tx-id-str
|
||||||
(log/infof "%s: eth-rpc req(%s) result: %s" tx-id-str request-id result)
|
(log/debugf "%s: eth-rpc req(%s) result: %s" tx-id-str request-id result)
|
||||||
(log/debugf "no-tx-id: eth-rpc req(%s) result: %s" request-id result))
|
(log/debugf "no-tx-id: eth-rpc req(%s) result: %s" request-id result))
|
||||||
(cond
|
(cond
|
||||||
;; Ignore any responses that have mismatching request ID
|
;; Ignore any responses that have mismatching request ID
|
||||||
|
@ -225,7 +228,7 @@
|
||||||
(hex->big-integer gas)
|
(hex->big-integer gas)
|
||||||
contract
|
contract
|
||||||
(:data params)
|
(:data params)
|
||||||
nonce)
|
(biginteger nonce))
|
||||||
tx-hash (try
|
tx-hash (try
|
||||||
(eth-rpc
|
(eth-rpc
|
||||||
{:method "eth_sendRawTransaction"
|
{:method "eth_sendRawTransaction"
|
||||||
|
|
|
@ -62,9 +62,44 @@
|
||||||
(:tx-hash @current-tx) (:type @current-tx))
|
(:tx-hash @current-tx) (:type @current-tx))
|
||||||
(reset! current-tx nil)))
|
(reset! current-tx nil)))
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
(defrecord ParallelTxTracker [current-txs]
|
||||||
|
ITxTracker
|
||||||
|
(try-reserve-nonce [this]
|
||||||
|
(let [nonce (get-nonce)
|
||||||
|
monitored-nonces (set (keys @current-txs))
|
||||||
|
first-available-nonce (some #(if (monitored-nonces %1) nil %1) (iterate inc nonce))]
|
||||||
|
(swap! current-txs assoc first-available-nonce nil)
|
||||||
|
first-available-nonce))
|
||||||
|
|
||||||
|
(drop-nonce [this nonce]
|
||||||
|
(swap! current-txs dissoc nonce))
|
||||||
|
|
||||||
|
(track-tx [this tx-info]
|
||||||
|
(swap! current-txs update (:nonce tx-info) merge tx-info))
|
||||||
|
|
||||||
|
(untrack-tx [this tx-info]
|
||||||
|
(when (contains? (set (keys @current-txs)) (:nonce tx-info))
|
||||||
|
(swap! current-txs dissoc (:nonce tx-info))))
|
||||||
|
|
||||||
|
(prune-txs [this unmined-txs]
|
||||||
|
(swap! current-txs
|
||||||
|
(fn [txs]
|
||||||
|
(let [unmined-tx-hashes (set (map :tx-hash unmined-txs))
|
||||||
|
time-threshold (t/minus (t/now) (t/minutes 10))
|
||||||
|
nonces-to-remove
|
||||||
|
(->> txs
|
||||||
|
vals
|
||||||
|
(filter #(or (unmined-tx-hashes (:tx-hash %1))
|
||||||
|
(and (:timestamp %1)
|
||||||
|
(t/before? (:timestamp %1) time-threshold))))
|
||||||
|
(map :nonce))]
|
||||||
|
(apply dissoc txs nonces-to-remove)))))
|
||||||
)
|
)
|
||||||
|
|
||||||
(def tx-tracker (SequentialTxTracker. (atom nil)))
|
|
||||||
|
(def tx-tracker (ParallelTxTracker. (atom nil)))
|
||||||
|
|
||||||
(defn try-reserve-nonce! []
|
(defn try-reserve-nonce! []
|
||||||
(try-reserve-nonce tx-tracker))
|
(try-reserve-nonce tx-tracker))
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
[commiteth.db.issues :as db-issues]
|
[commiteth.db.issues :as db-issues]
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
[cheshire.core :as json]
|
[cheshire.core :as json]
|
||||||
|
[commiteth.util.png-rendering :as png-rendering]
|
||||||
|
[commiteth.db.issues :as db-issues]
|
||||||
|
[commiteth.db.bounties :as db-bounties]
|
||||||
|
[commiteth.db.comment-images :as comment-images]
|
||||||
[clojure.string :as str])
|
[clojure.string :as str])
|
||||||
(:import [java.util UUID]))
|
(:import [java.util UUID]))
|
||||||
|
|
||||||
|
@ -230,7 +234,7 @@
|
||||||
(str "Contract address: [" addr "](" url-base "/address/" addr ")\n")))
|
(str "Contract address: [" addr "](" url-base "/address/" addr ")\n")))
|
||||||
|
|
||||||
(defn generate-open-comment
|
(defn generate-open-comment
|
||||||
[owner repo issue-number contract-address eth-balance eth-balance-str tokens]
|
[owner repo issue-number contract-address eth-balance tokens]
|
||||||
(let [image-url (md-image "QR Code" (get-qr-url owner repo issue-number eth-balance))
|
(let [image-url (md-image "QR Code" (get-qr-url owner repo issue-number eth-balance))
|
||||||
site-url (md-url (server-address) (server-address))]
|
site-url (md-url (server-address) (server-address))]
|
||||||
(format (str "Current balance: %s ETH\n"
|
(format (str "Current balance: %s ETH\n"
|
||||||
|
@ -243,14 +247,14 @@
|
||||||
(if (on-testnet?)
|
(if (on-testnet?)
|
||||||
"To fund it, send test ETH or test ERC20/ERC223 tokens to the contract address."
|
"To fund it, send test ETH or test ERC20/ERC223 tokens to the contract address."
|
||||||
"To fund it, send ETH or ERC20/ERC223 tokens to the contract address."))
|
"To fund it, send ETH or ERC20/ERC223 tokens to the contract address."))
|
||||||
eth-balance-str image-url site-url)))
|
(.toPlainString (bigdec eth-balance)) image-url site-url)))
|
||||||
|
|
||||||
(defn learn-more-text []
|
(defn learn-more-text []
|
||||||
(let [site-url (md-url (server-address) (server-address))]
|
(let [site-url (md-url (server-address) (server-address))]
|
||||||
(format "Visit %s to learn more.\n" site-url)))
|
(format "Visit %s to learn more.\n" site-url)))
|
||||||
|
|
||||||
(defn generate-merged-comment
|
(defn generate-merged-comment
|
||||||
[contract-address eth-balance-str tokens winner-login winner-address-missing?]
|
[contract-address eth-balance tokens winner-login winner-address-missing?]
|
||||||
(format (str "Balance: %s ETH\n"
|
(format (str "Balance: %s ETH\n"
|
||||||
(token-balances-text tokens)
|
(token-balances-text tokens)
|
||||||
(contract-addr-text contract-address)
|
(contract-addr-text contract-address)
|
||||||
|
@ -260,17 +264,17 @@
|
||||||
"Pending maintainer confirmation") "\n")
|
"Pending maintainer confirmation") "\n")
|
||||||
"Winner: %s\n"
|
"Winner: %s\n"
|
||||||
(learn-more-text))
|
(learn-more-text))
|
||||||
eth-balance-str winner-login))
|
(.toPlainString (bigdec eth-balance)) winner-login))
|
||||||
|
|
||||||
(defn generate-paid-comment
|
(defn generate-paid-comment
|
||||||
[contract-address eth-balance-str tokens payee-login]
|
[contract-address eth-balance tokens payee-login]
|
||||||
(format (str "Balance: %s ETH\n"
|
(format (str "Balance: %s ETH\n"
|
||||||
(token-balances-text tokens)
|
(token-balances-text tokens)
|
||||||
(contract-addr-text contract-address)
|
(contract-addr-text contract-address)
|
||||||
(network-text)
|
(network-text)
|
||||||
"Paid to: %s\n"
|
"Paid to: %s\n"
|
||||||
(learn-more-text))
|
(learn-more-text))
|
||||||
eth-balance-str payee-login))
|
(.toPlainString (bigdec eth-balance)) payee-login))
|
||||||
|
|
||||||
(defn make-patch-request [end-point positional query]
|
(defn make-patch-request [end-point positional query]
|
||||||
(let [{:keys [auth oauth-token]
|
(let [{:keys [auth oauth-token]
|
||||||
|
@ -290,6 +294,22 @@
|
||||||
:otp))]
|
:otp))]
|
||||||
(assoc req :body (json/generate-string (or raw-query proper-query)))))
|
(assoc req :body (json/generate-string (or raw-query proper-query)))))
|
||||||
|
|
||||||
|
(defn update-bounty-comment-image [{:keys [issue-id owner repo issue-number contract-address balance-eth tokens]}]
|
||||||
|
(let [hash (github-comment-hash owner repo issue-number balance-eth)
|
||||||
|
issue-url (str owner "/" repo "/issues/" (str issue-number))
|
||||||
|
png-data (png-rendering/gen-comment-image
|
||||||
|
contract-address
|
||||||
|
(.toPlainString (bigdec balance-eth))
|
||||||
|
tokens
|
||||||
|
issue-url)]
|
||||||
|
(log/debug "update-bounty-comment-image" issue-id owner repo issue-number)
|
||||||
|
(log/debug contract-address balance-eth)
|
||||||
|
(log/debug "hash" hash)
|
||||||
|
|
||||||
|
(if png-data
|
||||||
|
(comment-images/save-image! issue-id hash png-data)
|
||||||
|
(log/error "Failed ot generate PNG"))))
|
||||||
|
|
||||||
(defn post-deploying-comment
|
(defn post-deploying-comment
|
||||||
[issue-id tx-id]
|
[issue-id tx-id]
|
||||||
(let [{owner :owner
|
(let [{owner :owner
|
||||||
|
@ -310,52 +330,51 @@
|
||||||
|
|
||||||
(defn update-comment
|
(defn update-comment
|
||||||
"Update comment for an open bounty issue"
|
"Update comment for an open bounty issue"
|
||||||
[owner repo comment-id issue-number contract-address eth-balance eth-balance-str tokens]
|
[{:keys [issue-id owner repo comment-id issue-number contract-address
|
||||||
(let [comment (generate-open-comment owner
|
balance-eth tokens
|
||||||
repo
|
payout-receipt
|
||||||
issue-number
|
owner-login
|
||||||
contract-address
|
winner-login transaction-hash] :as issue}
|
||||||
eth-balance
|
state]
|
||||||
eth-balance-str
|
(let [comment (case state
|
||||||
tokens)]
|
:deploying
|
||||||
(log/debug (str "Updating " owner "/" repo "/" issue-number
|
(generate-deploying-comment owner repo issue-number transaction-hash)
|
||||||
" comment #" comment-id " with contents: " comment))
|
(:opened :update-balances)
|
||||||
(let [req (make-patch-request "repos/%s/%s/issues/comments/%s"
|
(generate-open-comment owner
|
||||||
[owner repo comment-id]
|
repo
|
||||||
(assoc (self-auth-params) :body comment))]
|
issue-number
|
||||||
(tentacles/safe-parse (http/request req)))))
|
contract-address
|
||||||
|
balance-eth
|
||||||
|
tokens)
|
||||||
|
:pending-sob-confirmation
|
||||||
(defn update-merged-issue-comment
|
(generate-merged-comment contract-address
|
||||||
"Update comment for a bounty issue with winning claim (waiting to be
|
balance-eth
|
||||||
signed off by maintainer/user ETH address missing)"
|
tokens
|
||||||
[owner repo comment-id contract-address eth-balance-str tokens winner-login winner-address-missing?]
|
(or winner-login owner-login)
|
||||||
(let [comment (generate-merged-comment contract-address
|
false)
|
||||||
eth-balance-str
|
:pending-contributor-address
|
||||||
|
(generate-merged-comment contract-address
|
||||||
|
balance-eth
|
||||||
|
tokens
|
||||||
|
(or winner-login owner-login)
|
||||||
|
true)
|
||||||
|
:paid
|
||||||
|
(generate-paid-comment contract-address
|
||||||
|
balance-eth
|
||||||
tokens
|
tokens
|
||||||
winner-login
|
(or winner-login owner-login))
|
||||||
winner-address-missing?)]
|
nil)]
|
||||||
(log/debug (str "Updating merged bounty issue (" owner "/" repo ")"
|
(log/info (str "Updating " owner "/" repo "/" issue-number
|
||||||
" comment#" comment-id " with contents: " comment))
|
" comment #" comment-id " with contents: " comment))
|
||||||
(let [req (make-patch-request "repos/%s/%s/issues/comments/%s"
|
(if (= state :deploying)
|
||||||
[owner repo comment-id]
|
(let [resp (issues/create-comment owner repo issue-number comment (self-auth-params))
|
||||||
(assoc (self-auth-params) :body comment))]
|
comment-id (:id resp)]
|
||||||
(tentacles/safe-parse (http/request req)))))
|
(db-issues/update-comment-id issue-id comment-id))
|
||||||
|
(when comment
|
||||||
(defn update-paid-issue-comment
|
(let [req (make-patch-request "repos/%s/%s/issues/comments/%s"
|
||||||
"Update comment for a paid out bounty issue"
|
[owner repo comment-id]
|
||||||
[owner repo comment-id contract-address eth-balance-str tokens payee-login]
|
(assoc (self-auth-params) :body comment))]
|
||||||
(let [comment (generate-paid-comment contract-address
|
(tentacles/safe-parse (http/request req)))))))
|
||||||
eth-balance-str
|
|
||||||
tokens
|
|
||||||
payee-login)]
|
|
||||||
(log/debug (str "Updating paid bounty (" owner "/" repo ")"
|
|
||||||
" comment#" comment-id " with contents: " comment))
|
|
||||||
(let [req (make-patch-request "repos/%s/%s/issues/comments/%s"
|
|
||||||
[owner repo comment-id]
|
|
||||||
(assoc (self-auth-params) :body comment))]
|
|
||||||
(tentacles/safe-parse (http/request req)))))
|
|
||||||
|
|
||||||
(defn get-issue
|
(defn get-issue
|
||||||
[owner repo issue-number]
|
[owner repo issue-number]
|
||||||
|
|
|
@ -12,21 +12,18 @@
|
||||||
(context "/qr" []
|
(context "/qr" []
|
||||||
(GET "/:owner/:repo/bounty/:issue{[0-9]{1,9}}/:hash/qr.png" [owner repo issue hash]
|
(GET "/:owner/:repo/bounty/:issue{[0-9]{1,9}}/:hash/qr.png" [owner repo issue hash]
|
||||||
(log/debug "qr PNG GET" owner repo issue hash)
|
(log/debug "qr PNG GET" owner repo issue hash)
|
||||||
(if-let [{address :contract_address
|
(if-let [{:keys [contract-address repo issue-id balance-eth]}
|
||||||
repo :repo
|
|
||||||
issue-id :issue_id
|
|
||||||
balance-eth :balance_eth}
|
|
||||||
(bounties/get-bounty owner
|
(bounties/get-bounty owner
|
||||||
repo
|
repo
|
||||||
(Integer/parseInt issue))]
|
(Integer/parseInt issue))]
|
||||||
(do
|
(do
|
||||||
(log/debug "address:" address)
|
(log/debug "address:" contract-address)
|
||||||
(log/debug owner repo issue balance-eth)
|
(log/debug owner repo issue balance-eth)
|
||||||
(log/debug hash (github/github-comment-hash owner repo issue balance-eth))
|
(log/debug hash (github/github-comment-hash owner repo issue balance-eth))
|
||||||
(if address
|
(if contract-address
|
||||||
(if-let [{png-data :png_data}
|
(if-let [{:keys [png-data]}
|
||||||
(comment-images/get-image-data
|
(comment-images/get-image-data
|
||||||
issue-id hash)]
|
issue-id hash)]
|
||||||
(do (log/debug "PNG found")
|
(do (log/debug "PNG found")
|
||||||
{:status 200
|
{:status 200
|
||||||
:content-type "image/png"
|
:content-type "image/png"
|
||||||
|
|
|
@ -27,10 +27,10 @@
|
||||||
[token]
|
[token]
|
||||||
(let [user (github/get-user token)
|
(let [user (github/get-user token)
|
||||||
{email :email
|
{email :email
|
||||||
user-id :id} user]
|
user-id :id} user
|
||||||
(log/debug "get-or-create-user" user)
|
db-user (users/get-user user-id)]
|
||||||
(or
|
(if (:id db-user)
|
||||||
(users/get-user user-id)
|
db-user
|
||||||
(create-user token user))))
|
(create-user token user))))
|
||||||
|
|
||||||
(defroutes redirect-routes
|
(defroutes redirect-routes
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns commiteth.routes.services
|
(ns commiteth.routes.services
|
||||||
(:require [ring.util.http-response :refer :all]
|
(:require [ring.util.http-response :refer :all]
|
||||||
[compojure.api.sweet :refer :all]
|
[compojure.api.sweet :refer :all]
|
||||||
|
[compojure.api.exception :as ex]
|
||||||
[schema.core :as s]
|
[schema.core :as s]
|
||||||
[compojure.api.meta :refer [restructure-param]]
|
[compojure.api.meta :refer [restructure-param]]
|
||||||
[buddy.auth.accessrules :refer [restrict]]
|
[buddy.auth.accessrules :refer [restrict]]
|
||||||
|
@ -10,16 +11,20 @@
|
||||||
[commiteth.db.usage-metrics :as usage-metrics]
|
[commiteth.db.usage-metrics :as usage-metrics]
|
||||||
[commiteth.db.repositories :as repositories]
|
[commiteth.db.repositories :as repositories]
|
||||||
[commiteth.db.bounties :as bounties-db]
|
[commiteth.db.bounties :as bounties-db]
|
||||||
|
[commiteth.db.issues :as issues]
|
||||||
[commiteth.bounties :as bounties]
|
[commiteth.bounties :as bounties]
|
||||||
[commiteth.eth.core :as eth]
|
[commiteth.eth.core :as eth]
|
||||||
|
[commiteth.eth.tracker :as tracker]
|
||||||
[commiteth.github.core :as github]
|
[commiteth.github.core :as github]
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
[commiteth.config :refer [env]]
|
[commiteth.config :refer [env]]
|
||||||
[commiteth.util.util :refer [usd-decimal->str
|
[commiteth.util.util :refer [usd-decimal->str
|
||||||
eth-decimal->str]]
|
eth-decimal->str
|
||||||
|
to-db-map]]
|
||||||
[crypto.random :as random]
|
[crypto.random :as random]
|
||||||
[clojure.set :refer [rename-keys]]
|
[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? []
|
(defn add-bounties-for-existing-issues? []
|
||||||
(env :add-bounties-for-existing-issues false))
|
(env :add-bounties-for-existing-issues false))
|
||||||
|
@ -63,26 +68,14 @@
|
||||||
(def bounty-renames
|
(def bounty-renames
|
||||||
;; TODO this needs to go away ASAP we need to be super consistent
|
;; TODO this needs to go away ASAP we need to be super consistent
|
||||||
;; about keys unless we will just step on each others toes constantly
|
;; about keys unless we will just step on each others toes constantly
|
||||||
{:user_name :display-name
|
{:user-name :display-name
|
||||||
:user_avatar_url :avatar-url
|
:user-avatar-url :avatar-url
|
||||||
:issue_title :issue-title
|
:type :item-type})
|
||||||
:pr_title :pr-title
|
|
||||||
:pr_number :pr-number
|
|
||||||
:pr_id :pr-id
|
|
||||||
:type :item-type
|
|
||||||
:repo_name :repo-name
|
|
||||||
:repo_owner :repo-owner
|
|
||||||
:issue_number :issue-number
|
|
||||||
:issue_id :issue-id
|
|
||||||
:value_usd :value-usd
|
|
||||||
:claim_count :claim-count
|
|
||||||
:balance_eth :balance-eth
|
|
||||||
:user_has_address :user-has-address})
|
|
||||||
|
|
||||||
(defn ^:private enrich-owner-bounties [owner-bounty]
|
(defn ^:private enrich-owner-bounties [owner-bounty]
|
||||||
(let [claims (map
|
(let [claims (map
|
||||||
#(update % :value_usd usd-decimal->str)
|
#(update % :value-usd usd-decimal->str)
|
||||||
(bounties-db/bounty-claims (:issue_id owner-bounty)))
|
(bounties-db/bounty-claims (:issue-id owner-bounty)))
|
||||||
with-claims (assoc owner-bounty :claims claims)]
|
with-claims (assoc owner-bounty :claims claims)]
|
||||||
(-> with-claims
|
(-> with-claims
|
||||||
(rename-keys bounty-renames)
|
(rename-keys bounty-renames)
|
||||||
|
@ -98,9 +91,7 @@
|
||||||
(into {}))))
|
(into {}))))
|
||||||
|
|
||||||
(defn top-hunters []
|
(defn top-hunters []
|
||||||
(let [renames {:user_name :display-name
|
(let [renames {:user-name :display-name}]
|
||||||
:avatar_url :avatar-url
|
|
||||||
:total_usd :total-usd}]
|
|
||||||
(map #(-> %
|
(map #(-> %
|
||||||
(rename-keys renames)
|
(rename-keys renames)
|
||||||
(update :total-usd usd-decimal->str))
|
(update :total-usd usd-decimal->str))
|
||||||
|
@ -155,13 +146,25 @@
|
||||||
(let [whitelist (env :user-whitelist #{})]
|
(let [whitelist (env :user-whitelist #{})]
|
||||||
(whitelist user)))
|
(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 (bounties/execute-payout issue-id contract-address payout-address)]
|
||||||
|
(:tx-hash tx-info))
|
||||||
|
(catch Throwable ex
|
||||||
|
(log/errorf ex "error revoking funds for %s" issue-id))))
|
||||||
|
|
||||||
|
|
||||||
(defapi service-routes
|
(defapi service-routes
|
||||||
(when (:dev env)
|
(when (:dev env)
|
||||||
{:swagger {:ui "/swagger-ui"
|
{:swagger {:ui "/swagger-ui"
|
||||||
:spec "/swagger.json"
|
:spec "/swagger.json"
|
||||||
:data {:info {:version "0.1"
|
:data {:info {:version "0.1"
|
||||||
:title "commitETH API"
|
:title "commitETH API"
|
||||||
:description "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" []
|
(context "/api" []
|
||||||
(GET "/top-hunters" []
|
(GET "/top-hunters" []
|
||||||
|
@ -191,12 +194,12 @@
|
||||||
(POST "/" []
|
(POST "/" []
|
||||||
:auth-rules authenticated?
|
:auth-rules authenticated?
|
||||||
:current-user user
|
:current-user user
|
||||||
:body [body {:address s/Str
|
:body [body {:address s/Str
|
||||||
:is_hidden_in_hunters s/Bool}]
|
:is-hidden-in-hunters s/Bool}]
|
||||||
:summary "Updates user's fields."
|
:summary "Updates user's fields."
|
||||||
|
|
||||||
(let [user-id (:id user)
|
(let [user-id (:id user)
|
||||||
{:keys [address]} body]
|
{:keys [address is-hidden-in-hunters]} body]
|
||||||
|
|
||||||
(when-not (eth/valid-address? address)
|
(when-not (eth/valid-address? address)
|
||||||
(log/debugf "POST /user: Wrong address %s" address)
|
(log/debugf "POST /user: Wrong address %s" address)
|
||||||
|
@ -205,7 +208,8 @@
|
||||||
(db/with-tx
|
(db/with-tx
|
||||||
(when-not (db/user-exists? {:id user-id})
|
(when-not (db/user-exists? {:id user-id})
|
||||||
(not-found! "No such a user."))
|
(not-found! "No such a user."))
|
||||||
(db/update! :users body ["id = ?" user-id]))
|
(db/update! :users (to-db-map address is-hidden-in-hunters)
|
||||||
|
["id = ?" user-id]))
|
||||||
|
|
||||||
(ok)))
|
(ok)))
|
||||||
|
|
||||||
|
@ -226,10 +230,10 @@
|
||||||
(log/info "/bounty/X/payout" params)
|
(log/info "/bounty/X/payout" params)
|
||||||
(let [{issue :issue
|
(let [{issue :issue
|
||||||
payout-hash :payout-hash} params
|
payout-hash :payout-hash} params
|
||||||
result (bounties-db/update-payout-hash
|
result (bounties-db/update-payout-hash
|
||||||
(Integer/parseInt issue)
|
(Integer/parseInt issue)
|
||||||
payout-hash)]
|
payout-hash)]
|
||||||
(log/info "result" result)
|
(log/debug "result" result)
|
||||||
(if (= 1 result)
|
(if (= 1 result)
|
||||||
(ok)
|
(ok)
|
||||||
(internal-server-error)))))
|
(internal-server-error)))))
|
||||||
|
@ -237,4 +241,22 @@
|
||||||
:auth-rules authenticated?
|
:auth-rules authenticated?
|
||||||
:current-user user
|
:current-user user
|
||||||
(log/debug "/user/bounties")
|
(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 [{:keys [contract-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 [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))))))))
|
||||||
|
|
|
@ -125,8 +125,8 @@
|
||||||
:pr_number pr-number
|
:pr_number pr-number
|
||||||
:title pr-title
|
: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
|
||||||
|
@ -134,17 +134,17 @@
|
||||||
;; merged
|
;; merged
|
||||||
(cond
|
(cond
|
||||||
open-or-edit? (do
|
open-or-edit? (do
|
||||||
(log/infof "issue %s: PR with reference to bounty issue opened" (:issue_number issue))
|
(log/infof "issue %s: PR with reference to bounty issue opened" (:issue-number issue))
|
||||||
(pull-requests/save (merge pr-data {:state :opened
|
(pull-requests/save (merge pr-data {:state :opened
|
||||||
:commit_sha head-sha})))
|
:commit_sha head-sha})))
|
||||||
close? (if merged?
|
close? (if merged?
|
||||||
(do (log/infof "issue %s: PR with reference to bounty issue merged" (:issue_number issue))
|
(do (log/infof "issue %s: PR with reference to bounty issue merged" (:issue-number issue))
|
||||||
(pull-requests/save
|
(pull-requests/save
|
||||||
(merge pr-data {:state :merged
|
(merge pr-data {:state :merged
|
||||||
:commit_sha head-sha}))
|
:commit_sha head-sha}))
|
||||||
(issues/update-commit-sha (:issue_id issue) head-sha)
|
(issues/update-commit-sha (:issue-id issue) head-sha)
|
||||||
(db-bounties/update-winner-login (:issue_id issue) login))
|
(db-bounties/update-winner-login (:issue-id issue) login))
|
||||||
(do (log/infof "issue %s: PR with reference to bounty issue closed with no merge" (:issue_number issue))
|
(do (log/infof "issue %s: PR with reference to bounty issue closed with no merge" (:issue-number issue))
|
||||||
(pull-requests/save
|
(pull-requests/save
|
||||||
(merge pr-data {:state :closed
|
(merge pr-data {:state :closed
|
||||||
:commit_sha head-sha})))))))
|
:commit_sha head-sha})))))))
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
(doseq [issue issues]
|
(doseq [issue issues]
|
||||||
(if-not (:commit_sha issue) ; no PR has been merged yet referencing this issue
|
(if-not (:commit_sha issue) ; no PR has been merged yet referencing this issue
|
||||||
(do
|
(do
|
||||||
(log/info "Referenced bounty issue found" owner repo (:issue_number issue))
|
(log/info "Referenced bounty issue found" owner repo (:issue-number issue))
|
||||||
(handle-claim issue
|
(handle-claim issue
|
||||||
user-id
|
user-id
|
||||||
login name
|
login name
|
||||||
|
|
|
@ -2,16 +2,18 @@
|
||||||
(:require [commiteth.eth.core :as eth]
|
(:require [commiteth.eth.core :as eth]
|
||||||
[commiteth.eth.multisig-wallet :as multisig]
|
[commiteth.eth.multisig-wallet :as multisig]
|
||||||
[commiteth.eth.token-data :as token-data]
|
[commiteth.eth.token-data :as token-data]
|
||||||
[commiteth.eth.tracker :as tracker]
|
|
||||||
[commiteth.github.core :as github]
|
[commiteth.github.core :as github]
|
||||||
[commiteth.db.issues :as issues]
|
[commiteth.db.issues :as issues]
|
||||||
|
[commiteth.eth.tracker :as tracker]
|
||||||
|
[commiteth.util.util :refer [to-map]]
|
||||||
[taoensso.tufte :as tufte :refer (defnp p profiled profile)]
|
[taoensso.tufte :as tufte :refer (defnp p profiled profile)]
|
||||||
[commiteth.db.bounties :as db-bounties]
|
[commiteth.db.bounties :as db-bounties]
|
||||||
[commiteth.bounties :as bounties]
|
[commiteth.bounties :as bounties]
|
||||||
[commiteth.util.crypto-fiat-value :as fiat-util]
|
[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]
|
[clojure.tools.logging :as log]
|
||||||
[mount.core :as mount]
|
[mount.core :as mount]
|
||||||
|
[clojure.string :as str]
|
||||||
[clj-time.core :as t]
|
[clj-time.core :as t]
|
||||||
[clj-time.coerce :as time-coerce]
|
[clj-time.coerce :as time-coerce]
|
||||||
[clj-time.periodic :refer [periodic-seq]]
|
[clj-time.periodic :refer [periodic-seq]]
|
||||||
|
@ -30,12 +32,10 @@
|
||||||
(profile {} (update-watch-hash))
|
(profile {} (update-watch-hash))
|
||||||
(profile {} (update-payout-receipt))
|
(profile {} (update-payout-receipt))
|
||||||
(profile {} (update-contract-internal-balances))
|
(profile {} (update-contract-internal-balances))
|
||||||
(profile {} (update-open-issue-usd-values))
|
|
||||||
(profile {} (update-balances))
|
(profile {} (update-balances))
|
||||||
(profile {}
|
(profile {}
|
||||||
(doseq [i (range 5)]
|
(doseq [i (range 5)]
|
||||||
(update-contract-internal-balances)
|
(update-contract-internal-balances)
|
||||||
(update-open-issue-usd-values)
|
|
||||||
(update-balances)))
|
(update-balances)))
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -46,43 +46,16 @@
|
||||||
[]
|
[]
|
||||||
(log/info "In update-issue-contract-address")
|
(log/info "In update-issue-contract-address")
|
||||||
(p :update-issue-contract-address
|
(p :update-issue-contract-address
|
||||||
(doseq [{issue-id :issue_id
|
(doseq [{:keys [issue-id transaction-hash] :as issue} (issues/list-pending-deployments)]
|
||||||
transaction-hash :transaction_hash} (issues/list-pending-deployments)]
|
|
||||||
(log/infof "issue %s: pending deployment: %s" issue-id transaction-hash)
|
(log/infof "issue %s: pending deployment: %s" issue-id transaction-hash)
|
||||||
(try
|
(try
|
||||||
(when-let [receipt (eth/get-transaction-receipt transaction-hash)]
|
(when-let [receipt (eth/get-transaction-receipt transaction-hash)]
|
||||||
(log/infof "issue %s: update-issue-contract-address: tx receipt: %s" 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)]
|
(if-let [contract-address (multisig/find-created-multisig-address receipt)]
|
||||||
(let [_ (tracker/untrack-tx! {:issue-id issue-id
|
(bounties/transition (assoc issue :contract-address contract-address)
|
||||||
:tx-hash transaction-hash
|
:opened)
|
||||||
:result contract-address
|
|
||||||
:type :deploy})
|
|
||||||
{owner :owner
|
|
||||||
repo :repo
|
|
||||||
comment-id :comment_id
|
|
||||||
issue-number :issue_number} (issues/get-issue-by-id issue-id)
|
|
||||||
balance-eth-str (eth/get-balance-eth contract-address 6)
|
|
||||||
balance-eth (read-string balance-eth-str)]
|
|
||||||
(log/infof "issue %s: Updating comment image" issue-id)
|
|
||||||
(bounties/update-bounty-comment-image issue-id
|
|
||||||
owner
|
|
||||||
repo
|
|
||||||
issue-number
|
|
||||||
contract-address
|
|
||||||
balance-eth
|
|
||||||
balance-eth-str
|
|
||||||
{})
|
|
||||||
(log/infof "issue %s: Updating comment" issue-id)
|
|
||||||
(github/update-comment owner
|
|
||||||
repo
|
|
||||||
comment-id
|
|
||||||
issue-number
|
|
||||||
contract-address
|
|
||||||
balance-eth
|
|
||||||
balance-eth-str
|
|
||||||
{}))
|
|
||||||
(log/errorf "issue %s: Failed to find contract address in tx logs" issue-id)))
|
(log/errorf "issue %s: Failed to find contract address in tx logs" issue-id)))
|
||||||
(catch Throwable ex
|
(catch Throwable ex
|
||||||
(log/errorf ex "issue %s: update-issue-contract-address exception:" issue-id)))))
|
(log/errorf ex "issue %s: update-issue-contract-address exception:" issue-id)))))
|
||||||
(log/info "Exit update-issue-contract-address"))
|
(log/info "Exit update-issue-contract-address"))
|
||||||
|
|
||||||
|
@ -93,8 +66,8 @@
|
||||||
label is addded to an issue. This function deploys such contracts."
|
label is addded to an issue. This function deploys such contracts."
|
||||||
[]
|
[]
|
||||||
(p :deploy-pending-contracts
|
(p :deploy-pending-contracts
|
||||||
(doseq [{issue-id :issue_id
|
(doseq [{:keys [issue-id owner-address]}
|
||||||
owner-address :owner_address} (db-bounties/pending-contracts)]
|
(db-bounties/pending-contracts)]
|
||||||
(log/infof "issue %s: Trying to re-deploy failed bounty contract deployment" issue-id)
|
(log/infof "issue %s: Trying to re-deploy failed bounty contract deployment" issue-id)
|
||||||
(try
|
(try
|
||||||
(bounties/deploy-contract owner-address issue-id)
|
(bounties/deploy-contract owner-address issue-id)
|
||||||
|
@ -106,87 +79,69 @@
|
||||||
[]
|
[]
|
||||||
(log/info "In self-sign-bounty")
|
(log/info "In self-sign-bounty")
|
||||||
(p :self-sign-bounty
|
(p :self-sign-bounty
|
||||||
(doseq [{contract-address :contract_address
|
(doseq [{:keys [contract-address winner-address issue-id] :as issue}
|
||||||
issue-id :issue_id
|
(db-bounties/pending-bounties)]
|
||||||
payout-address :payout_address
|
|
||||||
repo :repo
|
|
||||||
owner :owner
|
|
||||||
comment-id :comment_id
|
|
||||||
issue-number :issue_number
|
|
||||||
balance-eth :balance_eth
|
|
||||||
tokens :tokens
|
|
||||||
winner-login :winner_login} (db-bounties/pending-bounties)]
|
|
||||||
(try
|
(try
|
||||||
;; TODO(martin) delete this shortly after org-dashboard deploy
|
;; TODO(martin) delete this shortly after org-dashboard deploy
|
||||||
;; as we're now setting `winner_login` when handling a new claims
|
;; as we're now setting `winner_login` when handling a new claims
|
||||||
;; coming in via webhooks (see `commiteth.routes.webhooks/handle-claim`)
|
;; coming in via webhooks (see `commiteth.routes.webhooks/handle-claim`)
|
||||||
(db-bounties/update-winner-login issue-id winner-login)
|
;(db-bounties/update-winner-login issue-id winner-login)
|
||||||
(let [value (eth/get-balance-hex contract-address)]
|
(bounties/execute-payout issue-id contract-address winner-address)
|
||||||
(if (empty? payout-address)
|
(catch Throwable ex
|
||||||
(do
|
|
||||||
(log/warn "issue %s: Cannot sign pending bounty - winner (%s) has no payout address" issue-id winner-login)
|
|
||||||
(github/update-merged-issue-comment owner
|
|
||||||
repo
|
|
||||||
comment-id
|
|
||||||
contract-address
|
|
||||||
(eth-decimal->str balance-eth)
|
|
||||||
tokens
|
|
||||||
winner-login
|
|
||||||
true))
|
|
||||||
(let [tx-info (multisig/send-all {:contract contract-address
|
|
||||||
:payout-address payout-address
|
|
||||||
:internal-tx-id [:execute issue-id]})]
|
|
||||||
(log/infof "issue %s: Payout self-signed, called sign-all(%s) tx: %s" issue-id contract-address payout-address (:tx-hash tx-info))
|
|
||||||
(tracker/track-tx! tx-info)
|
|
||||||
(github/update-merged-issue-comment owner
|
|
||||||
repo
|
|
||||||
comment-id
|
|
||||||
contract-address
|
|
||||||
(eth-decimal->str balance-eth)
|
|
||||||
tokens
|
|
||||||
winner-login
|
|
||||||
false))))
|
|
||||||
(catch Throwable ex
|
|
||||||
(log/error ex "issue %s: self-sign-bounty exception" issue-id)))))
|
(log/error ex "issue %s: self-sign-bounty exception" issue-id)))))
|
||||||
(log/info "Exit self-sign-bounty"))
|
(log/info "Exit self-sign-bounty"))
|
||||||
|
|
||||||
(defn update-confirm-hash
|
(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."
|
"Gets transaction receipt for each pending payout and updates DB confirm_hash with tranaction ID of commiteth bot account's confirmation."
|
||||||
[]
|
[issue-id execute-hash]
|
||||||
(log/info "In update-confirm-hash")
|
(log/info "In update-confirm-hash")
|
||||||
(p :update-confirm-hash
|
(p :update-confirm-hash
|
||||||
(doseq [{issue-id :issue_id
|
(try
|
||||||
execute-hash :execute_hash} (db-bounties/pending-payouts)]
|
|
||||||
(log/infof "issue %s: pending payout: %s" 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)]
|
||||||
(when-let [receipt (eth/get-transaction-receipt execute-hash)]
|
(log/infof "issue %s: execution receipt for issue " issue-id receipt)
|
||||||
(log/infof "issue %s: execution receipt for issue " issue-id receipt)
|
(when-let [confirm-hash (multisig/find-confirmation-tx-id receipt)]
|
||||||
(when-let [confirm-hash (multisig/find-confirmation-tx-id receipt)]
|
(log/infof "issue %s: confirm hash: %s" issue-id confirm-hash)
|
||||||
(log/infof "issue %s: confirm hash:" issue-id confirm-hash)
|
(bounties/transition {:issue-id issue-id
|
||||||
(tracker/untrack-tx! {:issue-id issue-id
|
:confirm-hash confirm-hash
|
||||||
:tx-hash execute-hash
|
:tx-info {:issue-id issue-id
|
||||||
:result confirm-hash
|
:tx-hash execute-hash
|
||||||
:type :execute})))
|
:result confirm-hash
|
||||||
(catch Throwable ex
|
:type :execute}}
|
||||||
(log/errorf ex "issue %s: update-confirm-hash exception:" issue-id)))))
|
:pending-maintainer-confirmation)
|
||||||
(log/info "Exit update-confirm-hash"))
|
|
||||||
|
|
||||||
|
))
|
||||||
|
(catch Throwable ex
|
||||||
|
(log/errorf ex "issue %s: update-confirm-hash exception:" issue-id)))
|
||||||
|
(log/info "Exit update-confirm-hash")))
|
||||||
|
|
||||||
|
(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 [{:keys [issue-id execute-hash]} (db-bounties/pending-payouts)]
|
||||||
|
|
||||||
|
(update-confirm-hash issue-id execute-hash)))
|
||||||
|
(log/info "Exit update-confirm-hashes"))
|
||||||
|
|
||||||
(defn update-watch-hash
|
(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"
|
"Sets watch-hash to NULL for bounties where watch tx has been mined. Used to avoid unneeded watch transactions in update-bounty-token-balances"
|
||||||
[]
|
[]
|
||||||
(p :update-watch-hash
|
(p :update-watch-hash
|
||||||
(doseq [{issue-id :issue_id
|
(doseq [{:keys [issue-id watch-hash]} (db-bounties/pending-watch-calls)]
|
||||||
watch-hash :watch_hash} (db-bounties/pending-watch-calls)]
|
|
||||||
(log/infof "issue %s: pending watch call %s" issue-id watch-hash)
|
(log/infof "issue %s: pending watch call %s" issue-id watch-hash)
|
||||||
(try
|
(try
|
||||||
(when-let [receipt (eth/get-transaction-receipt watch-hash)]
|
(when-let [receipt (eth/get-transaction-receipt watch-hash)]
|
||||||
(tracker/untrack-tx! {:issue-id issue-id
|
(bounties/transition {:issue-id issue-id
|
||||||
:tx-hash watch-hash
|
:tx-info
|
||||||
:result nil
|
{:issue-id issue-id
|
||||||
:type :watch}))
|
:tx-hash watch-hash
|
||||||
|
:result nil
|
||||||
|
:type :watch}} :watch-reset))
|
||||||
(catch Throwable ex
|
(catch Throwable ex
|
||||||
(log/errorf ex "issue %s: update-watch-hash exception:" issue-id))))))
|
(log/errorf ex "issue %s: update-watch-hash exception:" issue-id))
|
||||||
|
))))
|
||||||
|
|
||||||
|
|
||||||
(defn older-than-3h?
|
(defn older-than-3h?
|
||||||
|
@ -199,52 +154,51 @@
|
||||||
|
|
||||||
(defn update-payout-receipt
|
(defn update-payout-receipt
|
||||||
"Gets transaction receipt for each confirmed payout and updates payout_hash"
|
"Gets transaction receipt for each confirmed payout and updates payout_hash"
|
||||||
[]
|
[{:keys [payout-hash contract-address confirm-hash issue-id updated] :as bounty}]
|
||||||
|
{:pre [(util/contains-all-keys bounty db-bounties/payout-receipt-keys)]}
|
||||||
(log/info "In update-payout-receipt")
|
(log/info "In update-payout-receipt")
|
||||||
(p :update-payout-receipt
|
(p :update-payout-receipt
|
||||||
(doseq [{issue-id :issue_id
|
(try
|
||||||
payout-hash :payout_hash
|
(log/infof "issue %s: confirmed payout: %s" issue-id payout-hash)
|
||||||
contract-address :contract_address
|
(if-let [receipt (eth/get-transaction-receipt payout-hash)]
|
||||||
repo :repo
|
(let [contract-tokens (multisig/token-balances contract-address)
|
||||||
owner :owner
|
contract-eth-balance (eth/get-balance-wei contract-address)]
|
||||||
comment-id :comment_id
|
(if (or
|
||||||
issue-number :issue_number
|
(some #(> (second %) 0.0) contract-tokens)
|
||||||
balance-eth :balance_eth
|
(> contract-eth-balance 0))
|
||||||
tokens :tokens
|
(do
|
||||||
confirm-id :confirm_hash
|
(log/infof "issue %s: Contract (%s) still has funds" issue-id contract-address)
|
||||||
payee-login :payee_login
|
(when (multisig/is-confirmed? contract-address confirm-hash)
|
||||||
updated :updated} (db-bounties/confirmed-payouts)]
|
(log/infof "issue %s: Detected bounty with funds and confirmed payout, calling executeTransaction" issue-id)
|
||||||
(log/infof "issue %s: confirmed payout: %s" issue-id payout-hash)
|
(let [execute-tx-hash (multisig/execute-tx contract-address confirm-hash)]
|
||||||
(try
|
(log/infof "issue %s: execute tx: %s" issue-id execute-tx-hash))))
|
||||||
(if-let [receipt (eth/get-transaction-receipt payout-hash)]
|
(do
|
||||||
(let [contract-tokens (multisig/token-balances contract-address)
|
(log/infof "issue %s: Payout has succeeded, payout receipt %s" issue-id receipt)
|
||||||
contract-eth-balance (eth/get-balance-wei contract-address)]
|
(bounties/transition (assoc bounty :payout-receipt receipt) :paid))))
|
||||||
(if (or
|
(when (older-than-3h? updated)
|
||||||
(some #(> (second %) 0.0) contract-tokens)
|
(log/warn "issue %s: Resetting payout hash for issue as it has not been mined in 3h" issue-id)
|
||||||
(> contract-eth-balance 0))
|
(db-bounties/reset-payout-hash issue-id)))
|
||||||
(do
|
(catch Throwable ex
|
||||||
(log/infof "issue %s: Contract (%s) still has funds" issue-id contract-address)
|
(log/error ex "issue %s: update-payout-receipt exception" issue-id)))))
|
||||||
(when (multisig/is-confirmed? contract-address confirm-id)
|
|
||||||
(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)]
|
|
||||||
(log/infof "issue %s: execute tx: %s" issue-id execute-tx-hash))))
|
|
||||||
|
|
||||||
(do
|
(defn update-payout-receipts
|
||||||
(log/infof "issue %s: Payout has succeeded, payout receipt %s" issue-id receipt)
|
"Gets transaction receipt for each confirmed payout and updates payout_hash"
|
||||||
(db-bounties/update-payout-receipt issue-id receipt)
|
[]
|
||||||
(github/update-paid-issue-comment owner
|
(log/info "In update-payout-receipts")
|
||||||
repo
|
(p :update-payout-receipts
|
||||||
comment-id
|
(doseq [bounty (db-bounties/confirmed-payouts)]
|
||||||
contract-address
|
(update-payout-receipt bounty))
|
||||||
(eth-decimal->str balance-eth)
|
(log/info "Exit update-payout-receipts")))
|
||||||
tokens
|
|
||||||
payee-login))))
|
(defn update-revoked-payout-receipts
|
||||||
(when (older-than-3h? updated)
|
"Gets transaction receipt for each confirmed revocation and updates payout_hash"
|
||||||
(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)))
|
(log/info "In update-revoked-payout-receipts")
|
||||||
(catch Throwable ex
|
(p :update-revoked-payout-receipts
|
||||||
(log/error ex "issue %s: update-payout-receipt exception" issue-id)))))
|
;; todo see if confirmed-payouts & confirmed-revocation-payouts can be combined
|
||||||
(log/info "Exit update-payout-receipt"))
|
(doseq [bounty (db-bounties/confirmed-revocation-payouts)]
|
||||||
|
(update-payout-receipt bounty))
|
||||||
|
(log/info "Exit update-revoked-payout-receipts")))
|
||||||
|
|
||||||
(defn abs
|
(defn abs
|
||||||
"(abs n) is the absolute value of n"
|
"(abs n) is the absolute value of n"
|
||||||
|
@ -255,7 +209,6 @@
|
||||||
(neg? n) (- n)
|
(neg? n) (- n)
|
||||||
:else n))
|
:else n))
|
||||||
|
|
||||||
|
|
||||||
(defn update-bounty-token-balances
|
(defn update-bounty-token-balances
|
||||||
"Helper function for updating internal ERC20 token balances to token
|
"Helper function for updating internal ERC20 token balances to token
|
||||||
multisig contract. Will be called periodically for all open bounty
|
multisig contract. Will be called periodically for all open bounty
|
||||||
|
@ -275,7 +228,9 @@
|
||||||
(let [tx-info (multisig/watch-token {:bounty-addr bounty-addr
|
(let [tx-info (multisig/watch-token {:bounty-addr bounty-addr
|
||||||
:token tla
|
:token tla
|
||||||
:internal-tx-id [:watch issue-id]})]
|
:internal-tx-id [:watch issue-id]})]
|
||||||
(tracker/track-tx! tx-info)))))))
|
(bounties/transition {:issue-id issue-id
|
||||||
|
:tx-info tx-info}
|
||||||
|
:watch-set)))))))
|
||||||
(catch Throwable ex
|
(catch Throwable ex
|
||||||
(log/error ex "bounty %s: update-bounty-token-balances exception" bounty-addr))))
|
(log/error ex "bounty %s: update-bounty-token-balances exception" bounty-addr))))
|
||||||
(log/info "Exit update-bounty-token-balances"))
|
(log/info "Exit update-bounty-token-balances"))
|
||||||
|
@ -286,39 +241,11 @@
|
||||||
[]
|
[]
|
||||||
(log/info "In update-contract-internal-balances")
|
(log/info "In update-contract-internal-balances")
|
||||||
(p :update-contract-internal-balances
|
(p :update-contract-internal-balances
|
||||||
(doseq [{issue-id :issue_id
|
(doseq [{:keys [issue-id contract-address watch-hash]}
|
||||||
bounty-address :contract_address
|
|
||||||
watch-hash :watch_hash}
|
|
||||||
(db-bounties/open-bounty-contracts)]
|
(db-bounties/open-bounty-contracts)]
|
||||||
(update-bounty-token-balances issue-id bounty-address watch-hash)))
|
(update-bounty-token-balances issue-id contract-address watch-hash)))
|
||||||
(log/info "Exit update-contract-internal-balances"))
|
(log/info "Exit update-contract-internal-balances"))
|
||||||
|
|
||||||
(defn get-bounty-funds
|
|
||||||
"Get funds in given bounty contract.
|
|
||||||
Returns map of asset -> balance
|
|
||||||
+ key total-usd -> current total USD value for all funds"
|
|
||||||
[bounty-addr]
|
|
||||||
(let [token-balances (multisig/token-balances bounty-addr)
|
|
||||||
eth-balance (read-string (eth/get-balance-eth bounty-addr 6))
|
|
||||||
all-funds
|
|
||||||
(merge token-balances
|
|
||||||
{:ETH eth-balance})]
|
|
||||||
(merge all-funds {:total-usd (fiat-util/bounty-usd-value all-funds)})))
|
|
||||||
|
|
||||||
|
|
||||||
(defn update-issue-usd-value
|
|
||||||
[bounty-addr]
|
|
||||||
(let [funds (get-bounty-funds bounty-addr)]
|
|
||||||
(issues/update-usd-value bounty-addr
|
|
||||||
(:total-usd funds))))
|
|
||||||
|
|
||||||
(defn update-open-issue-usd-values
|
|
||||||
"Sum up current USD values of all crypto assets in a bounty and store to DB"
|
|
||||||
[]
|
|
||||||
(p :update-open-issue-usd-values
|
|
||||||
(doseq [{bounty-addr :contract_address}
|
|
||||||
(db-bounties/open-bounty-contracts)]
|
|
||||||
(update-issue-usd-value bounty-addr))))
|
|
||||||
|
|
||||||
(defn float=
|
(defn float=
|
||||||
([x y] (float= x y 0.0000001))
|
([x y] (float= x y 0.0000001))
|
||||||
|
@ -331,58 +258,45 @@
|
||||||
(and (= (set (keys m1)) (set (keys m2)))
|
(and (= (set (keys m1)) (set (keys m2)))
|
||||||
(every? #(float= (get m1 %1) (get m2 %1)) (keys m1))))
|
(every? #(float= (get m1 %1) (get m2 %1)) (keys m1))))
|
||||||
|
|
||||||
|
|
||||||
(defn update-balances
|
(defn update-balances
|
||||||
[]
|
[]
|
||||||
(log/info "In update-balances")
|
(log/info "In update-balances")
|
||||||
(p :update-balances
|
(p :update-balances
|
||||||
(doseq [{contract-address :contract_address
|
(doseq [{:keys [contract-address owner
|
||||||
owner :owner
|
repo balance-eth tokens
|
||||||
repo :repo
|
issue-id
|
||||||
comment-id :comment_id
|
issue-number
|
||||||
issue-id :issue_id
|
comment-id] :as issue}
|
||||||
db-balance-eth :balance_eth
|
(db-bounties/open-bounty-contracts)]
|
||||||
db-tokens :tokens
|
(try
|
||||||
issue-number :issue_number} (db-bounties/open-bounty-contracts)]
|
(when comment-id
|
||||||
(try
|
(let [balance-eth-str (eth/get-balance-eth contract-address 6)
|
||||||
(when comment-id
|
current-balance-eth (read-string balance-eth-str)
|
||||||
(let [balance-eth-str (eth/get-balance-eth contract-address 6)
|
token-balances (multisig/token-balances contract-address)]
|
||||||
balance-eth (read-string balance-eth-str)
|
(log/debug "update-balances" balance-eth
|
||||||
token-balances (multisig/token-balances contract-address)]
|
balance-eth-str token-balances owner repo issue-number)
|
||||||
(log/debug "issue" issue-id ": update-balances" balance-eth
|
|
||||||
balance-eth-str token-balances owner repo issue-number)
|
|
||||||
|
|
||||||
(when (or
|
(when (or
|
||||||
(not (float= db-balance-eth balance-eth))
|
(not (float= current-balance-eth balance-eth))
|
||||||
(not (map-float= db-tokens token-balances)))
|
(not (map-float= tokens token-balances)))
|
||||||
(log/info "balances differ")
|
(log/info "balances differ")
|
||||||
(log/info "ETH (db):" db-balance-eth (type db-balance-eth) )
|
(log/info "ETH (db):" balance-eth (type balance-eth) )
|
||||||
(log/info "ETH (chain):" balance-eth (type balance-eth) )
|
(log/info "ETH (chain):" current-balance-eth (type current-balance-eth) )
|
||||||
(log/info "ETH cmp:" (float= db-balance-eth balance-eth))
|
(log/info "ETH cmp:" (float= balance-eth current-balance-eth))
|
||||||
(log/info "tokens (db):" db-tokens (type db-tokens) (type (:SNT db-tokens)))
|
(log/info "tokens (db):" tokens (type tokens) (type (:SNT tokens)))
|
||||||
(log/info "tokens (chain):" token-balances (type token-balances) (type (:SNT token-balances)))
|
(log/info "tokens (chain):" token-balances (type token-balances) (type (:SNT token-balances)))
|
||||||
(log/debug "tokens cmp:" (= db-tokens token-balances))
|
(log/debug "tokens cmp:" (= tokens token-balances))
|
||||||
|
(bounties/transition {:issue-id issue-id
|
||||||
|
:balance-eth current-balance-eth
|
||||||
|
:tokens token-balances
|
||||||
|
:value-usd (fiat-util/bounty-usd-value
|
||||||
|
(merge token-balances {:ETH current-balance-eth}))} :update-balances)
|
||||||
|
|
||||||
(issues/update-eth-balance contract-address balance-eth)
|
|
||||||
(issues/update-token-balances contract-address token-balances)
|
)))
|
||||||
(bounties/update-bounty-comment-image issue-id
|
(catch Throwable ex
|
||||||
owner
|
(log/error ex "issue %s: update-balances exception" issue-id)))))
|
||||||
repo
|
|
||||||
issue-number
|
|
||||||
contract-address
|
|
||||||
balance-eth
|
|
||||||
balance-eth-str
|
|
||||||
token-balances)
|
|
||||||
(github/update-comment owner
|
|
||||||
repo
|
|
||||||
comment-id
|
|
||||||
issue-number
|
|
||||||
contract-address
|
|
||||||
balance-eth
|
|
||||||
balance-eth-str
|
|
||||||
token-balances)
|
|
||||||
(update-issue-usd-value contract-address))))
|
|
||||||
(catch Throwable ex
|
|
||||||
(log/error ex "issue %s: update-balances exception" issue-id)))))
|
|
||||||
(log/info "Exit update-balances"))
|
(log/info "Exit update-balances"))
|
||||||
|
|
||||||
(defn check-tx-receipts
|
(defn check-tx-receipts
|
||||||
|
@ -412,8 +326,9 @@
|
||||||
(run-tasks
|
(run-tasks
|
||||||
[deploy-pending-contracts
|
[deploy-pending-contracts
|
||||||
update-issue-contract-address
|
update-issue-contract-address
|
||||||
update-confirm-hash
|
update-confirm-hashes
|
||||||
update-payout-receipt
|
update-payout-receipts
|
||||||
|
update-revoked-payout-receipts
|
||||||
update-watch-hash
|
update-watch-hash
|
||||||
check-tx-receipts
|
check-tx-receipts
|
||||||
self-sign-bounty
|
self-sign-bounty
|
||||||
|
@ -426,8 +341,7 @@
|
||||||
(log/info "run-10-min-interval-tasks" time)
|
(log/info "run-10-min-interval-tasks" time)
|
||||||
(run-tasks
|
(run-tasks
|
||||||
[update-contract-internal-balances
|
[update-contract-internal-balances
|
||||||
update-balances
|
update-balances])
|
||||||
update-open-issue-usd-values])
|
|
||||||
(log/info "run-10-min-interval-tasks done")))
|
(log/info "run-10-min-interval-tasks done")))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
(ns commiteth.util.png-rendering
|
(ns commiteth.util.png-rendering
|
||||||
(:require [commiteth.layout :refer [render]]
|
(:require [commiteth.layout :refer [render]]
|
||||||
[commiteth.config :refer [env]]
|
[commiteth.config :refer [env]]
|
||||||
[commiteth.github.core :as github]
|
|
||||||
[commiteth.db.comment-images :as db]
|
[commiteth.db.comment-images :as db]
|
||||||
[commiteth.db.bounties :as db-bounties]
|
[commiteth.db.bounties :as db-bounties]
|
||||||
[clj.qrgen :as qr]
|
[clj.qrgen :as qr]
|
||||||
|
@ -58,21 +57,6 @@
|
||||||
nil))))
|
nil))))
|
||||||
|
|
||||||
|
|
||||||
(defn export-comment-image
|
|
||||||
"Retrieve image PNG from DB and write to file"
|
|
||||||
[owner repo issue-number filename]
|
|
||||||
(let [{owner :owner
|
|
||||||
repo :repo
|
|
||||||
issue-id :issue_id
|
|
||||||
balance-eth :balance_eth} (db-bounties/get-bounty owner repo issue-number)
|
|
||||||
hash (github/github-comment-hash
|
|
||||||
owner
|
|
||||||
repo
|
|
||||||
issue-number
|
|
||||||
balance-eth)]
|
|
||||||
(with-open [w (io/output-stream filename)]
|
|
||||||
(.write w (:png_data (db/get-image-data issue-id hash))))))
|
|
||||||
|
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(with-open [w (io/output-stream "foo.png")]
|
(with-open [w (io/output-stream "foo.png")]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns commiteth.util.util
|
(ns commiteth.util.util
|
||||||
(:require
|
(:require
|
||||||
[clj-http.client :as http]
|
[clj-http.client :as http]
|
||||||
|
[clojure.string :as str]
|
||||||
[clojure.data.json :as json]))
|
[clojure.data.json :as json]))
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,3 +15,14 @@
|
||||||
(->> (http/get url)
|
(->> (http/get url)
|
||||||
(:body)
|
(:body)
|
||||||
(json/read-str)))
|
(json/read-str)))
|
||||||
|
|
||||||
|
(defmacro to-map [& vars]
|
||||||
|
(into {} (map #(vector (keyword %1) %1) vars)))
|
||||||
|
|
||||||
|
(defmacro to-db-map [& vars]
|
||||||
|
(into {} (map #(vector (keyword (str/replace (name %1) "-" "_")) %1) vars)))
|
||||||
|
|
||||||
|
(defn contains-all-keys [m ks]
|
||||||
|
{:pre [(map? m) [(vector? ks)]]}
|
||||||
|
(every?
|
||||||
|
#(contains? m %) ks))
|
||||||
|
|
|
@ -11,20 +11,20 @@
|
||||||
;; to communicate what datatypes are returned where.
|
;; to communicate what datatypes are returned where.
|
||||||
|
|
||||||
(defn open? [claim]
|
(defn open? [claim]
|
||||||
(assert (find claim :pr_state))
|
(assert (find claim :pr-state))
|
||||||
(= 0 (:pr_state claim)))
|
(= 0 (:pr-state claim)))
|
||||||
|
|
||||||
(defn merged? [claim]
|
(defn merged? [claim]
|
||||||
(assert (find claim :pr_state))
|
(assert (find claim :pr-state))
|
||||||
(= 1 (:pr_state claim)))
|
(= 1 (:pr-state claim)))
|
||||||
|
|
||||||
(defn paid? [claim]
|
(defn paid? [claim]
|
||||||
(assert (find claim :payout_hash))
|
(assert (find claim :payout-hash))
|
||||||
(not-empty (:payout_hash claim)))
|
(not-empty (:payout-hash claim)))
|
||||||
|
|
||||||
(defn bot-confirm-unmined? [bounty]
|
(defn bot-confirm-unmined? [bounty]
|
||||||
(assert (find bounty :confirm_hash))
|
(assert (find bounty :confirm-hash))
|
||||||
(empty? (:confirm_hash bounty)))
|
(empty? (:confirm-hash bounty)))
|
||||||
|
|
||||||
(defn confirming? [bounty]
|
(defn confirming? [bounty]
|
||||||
(:confirming? bounty))
|
(:confirming? bounty))
|
||||||
|
|
|
@ -5,6 +5,14 @@
|
||||||
{:pre [(string? tla)]}
|
{:pre [(string? tla)]}
|
||||||
(get {"ETH" "#57a7ed"} tla "#4360df"))
|
(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
|
(defn balance-badge
|
||||||
[tla balance]
|
[tla balance]
|
||||||
{:pre [(keyword? tla)]}
|
{:pre [(keyword? tla)]}
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
(defn bounty-item [bounty]
|
(defn bounty-item [bounty]
|
||||||
(let [open-bounty-claims (rf/subscribe [::subs/open-bounty-claims])]
|
(let [open-bounty-claims (rf/subscribe [::subs/open-bounty-claims])]
|
||||||
(fn [bounty]
|
(fn [bounty]
|
||||||
(let [{avatar-url :repo_owner_avatar_url
|
(let [{avatar-url :repo-owner-avatar-url
|
||||||
owner :repo-owner
|
owner :repo-owner
|
||||||
repo-name :repo-name
|
repo-name :repo-name
|
||||||
issue-title :issue-title
|
issue-title :issue-title
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
[commiteth.routes]
|
[commiteth.routes]
|
||||||
[commiteth.handlers]
|
[commiteth.handlers]
|
||||||
[commiteth.subscriptions]
|
[commiteth.subscriptions]
|
||||||
|
[commiteth.interceptors]
|
||||||
[commiteth.activity :refer [activity-page]]
|
[commiteth.activity :refer [activity-page]]
|
||||||
[commiteth.bounties :refer [bounties-page]]
|
[commiteth.bounties :refer [bounties-page]]
|
||||||
[commiteth.repos :refer [repos-page]]
|
[commiteth.repos :refer [repos-page]]
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
::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 #{}
|
::open-bounty-claims #{}
|
||||||
|
::pending-revocations {}
|
||||||
:owner-bounties {}
|
:owner-bounties {}
|
||||||
:top-hunters []
|
:top-hunters []
|
||||||
:activity-feed []})
|
:activity-feed []})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns commiteth.handlers
|
(ns commiteth.handlers
|
||||||
(:require [commiteth.db :as db]
|
(:require [commiteth.db :as db]
|
||||||
[re-frame.core :refer [dispatch
|
[re-frame.core :refer [debug
|
||||||
|
dispatch
|
||||||
reg-event-db
|
reg-event-db
|
||||||
reg-event-fx
|
reg-event-fx
|
||||||
reg-fx
|
reg-fx
|
||||||
|
@ -14,12 +15,16 @@
|
||||||
:refer [reg-co-fx!]]
|
:refer [reg-co-fx!]]
|
||||||
[commiteth.ui-model :as ui-model]
|
[commiteth.ui-model :as ui-model]
|
||||||
[commiteth.common :as common]
|
[commiteth.common :as common]
|
||||||
[commiteth.routes :as routes]))
|
[commiteth.routes :as routes]
|
||||||
|
[commiteth.interceptors]))
|
||||||
|
|
||||||
|
|
||||||
(rf-storage/reg-co-fx! :commiteth-sob {:fx :store
|
(rf-storage/reg-co-fx! :commiteth-sob {:fx :store
|
||||||
:cofx :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
|
(reg-fx
|
||||||
:http
|
:http
|
||||||
(fn [{:keys [method url on-success on-error finally params]}]
|
(fn [{:keys [method url on-success on-error finally params]}]
|
||||||
|
@ -110,6 +115,16 @@
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(dissoc db :flash-message)))
|
(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]
|
(defn assoc-in-if-not-empty [m path val]
|
||||||
(if (seq val)
|
(if (seq val)
|
||||||
(assoc-in m path val)
|
(assoc-in m path val)
|
||||||
|
@ -217,6 +232,8 @@
|
||||||
|
|
||||||
(reg-event-db
|
(reg-event-db
|
||||||
:set-owner-bounties
|
:set-owner-bounties
|
||||||
|
[commiteth.interceptors/watch-confirm-hash
|
||||||
|
commiteth.interceptors/watch-payout-receipt]
|
||||||
(fn [db [_ issues]]
|
(fn [db [_ issues]]
|
||||||
(assoc db
|
(assoc db
|
||||||
:owner-bounties issues
|
:owner-bounties issues
|
||||||
|
@ -376,6 +393,7 @@
|
||||||
|
|
||||||
(reg-event-fx
|
(reg-event-fx
|
||||||
:save-payout-hash
|
:save-payout-hash
|
||||||
|
interceptors
|
||||||
(fn [{:keys [db]} [_ issue-id payout-hash]]
|
(fn [{:keys [db]} [_ issue-id payout-hash]]
|
||||||
{:db db
|
{:db db
|
||||||
:http {:method POST
|
:http {:method POST
|
||||||
|
@ -386,13 +404,15 @@
|
||||||
|
|
||||||
|
|
||||||
(defn send-transaction-callback
|
(defn send-transaction-callback
|
||||||
[issue-id]
|
[issue-id pending-revocations]
|
||||||
(fn [error payout-hash]
|
(fn [error payout-hash]
|
||||||
(println "send-transaction-callback" error payout-hash)
|
(println "send-transaction-callback" error payout-hash)
|
||||||
(when error
|
(when error
|
||||||
(dispatch [:set-flash-message
|
(if (empty? pending-revocations)
|
||||||
:error
|
(dispatch [:set-flash-message
|
||||||
(str "Error sending transaction: " error)])
|
:error
|
||||||
|
(str "Error sending transaction: " error)])
|
||||||
|
(dispatch [:remove-bot-confirmation issue-id]))
|
||||||
(dispatch [:payout-confirm-failed issue-id]))
|
(dispatch [:payout-confirm-failed issue-id]))
|
||||||
(when payout-hash
|
(when payout-hash
|
||||||
(dispatch [:save-payout-hash issue-id payout-hash]))))
|
(dispatch [:save-payout-hash issue-id payout-hash]))))
|
||||||
|
@ -405,14 +425,73 @@
|
||||||
(defn strip-0x [x]
|
(defn strip-0x [x]
|
||||||
(str/replace x #"^0x" ""))
|
(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
|
(reg-event-fx
|
||||||
:confirm-payout
|
:confirm-payout
|
||||||
(fn [{:keys [db]} [_ {issue-id :issue_id
|
interceptors
|
||||||
owner-address :owner_address
|
(fn [{:keys [db]} [_ {:keys [issue-id
|
||||||
contract-address :contract_address
|
owner-address
|
||||||
confirm-hash :confirm_hash} issue]]
|
contract-address
|
||||||
|
confirm-hash]
|
||||||
|
:as issue}]]
|
||||||
(println (:web3 db))
|
(println (:web3 db))
|
||||||
(let [w3 (:web3 db)
|
(let [w3 (:web3 db)
|
||||||
|
pending-revocations (::db/pending-revocations db)
|
||||||
confirm-method-id (sig->method-id w3 "confirmTransaction(uint256)")
|
confirm-method-id (sig->method-id w3 "confirmTransaction(uint256)")
|
||||||
confirm-id (strip-0x confirm-hash)
|
confirm-id (strip-0x confirm-hash)
|
||||||
data (str confirm-method-id
|
data (str confirm-method-id
|
||||||
|
@ -426,7 +505,7 @@
|
||||||
(println "data:" data)
|
(println "data:" data)
|
||||||
(try
|
(try
|
||||||
(web3-eth/send-transaction! w3 payload
|
(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)}
|
{:db (assoc-in db [:owner-bounties issue-id :confirming?] true)}
|
||||||
(catch js/Error e
|
(catch js/Error e
|
||||||
{:db (assoc-in db [:owner-bounties issue-id :confirm-failed?] true)
|
{:db (assoc-in db [:owner-bounties issue-id :confirm-failed?] true)
|
||||||
|
@ -437,6 +516,7 @@
|
||||||
|
|
||||||
(reg-event-fx
|
(reg-event-fx
|
||||||
:payout-confirmed
|
:payout-confirmed
|
||||||
|
interceptors
|
||||||
(fn [{:keys [db]} [_ issue-id]]
|
(fn [{:keys [db]} [_ issue-id]]
|
||||||
{:dispatch [:load-owner-bounties]
|
{:dispatch [:load-owner-bounties]
|
||||||
:db (-> db
|
:db (-> db
|
||||||
|
@ -490,6 +570,21 @@
|
||||||
(.removeEventListener js/window "click" close-dropdown)
|
(.removeEventListener js/window "click" close-dropdown)
|
||||||
(assoc db :user-dropdown-open? false)))
|
(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
|
(reg-event-db
|
||||||
::open-bounty-claim
|
::open-bounty-claim
|
||||||
(fn [db [_ opening-issue-id]]
|
(fn [db [_ opening-issue-id]]
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
(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 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,13 +6,24 @@
|
||||||
[commiteth.routes :as routes]
|
[commiteth.routes :as routes]
|
||||||
[commiteth.model.bounty :as bnt]
|
[commiteth.model.bounty :as bnt]
|
||||||
[commiteth.ui.balances :as ui-balances]
|
[commiteth.ui.balances :as ui-balances]
|
||||||
|
[commiteth.config :as config]
|
||||||
[commiteth.common :as common :refer [human-time]]))
|
[commiteth.common :as common :refer [human-time]]))
|
||||||
|
|
||||||
(defn pr-url [{owner :repo_owner
|
(defn pr-url [{owner :repo-owner
|
||||||
pr-number :pr_number
|
pr-number :pr-number
|
||||||
repo :repo_name}]
|
repo :repo-name}]
|
||||||
(str "https://github.com/" owner "/" repo "/pull/" pr-number))
|
(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-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)
|
(def primary-button-link :a.dib.tc.f7.ttu.tracked.bg-sob-blue.white.pv2.ph3.pg-med.br2.pointer.hover-white.shadow-7)
|
||||||
|
|
||||||
|
@ -46,7 +57,7 @@
|
||||||
(when (and merged? (not paid?))
|
(when (and merged? (not paid?))
|
||||||
[primary-button-button
|
[primary-button-button
|
||||||
(merge {:on-click #(rf/dispatch [:confirm-payout claim])}
|
(merge {:on-click #(rf/dispatch [:confirm-payout claim])}
|
||||||
(if (and merged? (not paid?) (:payout_address bounty))
|
(if (and merged? (not paid?) (:payout-address bounty))
|
||||||
{}
|
{}
|
||||||
{:disabled true})
|
{:disabled true})
|
||||||
(when (and (or (bnt/confirming? bounty)
|
(when (and (or (bnt/confirming? bounty)
|
||||||
|
@ -57,12 +68,14 @@
|
||||||
"Signed off"
|
"Signed off"
|
||||||
"Confirm Payment")])))
|
"Confirm Payment")])))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn confirm-row [bounty claim]
|
(defn confirm-row [bounty claim]
|
||||||
(let [payout-address-available? (:payout_address bounty)]
|
(let [payout-address-available? (:payout-address bounty)]
|
||||||
[:div
|
[:div
|
||||||
(when-not payout-address-available?
|
(when-not payout-address-available?
|
||||||
[:div.bg-sob-blue-o-20.pv2.ph3.br3.mb3.f6
|
[:div.bg-sob-blue-o-20.pv2.ph3.br3.mb3.f6
|
||||||
[:p [:span.pg-med (or (:user_name claim) (:user_login claim))
|
[:p [:span.pg-med (or (:user-name claim) (:user-login claim))
|
||||||
"’s payment address is pending."] " You will be able to confirm the payment once the address is provided."]])
|
"’s payment address is pending."] " You will be able to confirm the payment once the address is provided."]])
|
||||||
[:div.cf
|
[:div.cf
|
||||||
[:div.dt.fr
|
[:div.dt.fr
|
||||||
|
@ -84,10 +97,10 @@
|
||||||
"View Pull Request"])
|
"View Pull Request"])
|
||||||
|
|
||||||
(defn claim-card [bounty claim {:keys [render-view-claim-button?] :as opts}]
|
(defn claim-card [bounty claim {:keys [render-view-claim-button?] :as opts}]
|
||||||
(let [{user-name :user_name
|
(let [{user-name :user-name
|
||||||
user-login :user_login
|
user-login :user-login
|
||||||
avatar-url :user_avatar_url} claim
|
avatar-url :user-avatar-url} claim
|
||||||
winner-login (:winner_login bounty)]
|
winner-login (:winner-login bounty)]
|
||||||
[:div.pv2
|
[:div.pv2
|
||||||
[:div.flex
|
[:div.flex
|
||||||
{:class (when (and (bnt/paid? claim) (not (= user-login winner-login)))
|
{:class (when (and (bnt/paid? claim) (not (= user-login winner-login)))
|
||||||
|
@ -105,7 +118,7 @@
|
||||||
[:span "No payout"]))]
|
[:span "No payout"]))]
|
||||||
[:div.f6.gray "Submitted a claim via "
|
[:div.f6.gray "Submitted a claim via "
|
||||||
[:a.gray {:href (pr-url claim)}
|
[:a.gray {:href (pr-url claim)}
|
||||||
(str (:repo_owner claim) "/" (:repo_name claim) " PR #" (:pr_number claim))]]
|
(str (:repo-owner claim) "/" (:repo-name claim) " PR #" (:pr-number claim))]]
|
||||||
;; We render the button twice for difference screen sizes, first button is for small screens:
|
;; We render the button twice for difference screen sizes, first button is for small screens:
|
||||||
;; 1) db + dn-ns: `display: block` + `display: none` for not-small screens
|
;; 1) db + dn-ns: `display: block` + `display: none` for not-small screens
|
||||||
;; 2) dn + db-ns: `display: none` + `display: block` for not-small screens
|
;; 2) dn + db-ns: `display: none` + `display: block` for not-small screens
|
||||||
|
@ -126,7 +139,7 @@
|
||||||
;; FIXME we remove all bounties that Andy 'won' as this basically
|
;; FIXME we remove all bounties that Andy 'won' as this basically
|
||||||
;; has been our method for revocations. This needs to be cleaned up ASAP.
|
;; has been our method for revocations. This needs to be cleaned up ASAP.
|
||||||
;; https://github.com/status-im/open-bounty/issues/284
|
;; https://github.com/status-im/open-bounty/issues/284
|
||||||
(for [bounty (filter #(not= "andytudhope" (:winner_login %)) bounties)
|
(for [bounty (filter #(not= "andytudhope" (:winner-login %)) bounties)
|
||||||
;; Identifying the winning claim like this is a bit
|
;; Identifying the winning claim like this is a bit
|
||||||
;; imprecise if there have been two PRs for the same
|
;; imprecise if there have been two PRs for the same
|
||||||
;; bounty by the same contributor
|
;; bounty by the same contributor
|
||||||
|
@ -134,8 +147,8 @@
|
||||||
;; ignore this edge case for a first version
|
;; ignore this edge case for a first version
|
||||||
:let [winning-claim (->> (:claims bounty)
|
:let [winning-claim (->> (:claims bounty)
|
||||||
(filter #(and (bnt/merged? %)
|
(filter #(and (bnt/merged? %)
|
||||||
(= (:user_login %)
|
(= (:user-login %)
|
||||||
(:winner_login bounty))))
|
(:winner-login bounty))))
|
||||||
util/assert-first)]]
|
util/assert-first)]]
|
||||||
^{:key (:issue-id bounty)}
|
^{:key (:issue-id bounty)}
|
||||||
[:div.mb3.br3.shadow-6.bg-white
|
[:div.mb3.br3.shadow-6.bg-white
|
||||||
|
@ -163,7 +176,7 @@
|
||||||
(str "Current Claims (" (count claims) ")")
|
(str "Current Claims (" (count claims) ")")
|
||||||
"Current Claim")]
|
"Current Claim")]
|
||||||
(for [[idx claim] (zipmap (range) claims)]
|
(for [[idx claim] (zipmap (range) claims)]
|
||||||
^{:key (:pr_id claim)}
|
^{:key (:pr-id claim)}
|
||||||
[:div
|
[:div
|
||||||
{:class (when (> idx 0) "bt b--light-gray pt2")}
|
{:class (when (> idx 0) "bt b--light-gray pt2")}
|
||||||
[claim-card bounty claim {:render-view-claim-button? true}]])]]))))
|
[claim-card bounty claim {:render-view-claim-button? true}]])]]))))
|
||||||
|
@ -231,16 +244,80 @@
|
||||||
[:div bottom]]])
|
[:div bottom]]])
|
||||||
|
|
||||||
(defn small-card-balances [bounty]
|
(defn small-card-balances [bounty]
|
||||||
[:div.f6
|
(let [pending-revocations (rf/subscribe [:pending-revocations])]
|
||||||
[ui-balances/token-balances (bnt/crypto-balances bounty) :label]
|
(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
|
[: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]
|
(defn unclaimed-bounty [bounty]
|
||||||
[:div.w-third-l.fl-l.pa2
|
[:div.w-third-l.fl-l.pa2
|
||||||
[square-card
|
[square-card
|
||||||
[bounty-title-link bounty {:show-date? true :max-length 60}]
|
[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]
|
(defn paid-bounty [bounty]
|
||||||
[:div.w-third-l.fl-l.pa2
|
[:div.w-third-l.fl-l.pa2
|
||||||
|
@ -248,7 +325,10 @@
|
||||||
[:div
|
[:div
|
||||||
[bounty-title-link bounty {:show-date? false :max-length 60}]
|
[bounty-title-link bounty {:show-date? false :max-length 60}]
|
||||||
[:div.f6.mt1.gray
|
[: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]]])
|
[small-card-balances bounty]]])
|
||||||
|
|
||||||
(defn expandable-bounty-list [bounty-component bounties]
|
(defn expandable-bounty-list [bounty-component bounties]
|
||||||
|
@ -272,9 +352,27 @@
|
||||||
(defn count-pill [n]
|
(defn count-pill [n]
|
||||||
[:span.v-top.ml3.ph3.pv1.bg-black-05.gray.br3.f7 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])]
|
(let [msg-info (rf/subscribe [:dashboard/banner-msg])]
|
||||||
(fn salute-render [name]
|
(fn salute-render []
|
||||||
(when @msg-info
|
(when @msg-info
|
||||||
[:div.relative.pa3.pr4.bg-sob-blue-o-20.br3.nt1
|
[:div.relative.pa3.pr4.bg-sob-blue-o-20.br3.nt1
|
||||||
[:div.f3.dark-gray.absolute.top-0.right-0.pa3.b.pointer
|
[:div.f3.dark-gray.absolute.top-0.right-0.pa3.b.pointer
|
||||||
|
@ -326,7 +424,8 @@
|
||||||
user (rf/subscribe [:user])
|
user (rf/subscribe [:user])
|
||||||
owner-bounties (rf/subscribe [:owner-bounties])
|
owner-bounties (rf/subscribe [:owner-bounties])
|
||||||
bounty-stats-data (rf/subscribe [:owner-bounties-stats])
|
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 []
|
(fn manage-payouts-page-render []
|
||||||
(cond
|
(cond
|
||||||
(nil? @user)
|
(nil? @user)
|
||||||
|
@ -348,13 +447,16 @@
|
||||||
(get grouped :pending-contributor-address))]
|
(get grouped :pending-contributor-address))]
|
||||||
[:div.center.mw9.pa2.pa0-l
|
[:div.center.mw9.pa2.pa0-l
|
||||||
[manage-bounties-title]
|
[manage-bounties-title]
|
||||||
[salute "Andy"]
|
[salute]
|
||||||
|
[pending-banner]
|
||||||
[:div.dn-l.db-ns.mt4
|
[:div.dn-l.db-ns.mt4
|
||||||
[bounty-stats-new @bounty-stats-data]]
|
[bounty-stats-new @bounty-stats-data]]
|
||||||
(when (nil? (common/web3))
|
(when (nil? (common/web3))
|
||||||
[:div.ui.warning.message
|
[:div.ui.warning.message
|
||||||
[:i.warning.icon]
|
[:i.warning.icon]
|
||||||
"To sign off claims, please view Status Open Bounty in Status, Mist or Metamask"])
|
"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]
|
[manage-bounties-nav route-id]
|
||||||
[:div.cf
|
[:div.cf
|
||||||
[:div.fr.w-third.pl4.mb3.dn.db-l
|
[:div.fr.w-third.pl4.mb3.dn.db-l
|
||||||
|
|
|
@ -41,6 +41,11 @@
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(:flash-message db)))
|
(:flash-message db)))
|
||||||
|
|
||||||
|
(reg-sub
|
||||||
|
:revoke-modal-bounty
|
||||||
|
(fn [db _]
|
||||||
|
(:revoke-modal-bounty db)))
|
||||||
|
|
||||||
(reg-sub
|
(reg-sub
|
||||||
:open-bounties
|
:open-bounties
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
|
@ -82,7 +87,10 @@
|
||||||
;; special prefix or namespace for derived properties that
|
;; special prefix or namespace for derived properties that
|
||||||
;; are added to domain records like this
|
;; are added to domain records like this
|
||||||
;; e.g. `derived/paid?`
|
;; 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 {}))))
|
(into {}))))
|
||||||
|
|
||||||
(reg-sub
|
(reg-sub
|
||||||
|
@ -179,6 +187,19 @@
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
(:user-dropdown-open? 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
|
(reg-sub
|
||||||
::open-bounty-claims
|
::open-bounty-claims
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
(let [db (rf/subscribe [:db])
|
(let [db (rf/subscribe [:db])
|
||||||
updating-user (rf/subscribe [:get-in [:updating-user]])
|
updating-user (rf/subscribe [:get-in [:updating-user]])
|
||||||
address (r/atom @(rf/subscribe [:get-in [:user :address]]))
|
address (r/atom @(rf/subscribe [:get-in [:user :address]]))
|
||||||
hidden (r/atom @(rf/subscribe [:get-in [:user :is_hidden_in_hunters]]))]
|
hidden (r/atom @(rf/subscribe [:get-in [:user :is-hidden-in-hunters]]))]
|
||||||
|
|
||||||
(fn []
|
(fn []
|
||||||
(let [web3 (:web3 @db)
|
(let [web3 (:web3 @db)
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
[:button
|
[:button
|
||||||
(merge {:on-click
|
(merge {:on-click
|
||||||
#(rf/dispatch [:save-user-fields {:address @address
|
#(rf/dispatch [:save-user-fields {:address @address
|
||||||
:is_hidden_in_hunters @hidden}])
|
:is-hidden-in-hunters @hidden}])
|
||||||
:disabled (str/blank? @address)
|
:disabled (str/blank? @address)
|
||||||
:class (str "ui button small update-address-button"
|
:class (str "ui button small update-address-button"
|
||||||
(when @updating-user
|
(when @updating-user
|
||||||
|
|
|
@ -250,9 +250,29 @@ label[for="input-hidden"] {
|
||||||
a {
|
a {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
pointer: default;
|
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 {
|
.logout-link {
|
||||||
color: #fff!important;
|
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-sob-sky:hover, .hover-sob-sky:focus { color: #f2f5f8; }
|
||||||
.hover-bg-sob-sky:hover, .hover-bg-sob-sky:focus { background-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; }
|
.sob-tint { color: #f7f9fa; }
|
||||||
.bg-sob-tint { background-color: #f7f9fa; }
|
.bg-sob-tint { background-color: #f7f9fa; }
|
||||||
.b--sob-tint { border-color: #f7f9fa; }
|
.b--sob-tint { border-color: #f7f9fa; }
|
||||||
|
|
|
@ -36,5 +36,5 @@
|
||||||
:address "address"
|
:address "address"
|
||||||
:created nil
|
:created nil
|
||||||
:welcome_email_sent 0
|
:welcome_email_sent 0
|
||||||
:is_hidden_in_hunters false}
|
:is-hidden-in-hunters false}
|
||||||
(db/get-user t-conn {:id 1})))))
|
(db/get-user t-conn {:id 1})))))
|
||||||
|
|
Loading…
Reference in New Issue