Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions wallet-earn/app/api/deposit/execute/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { CHAIN } from "@/utils/constants";
import { CompassApiSDK } from "@compass-labs/api-sdk";
import { type UnsignedTransaction } from "@compass-labs/api-sdk/models/components";
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

export async function POST(request: Request) {
try {
const { owner, eip712, signature } = await request.json();

if (!owner || !eip712 || !signature) {
return Response.json(
{ error: "Missing required parameters (owner, eip712, signature)" },
{ status: 400 }
);
}

// Sponsor account - pays gas and submits transaction
const sponsorAccount = privateKeyToAccount(
process.env.GAS_SPONSOR_PK as `0x${string}`
);

const sponsorWalletClient = createWalletClient({
account: sponsorAccount,
chain: base,
transport: http(process.env.RPC_URL),
});

const publicClient = createPublicClient({
chain: base,
transport: http(process.env.RPC_URL),
});

const compassApiSDK = new CompassApiSDK({
apiKeyAuth: process.env.COMPASS_API_KEY,
});

// Prepare gas-sponsored transaction with user's signature
const sponsorGasResponse = await compassApiSDK.gasSponsorship.gasSponsorshipPrepare({
owner,
chain: CHAIN,
eip712: eip712 as any,
signature,
sender: sponsorAccount.address,
});

const sponsoredTransaction = sponsorGasResponse.transaction as UnsignedTransaction;

if (!sponsoredTransaction) {
return Response.json(
{ error: "No transaction returned from gasSponsorshipPrepare" },
{ status: 500 }
);
}

// Sponsor signs and submits the transaction
const depositTxHash = await sponsorWalletClient.sendTransaction({
...(sponsoredTransaction as any),
value: BigInt(sponsoredTransaction.value),
gas: sponsoredTransaction.gas ? BigInt(sponsoredTransaction.gas) : undefined,
maxFeePerGas: BigInt(sponsoredTransaction.maxFeePerGas),
maxPriorityFeePerGas: BigInt(sponsoredTransaction.maxPriorityFeePerGas),
});

const tx = await publicClient.waitForTransactionReceipt({
hash: depositTxHash,
});

if (tx.status !== "success") {
return Response.json(
{ error: "Deposit transaction reverted" },
{ status: 500 }
);
}

return Response.json({
success: true,
txHash: depositTxHash,
});
} catch (error) {
console.error("Error executing deposit:", error);

if (error instanceof Error) {
return Response.json({ error: error.message }, { status: 400 });
}

return Response.json(
{ error: "Failed to execute deposit" },
{ status: 500 }
);
}
}
66 changes: 66 additions & 0 deletions wallet-earn/app/api/deposit/prepare/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { CHAIN } from "@/utils/constants";
import { CompassApiSDK } from "@compass-labs/api-sdk";

export async function POST(request: Request) {
try {
const { vaultAddress, amount, token, owner } = await request.json();

if (!vaultAddress || !amount || !token || !owner) {
return Response.json(
{ error: "Missing required parameters" },
{ status: 400 }
);
}

const compassApiSDK = new CompassApiSDK({
apiKeyAuth: process.env.COMPASS_API_KEY,
});

// Call earnManage with gas sponsorship enabled
const deposit = await compassApiSDK.earn.earnManage({
owner,
chain: CHAIN,
venue: {
type: "VAULT",
vaultAddress,
},
action: "DEPOSIT",
amount,
gasSponsorship: true,
});

const eip712TypedData = deposit.eip712;

if (!eip712TypedData) {
return Response.json(
{ error: "No EIP-712 typed data returned from earnManage" },
{ status: 500 }
);
}

// Normalize types for viem compatibility
// SDK returns camelCase keys (safeTx, eip712Domain) but primaryType as "SafeTx"
const normalizedTypes = {
EIP712Domain: (eip712TypedData.types as any).eip712Domain,
SafeTx: (eip712TypedData.types as any).safeTx,
};

return Response.json({
eip712: eip712TypedData,
normalizedTypes,
domain: eip712TypedData.domain,
message: eip712TypedData.message,
});
} catch (error) {
console.error("Error preparing deposit:", error);

if (error instanceof Error) {
return Response.json({ error: error.message }, { status: 400 });
}

return Response.json(
{ error: "Failed to prepare deposit" },
{ status: 500 }
);
}
}
97 changes: 97 additions & 0 deletions wallet-earn/app/api/earn-account/check/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { CHAIN } from "@/utils/constants";
import { CompassApiSDK } from "@compass-labs/api-sdk";
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";

const publicClient = createPublicClient({
chain: base,
transport: http(process.env.RPC_URL),
});

export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const owner = searchParams.get("owner");

if (!owner) {
return Response.json(
{ error: "Missing owner address" },
{ status: 400 }
);
}

const compassApiSDK = new CompassApiSDK({
apiKeyAuth: process.env.COMPASS_API_KEY,
});

// Try to create account - if it returns 400, account already exists
try {
const response = await compassApiSDK.earn.earnCreateAccount({
chain: CHAIN,
owner,
sender: owner,
estimateGas: false,
});

const earnAccountAddress = response.earnAccountAddress;

// Check if account is already deployed by checking bytecode
const bytecode = await publicClient.getCode({
address: earnAccountAddress as `0x${string}`,
});

const isDeployed = bytecode !== undefined && bytecode !== "0x";

return Response.json({
earnAccountAddress,
isDeployed,
});
} catch (sdkError: any) {
// If SDK returns 400, account already exists - this means it's deployed
// Try to extract the earn account address from the error or response
console.log("SDK error checking earn account:", sdkError);

// Check if it's a 400 error (account already exists)
const statusCode = sdkError?.statusCode || sdkError?.status;
if (statusCode === 400) {
// Account exists - try to get the address from the error body
const errorBody = sdkError?.body || sdkError?.rawResponse;
let earnAccountAddress = null;

// Try to parse earn_account_address from error response
if (typeof errorBody === "string") {
try {
const parsed = JSON.parse(errorBody);
earnAccountAddress = parsed.earn_account_address || parsed.earnAccountAddress;
} catch {
// Ignore parse errors
}
} else if (errorBody?.earn_account_address) {
earnAccountAddress = errorBody.earn_account_address;
}

return Response.json({
earnAccountAddress,
isDeployed: true, // 400 means account already exists
});
}

return Response.json({
earnAccountAddress: null,
isDeployed: false,
error: "Could not determine earn account status",
});
}
} catch (error) {
console.error("Error checking earn account:", error);

if (error instanceof Error) {
return Response.json({ error: error.message }, { status: 400 });
}

return Response.json(
{ error: "Failed to check earn account" },
{ status: 500 }
);
}
}
44 changes: 44 additions & 0 deletions wallet-earn/app/api/earn-account/create/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { CHAIN } from "@/utils/constants";
import { CompassApiSDK } from "@compass-labs/api-sdk";

export async function POST(request: Request) {
try {
const { owner } = await request.json();

if (!owner) {
return Response.json(
{ error: "Missing owner address" },
{ status: 400 }
);
}

const compassApiSDK = new CompassApiSDK({
apiKeyAuth: process.env.COMPASS_API_KEY,
});

// Call Compass API to create earn account
// The owner will also be the sender (they sign and submit the tx)
const response = await compassApiSDK.earn.earnCreateAccount({
chain: CHAIN,
owner,
sender: owner, // Owner sends the transaction themselves
estimateGas: true,
});

return Response.json({
transaction: response.transaction,
earnAccountAddress: response.earnAccountAddress,
});
} catch (error) {
console.error("Error creating earn account:", error);

if (error instanceof Error) {
return Response.json({ error: error.message }, { status: 400 });
}

return Response.json(
{ error: "Failed to create earn account" },
{ status: 500 }
);
}
}
13 changes: 10 additions & 3 deletions wallet-earn/app/api/positions/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { CHAIN } from "@/utils/constants";
import { getWalletAddress } from "@/utils/utils";
import { CompassApiSDK } from "@compass-labs/api-sdk";

export async function GET() {
export async function GET(request: Request) {
try {
const walletAddress = getWalletAddress();
const { searchParams } = new URL(request.url);
const walletAddress = searchParams.get("wallet");

if (!walletAddress) {
return new Response(
JSON.stringify({ error: "Missing wallet address" }),
{ status: 400 }
);
}

const compassApiSDK = new CompassApiSDK({
apiKeyAuth: process.env.COMPASS_API_KEY,
Expand Down
14 changes: 10 additions & 4 deletions wallet-earn/app/api/token/[token]/route.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { CHAIN } from "@/utils/constants";
import { getWalletAddress } from "@/utils/utils";
import { CompassApiSDK } from "@compass-labs/api-sdk";

export async function GET(
_: Request,
request: Request,
{ params }: { params: Promise<{ token: string }> }
) {
const { token } = await params;

const walletAddress = getWalletAddress();
const { searchParams } = new URL(request.url);
const walletAddress = searchParams.get("wallet");

if (!walletAddress) {
return new Response(
JSON.stringify({ error: "Missing wallet address" }),
{ status: 400 }
);
}

const compassApiSDK = new CompassApiSDK({
apiKeyAuth: process.env.COMPASS_API_KEY,
Expand Down
Loading
Loading