Skip to content

Commit

Permalink
DCA dapp using userOpbuilder Service (#686)
Browse files Browse the repository at this point in the history
* handle userOp building using UserOpBuilderService

* refactor dashboard

* refactored wc cosigner service

* updated USEROP_BUILDER_SERVICE_BASE_URL

* chores: text change

* chores: run prettier, move const to constantUtils

* update CoSignResponse type

* remove NEXT_PUBLIC_APPLICATION_PRIVATE_KEY

* add .env.example
  • Loading branch information
KannuSingh authored Aug 31, 2024
1 parent 4fec2f0 commit efa0ddd
Show file tree
Hide file tree
Showing 18 changed files with 446 additions and 567 deletions.
4 changes: 4 additions & 0 deletions advanced/dapps/dca-dapp-demo/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
NEXT_PUBLIC_PROJECT_ID=
NEXT_PUBLIC_RELAY_URL=wss://relay.walletconnect.org
NEXT_PUBLIC_SECURE_SITE_SDK_URL=
APPLICATION_PRIVATE_KEY=
1 change: 1 addition & 0 deletions advanced/dapps/dca-dapp-demo/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ yarn-error.log*

# local env files
.env*.local
.env

# vercel
.vercel
Expand Down
2 changes: 1 addition & 1 deletion advanced/dapps/dca-dapp-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-tooltip": "^1.1.2",
"@shadcn/ui": "^0.0.4",
"@tanstack/react-query": "5.24.8",
"@wagmi/connectors": "5.1.2",
Expand All @@ -33,7 +34,6 @@
"lucide-react": "^0.427.0",
"next": "14.2.5",
"next-themes": "^0.3.0",
"permissionless": "0.1.31",
"pino-pretty": "^11.2.2",
"react": "^18",
"react-dom": "^18",
Expand Down
17 changes: 8 additions & 9 deletions advanced/dapps/dca-dapp-demo/src/app/api/dca/execute/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
address as donutContractAddress,
} from "@/utils/DonutContract";
import { executeActionsWithECDSAAndCosignerPermissions } from "@/utils/ERC7715PermissionsAsyncUtils";
import { CoSignerApiError } from "@/utils/WalletConnectCosigner";
import { CoSignerApiError } from "@/utils/WalletConnectCosignerUtils";
import { NextResponse } from "next/server";
import { encodeFunctionData, parseEther } from "viem";
import { GrantPermissionsReturnType } from "viem/experimental";
Expand All @@ -20,8 +20,7 @@ export async function POST(request: Request) {
permissions: GrantPermissionsReturnType;
pci: string;
} = await request.json();
const APPLICATION_PRIVATE_KEY = process.env
.NEXT_PUBLIC_APPLICATION_PRIVATE_KEY as `0x${string}`;
const APPLICATION_PRIVATE_KEY = process.env.APPLICATION_PRIVATE_KEY as `0x${string}`;

try {
if (!APPLICATION_PRIVATE_KEY) {
Expand All @@ -32,18 +31,18 @@ export async function POST(request: Request) {
}
if (!strategy) {
return NextResponse.json(
{ message: "No strategy provider" },
{ message: "No strategy provided" },
{ status: 400 },
);
}
if (!permissions) {
return NextResponse.json(
{ message: "No permissions provider" },
{ message: "No permissions provided" },
{ status: 400 },
);
}
if (!pci) {
return NextResponse.json({ message: "No pci provider" }, { status: 400 });
return NextResponse.json({ message: "No pci provided" }, { status: 400 });
}

const purchaseDonutCallData = encodeFunctionData({
Expand All @@ -53,12 +52,12 @@ export async function POST(request: Request) {
});
const purchaseDonutCallDataExecution = [
{
target: donutContractAddress as `0x${string}`,
to: donutContractAddress as `0x${string}`,
value: parseEther("0.0001"),
callData: purchaseDonutCallData,
data: purchaseDonutCallData,
},
];
executeActionsWithECDSAAndCosignerPermissions({
await executeActionsWithECDSAAndCosignerPermissions({
ecdsaPrivateKey: APPLICATION_PRIVATE_KEY,
pci,
permissions,
Expand Down
5 changes: 2 additions & 3 deletions advanced/dapps/dca-dapp-demo/src/app/api/signer/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ import { privateKeyToAccount } from "viem/accounts";

export function GET() {
try {
const APPLICATION_PRIVATE_KEY = process.env
.NEXT_PUBLIC_APPLICATION_PRIVATE_KEY as `0x${string}`;
const APPLICATION_PRIVATE_KEY = process.env.APPLICATION_PRIVATE_KEY as `0x${string}`;
const account = privateKeyToAccount(APPLICATION_PRIVATE_KEY);

return NextResponse.json({ key: account.publicKey });
} catch (e) {
console.warn("Error getting signer:", e);

return NextResponse.json(
{ message: "Error getting signer", error: (e as Error).message },
{ message: "Error getting application signer" },
{ status: 500 },
);
}
Expand Down
2 changes: 1 addition & 1 deletion advanced/dapps/dca-dapp-demo/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function Home() {
</div>
) : (
<p className="text-lg text-gray-600 dark:text-gray-400 font-bold">
Connect Wallet to get started.
Connect wallet to create startegy.
</p>
)}

Expand Down
53 changes: 53 additions & 0 deletions advanced/dapps/dca-dapp-demo/src/components/AddressDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useState } from "react";
import { Button } from "@/components/ui/button";
import { Copy, Check } from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";

interface AddressDisplayProps {
address: string;
}

export default function AddressDisplay({ address }: AddressDisplayProps) {
const [copied, setCopied] = useState(false);

function shortenAddress(address: string) {
return `${address.slice(0, 6)}...${address.slice(-4)}`;
}

function copyToClipboard() {
navigator.clipboard.writeText(address).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000); // Reset after 2 seconds
});
}

return (
<div className="flex justify-between items-center border-b pb-2">
<p className="font-semibold">Address</p>
<div className="flex items-center space-x-2">
<p className="text-sm">{shortenAddress(address)}</p>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline" size="icon" onClick={copyToClipboard}>
{copied ? (
<Check className="h-4 w-4" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{copied ? "Copied!" : "Copy"}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
);
}
26 changes: 26 additions & 0 deletions advanced/dapps/dca-dapp-demo/src/components/AssetBalance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";

interface AssetBalanceProps {
assetName: string;
balance: string | undefined;
isLoading: boolean;
}

export default function AssetBalance({
assetName,
balance,
isLoading,
}: AssetBalanceProps) {
return (
<>
<div className="flex justify-between border-b pb-2">
<p className="font-semibold">Asset</p>
<p className="font-semibold">Balance</p>
</div>
<div className="flex justify-between items-center">
<p>{assetName}</p>
{isLoading ? <p>...</p> : <p>{balance}</p>}
</div>
</>
);
}
59 changes: 38 additions & 21 deletions advanced/dapps/dca-dapp-demo/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,33 @@ import {
} from "@/utils/DonutContract";
import { useReadContract } from "wagmi";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import AddressDisplay from "./AddressDisplay";
import AssetBalance from "./AssetBalance";

export default function Dashboard() {
const { address: accountAddress } = useDcaApplicationContext();
const { address: connectedAddress, grantedPermissions } =
useDcaApplicationContext();
const lastAddress = grantedPermissions
? grantedPermissions.signerData?.submitToAddress
: connectedAddress;

return (
<Card className="max-w-md mx-auto mt-8">
<CardHeader>
<CardTitle className="text-center">Dashboard</CardTitle>
</CardHeader>
<CardContent>
{lastAddress ? (
<DashboardContent address={lastAddress} />
) : (
<EmptyDashboardContent />
)}
</CardContent>
</Card>
);
}

function DashboardContent({ address }: { address: string }) {
const {
data: donutsOwned,
isLoading: donutsQueryLoading,
Expand All @@ -17,31 +41,24 @@ export default function Dashboard() {
abi: donutAbi,
address: donutAddress,
functionName: "getBalance",
args: [accountAddress],
args: [address],
query: {
refetchOnWindowFocus: false,
},
});

return (
<Card className="max-w-md mx-auto mt-8">
<CardHeader>
<CardTitle className="text-center">Dashboard</CardTitle>
</CardHeader>
<CardContent>
<div className="flex justify-between border-b pb-2 mb-2">
<p className="font-semibold">Asset</p>
<p className="font-semibold">Balance</p>
</div>
<div className="flex justify-between items-center">
<p>Donut</p>
{donutsQueryLoading || donutsQueryRefetching ? (
<p>...</p>
) : (
<p>{donutsOwned?.toString()}</p>
)}
</div>
</CardContent>
</Card>
<div className="flex flex-col space-y-4">
<AddressDisplay address={address} />
<AssetBalance
assetName="Donut"
balance={donutsOwned?.toString()}
isLoading={donutsQueryLoading || donutsQueryRefetching}
/>
</div>
);
}

function EmptyDashboardContent() {
return <p className="text-center">No address found</p>;
}
30 changes: 30 additions & 0 deletions advanced/dapps/dca-dapp-demo/src/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";

import { cn } from "@/lib/utils";

const TooltipProvider = TooltipPrimitive.Provider;

const Tooltip = TooltipPrimitive.Root;

const TooltipTrigger = TooltipPrimitive.Trigger;

const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from "../utils/LocalStorage";
import { useLocalStorageState } from "../hooks/useLocalStorageState";
import { GrantPermissionsReturnType } from "viem/experimental";
import type { AddPermissionResponse } from "../utils/WalletConnectCosigner";
import type { AddPermissionResponse } from "../utils/WalletConnectCosignerUtils";
import { Chain } from "viem";
import {
type Provider,
Expand Down
2 changes: 1 addition & 1 deletion advanced/dapps/dca-dapp-demo/src/hooks/useDCA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from "../utils/EncodingUtils";
import { walletActionsErc7715 } from "viem/experimental";
import { createPublicClient, custom } from "viem";
import { WalletConnectCosigner } from "../utils/WalletConnectCosigner";
import { WalletConnectCosigner } from "../utils/WalletConnectCosignerUtils";
import { useDcaApplicationContext } from "../context/DcaApplicationContextProvider";
import { DCAFormSchemaType } from "@/schema/DCAFormSchema";
import { getSampleAsyncDCAPermissions } from "@/utils/DCAUtils";
Expand Down
4 changes: 3 additions & 1 deletion advanced/dapps/dca-dapp-demo/src/utils/ConstantsUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export function getPublicUrl() {
}

export const CUSTOM_WALLET = "wc:custom_wallet";

export const WC_COSIGNER_BASE_URL = "https://rpc.walletconnect.com/v1/sessions";
export const USEROP_BUILDER_SERVICE_BASE_URL =
"https://react-wallet.walletconnect.com/api";
// eslint-disable-next-line init-declarations
let storedCustomWallet;
if (typeof window !== "undefined") {
Expand Down
Loading

0 comments on commit efa0ddd

Please sign in to comment.