Skip to content

Commit

Permalink
Merge branch 'main' of github.com:shamin/share-ride
Browse files Browse the repository at this point in the history
  • Loading branch information
ashikmeerankutty committed Sep 4, 2021
2 parents 444ef50 + 9a92f04 commit 3e3f167
Show file tree
Hide file tree
Showing 29 changed files with 191 additions and 77 deletions.
18 changes: 18 additions & 0 deletions client/README.md
@@ -0,0 +1,18 @@
# Share ride client

## Design considerations
- Support for phantom wallet.
- Arweave for storing the ride details.
- Escrow for secure token exchange b/w the riders.

## Mint
For the ease of testing mint authority is hardcoded in the client source code.

### Creating a mint for development

To create a mint public key with our mint authority use the npm script
```sh
npm run create-mint
```

Copy and paste the mint public key to [mint.ts](https://github.com/shamin/share-ride/blob/main/client/src/app/web3/provider/account/mint.ts) file.
2 changes: 1 addition & 1 deletion client/index.html
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Share Ride App</title>
</head>
Expand Down
3 changes: 2 additions & 1 deletion client/package.json
Expand Up @@ -5,7 +5,8 @@
"start": "npm run dev",
"build": "tsc && vite build",
"serve": "vite preview",
"create-mint": "node scripts/createMint.js"
"create-mint": "node scripts/createMint.js",
"deploy": "npx surge dist share-ride.surge.sh"
},
"dependencies": {
"@project-serum/anchor": "^0.13.2",
Expand Down
2 changes: 1 addition & 1 deletion client/src/app/pages/auth/login.tsx
Expand Up @@ -20,7 +20,7 @@ export const Login = () => {
<div className="container">
<div className="login">
<img alt="share ride logo" src={Logo} />
<h3>Welcome back to share ride</h3>
<h3>Welcome back to Share Ride</h3>
<p>Ready to go somewhere? Connect your wallet to get started.</p>
<Button isLoading={loading} onClick={login}>Connect with wallet</Button>
</div>
Expand Down
2 changes: 2 additions & 0 deletions client/src/app/pages/dashboard/loadingModal.tsx
Expand Up @@ -18,6 +18,8 @@ const LoadingModal: React.FC<LoadingModalProps> = ({
confirmLabel="Custom Label"
hasClose={false}
hasFooter={false}
shouldCloseOnOverlayClick={false}
shouldCloseOnEscapePress={false}
>
<div className="loading-modal">
<Spinner />
Expand Down
17 changes: 7 additions & 10 deletions client/src/app/pages/home/home.tsx
Expand Up @@ -3,14 +3,7 @@ import { Table, Button } from "evergreen-ui";
import { useHistory } from "react-router-dom";
import "./home.scss";
import { useShareRide } from "../../web3/provider";

type Ride = {
id: string,
from: string,
to: string,
date: string,
driver: string,
}
import { Ride } from "../../web3/provider/state";

const formatDate = (d: Date) => {
let dd: string | number = d.getDate();
Expand All @@ -26,6 +19,10 @@ const formatDate = (d: Date) => {
return dd + "/" + mm + "/" + yyyy;
};

const ridersAccepted = (rides: Ride[]) => {
return rides.map((r)=>r.selectedSeats).reduce((a, b) => a + b, 0)
}

export const Home = () => {
const { wallet, shareRideState, completeRide } = useShareRide();
const walletKey = wallet?.publicKey?.toBase58();
Expand Down Expand Up @@ -146,7 +143,7 @@ export const Home = () => {
{ride.seatsOffered}
</Table.TextCell>
<Table.TextCell isNumber>
{ride.riders.length}
{ridersAccepted(ride.riders)}
</Table.TextCell>
<Table.TextCell>
<Button
Expand All @@ -157,7 +154,7 @@ export const Home = () => {
disabled={formatDate(new Date()) !== ride.date}
intent="success"
>
Complete Ride
Mark as complete
</Button>
</Table.TextCell>
</Table.Row>
Expand Down
2 changes: 0 additions & 2 deletions client/src/app/pages/offer/offer.tsx
Expand Up @@ -37,7 +37,6 @@ export const Offer = () => {
const {
addDriver,
loadDrivers,
loading,
showCompleteModal,
setShowCompleteModal,
} = shareRideState;
Expand Down Expand Up @@ -176,7 +175,6 @@ export const Offer = () => {
</div>
{
<Button
isLoading={loading}
disabled={
!fromAddress || !toAddress || !selectedSeats || !costPerKm
}
Expand Down
2 changes: 1 addition & 1 deletion client/src/app/pages/ride/PolyLineOverlay.tsx
@@ -1,4 +1,4 @@
import React, { PureComponent } from "react";
import React from "react";
import { CanvasOverlay } from "react-map-gl";

const settings = {
Expand Down
56 changes: 43 additions & 13 deletions client/src/app/pages/ride/Riders.tsx
@@ -1,5 +1,13 @@
import { Pane, Dialog, Button, Table } from "evergreen-ui";
import {
Pane,
Dialog,
Button,
Table,
InfoSignIcon,
Tooltip,
} from "evergreen-ui";
import React from "react";
import { useShareRide } from "../../web3/provider";
import { Driver } from "../../web3/provider/state";

interface RiderModalProps {
Expand All @@ -19,6 +27,7 @@ export const RidersModal: React.FC<RiderModalProps> = ({
show,
onClose,
}) => {
const { tokenAccount } = useShareRide();
return (
<Dialog
isShown={show}
Expand All @@ -30,12 +39,8 @@ export const RidersModal: React.FC<RiderModalProps> = ({
>
<Table.Body>
<Table.Head>
<Table.TextCell>
From
</Table.TextCell>
<Table.TextCell>
To
</Table.TextCell>
<Table.TextCell>From</Table.TextCell>
<Table.TextCell>To</Table.TextCell>
<Table.TextCell>Total seats</Table.TextCell>
<Table.TextCell>Cost per km</Table.TextCell>
<Table.TextCell>Total Cost</Table.TextCell>
Expand All @@ -44,16 +49,41 @@ export const RidersModal: React.FC<RiderModalProps> = ({
<Table.Body>
{drivers.map((d) => (
<Table.Row key={d.archiveId}>
<Table.TextCell>{d.fromAddress.address}</Table.TextCell>
<Table.TextCell>{d.toAddress.address}</Table.TextCell>
<Table.TextCell>{d.selectedSeats}</Table.TextCell>
<Table.TextCell>{d.costPerKm}</Table.TextCell>
<Table.TextCell>
{d.fromAddress.address}
{Math.ceil(d.costPerKm * distance * seatsRequired)}
</Table.TextCell>
<Table.TextCell>
{d.toAddress.address}
<div style={{ display: "flex", alignItems: "center" }}>
<Button
onClick={() => onDriverClicked(d)}
appearance="primary"
intent="success"
disabled={
(tokenAccount?.amount || 0) <
Math.ceil(d.costPerKm * distance * seatsRequired)
}
>
Accept
</Button>
{(tokenAccount?.amount || 0) <
Math.ceil(d.costPerKm * distance * seatsRequired) && (
<div style={{ marginLeft: 10 }}>
<Tooltip
position="right"
content={`Does not have required balance (${Math.ceil(
d.costPerKm * distance * seatsRequired
)} sherekhans) in your token account.`}
>
<InfoSignIcon />
</Tooltip>
</div>
)}
</div>
</Table.TextCell>
<Table.TextCell>{d.selectedSeats}</Table.TextCell>
<Table.TextCell>{d.costPerKm}</Table.TextCell>
<Table.TextCell>{Math.ceil(d.costPerKm * distance * seatsRequired)}</Table.TextCell>
<Table.TextCell><Button onClick={() => onDriverClicked(d)} appearance="primary" intent="success">Accept</Button></Table.TextCell>
</Table.Row>
))}
</Table.Body>
Expand Down
20 changes: 19 additions & 1 deletion client/src/app/web3/idls/share_ride.json
Expand Up @@ -104,6 +104,24 @@
}
}
]
},
{
"name": "removeDriver",
"accounts": [
{
"name": "authority",
"isMut": false,
"isSigner": true
}
],
"args": [
{
"name": "archive",
"type": {
"defined": "Archive"
}
}
]
}
]
},
Expand All @@ -129,6 +147,6 @@
}
],
"metadata": {
"address": "HtdnYeMXt5yCQi8KsKQnjH5ZyqYN9ePiGZCxvWyMwpNZ"
"address": "3LL5Wa7JN4cNyPoXdCnxUnJUoLLFWANaRRjirRTtARtS"
}
}
2 changes: 1 addition & 1 deletion client/src/app/web3/idls/share_ride_escrow.json
Expand Up @@ -107,6 +107,6 @@
}
],
"metadata": {
"address": "5THBvQbhwDsAoaULFvJkwinvYsyLjPqvmWwWpcS8kjbu"
"address": "LRo4uMZpDDXpHiWgspg8yMgvHqjSRGBZkJpdawfhonL"
}
}
6 changes: 5 additions & 1 deletion client/src/app/web3/provider/account/index.ts
Expand Up @@ -21,9 +21,10 @@ const getToken = () => {

export const useTokenAccount = (
provider: Provider,
setLoadingText: React.Dispatch<React.SetStateAction<string>>
setLoadingText: (loadingText: string) => void
) => {
const [tokenAccount, setTokenAccount] = useState<AccountInfo>();
const [tokenAccountCreateLoading, setTokenAccountCreateLoading] = useState(false);

const loadTokenAccount = async () => {
console.log("Loading token account");
Expand All @@ -33,7 +34,9 @@ export const useTokenAccount = (
setLoadingText(
"Token account does not exist. Creating a new token account."
);
setTokenAccountCreateLoading(true)
token = (await createTokenAccount(provider, mintPublicKey)).toString();
setTokenAccountCreateLoading(false)
saveToken(token);
}

Expand Down Expand Up @@ -68,5 +71,6 @@ export const useTokenAccount = (
tokenAccount,
loadTokenAccount,
mintAmountToTokenAccount,
tokenAccountCreateLoading,
};
};
2 changes: 1 addition & 1 deletion client/src/app/web3/provider/account/mint.ts
Expand Up @@ -3,7 +3,7 @@ import { PublicKey } from "@solana/web3.js";
import mintAccountJson from "./mint-account.json";

export const mintPublicKey = new PublicKey(
"G8i3di2WFBtYYgfksynQ2gL7pEqc68bYMSWcqAEuEjxF"
"CjhAyWDmUXxKTfUefqGVsnd1uXk82sAT4q8R5usbxYtz"
);

export const getMintAuthority = () => {
Expand Down
2 changes: 2 additions & 0 deletions client/src/app/web3/provider/account/tokenAccount.ts
Expand Up @@ -53,7 +53,9 @@ export async function createTokenAccount(
provider.wallet.publicKey
))
);
console.log("Creating token account")
await provider.send(transaction, [newAccountPubkey]);
console.log("Token account complete")

return newAccountPubkey.publicKey;
}
Expand Down
39 changes: 26 additions & 13 deletions client/src/app/web3/provider/index.tsx
Expand Up @@ -48,17 +48,22 @@ const ShareRideProviderProvider: React.FC<ShareRideProviderProviderProps> = ({
children,
}: ShareRideProviderProviderProps) => {
const [wallet, setWallet] = useState<SolanaWallet>();
const [loadingText, setLoadingText] = useState("");
const [loadingText, _setLoadingText] = useState("");
const [provider, setProvider] = useState<SolanaProvider>();
// const [tokenAccount, setTokenAccount] = useState<AccountInfo>();
const { tokenAccount, loadTokenAccount, mintAmountToTokenAccount } =

const setLoadingText = (loading: string) => {
_setLoadingText(loading);
};

const { tokenAccount, loadTokenAccount, mintAmountToTokenAccount, tokenAccountCreateLoading } =
useTokenAccount(provider as Provider, setLoadingText);

const loadWallet = useCallback(async () => {
const _wallet = await connectWallet();
setWallet(_wallet);
const provider = new SolanaProvider(
new Connection(SolanaNetworks.LOCAL),
new Connection(SolanaNetworks.DEV),
_wallet,
{}
);
Expand Down Expand Up @@ -91,23 +96,31 @@ const ShareRideProviderProvider: React.FC<ShareRideProviderProviderProps> = ({
};

const completeRide = async (driverId: string) => {
const rides = shareRideState.rides.filter(({ driveId }) => driveId === driverId)
const rides = shareRideState.rides.filter(
({ driveId }) => driveId === driverId
);
console.log("Complete ride", rides);

console.log("Remove driver", driverId)
console.log("Remove rides", rides.map((r)=>r.archiveId))
  setLoadingText("Transfering sherekhans to your account")
console.log("Remove driver", driverId);
console.log(
"Remove rides",
rides.map((r) => r.archiveId)
);
setLoadingText("Transfering sherekhans to your account");

if (provider && tokenAccount) {
await Promise.all(rides.map(async (r)=>{
await exchangeEscrow(provider, tokenAccount, r.escrow)
}))
await Promise.all(
rides.map(async (r) => {
await exchangeEscrow(provider, tokenAccount, r.escrow);
})
);
}

setLoadingText("Reloading token account")
setLoadingText("Reloading token account");
await loadTokenAccount();
await shareRideState.removeDriver(driverId);
setLoadingText("")
setLoadingText("Reloading rides");
await shareRideState.loadDrivers();

//TODO: Fix this hardcoded ride
// console.log(shareRideState.rides);
Expand All @@ -131,7 +144,7 @@ const ShareRideProviderProvider: React.FC<ShareRideProviderProviderProps> = ({
intializeEscrow: _intializeEscrow,
exchangeEscrow: _exchangeEscrow,
mintAmountToTokenAccount,
loadingText,
loadingText: tokenAccountCreateLoading ? "Creating token account" : loadingText,
completeRide,
}),
[wallet, shareRideState, tokenAccount, provider]
Expand Down

0 comments on commit 3e3f167

Please sign in to comment.