Skip to content

Commit

Permalink
feat(share credits): init credit sharing UI MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
fedellen committed Nov 6, 2024
1 parent f54a1e0 commit 98899fd
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { RedeemPage } from "./pages/RedeemPage";
import { FiatTopUpPage } from "./pages/FiatTopUpPage";
import { CryptoTopUpPage } from "./pages/CryptoTopUpPage";
import { UploadPage } from "./pages/UploadPage";
import { ShareCreditsPage } from "./pages/ShareCreditPage";

export function Router() {
return (
Expand All @@ -15,6 +16,7 @@ export function Router() {
<Route path="/top-up/fiat" element={<FiatTopUpPage />} />
<Route path="/top-up/crypto" element={<CryptoTopUpPage />} />
<Route path="/upload" element={<UploadPage />} />
<Route path="/share-credits" element={<ShareCreditsPage />} />

<Route
path="/"
Expand Down
3 changes: 3 additions & 0 deletions src/components/TurboWalletConnector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ export default function TurboWalletConnector({
TurboFactory.authenticated({
token: "ethereum",
walletAdapter: {
// @ts-expect-error ethers provider is not typed

Check failure on line 58 in src/components/TurboWalletConnector.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, build)

Unused '@ts-expect-error' directive.

Check failure on line 58 in src/components/TurboWalletConnector.tsx

View workflow job for this annotation

GitHub Actions / build (20.x, build)

Unused '@ts-expect-error' directive.
getSigner: () => signer,
},
...turboConfig,
}),
);

Expand Down Expand Up @@ -89,6 +91,7 @@ export default function TurboWalletConnector({
TurboFactory.authenticated({
token: "solana",
walletAdapter: wallet,
...turboConfig,
}),
);
setCurrentToken("solana");
Expand Down
16 changes: 11 additions & 5 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
export const paymentServiceUrl =
import.meta.env.PROD === true
? "https://payment.ardrive.io"
: "http://localhost:3000";
import { TurboUnauthenticatedConfiguration } from "@ardrive/turbo-sdk";

const isProd = import.meta.env.PROD === true;
export const paymentServiceUrl = isProd
? "https://payment.ardrive.io"
: "https://payment.ardrive.dev";
export const uploadServiceUrl = isProd
? "https://upload.ardrive.io"
: "https://upload.ardrive.dev";
export const termsOfServiceUrl = "https://ardrive.io/tos-and-privacy/";
export const defaultUSDAmount = 10.0;
export const turboConfig = {
export const turboConfig: TurboUnauthenticatedConfiguration = {
paymentServiceConfig: { url: paymentServiceUrl },
uploadServiceConfig: { url: uploadServiceUrl },
};
export const wincPerCredit = 1_000_000_000_000;
export const defaultDebounceMs = 500;
Expand Down
239 changes: 239 additions & 0 deletions src/pages/ShareCreditPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import { useEffect, useState } from "react";
import { ErrMsgCallbackAsProps } from "../types";
import { ardriveAppUrl, wincPerCredit } from "../constants";
import { Page } from "./Page";
import {
TurboAuthenticatedClient,
TurboBalanceResponse,
} from "@ardrive/turbo-sdk/web";
import TurboWalletConnector from "../components/TurboWalletConnector";

function ShareCreditsForm({ errorCallback }: ErrMsgCallbackAsProps) {
const [turbo, setTurbo] = useState<undefined | TurboAuthenticatedClient>(
undefined,
);

const [creditAmount, setCreditAmount] = useState<number>(0.1);
const [approvedAddress, setApprovedAddress] = useState<string>("");
const [expiresBySeconds, setExpiresBySeconds] = useState<number>(
60 * 60 * 24 * 365,
); //1 year

const [sending, setSending] = useState<boolean>(false);

// TODO: Query params if needed
// const location = useLocation();
// useEffect(() => {

// }, [location.search]);

const canSubmitShareCredits =
!!turbo && !!creditAmount && !!approvedAddress && !sending;

const [balanceResponse, setBalanceResponse] = useState<
TurboBalanceResponse | undefined
>(undefined);

useEffect(() => {
if (!turbo) {
return;
}

turbo
.getBalance()
.then((res) => {
setBalanceResponse(res);
})
.catch((err) => {
console.error("err", err);
errorCallback(
`Error getting balance: ${err instanceof Error ? err.message : err}`,
);
// throw err;
});
}, [turbo, errorCallback]);

const handleShareSubmit = (
e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
) => {
e.preventDefault();

if (!canSubmitShareCredits) {
return;
}

const approvedWincAmount = (creditAmount * wincPerCredit).toFixed();
setSending(true);
turbo
.shareCredits({

Check failure on line 68 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, build)

Property 'shareCredits' does not exist on type 'TurboAuthenticatedClient'.

Check failure on line 68 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (20.x, build)

Property 'shareCredits' does not exist on type 'TurboAuthenticatedClient'.
approvedAddress,
approvedWincAmount,
expiresBySeconds,
})
.catch((err: unknown) => {
console.error("err", err);
errorCallback(
`Error sharing credits: ${err instanceof Error ? err.message : err}`,
);
// throw err;
})
.then((res: unknown) => {
console.log("res", res);
setSending(false);

// TODO: Success Modal or Page
alert("Great share!!\n" + JSON.stringify(res));
});
};

console.log("balanceResponse.winc", balanceResponse?.winc);
return (
<>
<h1>
Share credits from your Turbo compatible wallet to a Turbo compatible
native wallet address
</h1>
<p>
If you do not have a wallet, you can create one in{" "}
{/* TODO: Create wallet from turbo sdk/app */}
<a href={ardriveAppUrl}>ArDrive App</a>.
</p>

<TurboWalletConnector setTurbo={setTurbo} />

<form className="form">
{/* TODO: Inputs for manifest options, concurrent uploads, etc. */}

<div className="form-section">
<label className="form-label">Amount of Credits</label>
<input
type="number"
className="form-input"
onChange={(e) => setCreditAmount(e.target.valueAsNumber)}
></input>
<br />
<label className="form-label">Approved Address</label>
<input
type="text"
className="form-input"
placeholder="Enter the native wallet address to share credits to"
onChange={(e) => setApprovedAddress(e.target.value)}
></input>
<br />
<label className="form-label">Expires By Seconds</label>
<input
type="number"
className="form-input"
onChange={(e) => setExpiresBySeconds(e.target.valueAsNumber)}
></input>
</div>

{sending && <p>Now sharing credits...</p>}

<button
type="submit"
className="proceed-button"
onClick={(e) => handleShareSubmit(e)}
disabled={!canSubmitShareCredits}
>
Share Credits
</button>
</form>

<Balance balanceResponse={balanceResponse} />
</>
);
}

interface BalanceProps {
balanceResponse: TurboBalanceResponse | undefined;
}
function Balance({ balanceResponse }: BalanceProps) {
if (!balanceResponse) {
return <></>;
}

const {
controlledWinc,

Check failure on line 157 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, build)

Property 'controlledWinc' does not exist on type 'TurboBalanceResponse'.

Check failure on line 157 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (20.x, build)

Property 'controlledWinc' does not exist on type 'TurboBalanceResponse'.
effectiveBalance,

Check failure on line 158 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, build)

Property 'effectiveBalance' does not exist on type 'TurboBalanceResponse'.

Check failure on line 158 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (20.x, build)

Property 'effectiveBalance' does not exist on type 'TurboBalanceResponse'.
givenApprovals,

Check failure on line 159 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, build)

Property 'givenApprovals' does not exist on type 'TurboBalanceResponse'.

Check failure on line 159 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (20.x, build)

Property 'givenApprovals' does not exist on type 'TurboBalanceResponse'.
receivedApprovals,

Check failure on line 160 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, build)

Property 'receivedApprovals' does not exist on type 'TurboBalanceResponse'.

Check failure on line 160 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (20.x, build)

Property 'receivedApprovals' does not exist on type 'TurboBalanceResponse'.
winc,
} = balanceResponse;
const spendPower = (+winc / wincPerCredit).toFixed(12);
const sharedCredits = ((+controlledWinc - +winc) / wincPerCredit).toFixed(12);
const receivedCredits = ((+winc - +effectiveBalance) / wincPerCredit).toFixed(
12,
);

return (
<div className="form-section">
<label className="form-label">
Upload and Sharing spend power in credits: {spendPower}
</label>
{+receivedCredits > 0 && (
<label className="form-label">
Credits shared FROM other wallets: {receivedCredits}
</label>
)}
{+sharedCredits > 0 && (
<label className="form-label">
Shared credits to other wallets:{" "}
{((+controlledWinc - +winc) / wincPerCredit).toFixed(4)}
</label>
)}

<br />

{givenApprovals.length > 0 && (
<div className="form-section">
<label className="form-label">Given Approvals</label>
{/* TODO: Approvals component */}
{givenApprovals.map((approval) => (

Check failure on line 192 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, build)

Parameter 'approval' implicitly has an 'any' type.

Check failure on line 192 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (20.x, build)

Parameter 'approval' implicitly has an 'any' type.
<div key={approval.approvedAddress}>
<p>Approved Address: {approval.approvedAddress}</p>
<p>
Approved Credits:{" "}
{(+approval.approvedWincAmount / wincPerCredit).toFixed(12)}
</p>
{approval.expirationDate && (
<p>
Expiration Date:{" "}
{new Date(approval.expirationDate).toString()}
</p>
)}
</div>
))}
</div>
)}

<br />

{receivedApprovals.length > 0 && (
<div className="form-section">
<label className="form-label">Given Approvals</label>
{/* TODO: Approvals component */}
{receivedApprovals.map((approval) => (

Check failure on line 216 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (18.x, build)

Parameter 'approval' implicitly has an 'any' type.

Check failure on line 216 in src/pages/ShareCreditPage.tsx

View workflow job for this annotation

GitHub Actions / build (20.x, build)

Parameter 'approval' implicitly has an 'any' type.
<div key={approval.approvedAddress}>
<p>Approved Address: {approval.approvedAddress}</p>
<p>
Approved Credits:{" "}
{(+approval.approvedWincAmount / wincPerCredit).toFixed(12)}
</p>
{approval.expirationDate && (
<p>
Expiration Date:{" "}
{new Date(approval.expirationDate).toString()}
</p>
)}
</div>
))}
</div>
)}
</div>
);
}

export const ShareCreditsPage = () => (
<Page page={(e) => <ShareCreditsForm errorCallback={e} />} />
);

0 comments on commit 98899fd

Please sign in to comment.