Skip to content

Commit

Permalink
Merge pull request #115 from xuhcc/project-page
Browse files Browse the repository at this point in the history
Project page
  • Loading branch information
xuhcc authored Sep 15, 2020
2 parents afe36b6 + 04b3df4 commit 35a95f5
Show file tree
Hide file tree
Showing 21 changed files with 510 additions and 130 deletions.
2 changes: 2 additions & 0 deletions contracts/e2e/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,14 @@ describe('End-to-end Tests', function () {

// Claim funds
tally.claims = {}
const recipientTreeDepth = (await maci.treeDepths()).voteOptionTreeDepth
for (const recipientIndex of [1, 2]) {
const recipient = recipientIndex === 1 ? recipient1 : recipient2
const recipientAddress = await recipient.getAddress()
const recipientClaimData = getRecipientClaimData(
recipientAddress,
recipientIndex,
recipientTreeDepth,
tally,
)
const claimTx = await fundingRound.connect(recipient).claimFunds(...recipientClaimData)
Expand Down
12 changes: 6 additions & 6 deletions contracts/scripts/claim.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import fs from 'fs'
import { ethers } from '@nomiclabs/buidler'

import MACIArtifact from '../build/contracts/MACI.json'
import { getEventArg } from '../utils/contracts'
import { getRecipientClaimData } from '../utils/maci'

async function main() {
const [,,, recipient1, recipient2] = await ethers.getSigners()
// Finalize the round
const state = JSON.parse(fs.readFileSync('state.json').toString())
const factory = await ethers.getContractAt('FundingRoundFactory', state.factory)
const tally = JSON.parse(fs.readFileSync('tally.json').toString())
const totalSpent = parseInt(tally.totalVoiceCredits.spent)
const totalSpentSalt = tally.totalVoiceCredits.salt
await factory.transferMatchingFunds(totalSpent, totalSpentSalt)
console.log('Round finalized, totals verified.')

const fundingRound = await ethers.getContractAt('FundingRound', state.fundingRound)
const maciAddress = await fundingRound.maci()
const maci = await ethers.getContractAt(MACIArtifact.abi, maciAddress)
const recipientTreeDepth = (await maci.treeDepths()).voteOptionTreeDepth

// Claim funds
for (const recipientIndex of [1, 2]) {
const recipient = recipientIndex === 1 ? recipient1 : recipient2
const recipientClaimData = getRecipientClaimData(
await recipient.getAddress(),
recipientIndex,
recipientTreeDepth,
tally,
)
const fundingRoundAsRecipient = fundingRound.connect(recipient)
Expand Down
8 changes: 8 additions & 0 deletions contracts/scripts/tally.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ async function main() {
yarn maci-cli verify --tally-file tally.json
`)
console.log(verifyCmdOutput)

// Finalize the round
const factory = await ethers.getContractAt('FundingRoundFactory', state.factory)
const tally = JSON.parse(fs.readFileSync('tally.json').toString())
const totalSpent = parseInt(tally.totalVoiceCredits.spent)
const totalSpentSalt = tally.totalVoiceCredits.salt
await factory.transferMatchingFunds(totalSpent, totalSpentSalt)
console.log('Round finalized, totals verified.')
}

main()
Expand Down
6 changes: 3 additions & 3 deletions contracts/utils/maci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,21 @@ export function createMessage(
export function getRecipientClaimData(
recipientAddress: string,
recipientIndex: number,
recipientTreeDepth: number,
tally: any,
): any[] {
const TREE_DEPTH = 2
// Create proof for tally result
const result = tally.results.tally[recipientIndex]
const resultSalt = tally.results.salt
const resultTree = new IncrementalQuinTree(TREE_DEPTH, bigInt(0))
const resultTree = new IncrementalQuinTree(recipientTreeDepth, bigInt(0))
for (const leaf of tally.results.tally) {
resultTree.insert(leaf)
}
const resultProof = resultTree.genMerklePath(recipientIndex)
// Create proof for total amount of spent voice credits
const spent = tally.totalVoiceCreditsPerVoteOption.tally[recipientIndex]
const spentSalt = tally.totalVoiceCreditsPerVoteOption.salt
const spentTree = new IncrementalQuinTree(TREE_DEPTH, bigInt(0))
const spentTree = new IncrementalQuinTree(recipientTreeDepth, bigInt(0))
for (const leaf of tally.totalVoiceCreditsPerVoteOption.tally) {
spentTree.insert(leaf)
}
Expand Down
1 change: 1 addition & 0 deletions vue-app/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
'error',
'never',
],
'@typescript-eslint/no-explicit-any': 'off',
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
},
Expand Down
69 changes: 60 additions & 9 deletions vue-app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,26 @@
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import Cart from '@/components/Cart.vue'
import Profile from '@/components/Profile.vue'
import { LOAD_ROUND_INFO } from '@/store/action-types'
export default {
@Component({
name: 'clr.fund',
components: {
Cart,
Profile,
},
})
export default class App extends Vue {
created() {
this.$store.dispatch(LOAD_ROUND_INFO)
}
}
</script>

Expand All @@ -43,6 +54,22 @@ body {
html {
background-color: $bg-primary-color;
color: $text-color;
font-family: Inter, sans-serif;
font-size: 14px;
}
a {
color: $text-color;
text-decoration: none;
}
.input {
background-color: $bg-light-color;
border: 2px solid $button-color;
border-radius: 2px;
box-sizing: border-box;
color: $text-color;
padding: 7px;
}
.btn {
Expand All @@ -69,9 +96,7 @@ html {
#app {
display: flex;
font-family: Inter, sans-serif;
font-size: 14px;
height: 100%;
min-height: 100%;
}
#nav-bar {
Expand Down Expand Up @@ -130,24 +155,50 @@ html {
flex-grow: 1;
padding: $content-space;
h1 {
border-bottom: $border;
.content-heading {
display: block;
font-family: 'Glacial Indifference', sans-serif;
font-size: 14px;
font-weight: normal;
letter-spacing: 6px;
margin: 0 0 $content-space;
margin: 0;
padding-bottom: $content-space;
text-transform: uppercase;
}
}
#user-bar {
background-color: $bg-light-color;
display: flex;
flex-direction: column;
flex-shrink: 0;
min-width: 300px;
width: 20%;
}
.loader {
display: block;
width: 40px;
height: 40px;
margin: 20px auto;
}
.loader:after {
content: " ";
display: block;
width: 32px;
height: 32px;
margin: 4px;
border-radius: 50%;
border: 6px solid #fff;
border-color: #fff transparent #fff transparent;
animation: loader 1.2s linear infinite;
}
@keyframes loader {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
23 changes: 23 additions & 0 deletions vue-app/src/api/claims.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { BigNumber, Contract } from 'ethers'

import { FundingRound } from './abi'
import { provider } from './core'

export async function getClaimedAmount(
fundingRoundAddress: string,
recipientAddress: string,
): Promise<BigNumber | null> {
const fundingRound = new Contract(fundingRoundAddress, FundingRound, provider)
// TODO: filter by recipient
const eventFilter = fundingRound.filters.FundsClaimed()
const events = await fundingRound.queryFilter(eventFilter, 0)
for (const event of events) {
if (!event.args) {
continue
}
if (event.args._recipient.toLowerCase() === recipientAddress.toLowerCase()) {
return event.args._amount
}
}
return null
}
3 changes: 3 additions & 0 deletions vue-app/src/api/contributions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { FundingRound } from './abi'
import { provider } from './core'
import { Project } from './projects'

// A size of message batch
export const CART_MAX_SIZE = 10

export interface CartItem extends Project {
amount: number;
}
Expand Down
43 changes: 30 additions & 13 deletions vue-app/src/api/projects.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { Event } from 'ethers'
import { isAddress } from '@ethersproject/address'

import { factory, ipfsGatewayUrl } from './core'

export interface Project {
Expand All @@ -8,22 +11,36 @@ export interface Project {
index: number;
}

function decodeRecipientAdded(event: Event): Project {
const args = event.args as any // eslint-disable-line @typescript-eslint/no-explicit-any
const metadata = JSON.parse(args._metadata)
return {
address: args._fundingAddress,
name: metadata.name,
description: metadata.description,
imageUrl: `${ipfsGatewayUrl}${metadata.imageHash}`,
index: args._index.toNumber(),
}
}

export async function getProjects(): Promise<Project[]> {
const recipientFilter = factory.filters.RecipientAdded()
const events = await factory.queryFilter(recipientFilter, 0)
const projects: Project[] = []
events.forEach(event => {
if (!event.args) {
return
}
const metadata = JSON.parse(event.args._metadata)
projects.push({
address: event.args._fundingAddress,
name: metadata.name,
description: metadata.description,
imageUrl: `${ipfsGatewayUrl}${metadata.imageHash}`,
index: event.args._index.toNumber(),
})
})
for (const event of events) {
projects.push(decodeRecipientAdded(event))
}
return projects
}

export async function getProject(address: string): Promise<Project | null> {
if (!isAddress(address)) {
return null
}
const recipientFilter = factory.filters.RecipientAdded(address)
const events = await factory.queryFilter(recipientFilter, 0)
if (events.length === 1) {
return decodeRecipientAdded(events[0])
}
return null
}
12 changes: 8 additions & 4 deletions vue-app/src/api/round.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ethers, BigNumber, FixedNumber } from 'ethers'
import { BigNumber, Contract, FixedNumber } from 'ethers'
import { DateTime } from 'luxon'
import { bigInt } from 'maci-crypto'
import { PubKey } from 'maci-domainobjs'

import { FundingRound, ERC20 } from './abi'
import { FundingRound, MACI, ERC20 } from './abi'
import { provider, factory } from './core'

export interface RoundInfo {
fundingRoundAddress: string;
maciAddress: string;
recipientTreeDepth: number;
coordinatorPubKey: PubKey;
nativeTokenAddress: string;
nativeTokenSymbol: string;
Expand All @@ -34,19 +35,21 @@ export async function getRoundInfo(): Promise<RoundInfo | null> {
if (fundingRoundAddress === '0x0000000000000000000000000000000000000000') {
return null
}
const fundingRound = new ethers.Contract(
const fundingRound = new Contract(
fundingRoundAddress,
FundingRound,
provider,
)
const maciAddress = await fundingRound.maci()
const maci = new Contract(maciAddress, MACI, provider)
const recipientTreeDepth = (await maci.treeDepths()).voteOptionTreeDepth
const coordinatorPubKeyRaw = await fundingRound.coordinatorPubKey()
const coordinatorPubKey = new PubKey([
bigInt(coordinatorPubKeyRaw.x),
bigInt(coordinatorPubKeyRaw.y),
])
const nativeTokenAddress = await fundingRound.nativeToken()
const nativeToken = new ethers.Contract(
const nativeToken = new Contract(
nativeTokenAddress,
ERC20,
provider,
Expand Down Expand Up @@ -96,6 +99,7 @@ export async function getRoundInfo(): Promise<RoundInfo | null> {
return {
fundingRoundAddress,
maciAddress,
recipientTreeDepth,
coordinatorPubKey,
nativeTokenAddress,
nativeTokenSymbol,
Expand Down
Loading

0 comments on commit 35a95f5

Please sign in to comment.