zcash-grant-system/backend/grant/web3/proposal.py

179 lines
5.9 KiB
Python

import time
import requests
from flask_web3 import current_web3
from grant.settings import CROWD_FUND_URL
from .util import batch_call, call_array, RpcError
crowd_fund_abi = None
def get_crowd_fund_abi():
global crowd_fund_abi
if crowd_fund_abi:
return crowd_fund_abi
crowd_fund_json = requests.get(CROWD_FUND_URL).json()
crowd_fund_abi = crowd_fund_json['abi']
return crowd_fund_abi
def read_proposal(address):
if not address:
return None
current_web3.eth.defaultAccount = '0x537680D921C000fC52Af9962ceEb4e359C50F424' if not current_web3.eth.accounts else \
current_web3.eth.accounts[0]
crowd_fund_abi = get_crowd_fund_abi()
contract = current_web3.eth.contract(address=address, abi=crowd_fund_abi)
crowd_fund = {}
methods = [
"immediateFirstMilestonePayout",
"raiseGoal",
"amountVotingForRefund",
"beneficiary",
"deadline",
"milestoneVotingPeriod",
"frozen",
"isRaiseGoalReached",
]
# batched
calls = list(map(lambda x: [x, None], methods))
try:
crowd_fund = batch_call(current_web3, address, crowd_fund_abi, calls, contract)
# catch dead contracts here
except RpcError:
return None
# balance (sync)
crowd_fund['balance'] = current_web3.eth.getBalance(address)
# arrays (sync)
crowd_fund['milestones'] = call_array(contract.functions.milestones)
crowd_fund['trustees'] = call_array(contract.functions.trustees)
contributor_list = call_array(contract.functions.contributorList)
# make milestones
def make_ms(enum_ms):
index = enum_ms[0]
ms = enum_ms[1]
is_immediate = index == 0 and crowd_fund['immediateFirstMilestonePayout']
deadline = ms[2] * 1000
amount_against = ms[1]
pct_against = round(amount_against * 100 / crowd_fund['raiseGoal'])
paid = ms[3]
state = 'WAITING'
if crowd_fund["isRaiseGoalReached"] and deadline > 0:
if paid:
state = 'PAID'
elif deadline > time.time() * 1000:
state = 'ACTIVE'
elif pct_against >= 50:
state = 'REJECTED'
else:
state = 'PAID'
return {
"index": index,
"state": state,
"amount": str(ms[0]),
"amountAgainstPayout": str(amount_against),
"percentAgainstPayout": pct_against,
"payoutRequestVoteDeadline": deadline,
"isPaid": paid,
"isImmediatePayout": is_immediate
}
crowd_fund['milestones'] = list(map(make_ms, enumerate(crowd_fund['milestones'])))
# contributor calls (batched)
contributors_calls = list(map(lambda c_addr: ['contributors', (c_addr,)], contributor_list))
contrib_votes_calls = []
for c_addr in contributor_list:
for msi in range(len(crowd_fund['milestones'])):
contrib_votes_calls.append(['getContributorMilestoneVote', (c_addr, msi)])
derived_calls = contributors_calls + contrib_votes_calls
derived_results = batch_call(current_web3, address, crowd_fund_abi, derived_calls, contract)
# make contributors
contributors = []
for contrib_address in contributor_list:
contrib_raw = derived_results['contributors' + contrib_address]
def get_no_vote(i):
return derived_results['getContributorMilestoneVote' + contrib_address + str(i)]
no_votes = list(map(get_no_vote, range(len(crowd_fund['milestones']))))
contrib = {
"address": contrib_address,
"contributionAmount": str(contrib_raw[0]),
"refundVote": contrib_raw[1],
"refunded": contrib_raw[2],
"milestoneNoVotes": no_votes,
}
contributors.append(contrib)
crowd_fund['contributors'] = contributors
# massage names and numbers
crowd_fund['target'] = crowd_fund.pop('raiseGoal')
crowd_fund['isFrozen'] = crowd_fund.pop('frozen')
crowd_fund['deadline'] = crowd_fund['deadline'] * 1000
crowd_fund['milestoneVotingPeriod'] = crowd_fund['milestoneVotingPeriod'] * 60 * 1000
if crowd_fund['isRaiseGoalReached']:
crowd_fund['funded'] = crowd_fund['target']
crowd_fund['percentFunded'] = 100
else:
crowd_fund['funded'] = crowd_fund['balance']
crowd_fund['percentFunded'] = round(crowd_fund['balance'] * 100 / crowd_fund['target'])
crowd_fund['percentVotingForRefund'] = round(crowd_fund['amountVotingForRefund'] * 100 / crowd_fund['target'])
bn_keys = ['amountVotingForRefund', 'balance', 'funded', 'target']
for k in bn_keys:
crowd_fund[k] = str(crowd_fund[k])
return crowd_fund
def read_user_proposal(address):
if not address:
return None
current_web3.eth.defaultAccount = '0x537680D921C000fC52Af9962ceEb4e359C50F424' if not current_web3.eth.accounts else \
current_web3.eth.accounts[0]
crowd_fund_abi = get_crowd_fund_abi()
contract = current_web3.eth.contract(address=address, abi=crowd_fund_abi)
crowd_fund = {}
methods = [
"raiseGoal",
]
# batched
calls = list(map(lambda x: [x, None], methods))
try:
crowd_fund = batch_call(current_web3, address, crowd_fund_abi, calls, contract)
# catch dead contracts here
except RpcError:
return None
# balance (sync)
crowd_fund['balance'] = current_web3.eth.getBalance(address)
crowd_fund['target'] = str(crowd_fund.pop('raiseGoal'))
crowd_fund['funded'] = str(crowd_fund.pop('balance'))
return crowd_fund
def validate_contribution_tx(tx_id, from_address, to_address, amount):
amount_wei = current_web3.toWei(amount, 'ether')
tx = current_web3.eth.getTransaction(tx_id)
if tx:
if from_address.lower() == tx.get("from").lower() and \
to_address == tx.get("to") and \
amount_wei == tx.get("value"):
return True
return False