diff --git a/wallet-earn/.env.example b/wallet-earn/.env.example
index 42bdc465..0fb56cdf 100644
--- a/wallet-earn/.env.example
+++ b/wallet-earn/.env.example
@@ -1,2 +1,3 @@
+COMPASS_WALLET_ADDRESS=<>
PRIVATE_KEY=<>
COMPASS_API_KEY=<>
\ No newline at end of file
diff --git a/wallet-earn/README.md b/wallet-earn/README.md
index fe1c128f..e93c26b2 100644
--- a/wallet-earn/README.md
+++ b/wallet-earn/README.md
@@ -21,6 +21,7 @@ npm run start
## Environment variables
```
+COMPASS_WALLET_ADDRESS=<>
PRIVATE_KEY=<>
COMPASS_API_KEY=<>
```
diff --git a/wallet-earn/app/api/deposit/route.ts b/wallet-earn/app/api/deposit/route.ts
index c2d04440..46c21392 100644
--- a/wallet-earn/app/api/deposit/route.ts
+++ b/wallet-earn/app/api/deposit/route.ts
@@ -32,54 +32,29 @@ export async function POST(request: Request) {
apiKeyAuth: process.env.COMPASS_API_KEY,
});
- const allowance = await compassApiSDK.universal.genericAllowance({
+ const deposit = await compassApiSDK.earn.earnManage({
+ owner: account.address,
chain: CHAIN,
- user: account.address,
- token,
- contract: vaultAddress,
+ venue: {
+ type: "VAULT",
+ vaultAddress,
+ },
+ action: "DEPOSIT",
+ amount,
});
- if (Number(allowance.amount) < amount) {
- const allowance = await compassApiSDK.universal.genericAllowanceSet({
- chain: CHAIN,
- sender: account.address,
- contract: vaultAddress,
- amount,
- token,
- });
-
- const transaction = allowance.transaction as UnsignedTransaction;
-
- const setAllowanceTxHash = await walletClient.sendTransaction({
- ...(transaction as any),
- value: BigInt(transaction.value),
- gas: BigInt(transaction.gas),
- maxFeePerGas: BigInt(transaction.maxFeePerGas),
- maxPriorityFeePerGas: BigInt(transaction.maxPriorityFeePerGas),
- });
+ const transaction = deposit.transaction as UnsignedTransaction;
- const tx = await publicClient.waitForTransactionReceipt({
- hash: setAllowanceTxHash,
- });
+ console.log(transaction)
- if (tx.status !== "success") {
- throw new Error("Allowance transaction reverted.");
- }
+ if (!transaction) {
+ throw new Error("No transaction returned from earnManage");
}
- const deposit = await compassApiSDK.erc4626Vaults.vaultsDeposit({
- chain: CHAIN,
- sender: account.address,
- vaultAddress,
- amount,
- });
-
- const transaction = deposit.transaction as UnsignedTransaction;
-
const depositTxHash = await walletClient.sendTransaction({
...(transaction as any),
value: BigInt(transaction.value),
- gas: BigInt(transaction.gas),
+ gas: transaction.gas ? BigInt(transaction.gas) : undefined,
maxFeePerGas: BigInt(transaction.maxFeePerGas),
maxPriorityFeePerGas: BigInt(transaction.maxPriorityFeePerGas),
});
diff --git a/wallet-earn/app/api/positions/route.ts b/wallet-earn/app/api/positions/route.ts
new file mode 100644
index 00000000..e74814fd
--- /dev/null
+++ b/wallet-earn/app/api/positions/route.ts
@@ -0,0 +1,44 @@
+import { CHAIN } from "@/utils/constants";
+import { getWalletAddress } from "@/utils/utils";
+import { CompassApiSDK } from "@compass-labs/api-sdk";
+
+export async function GET() {
+ try {
+ const walletAddress = getWalletAddress();
+
+ const compassApiSDK = new CompassApiSDK({
+ apiKeyAuth: process.env.COMPASS_API_KEY,
+ });
+
+ // Fetch user's earn positions
+ const positionsResponse = await compassApiSDK.earn.earnPositions({
+ chain: CHAIN,
+ userAddress: walletAddress,
+ offset: 0,
+ limit: 100,
+ days: 30,
+ });
+
+ return new Response(JSON.stringify(positionsResponse), {
+ status: 200,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ } catch (error: any) {
+ console.error("Error fetching earn positions:", error);
+
+ return new Response(
+ JSON.stringify({
+ error: "Failed to fetch earn positions",
+ details: error instanceof Error ? error.message : String(error)
+ }),
+ {
+ status: 500,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ }
+}
diff --git a/wallet-earn/app/api/token/[token]/route.ts b/wallet-earn/app/api/token/[token]/route.ts
index 401712e5..3f62d91e 100644
--- a/wallet-earn/app/api/token/[token]/route.ts
+++ b/wallet-earn/app/api/token/[token]/route.ts
@@ -1,15 +1,14 @@
import { CHAIN } from "@/utils/constants";
+import { getWalletAddress } from "@/utils/utils";
import { CompassApiSDK } from "@compass-labs/api-sdk";
-import { TokenEnum } from "@compass-labs/api-sdk/models/components";
-import { privateKeyToAccount } from "viem/accounts";
export async function GET(
_: Request,
- { params }: { params: Promise<{ token: TokenEnum | `0x${string}` }> }
+ { params }: { params: Promise<{ token: string }> }
) {
const { token } = await params;
- const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
+ const walletAddress = getWalletAddress();
const compassApiSDK = new CompassApiSDK({
apiKeyAuth: process.env.COMPASS_API_KEY,
@@ -18,7 +17,7 @@ export async function GET(
const tokenBalance = compassApiSDK.token.tokenBalance({
chain: CHAIN,
token,
- user: account.address,
+ user: walletAddress,
});
const tokenPrice = compassApiSDK.token.tokenPrice({
diff --git a/wallet-earn/app/api/vault/[address]/route.ts b/wallet-earn/app/api/vault/[address]/route.ts
index 7fce284e..ed8ee467 100644
--- a/wallet-earn/app/api/vault/[address]/route.ts
+++ b/wallet-earn/app/api/vault/[address]/route.ts
@@ -1,6 +1,6 @@
import { CHAIN } from "@/utils/constants";
+import { getWalletAddress } from "@/utils/utils";
import { CompassApiSDK } from "@compass-labs/api-sdk";
-import { privateKeyToAccount } from "viem/accounts";
export async function GET(
_: Request,
@@ -8,7 +8,7 @@ export async function GET(
) {
const { address } = await params;
- const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
+ const walletAddress = getWalletAddress();
const compassApiSDK = new CompassApiSDK({
apiKeyAuth: process.env.COMPASS_API_KEY,
@@ -17,7 +17,7 @@ export async function GET(
const vaultResponse = await compassApiSDK.erc4626Vaults.vaultsVault({
chain: CHAIN,
vaultAddress: address,
- userAddress: account.address,
+ userAddress: walletAddress,
});
return new Response(JSON.stringify(vaultResponse), {
diff --git a/wallet-earn/app/api/vaults/route.ts b/wallet-earn/app/api/vaults/route.ts
new file mode 100644
index 00000000..ba92f3c4
--- /dev/null
+++ b/wallet-earn/app/api/vaults/route.ts
@@ -0,0 +1,58 @@
+import { CHAIN } from "@/utils/constants";
+import { CompassApiSDK } from "@compass-labs/api-sdk";
+
+export async function GET() {
+ try {
+ const compassApiSDK = new CompassApiSDK({
+ apiKeyAuth: process.env.COMPASS_API_KEY,
+ });
+
+ // Call the SDK method - it will validate the response
+ const vaultsResponse = await compassApiSDK.earn.earnVaults({
+ chain: CHAIN,
+ orderBy: "one_month_returns",
+ direction: "desc",
+ offset: 0,
+ limit: 50,
+ });
+
+ return new Response(JSON.stringify(vaultsResponse), {
+ status: 200,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ } catch (error: any) {
+ console.error("Error fetching vaults:", error);
+
+ // If it's a validation error from the SDK, extract the raw response body
+ if (error?.rawResponse) {
+ console.log("Validation failed, extracting raw response body");
+ try {
+ const clonedResponse = error.rawResponse.clone();
+ const rawData = await clonedResponse.json();
+ return new Response(JSON.stringify(rawData), {
+ status: 200,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ } catch (parseError) {
+ console.error("Failed to parse raw response:", parseError);
+ }
+ }
+
+ return new Response(
+ JSON.stringify({
+ error: "Failed to fetch vaults",
+ details: error instanceof Error ? error.message : String(error)
+ }),
+ {
+ status: 500,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ }
+}
diff --git a/wallet-earn/app/api/withdraw/route.ts b/wallet-earn/app/api/withdraw/route.ts
index ff21963b..855551e2 100644
--- a/wallet-earn/app/api/withdraw/route.ts
+++ b/wallet-earn/app/api/withdraw/route.ts
@@ -32,62 +32,33 @@ export async function POST(request: Request) {
apiKeyAuth: process.env.COMPASS_API_KEY,
});
- const auth =
- await compassApiSDK.transactionBundler.transactionBundlerAuthorization({
- chain: CHAIN,
- sender: account.address,
- });
-
- const signedAuth = await walletClient.signAuthorization({
- account,
- contractAddress: auth.address as `0x${string}`,
- nonce: auth.nonce,
+ const withdraw = await compassApiSDK.earn.earnManage({
+ owner: account.address,
+ chain: CHAIN,
+ venue: {
+ type: "VAULT",
+ vaultAddress,
+ },
+ action: "WITHDRAW",
+ amount: isAll ? "ALL" : amount,
});
- const withdrawWithFee =
- await compassApiSDK.transactionBundler.transactionBundlerExecute({
- chain: CHAIN,
- sender: account.address,
- signedAuthorization: {
- nonce: signedAuth.nonce,
- address: signedAuth.address,
- chainId: signedAuth.chainId,
- r: signedAuth.r,
- s: signedAuth.s,
- yParity: signedAuth.yParity as number,
- },
- actions: [
- {
- body: {
- actionType: "VAULT_WITHDRAW",
- vaultAddress,
- amount: isAll ? "ALL" : amount,
- },
- },
- {
- // Extract 1% fee
- body: {
- actionType: "TOKEN_TRANSFER",
- to: "0xd92710ffFF5c6449ADc1b0B86283eb7dbF37567d",
- token,
- amount: amount * 0.01,
- },
- },
- ],
- });
+ const transaction = withdraw.transaction as UnsignedTransaction;
- const transaction = withdrawWithFee.transaction as UnsignedTransaction;
+ if (!transaction) {
+ throw new Error("No transaction returned from earnManage");
+ }
- const withdrawWithFeeTxHash = await walletClient.sendTransaction({
+ const withdrawTxHash = await walletClient.sendTransaction({
...(transaction as any),
value: BigInt(transaction.value),
- gas: BigInt(transaction.gas),
+ gas: transaction.gas ? BigInt(transaction.gas) : undefined,
maxFeePerGas: BigInt(transaction.maxFeePerGas),
maxPriorityFeePerGas: BigInt(transaction.maxPriorityFeePerGas),
});
const tx = await publicClient.waitForTransactionReceipt({
- hash: withdrawWithFeeTxHash,
+ hash: withdrawTxHash,
});
console.log("tx", tx);
diff --git a/wallet-earn/app/page.tsx b/wallet-earn/app/page.tsx
index 906ff597..12b0d450 100644
--- a/wallet-earn/app/page.tsx
+++ b/wallet-earn/app/page.tsx
@@ -1,9 +1,8 @@
import Screens from "@/components/Screens";
-import { cn, generateWalletGradient } from "@/utils/utils";
-import { privateKeyToAccount } from "viem/accounts";
+import { cn, generateWalletGradient, getWalletAddress } from "@/utils/utils";
export default async function Home() {
- const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
+ const walletAddress = getWalletAddress();
return (
@@ -13,11 +12,11 @@ export default async function Home() {
className={cn(
"w-6 h-6 border border-neutral-200 rounded-full mr-2 outline -outline-offset-2 outline-neutral-900/15"
)}
- style={{ background: generateWalletGradient(account.address) }}
+ style={{ background: generateWalletGradient(walletAddress) }}
/>
- {account.address.slice(0, 6)}
+ {walletAddress.slice(0, 6)}
●●●●
- {account.address.slice(-4)}
+ {walletAddress.slice(-4)}
diff --git a/wallet-earn/components/EarnItem.tsx b/wallet-earn/components/EarnItem.tsx
index b5ba6cb4..6765e80b 100644
--- a/wallet-earn/components/EarnItem.tsx
+++ b/wallet-earn/components/EarnItem.tsx
@@ -1,6 +1,7 @@
import React from "react";
-import { TokenData, VaultData } from "./Screens";
-import { TrendingUp, User } from "lucide-react";
+import { TokenData } from "./Screens";
+import { EnrichedVaultData } from "./TokenScreen";
+import { TrendingUp, Copy, Check } from "lucide-react";
import { cn } from "@/utils/utils";
import { Slider } from "./primitives/Slider";
import { Spinner } from "@geist-ui/core";
@@ -11,7 +12,7 @@ export default function EarnItem({
setIsOpen,
handleRefresh,
}: {
- vaultData: VaultData;
+ vaultData: EnrichedVaultData;
token: TokenData;
setIsOpen: (value: boolean) => void;
handleRefresh: () => void;
@@ -37,7 +38,7 @@ export default function EarnItem({
setOpen(true);
setIsOpen(true);
}}
- key={vaultData.symbol}
+ key={vaultData.address}
>
@@ -50,9 +51,9 @@ export default function EarnItem({
className="absolute -translate-x-full -left-1 text-green-600"
size={14}
/>
- {Number(vaultData.apy.apy7Day).toFixed(2)}%
+ {(Number(vaultData.oneMonthReturns) * 100).toFixed(2)}%
-
7 day
+
1 month
@@ -63,15 +64,15 @@ export default function EarnItem({
$
{(
- Number(vaultData.userPosition?.amountInUnderlyingToken) *
+ Number(vaultData.userPosition?.amountInUnderlyingToken || 0) *
Number(token.price)
).toFixed(2)}
{Number(
- vaultData.userPosition?.amountInUnderlyingToken
+ vaultData.userPosition?.amountInUnderlyingToken || 0
).toFixed(3)}{" "}
- {vaultData.underlyingToken.symbol}
+ {vaultData.denomination}
@@ -122,7 +123,7 @@ function EarnForm({
isLoading,
setIsClosing,
}: {
- vaultData: VaultData;
+ vaultData: EnrichedVaultData;
token: TokenData;
handleRefresh: () => void;
setIsLoading: (v: boolean) => void;
@@ -130,34 +131,31 @@ function EarnForm({
setIsClosing: (v: boolean) => void;
}) {
const [amount, setAmount] = React.useState(
- Number(vaultData.userPosition?.amountInUnderlyingToken)
+ Number(vaultData.userPosition?.amountInUnderlyingToken || 0)
);
const submitEarnTransaction = async () => {
setIsLoading(true);
try {
let response: Response;
- if (amount > Number(vaultData.userPosition?.amountInUnderlyingToken)) {
- const depositAmount = (
- amount - Number(vaultData.userPosition?.amountInUnderlyingToken)
- ).toFixed(token.decimals);
+ const currentPosition = Number(vaultData.userPosition?.amountInUnderlyingToken || 0);
+ if (amount > currentPosition) {
+ const depositAmount = (amount - currentPosition).toFixed(token.decimals);
response = await fetch("/api/deposit", {
method: "POST",
body: JSON.stringify({
- vaultAddress: vaultData.vaultAddress,
+ vaultAddress: vaultData.address,
amount: depositAmount,
token: token.tokenSymbol,
}),
});
} else {
- const withdrawAmount = (
- Number(vaultData.userPosition?.amountInUnderlyingToken) - amount
- ).toFixed(token.decimals);
+ const withdrawAmount = (currentPosition - amount).toFixed(token.decimals);
console.log("amount", amount);
response = await fetch("/api/withdraw", {
method: "POST",
body: JSON.stringify({
- vaultAddress: vaultData.vaultAddress,
+ vaultAddress: vaultData.address,
amount: withdrawAmount,
isAll: amount === 0 ? true : false,
token: token.tokenSymbol,
@@ -176,30 +174,57 @@ function EarnForm({
}
};
+ const [copied, setCopied] = React.useState(false);
+
+ const copyAddress = () => {
+ navigator.clipboard.writeText(vaultData.address);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ };
+
return (
+
+
+ {vaultData.name}
+
+
+
- Historical Earnings
+ Performance Metrics
- {Object.entries(vaultData.apy).map(
- ([key, value]) =>
- key !== "current" && (
- -
-
-
- {Number(value).toFixed(2)}%
-
-
- {key.replace("apy", "").replace("Day", " day")}
-
-
- )
- )}
+ -
+
+
+ {(Number(vaultData.cagr) * 100).toFixed(2)}%
+
+ CAGR
+
+ -
+
+ {(Number(vaultData.lifetimeReturn) * 100).toFixed(2)}%
+
+
+ Lifetime
+
+
@@ -232,12 +257,7 @@ function EarnForm({