Skip to content

Commit

Permalink
Merge pull request #161 from hmrtn/api-1.0-updates
Browse files Browse the repository at this point in the history
Api 1.0 updates
  • Loading branch information
hmrtn authored Mar 7, 2022
2 parents 9bc70d5 + 65c4eac commit 5163346
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 100 deletions.
40 changes: 37 additions & 3 deletions packages/react-app/src/routes/create/Create.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { useHistory } from "react-router-dom";
import { default as MultiAddressInput } from "./components/MultiAddressInput";
import { useColorModeValue } from "@chakra-ui/color-mode";
import Blockie from "../../components/Blockie";
import { ethers } from "ethers";



Expand All @@ -53,7 +54,10 @@ const Create = ({
const { isOpen, onOpen, onClose } = useDisclosure();
const cancelRef = React.useRef();
const [partyObj, setPartyObj] = useState({
version: "1.0",
name: "",
timestamp: "",
nonce: "",
description: "",
receipts: [],
config: {
Expand All @@ -63,6 +67,9 @@ const Create = ({
participants: [],
candidates: [],
ballots: [],
notes: [],
ipfs: "",
signature: "",
});

const onSubmit = async event => {
Expand All @@ -71,7 +78,26 @@ const Create = ({
setLoadingText("Submitting...");
setIsLoading(true);

const sig = await userSigner.signMessage(`Create party:\n${partyObj.name}`);
const partySignatureData = {
version: "1.0",
name: partyObj.name,
timestamp: "",
nonce: "",
description: partyObj.description,
participants: partyObj.participants,
candidates: partyObj.candidates,
};

const signedParty = {
data: partySignatureData,
signature: "",
};

const sig = await userSigner.signMessage(ethers.utils.keccak256(ethers.utils.id(partySignatureData)));
signedParty.signature = sig;

partyObj.signed = signedParty;

const res = await fetch(`${process.env.REACT_APP_API_URL}/party`, {
method: "post",
headers: { "Content-Type": "application/json" },
Expand All @@ -80,7 +106,8 @@ const Create = ({
const json = await res.json();
routeHistory.push(`/party/${json.id}`);
setIsLoading(false);
} catch {
} catch (err) {
console.log(err);
setIsLoading(false);
}
};
Expand Down Expand Up @@ -116,19 +143,26 @@ const Create = ({
const config = {
strategy: "",
nvotes: candidateAddresses.length * 5,
chainId: targetNetwork.chainId,
};
if (name !== "" && candidateAddresses.length > 0 && voterAddresses.length > 0) {
setIsConfirmDisabled(false);
}
setIsInvalidName(false);
setPartyObj({
version: "1.0",
name: name,
timestamp: "",
nonce: "",
description: description,
receipts: [],
config: config,
participants: voterAddresses,
candidates: candidateAddresses,
ballots: [],
notes: [],
ipfs: "",
signature: "",
});
} catch (err) {
setIsConfirmDisabled(true);
Expand Down Expand Up @@ -164,7 +198,7 @@ const Create = ({
{isInvalidName ? (
<HStack>
<WarningIcon />
<Text>Name has already been used. Please try another.</Text>
<Text>Name already taken. Please try another.</Text>
</HStack>
) : null}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,35 +52,35 @@ export default function MultiAddressInput(props) {

const handleChange = e => {
const lastInput = e.target.value[e.target.value.length - 1];
if (lastInput === "," || lastInput === "\n") {
const splitInput = e.currentTarget.value
.split(/[ ,\n]+/)
.filter(c => c !== "")
.map(async uin => {
// Data model
let val = { input: uin, isValid: null, address: null, ens: null };
try {
if (uin.endsWith(".eth") || uin.endsWith(".xyz")) {
val.address = await ensProvider.resolveName(uin);
val.ens = uin;
} else {
val.ens = await ensProvider.lookupAddress(uin);
val.address = uin;
}
val.isValid = true;
} catch {
val.isValid = false;
console.log("Bad Address: " + uin);
if (lastInput === "," || lastInput === "\n" || lastInput === " ") {
const splitInput = e.currentTarget.value
.split(/[ ,\n]+/)
.filter(c => c !== "")
.map(async uin => {
// Data model
let val = { input: uin, isValid: null, address: null, ens: null };
try {
if (uin.endsWith(".eth") || uin.endsWith(".xyz")) {
val.address = await ensProvider.resolveName(uin);
val.ens = uin;
} else {
val.ens = await ensProvider.lookupAddress(uin);
val.address = uin;
}
return val;
});
setIsLoading(true);
Promise.all(splitInput)
.then(d => {
onChange([...value, ...d]);
})
.finally(_ => setIsLoading(false));
e.target.value = "";
val.isValid = true;
} catch {
val.isValid = false;
console.log("Bad Address: " + uin);
}
return val;
});
setIsLoading(true);
Promise.all(splitInput)
.then(d => {
onChange([...value, ...d]);
})
.finally(_ => setIsLoading(false));
e.target.value = "";
}
};

Expand Down
103 changes: 65 additions & 38 deletions packages/react-app/src/routes/party/Party.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,46 @@ export default function Party({
useEffect(() => {
setLoading(true);
(async () => {
const res = await fetch(`${process.env.REACT_APP_API_URL}/party/${id}`);
const party = await res.json();
const submitted = party.ballots.filter(b => b.data.ballot.address.toLowerCase() === address);
const participating = party.participants.map(adr => adr.toLowerCase()).includes(address);
setAccountVoteData(submitted);
setCanVote(submitted.length === 0 && participating);
setIsPaid(party.receipts.length > 0);
const len = party.receipts.length;
if(len > 0) {
setAmountToDistribute(utils.formatEther(party.receipts[len-1].amount));
if (readContracts && readContracts.Distributor.address) {
const res = await fetch(`${process.env.REACT_APP_API_URL}/party/${id}`);
const party = await res.json();

// TODO: Put this data model in a seperate file for organization
// EIP-712 Typed Data
// See: https://eips.ethereum.org/EIPS/eip-712
const domain = {
name: "pay-party",
version: "1",
chainId: targetNetwork.chainId,
verifyingContract: readContracts.Distributor.address,
};
const types = {
Party: [{ name: "ballot", type: "Ballot" }],
Ballot: [
{ name: "votes", type: "string" },
{ name: "timestamp", type: "string" },
{ name: "partySignature", type: "string" },
],
};

const submitted = party.ballots.filter(
b => utils.verifyTypedData(domain, types, b.data, b.signature).toLowerCase() === address.toLowerCase(),
);

const participating = party.participants.map(adr => adr.toLowerCase()).includes(address);
setAccountVoteData(submitted);
setCanVote(submitted.length === 0 && participating);
setIsPaid(party.receipts.length > 0);
const len = party.receipts.length;
if (len > 0) {
setAmountToDistribute(utils.formatEther(party.receipts[len - 1].amount));
}
setIsParticipant(participating);
setPartyData(party);
setLoading(false);
}
setIsParticipant(participating);
setPartyData(party);
setLoading(false);
})();
}, []);
}, [readContracts]);

// Calculate percent distribution from submitted ballots and memo table
const calculateDistribution = () => {
Expand Down Expand Up @@ -88,25 +112,28 @@ export default function Party({
};

// Cache the calculated distribution and table component
const cachedViewTable = useMemo(() => {
try {
const dist = calculateDistribution();
setDistribution(dist);
return (
<ViewTable
partyData={partyData}
mainnetProvider={mainnetProvider}
votesData={accountVoteData}
distribution={dist}
strategy={strategy}
amountToDistribute={amountToDistribute}
/>
);
} catch (error) {
console.log(error);
return null;
}
}, [partyData, strategy, amountToDistribute]);
const cachedViewTable = useMemo(
_ => {
try {
const dist = calculateDistribution();
setDistribution(dist);
return (
<ViewTable
partyData={partyData}
mainnetProvider={mainnetProvider}
votesData={accountVoteData}
distribution={dist}
strategy={strategy}
amountToDistribute={amountToDistribute}
/>
);
} catch (error) {
console.log(error);
return null;
}
},
[partyData, strategy, amountToDistribute],
);

const cachedVoteTable = useMemo(() => {
try {
Expand Down Expand Up @@ -178,7 +205,7 @@ export default function Party({
{showDebug && <p>{JSON.stringify(partyData)}</p>}
{loading ? (
<Center>
<Spinner size='xl' />
<Spinner size="xl" />
</Center>
) : (
<Metadata
Expand Down Expand Up @@ -212,16 +239,16 @@ export default function Party({
readContracts={readContracts}
tx={tx}
distribution={distribution}
setDistribution={setDistribution}
strategy={strategy}
setStrategy={setStrategy}
isSmartContract={isSmartContract}
localProvider={localProvider}
setAmountToDistribute={setAmountToDistribute}
targetNetwork={targetNetwork}
/>
</Box>
{isPaid && <ReceiptsTable
partyData={partyData}
targetNetwork={targetNetwork}
/>}
{isPaid && <ReceiptsTable partyData={partyData} targetNetwork={targetNetwork} />}
</Box>
</Center>
</Box>
Expand Down
Loading

0 comments on commit 5163346

Please sign in to comment.