* deploy multisig contract bytecode
* check for transaction receipt every 5 minutes
This commit is contained in:
parent
094ce72a46
commit
4e8a5148d5
|
@ -36,6 +36,7 @@
|
|||
[org.postgresql/postgresql "9.4.1209"]
|
||||
[org.webjars/webjars-locator-jboss-vfs "0.1.0"]
|
||||
[luminus-immutant "0.2.2"]
|
||||
[overtone/at-at "1.2.0"]
|
||||
[tentacles "0.5.1"]]
|
||||
|
||||
:min-lein-version "2.0.0"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- noinspection SqlResolveForFile
|
||||
ALTER TABLE public.issues
|
||||
DROP COLUMN transaction_hash;
|
||||
ALTER TABLE public.issues
|
||||
DROP COLUMN contract_address;
|
||||
ALTER TABLE public.issues
|
||||
ADD address VARCHAR(256);
|
|
@ -0,0 +1,7 @@
|
|||
ALTER TABLE public.issues
|
||||
ADD transaction_hash VARCHAR(128) NULL;
|
||||
ALTER TABLE public.issues
|
||||
ADD contract_address VARCHAR(42) NULL;
|
||||
-- noinspection SqlResolve
|
||||
ALTER TABLE public.issues
|
||||
DROP COLUMN address;
|
|
@ -0,0 +1,386 @@
|
|||
//sol Wallet
|
||||
// Multi-sig, daily-limited account proxy/wallet.
|
||||
// @authors:
|
||||
// Gav Wood <g@ethdev.com>
|
||||
// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
|
||||
// single, or, crucially, each of a number of, designated owners.
|
||||
// usage:
|
||||
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
|
||||
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
||||
// interior is executed.
|
||||
contract multiowned {
|
||||
|
||||
// TYPES
|
||||
|
||||
// struct for the status of a pending operation.
|
||||
struct PendingState {
|
||||
uint yetNeeded;
|
||||
uint ownersDone;
|
||||
uint index;
|
||||
}
|
||||
|
||||
// EVENTS
|
||||
|
||||
// this contract only has six types of events: it can accept a confirmation, in which case
|
||||
// we record owner and operation (hash) alongside it.
|
||||
event Confirmation(address owner, bytes32 operation);
|
||||
event Revoke(address owner, bytes32 operation);
|
||||
// some others are in the case of an owner changing.
|
||||
event OwnerChanged(address oldOwner, address newOwner);
|
||||
event OwnerAdded(address newOwner);
|
||||
event OwnerRemoved(address oldOwner);
|
||||
// the last one is emitted if the required signatures change
|
||||
event RequirementChanged(uint newRequirement);
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple single-sig function modifier.
|
||||
modifier onlyowner {
|
||||
if (isOwner(msg.sender))
|
||||
_
|
||||
}
|
||||
// multi-sig function modifier: the operation must have an intrinsic hash in order
|
||||
// that later attempts can be realised as the same underlying operation and
|
||||
// thus count as confirmations.
|
||||
modifier onlymanyowners(bytes32 _operation) {
|
||||
if (confirmAndCheck(_operation))
|
||||
_
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||
// as well as the selection of addresses capable of confirming them.
|
||||
function multiowned(address[] _owners, uint _required) {
|
||||
m_numOwners = _owners.length + 1;
|
||||
m_owners[1] = uint(msg.sender);
|
||||
m_ownerIndex[uint(msg.sender)] = 1;
|
||||
for (uint i = 0; i < _owners.length; ++i)
|
||||
{
|
||||
m_owners[2 + i] = uint(_owners[i]);
|
||||
m_ownerIndex[uint(_owners[i])] = 2 + i;
|
||||
}
|
||||
m_required = _required;
|
||||
}
|
||||
|
||||
// Revokes a prior confirmation of the given operation
|
||||
function revoke(bytes32 _operation) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
var pending = m_pending[_operation];
|
||||
if (pending.ownersDone & ownerIndexBit > 0) {
|
||||
pending.yetNeeded++;
|
||||
pending.ownersDone -= ownerIndexBit;
|
||||
Revoke(msg.sender, _operation);
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces an owner `_from` with another `_to`.
|
||||
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_to)) return;
|
||||
uint ownerIndex = m_ownerIndex[uint(_from)];
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
clearPending();
|
||||
m_owners[ownerIndex] = uint(_to);
|
||||
m_ownerIndex[uint(_from)] = 0;
|
||||
m_ownerIndex[uint(_to)] = ownerIndex;
|
||||
OwnerChanged(_from, _to);
|
||||
}
|
||||
|
||||
function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
if (isOwner(_owner)) return;
|
||||
|
||||
clearPending();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
reorganizeOwners();
|
||||
if (m_numOwners >= c_maxOwners)
|
||||
return;
|
||||
m_numOwners++;
|
||||
m_owners[m_numOwners] = uint(_owner);
|
||||
m_ownerIndex[uint(_owner)] = m_numOwners;
|
||||
OwnerAdded(_owner);
|
||||
}
|
||||
|
||||
function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
if (ownerIndex == 0) return;
|
||||
if (m_required > m_numOwners - 1) return;
|
||||
|
||||
m_owners[ownerIndex] = 0;
|
||||
m_ownerIndex[uint(_owner)] = 0;
|
||||
clearPending();
|
||||
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
|
||||
OwnerRemoved(_owner);
|
||||
}
|
||||
|
||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
||||
if (_newRequired > m_numOwners) return;
|
||||
m_required = _newRequired;
|
||||
clearPending();
|
||||
RequirementChanged(_newRequired);
|
||||
}
|
||||
|
||||
// Gets an owner by 0-indexed position (using numOwners as the count)
|
||||
function getOwner(uint ownerIndex) external constant returns (address) {
|
||||
return address(m_owners[ownerIndex + 1]);
|
||||
}
|
||||
|
||||
function isOwner(address _addr) returns (bool) {
|
||||
return m_ownerIndex[uint(_addr)] > 0;
|
||||
}
|
||||
|
||||
function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
|
||||
var pending = m_pending[_operation];
|
||||
uint ownerIndex = m_ownerIndex[uint(_owner)];
|
||||
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return false;
|
||||
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
return !(pending.ownersDone & ownerIndexBit == 0);
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function confirmAndCheck(bytes32 _operation) internal returns (bool) {
|
||||
// determine what index the present sender is:
|
||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||
// make sure they're an owner
|
||||
if (ownerIndex == 0) return;
|
||||
|
||||
var pending = m_pending[_operation];
|
||||
// if we're not yet working on this operation, switch over and reset the confirmation status.
|
||||
if (pending.yetNeeded == 0) {
|
||||
// reset count of confirmations needed.
|
||||
pending.yetNeeded = m_required;
|
||||
// reset which owners have confirmed (none) - set our bitmap to 0.
|
||||
pending.ownersDone = 0;
|
||||
pending.index = m_pendingIndex.length++;
|
||||
m_pendingIndex[pending.index] = _operation;
|
||||
}
|
||||
// determine the bit to set for this owner.
|
||||
uint ownerIndexBit = 2**ownerIndex;
|
||||
// make sure we (the message sender) haven't confirmed this operation previously.
|
||||
if (pending.ownersDone & ownerIndexBit == 0) {
|
||||
Confirmation(msg.sender, _operation);
|
||||
// ok - check if count is enough to go ahead.
|
||||
if (pending.yetNeeded <= 1) {
|
||||
// enough confirmations: reset and run interior.
|
||||
delete m_pendingIndex[m_pending[_operation].index];
|
||||
delete m_pending[_operation];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not enough: record that this owner in particular confirmed.
|
||||
pending.yetNeeded--;
|
||||
pending.ownersDone |= ownerIndexBit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reorganizeOwners() private {
|
||||
uint free = 1;
|
||||
while (free < m_numOwners)
|
||||
{
|
||||
while (free < m_numOwners && m_owners[free] != 0) free++;
|
||||
while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
|
||||
if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
|
||||
{
|
||||
m_owners[free] = m_owners[m_numOwners];
|
||||
m_ownerIndex[m_owners[free]] = free;
|
||||
m_owners[m_numOwners] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
if (m_pendingIndex[i] != 0)
|
||||
delete m_pending[m_pendingIndex[i]];
|
||||
delete m_pendingIndex;
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
|
||||
// the number of owners that must confirm the same operation before it is run.
|
||||
uint public m_required;
|
||||
// pointer used to find a free slot in m_owners
|
||||
uint public m_numOwners;
|
||||
|
||||
// list of owners
|
||||
uint[256] m_owners;
|
||||
uint constant c_maxOwners = 250;
|
||||
// index on the list of owners to allow reverse lookup
|
||||
mapping(uint => uint) m_ownerIndex;
|
||||
// the ongoing operations.
|
||||
mapping(bytes32 => PendingState) m_pending;
|
||||
bytes32[] m_pendingIndex;
|
||||
}
|
||||
|
||||
// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
|
||||
// on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method
|
||||
// uses is specified in the modifier.
|
||||
contract daylimit is multiowned {
|
||||
|
||||
// MODIFIERS
|
||||
|
||||
// simple modifier for daily limit.
|
||||
modifier limitedDaily(uint _value) {
|
||||
if (underLimit(_value))
|
||||
_
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor - stores initial daily limit and records the present day's index.
|
||||
function daylimit(uint _limit) {
|
||||
m_dailyLimit = _limit;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
||||
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
|
||||
m_dailyLimit = _newLimit;
|
||||
}
|
||||
// resets the amount already spent today. needs many of the owners to confirm.
|
||||
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
|
||||
m_spentToday = 0;
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
|
||||
// returns true. otherwise just returns false.
|
||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||
// reset the spend limit if we're on a different day to last time.
|
||||
if (today() > m_lastDay) {
|
||||
m_spentToday = 0;
|
||||
m_lastDay = today();
|
||||
}
|
||||
// check to see if there's enough left - if so, subtract and return true.
|
||||
// overflow protection // dailyLimit check
|
||||
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
|
||||
m_spentToday += _value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// determines today's index.
|
||||
function today() private constant returns (uint) { return now / 1 days; }
|
||||
|
||||
// FIELDS
|
||||
|
||||
uint public m_dailyLimit;
|
||||
uint public m_spentToday;
|
||||
uint public m_lastDay;
|
||||
}
|
||||
|
||||
// interface contract for multisig proxy contracts; see below for docs.
|
||||
contract multisig {
|
||||
|
||||
// EVENTS
|
||||
|
||||
// logged events:
|
||||
// Funds has arrived into the wallet (record how much).
|
||||
event Deposit(address _from, uint value);
|
||||
// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
|
||||
event SingleTransact(address owner, uint value, address to, bytes data);
|
||||
// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
|
||||
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
|
||||
// Confirmation still needed for a transaction.
|
||||
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
// TODO: document
|
||||
function changeOwner(address _from, address _to) external;
|
||||
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
|
||||
function confirm(bytes32 _h) returns (bool);
|
||||
}
|
||||
|
||||
// usage:
|
||||
// bytes32 h = Wallet(w).from(oneOwner).transact(to, value, data);
|
||||
// Wallet(w).from(anotherOwner).confirm(h);
|
||||
contract Wallet is multisig, multiowned, daylimit {
|
||||
|
||||
// TYPES
|
||||
|
||||
// Transaction structure to remember details of transaction lest it need be saved for a later call.
|
||||
struct Transaction {
|
||||
address to;
|
||||
uint value;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
||||
// constructor - just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
function Wallet(address[] _owners, uint _required, uint _daylimit)
|
||||
multiowned(_owners, _required) daylimit(_daylimit) {
|
||||
}
|
||||
|
||||
// kills the contract sending everything to `_to`.
|
||||
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
|
||||
suicide(_to);
|
||||
}
|
||||
|
||||
// gets called when no other function matches
|
||||
function() {
|
||||
// just being sent some cash?
|
||||
if (msg.value > 0)
|
||||
Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
|
||||
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
|
||||
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
|
||||
// and _data arguments). They still get the option of using them if they want, anyways.
|
||||
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) {
|
||||
// first, take the opportunity to check that we're under the daily limit.
|
||||
if (underLimit(_value)) {
|
||||
SingleTransact(msg.sender, _value, _to, _data);
|
||||
// yes - just execute the call.
|
||||
_to.call.value(_value)(_data);
|
||||
return 0;
|
||||
}
|
||||
// determine our operation hash.
|
||||
_r = sha3(msg.data, block.number);
|
||||
if (!confirm(_r) && m_txs[_r].to == 0) {
|
||||
m_txs[_r].to = _to;
|
||||
m_txs[_r].value = _value;
|
||||
m_txs[_r].data = _data;
|
||||
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
|
||||
}
|
||||
}
|
||||
|
||||
// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
|
||||
// to determine the body of the transaction from the hash provided.
|
||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
|
||||
if (m_txs[_h].to != 0) {
|
||||
m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data);
|
||||
MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data);
|
||||
delete m_txs[_h];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
|
||||
function clearPending() internal {
|
||||
uint length = m_pendingIndex.length;
|
||||
for (uint i = 0; i < length; ++i)
|
||||
delete m_txs[m_pendingIndex[i]];
|
||||
super.clearPending();
|
||||
}
|
||||
|
||||
// FIELDS
|
||||
|
||||
// pending transactions we have at present.
|
||||
mapping (bytes32 => Transaction) m_txs;
|
||||
}
|
|
@ -79,13 +79,12 @@ WHERE repo_id = :repo_id;
|
|||
|
||||
-- :name create-issue! :! :n
|
||||
-- :doc creates issue
|
||||
INSERT INTO issues (repo_id, issue_id, issue_number, title, address)
|
||||
INSERT INTO issues (repo_id, issue_id, issue_number, title)
|
||||
SELECT
|
||||
:repo_id,
|
||||
:issue_id,
|
||||
:issue_number,
|
||||
:title,
|
||||
:address
|
||||
:title
|
||||
WHERE NOT exists(SELECT 1
|
||||
FROM issues
|
||||
WHERE repo_id = :repo_id AND issue_id = :issue_id);
|
||||
|
@ -95,7 +94,27 @@ INSERT INTO issues (repo_id, issue_id, issue_number, title, address)
|
|||
UPDATE issues
|
||||
SET commit_id = :commit_id
|
||||
WHERE issue_id = :issue_id
|
||||
RETURNING repo_id, issue_id, issue_number, title, address, commit_id;
|
||||
RETURNING repo_id, issue_id, issue_number, title, commit_id, contract_address;
|
||||
|
||||
-- :name update-transaction-hash :! :n
|
||||
-- :doc updates transaction-hash for a given issue
|
||||
UPDATE issues
|
||||
SET transaction_hash = :transaction_hash
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name update-contract-address :! :n
|
||||
-- :doc updates contract-address for a given issue
|
||||
UPDATE issues
|
||||
SET contract_address = :contract_address
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name list-pending-deployments :? :*
|
||||
-- :doc retrieves pending transaction ids
|
||||
SELECT
|
||||
issue_id,
|
||||
transaction_hash
|
||||
FROM issues
|
||||
WHERE contract_address IS NULL;
|
||||
|
||||
-- Pull Requests -------------------------------------------------------------------
|
||||
|
||||
|
@ -118,7 +137,7 @@ INSERT INTO pull_requests (repo_id, pr_id, pr_number, issue_number, commit_id, u
|
|||
-- :name bounties-list :? :*
|
||||
-- :doc lists fixed issues
|
||||
SELECT
|
||||
i.address AS issue_address,
|
||||
i.contract_address AS contract_address,
|
||||
i.issue_id AS issue_id,
|
||||
i.issue_number AS issue_number,
|
||||
i.title AS issue_title,
|
||||
|
@ -144,7 +163,7 @@ WHERE r.user_id = :owner_id;
|
|||
-- :name issues-list :? :*
|
||||
-- :doc lists all issues
|
||||
SELECT
|
||||
i.address AS issue_address,
|
||||
i.contract_address AS contract_address,
|
||||
i.issue_id AS issue_id,
|
||||
i.issue_number AS issue_number,
|
||||
i.title AS issue_title,
|
||||
|
|
|
@ -5,16 +5,35 @@
|
|||
|
||||
(defn create
|
||||
"Creates issue"
|
||||
[repo-id issue-id issue-number issue-title address]
|
||||
[repo-id issue-id issue-number issue-title]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/create-issue! con-db {:repo_id repo-id
|
||||
:issue_id issue-id
|
||||
:issue_number issue-number
|
||||
:title issue-title
|
||||
:address address})))
|
||||
:title issue-title})))
|
||||
|
||||
(defn close
|
||||
"Updates issue with commit_id"
|
||||
[commit-id issue-id]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/close-issue! con-db {:issue_id issue-id :commit_id commit-id})))
|
||||
|
||||
(defn update-transaction-hash
|
||||
"Updates issue with transaction-hash"
|
||||
[issue-id transaction-hash]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/update-transaction-hash con-db {:issue_id issue-id
|
||||
:transaction_hash transaction-hash})))
|
||||
|
||||
(defn update-contract-address
|
||||
"Updates issue with contract-address"
|
||||
[issue-id contract-address]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/update-contract-address con-db {:issue_id issue-id
|
||||
:contract_address contract-address})))
|
||||
|
||||
(defn list-pending-deployments
|
||||
"Retrieves pending transaction ids"
|
||||
[]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/list-pending-deployments con-db)))
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
(ns commiteth.eth.core
|
||||
(:require [clojure.data.json :as json]
|
||||
[org.httpkit.client :refer [post]]
|
||||
[clojure.java.io :as io]
|
||||
[overtone.at-at :refer [every mk-pool]]
|
||||
[commiteth.config :refer [env]]
|
||||
[commiteth.db.issues :as issues]
|
||||
[clojure.tools.logging :as log]))
|
||||
|
||||
(def eth-rpc-url "http://localhost:8545")
|
||||
(defn eth-account [] (:eth-account env))
|
||||
(defn eth-password [] (:eth-password env))
|
||||
|
||||
(defn eth-rpc
|
||||
[method params]
|
||||
(let [body (json/write-str {:jsonrpc "2.0"
|
||||
:method method
|
||||
:params params
|
||||
:id 1})
|
||||
options {:body body}
|
||||
result (:body @(post eth-rpc-url options))]
|
||||
(:result (json/read-str result :key-fn keyword))))
|
||||
|
||||
(defn compile-solidity
|
||||
[source]
|
||||
(eth-rpc "eth_compileSolidity" [source]))
|
||||
|
||||
(defn send-transaction
|
||||
[from to value & [params]]
|
||||
(eth-rpc "personal_signAndSendTransaction" [(merge params {:from from
|
||||
:to to
|
||||
:value value})
|
||||
(eth-password)]))
|
||||
|
||||
(defn get-transaction-receipt
|
||||
[hash]
|
||||
(eth-rpc "eth_getTransactionReceipt" [hash]))
|
||||
|
||||
(defn deploy-contract
|
||||
[]
|
||||
(let [contract-src (-> "sol/wallet.sol" io/resource slurp)
|
||||
contract-name :Wallet
|
||||
contract-data (compile-solidity contract-src)
|
||||
contract-code (get-in contract-data [contract-name :code])]
|
||||
(send-transaction (eth-account) nil 1
|
||||
{:gas "1248650"
|
||||
:data contract-code})))
|
||||
|
||||
;; @todo: move to another ns
|
||||
|
||||
(def pool (mk-pool))
|
||||
|
||||
(defn update-issue-contract-address []
|
||||
(for [{issue-id :issue_id
|
||||
transaction-hash :transaction_hash} (issues/list-pending-deployments)]
|
||||
(when-let [receipt (get-transaction-receipt transaction-hash)]
|
||||
(log/info "transaction receipt for issue #" issue-id ": " receipt)
|
||||
(when-let [contract-address (:contractAddress receipt)]
|
||||
(issues/update-contract-address issue-id contract-address)))))
|
||||
|
||||
(every (* 5 60 1000) update-issue-contract-address pool)
|
|
@ -93,9 +93,9 @@
|
|||
(repos/delete-hook user repo hook-id (auth-params token)))
|
||||
|
||||
(defn post-comment
|
||||
[user repo issue-id issue-address]
|
||||
[user repo issue-id]
|
||||
(issues/create-comment user repo issue-id
|
||||
(str "a comment with an image link to the web service. Issue address is " issue-address) (self-auth-params)))
|
||||
(str "a comment with an image link to the web service.") (self-auth-params)))
|
||||
|
||||
(defn get-commit
|
||||
[user repo commit-id]
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
[commiteth.db.pull-requests :as pull-requests]
|
||||
[commiteth.db.issues :as issues]
|
||||
[commiteth.db.users :as users]
|
||||
[commiteth.eth.core :as eth]
|
||||
[ring.util.http-response :refer [ok]]
|
||||
[clojure.string :refer [join]])
|
||||
(:import [java.util UUID]
|
||||
[java.lang Integer]))
|
||||
(:import [java.lang Integer]))
|
||||
|
||||
(def label-name "bounty")
|
||||
|
||||
|
@ -25,6 +25,16 @@
|
|||
(find-issue-event event-type user)
|
||||
(:commit_id)))
|
||||
|
||||
(defn handle-issue-labeled
|
||||
[issue]
|
||||
(let [{repo-id :id} (:repository issue)
|
||||
{issue-id :id
|
||||
issue-number :number
|
||||
issue-title :title} (:issue issue)
|
||||
created-issue (issues/create repo-id issue-id issue-number issue-title)]
|
||||
(when (= 1 created-issue)
|
||||
(issues/update-transaction-hash issue-id (eth/deploy-contract)))))
|
||||
|
||||
(defn handle-issue-closed
|
||||
[{{{user :login} :owner repo :name} :repository
|
||||
{issue-id :id issue-number :number} :issue}]
|
||||
|
@ -95,24 +105,11 @@
|
|||
(= "labeled" action)
|
||||
(= label-name (get-in issue [:label :name]))))
|
||||
|
||||
(defn gen-address []
|
||||
(UUID/randomUUID))
|
||||
|
||||
(defn handle-issue
|
||||
[issue]
|
||||
(when-let [action (:action issue)]
|
||||
(when (labeled-as-bounty? action issue)
|
||||
(let [repository (:repository issue)
|
||||
{repo-id :id
|
||||
{owner-login :login} :owner
|
||||
repo-name :name} repository
|
||||
issue (:issue issue)
|
||||
{issue-id :id
|
||||
issue-number :number
|
||||
issue-title :title} issue
|
||||
issue-address (gen-address)]
|
||||
(github/post-comment owner-login repo-name issue-number issue-address)
|
||||
(issues/create repo-id issue-id issue-number issue-title issue-address)))
|
||||
(handle-issue-labeled issue))
|
||||
(when (and
|
||||
(= "closed" action)
|
||||
(has-bounty-label? (:issue issue)))
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
repo :repo_name
|
||||
issue-title :issue_title
|
||||
address :payout_address
|
||||
issue-address :issue_address}]
|
||||
contract-address :contract_address}]
|
||||
^{:key issue-id}
|
||||
[:li.list-group-item
|
||||
[:div
|
||||
|
@ -37,7 +37,7 @@
|
|||
" by "
|
||||
[:a {:href (user-url user)} user]]
|
||||
[:div "Payout address: " address]
|
||||
[:div "Amount: " (get-amount issue-address) " ETH"]])
|
||||
[:div "Amount: " (get-amount contract-address) " ETH"]])
|
||||
|
||||
(defn bounties-list []
|
||||
(let [bounties (rf/subscribe [:bounties])]
|
||||
|
|
Loading…
Reference in New Issue