@compass-labs/widgets 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -0
- package/dist/index.d.mts +910 -0
- package/dist/index.d.ts +910 -0
- package/dist/index.js +3330 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3299 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server/index.d.mts +56 -0
- package/dist/server/index.d.ts +56 -0
- package/dist/server/index.js +253 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +251 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/styles.css +166 -0
- package/package.json +76 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
interface CompassHandlerConfig {
|
|
2
|
+
/** Compass API key - keep this in environment variables */
|
|
3
|
+
apiKey: string;
|
|
4
|
+
/** Optional custom API server URL */
|
|
5
|
+
serverUrl?: string;
|
|
6
|
+
/** Gas sponsor private key (required for executing gas-sponsored transactions) */
|
|
7
|
+
gasSponsorPrivateKey?: string;
|
|
8
|
+
/** RPC URLs per chain (required for executing gas-sponsored transactions) */
|
|
9
|
+
rpcUrls?: {
|
|
10
|
+
ethereum?: string;
|
|
11
|
+
base?: string;
|
|
12
|
+
arbitrum?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
interface CompassRouteContext {
|
|
16
|
+
params: Promise<{
|
|
17
|
+
path: string[];
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
type NextRequest = Request;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a Next.js API route handler for Compass widget operations.
|
|
23
|
+
*
|
|
24
|
+
* This handler proxies requests from client-side widgets to the Compass API,
|
|
25
|
+
* keeping your API key secure on the server.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // app/api/compass/[...path]/route.ts
|
|
30
|
+
* import { createCompassHandler } from '@compass-labs/widgets/server';
|
|
31
|
+
*
|
|
32
|
+
* const handler = createCompassHandler({
|
|
33
|
+
* apiKey: process.env.COMPASS_API_KEY!,
|
|
34
|
+
* gasSponsorPrivateKey: process.env.GAS_SPONSOR_PK,
|
|
35
|
+
* rpcUrls: {
|
|
36
|
+
* ethereum: process.env.ETHEREUM_MAINNET_RPC_URL,
|
|
37
|
+
* base: process.env.BASE_MAINNET_RPC_URL,
|
|
38
|
+
* arbitrum: process.env.ARBITRUM_MAINNET_RPC_URL,
|
|
39
|
+
* },
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* export const GET = handler;
|
|
43
|
+
* export const POST = handler;
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* The handler supports these routes:
|
|
47
|
+
* - GET /api/compass/earn-account/check?owner=0x...&chain=base
|
|
48
|
+
* - POST /api/compass/create-account
|
|
49
|
+
* - POST /api/compass/deposit/prepare
|
|
50
|
+
* - POST /api/compass/deposit/execute
|
|
51
|
+
* - POST /api/compass/withdraw/prepare
|
|
52
|
+
* - POST /api/compass/withdraw/execute
|
|
53
|
+
*/
|
|
54
|
+
declare function createCompassHandler(config: CompassHandlerConfig): (request: NextRequest, context: CompassRouteContext) => Promise<Response>;
|
|
55
|
+
|
|
56
|
+
export { type CompassHandlerConfig, type CompassRouteContext, createCompassHandler };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
interface CompassHandlerConfig {
|
|
2
|
+
/** Compass API key - keep this in environment variables */
|
|
3
|
+
apiKey: string;
|
|
4
|
+
/** Optional custom API server URL */
|
|
5
|
+
serverUrl?: string;
|
|
6
|
+
/** Gas sponsor private key (required for executing gas-sponsored transactions) */
|
|
7
|
+
gasSponsorPrivateKey?: string;
|
|
8
|
+
/** RPC URLs per chain (required for executing gas-sponsored transactions) */
|
|
9
|
+
rpcUrls?: {
|
|
10
|
+
ethereum?: string;
|
|
11
|
+
base?: string;
|
|
12
|
+
arbitrum?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
interface CompassRouteContext {
|
|
16
|
+
params: Promise<{
|
|
17
|
+
path: string[];
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
type NextRequest = Request;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a Next.js API route handler for Compass widget operations.
|
|
23
|
+
*
|
|
24
|
+
* This handler proxies requests from client-side widgets to the Compass API,
|
|
25
|
+
* keeping your API key secure on the server.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // app/api/compass/[...path]/route.ts
|
|
30
|
+
* import { createCompassHandler } from '@compass-labs/widgets/server';
|
|
31
|
+
*
|
|
32
|
+
* const handler = createCompassHandler({
|
|
33
|
+
* apiKey: process.env.COMPASS_API_KEY!,
|
|
34
|
+
* gasSponsorPrivateKey: process.env.GAS_SPONSOR_PK,
|
|
35
|
+
* rpcUrls: {
|
|
36
|
+
* ethereum: process.env.ETHEREUM_MAINNET_RPC_URL,
|
|
37
|
+
* base: process.env.BASE_MAINNET_RPC_URL,
|
|
38
|
+
* arbitrum: process.env.ARBITRUM_MAINNET_RPC_URL,
|
|
39
|
+
* },
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* export const GET = handler;
|
|
43
|
+
* export const POST = handler;
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* The handler supports these routes:
|
|
47
|
+
* - GET /api/compass/earn-account/check?owner=0x...&chain=base
|
|
48
|
+
* - POST /api/compass/create-account
|
|
49
|
+
* - POST /api/compass/deposit/prepare
|
|
50
|
+
* - POST /api/compass/deposit/execute
|
|
51
|
+
* - POST /api/compass/withdraw/prepare
|
|
52
|
+
* - POST /api/compass/withdraw/execute
|
|
53
|
+
*/
|
|
54
|
+
declare function createCompassHandler(config: CompassHandlerConfig): (request: NextRequest, context: CompassRouteContext) => Promise<Response>;
|
|
55
|
+
|
|
56
|
+
export { type CompassHandlerConfig, type CompassRouteContext, createCompassHandler };
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var apiSdk = require('@compass-labs/api-sdk');
|
|
4
|
+
var viem = require('viem');
|
|
5
|
+
var accounts = require('viem/accounts');
|
|
6
|
+
var chains = require('viem/chains');
|
|
7
|
+
|
|
8
|
+
// src/server/handler.ts
|
|
9
|
+
var CHAIN_MAP = {
|
|
10
|
+
ethereum: chains.mainnet,
|
|
11
|
+
base: chains.base,
|
|
12
|
+
arbitrum: chains.arbitrum
|
|
13
|
+
};
|
|
14
|
+
function createCompassHandler(config) {
|
|
15
|
+
const { apiKey, serverUrl = "https://api.compasslabs.ai" } = config;
|
|
16
|
+
const client = new apiSdk.CompassApiSDK({
|
|
17
|
+
apiKeyAuth: apiKey,
|
|
18
|
+
serverURL: serverUrl
|
|
19
|
+
});
|
|
20
|
+
return async function handler(request, context) {
|
|
21
|
+
try {
|
|
22
|
+
const { path } = await context.params;
|
|
23
|
+
const route = path.join("/");
|
|
24
|
+
const method = request.method;
|
|
25
|
+
if (method === "GET") {
|
|
26
|
+
const url = new URL(request.url);
|
|
27
|
+
const searchParams = Object.fromEntries(url.searchParams.entries());
|
|
28
|
+
switch (route) {
|
|
29
|
+
case "earn-account/check":
|
|
30
|
+
return await handleEarnAccountCheck(client, searchParams);
|
|
31
|
+
default:
|
|
32
|
+
return jsonResponse({ error: `Unknown GET route: ${route}` }, 404);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (method === "POST") {
|
|
36
|
+
const body = await request.json();
|
|
37
|
+
switch (route) {
|
|
38
|
+
case "create-account":
|
|
39
|
+
return await handleCreateAccount(client, body, config);
|
|
40
|
+
case "deposit/prepare":
|
|
41
|
+
return await handleManagePrepare(client, body, "DEPOSIT");
|
|
42
|
+
case "deposit/execute":
|
|
43
|
+
return await handleExecute(client, body, config);
|
|
44
|
+
case "withdraw/prepare":
|
|
45
|
+
return await handleManagePrepare(client, body, "WITHDRAW");
|
|
46
|
+
case "withdraw/execute":
|
|
47
|
+
return await handleExecute(client, body, config);
|
|
48
|
+
default:
|
|
49
|
+
return jsonResponse({ error: `Unknown POST route: ${route}` }, 404);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return jsonResponse({ error: `Method ${method} not allowed` }, 405);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error("[Compass Handler Error]", error);
|
|
55
|
+
const message = error instanceof Error ? error.message : "Internal server error";
|
|
56
|
+
return jsonResponse({ error: message }, 500);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function jsonResponse(data, status = 200) {
|
|
61
|
+
return new Response(JSON.stringify(data), {
|
|
62
|
+
status,
|
|
63
|
+
headers: { "Content-Type": "application/json" }
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async function handleEarnAccountCheck(client, params) {
|
|
67
|
+
const { owner, chain = "base" } = params;
|
|
68
|
+
if (!owner) {
|
|
69
|
+
return jsonResponse({ error: "Missing owner parameter" }, 400);
|
|
70
|
+
}
|
|
71
|
+
const response = await client.earn.earnCreateAccount({
|
|
72
|
+
chain,
|
|
73
|
+
owner,
|
|
74
|
+
sender: owner,
|
|
75
|
+
estimateGas: false
|
|
76
|
+
});
|
|
77
|
+
const earnAccountAddress = response.earnAccountAddress;
|
|
78
|
+
const hasTransaction = !!response.transaction;
|
|
79
|
+
return jsonResponse({
|
|
80
|
+
earnAccountAddress,
|
|
81
|
+
isDeployed: !hasTransaction,
|
|
82
|
+
needsCreation: hasTransaction
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async function handleCreateAccount(client, body, config) {
|
|
86
|
+
const { owner, chain = "base" } = body;
|
|
87
|
+
const { gasSponsorPrivateKey, rpcUrls } = config;
|
|
88
|
+
if (!owner) {
|
|
89
|
+
return jsonResponse({ error: "Missing owner parameter" }, 400);
|
|
90
|
+
}
|
|
91
|
+
if (!gasSponsorPrivateKey) {
|
|
92
|
+
return jsonResponse(
|
|
93
|
+
{ error: "Gas sponsor not configured. Set gasSponsorPrivateKey in handler config." },
|
|
94
|
+
500
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const viemChain = CHAIN_MAP[chain.toLowerCase()];
|
|
98
|
+
if (!viemChain) {
|
|
99
|
+
return jsonResponse({ error: `Unsupported chain: ${chain}` }, 500);
|
|
100
|
+
}
|
|
101
|
+
const rpcUrl = rpcUrls?.[chain.toLowerCase()];
|
|
102
|
+
if (!rpcUrl) {
|
|
103
|
+
return jsonResponse({ error: `No RPC URL configured for chain: ${chain}` }, 500);
|
|
104
|
+
}
|
|
105
|
+
const sponsorAccount = accounts.privateKeyToAccount(gasSponsorPrivateKey);
|
|
106
|
+
const walletClient = viem.createWalletClient({
|
|
107
|
+
account: sponsorAccount,
|
|
108
|
+
chain: viemChain,
|
|
109
|
+
transport: viem.http(rpcUrl)
|
|
110
|
+
});
|
|
111
|
+
const publicClient = viem.createPublicClient({
|
|
112
|
+
chain: viemChain,
|
|
113
|
+
transport: viem.http(rpcUrl)
|
|
114
|
+
});
|
|
115
|
+
const response = await client.earn.earnCreateAccount({
|
|
116
|
+
chain,
|
|
117
|
+
owner,
|
|
118
|
+
sender: sponsorAccount.address,
|
|
119
|
+
estimateGas: false
|
|
120
|
+
});
|
|
121
|
+
const earnAccountAddress = response.earnAccountAddress;
|
|
122
|
+
if (!response.transaction) {
|
|
123
|
+
return jsonResponse({
|
|
124
|
+
earnAccountAddress,
|
|
125
|
+
success: true,
|
|
126
|
+
alreadyExists: true
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const transaction = response.transaction;
|
|
130
|
+
const txHash = await walletClient.sendTransaction({
|
|
131
|
+
to: transaction.to,
|
|
132
|
+
data: transaction.data,
|
|
133
|
+
value: transaction.value ? BigInt(transaction.value) : 0n,
|
|
134
|
+
gas: transaction.gas ? BigInt(transaction.gas) : void 0
|
|
135
|
+
});
|
|
136
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
137
|
+
hash: txHash
|
|
138
|
+
});
|
|
139
|
+
if (receipt.status === "reverted") {
|
|
140
|
+
return jsonResponse({ error: "Account creation transaction reverted" }, 500);
|
|
141
|
+
}
|
|
142
|
+
return jsonResponse({
|
|
143
|
+
earnAccountAddress,
|
|
144
|
+
txHash,
|
|
145
|
+
success: true
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
async function handleManagePrepare(client, body, action) {
|
|
149
|
+
const { amount, token, owner, chain, venueType, vaultAddress, marketAddress, maxSlippagePercent } = body;
|
|
150
|
+
let venue;
|
|
151
|
+
if (venueType === "VAULT" && vaultAddress) {
|
|
152
|
+
venue = {
|
|
153
|
+
type: "VAULT",
|
|
154
|
+
vaultAddress
|
|
155
|
+
};
|
|
156
|
+
} else if (venueType === "AAVE") {
|
|
157
|
+
venue = {
|
|
158
|
+
type: "AAVE",
|
|
159
|
+
token
|
|
160
|
+
};
|
|
161
|
+
} else if (venueType === "PENDLE_PT" && marketAddress) {
|
|
162
|
+
venue = {
|
|
163
|
+
type: "PENDLE_PT",
|
|
164
|
+
marketAddress,
|
|
165
|
+
token: action === "DEPOSIT" ? token : void 0,
|
|
166
|
+
maxSlippagePercent: maxSlippagePercent ?? 1
|
|
167
|
+
};
|
|
168
|
+
} else {
|
|
169
|
+
return jsonResponse({ error: "Invalid venue type or missing address" }, 400);
|
|
170
|
+
}
|
|
171
|
+
const response = await client.earn.earnManage({
|
|
172
|
+
owner,
|
|
173
|
+
chain,
|
|
174
|
+
venue,
|
|
175
|
+
action,
|
|
176
|
+
amount,
|
|
177
|
+
gasSponsorship: true
|
|
178
|
+
});
|
|
179
|
+
const eip712 = response.eip712;
|
|
180
|
+
if (!eip712) {
|
|
181
|
+
return jsonResponse({ error: "No EIP-712 data returned from API" }, 500);
|
|
182
|
+
}
|
|
183
|
+
const types = eip712.types;
|
|
184
|
+
const normalizedTypes = {
|
|
185
|
+
EIP712Domain: types.eip712Domain,
|
|
186
|
+
SafeTx: types.safeTx
|
|
187
|
+
};
|
|
188
|
+
return jsonResponse({
|
|
189
|
+
eip712,
|
|
190
|
+
normalizedTypes,
|
|
191
|
+
domain: eip712.domain,
|
|
192
|
+
message: eip712.message
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async function handleExecute(client, body, config) {
|
|
196
|
+
const { owner, eip712, signature, chain } = body;
|
|
197
|
+
const { gasSponsorPrivateKey, rpcUrls } = config;
|
|
198
|
+
if (!gasSponsorPrivateKey) {
|
|
199
|
+
return jsonResponse(
|
|
200
|
+
{ error: "Gas sponsor not configured. Set gasSponsorPrivateKey in handler config." },
|
|
201
|
+
500
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const viemChain = CHAIN_MAP[chain.toLowerCase()];
|
|
205
|
+
if (!viemChain) {
|
|
206
|
+
return jsonResponse({ error: `Unsupported chain: ${chain}` }, 500);
|
|
207
|
+
}
|
|
208
|
+
const rpcUrl = rpcUrls?.[chain.toLowerCase()];
|
|
209
|
+
if (!rpcUrl) {
|
|
210
|
+
return jsonResponse({ error: `No RPC URL configured for chain: ${chain}` }, 500);
|
|
211
|
+
}
|
|
212
|
+
const sponsorAccount = accounts.privateKeyToAccount(gasSponsorPrivateKey);
|
|
213
|
+
const walletClient = viem.createWalletClient({
|
|
214
|
+
account: sponsorAccount,
|
|
215
|
+
chain: viemChain,
|
|
216
|
+
transport: viem.http(rpcUrl)
|
|
217
|
+
});
|
|
218
|
+
const publicClient = viem.createPublicClient({
|
|
219
|
+
chain: viemChain,
|
|
220
|
+
transport: viem.http(rpcUrl)
|
|
221
|
+
});
|
|
222
|
+
const response = await client.gasSponsorship.gasSponsorshipPrepare({
|
|
223
|
+
chain,
|
|
224
|
+
owner,
|
|
225
|
+
sender: sponsorAccount.address,
|
|
226
|
+
eip712,
|
|
227
|
+
signature
|
|
228
|
+
});
|
|
229
|
+
const transaction = response.transaction;
|
|
230
|
+
if (!transaction) {
|
|
231
|
+
return jsonResponse(
|
|
232
|
+
{ error: "No transaction returned from gas sponsorship prepare" },
|
|
233
|
+
500
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const txHash = await walletClient.sendTransaction({
|
|
237
|
+
to: transaction.to,
|
|
238
|
+
data: transaction.data,
|
|
239
|
+
value: transaction.value ? BigInt(transaction.value) : 0n,
|
|
240
|
+
gas: transaction.gas ? BigInt(transaction.gas) : void 0
|
|
241
|
+
});
|
|
242
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
243
|
+
hash: txHash
|
|
244
|
+
});
|
|
245
|
+
if (receipt.status === "reverted") {
|
|
246
|
+
return jsonResponse({ error: "Transaction reverted" }, 500);
|
|
247
|
+
}
|
|
248
|
+
return jsonResponse({ txHash, success: true });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
exports.createCompassHandler = createCompassHandler;
|
|
252
|
+
//# sourceMappingURL=index.js.map
|
|
253
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/handler.ts"],"names":["mainnet","base","arbitrum","CompassApiSDK","privateKeyToAccount","createWalletClient","http","createPublicClient"],"mappings":"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","file":"index.js","sourcesContent":["import { CompassApiSDK } from '@compass-labs/api-sdk';\nimport {\n createWalletClient,\n createPublicClient,\n http,\n type Chain,\n type Hex,\n} from 'viem';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport { mainnet, base, arbitrum } from 'viem/chains';\n\nconst CHAIN_MAP: Record<string, Chain> = {\n ethereum: mainnet,\n base: base,\n arbitrum: arbitrum,\n};\n\nexport interface CompassHandlerConfig {\n /** Compass API key - keep this in environment variables */\n apiKey: string;\n /** Optional custom API server URL */\n serverUrl?: string;\n /** Gas sponsor private key (required for executing gas-sponsored transactions) */\n gasSponsorPrivateKey?: string;\n /** RPC URLs per chain (required for executing gas-sponsored transactions) */\n rpcUrls?: {\n ethereum?: string;\n base?: string;\n arbitrum?: string;\n };\n}\n\nexport interface CompassRouteContext {\n params: Promise<{ path: string[] }>;\n}\n\ntype NextRequest = Request;\n\n/**\n * Creates a Next.js API route handler for Compass widget operations.\n *\n * This handler proxies requests from client-side widgets to the Compass API,\n * keeping your API key secure on the server.\n *\n * @example\n * ```typescript\n * // app/api/compass/[...path]/route.ts\n * import { createCompassHandler } from '@compass-labs/widgets/server';\n *\n * const handler = createCompassHandler({\n * apiKey: process.env.COMPASS_API_KEY!,\n * gasSponsorPrivateKey: process.env.GAS_SPONSOR_PK,\n * rpcUrls: {\n * ethereum: process.env.ETHEREUM_MAINNET_RPC_URL,\n * base: process.env.BASE_MAINNET_RPC_URL,\n * arbitrum: process.env.ARBITRUM_MAINNET_RPC_URL,\n * },\n * });\n *\n * export const GET = handler;\n * export const POST = handler;\n * ```\n *\n * The handler supports these routes:\n * - GET /api/compass/earn-account/check?owner=0x...&chain=base\n * - POST /api/compass/create-account\n * - POST /api/compass/deposit/prepare\n * - POST /api/compass/deposit/execute\n * - POST /api/compass/withdraw/prepare\n * - POST /api/compass/withdraw/execute\n */\nexport function createCompassHandler(config: CompassHandlerConfig) {\n const { apiKey, serverUrl = 'https://api.compasslabs.ai' } = config;\n\n // Create SDK client\n const client = new CompassApiSDK({\n apiKeyAuth: apiKey,\n serverURL: serverUrl,\n });\n\n return async function handler(\n request: NextRequest,\n context: CompassRouteContext\n ): Promise<Response> {\n try {\n const { path } = await context.params;\n const route = path.join('/');\n const method = request.method;\n\n // Handle GET requests\n if (method === 'GET') {\n const url = new URL(request.url);\n const searchParams = Object.fromEntries(url.searchParams.entries());\n\n switch (route) {\n case 'earn-account/check':\n return await handleEarnAccountCheck(client, searchParams);\n default:\n return jsonResponse({ error: `Unknown GET route: ${route}` }, 404);\n }\n }\n\n // Handle POST requests\n if (method === 'POST') {\n const body = await request.json();\n\n switch (route) {\n case 'create-account':\n return await handleCreateAccount(client, body, config);\n case 'deposit/prepare':\n return await handleManagePrepare(client, body, 'DEPOSIT');\n case 'deposit/execute':\n return await handleExecute(client, body, config);\n case 'withdraw/prepare':\n return await handleManagePrepare(client, body, 'WITHDRAW');\n case 'withdraw/execute':\n return await handleExecute(client, body, config);\n default:\n return jsonResponse({ error: `Unknown POST route: ${route}` }, 404);\n }\n }\n\n return jsonResponse({ error: `Method ${method} not allowed` }, 405);\n } catch (error) {\n console.error('[Compass Handler Error]', error);\n const message = error instanceof Error ? error.message : 'Internal server error';\n return jsonResponse({ error: message }, 500);\n }\n };\n}\n\nfunction jsonResponse(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\n// --- Earn Account Handlers ---\n\ninterface EarnAccountCheckParams {\n owner?: string;\n chain?: string;\n}\n\nasync function handleEarnAccountCheck(\n client: CompassApiSDK,\n params: EarnAccountCheckParams\n): Promise<Response> {\n const { owner, chain = 'base' } = params;\n\n if (!owner) {\n return jsonResponse({ error: 'Missing owner parameter' }, 400);\n }\n\n // Use the SDK's earnCreateAccount method to check if account exists\n // - If response has `transaction` field → account doesn't exist yet\n // - If response has no `transaction` field → account already exists\n const response = await client.earn.earnCreateAccount({\n chain: chain as any,\n owner: owner as `0x${string}`,\n sender: owner as `0x${string}`,\n estimateGas: false,\n });\n\n const earnAccountAddress = response.earnAccountAddress;\n const hasTransaction = !!response.transaction;\n\n return jsonResponse({\n earnAccountAddress,\n isDeployed: !hasTransaction,\n needsCreation: hasTransaction,\n });\n}\n\ninterface CreateAccountBody {\n owner: string;\n chain?: string;\n}\n\nasync function handleCreateAccount(\n client: CompassApiSDK,\n body: CreateAccountBody,\n config: CompassHandlerConfig\n): Promise<Response> {\n const { owner, chain = 'base' } = body;\n const { gasSponsorPrivateKey, rpcUrls } = config;\n\n if (!owner) {\n return jsonResponse({ error: 'Missing owner parameter' }, 400);\n }\n\n if (!gasSponsorPrivateKey) {\n return jsonResponse(\n { error: 'Gas sponsor not configured. Set gasSponsorPrivateKey in handler config.' },\n 500\n );\n }\n\n // Get chain config\n const viemChain = CHAIN_MAP[chain.toLowerCase()];\n if (!viemChain) {\n return jsonResponse({ error: `Unsupported chain: ${chain}` }, 500);\n }\n\n // Get RPC URL for the chain\n const rpcUrl = rpcUrls?.[chain.toLowerCase() as keyof typeof rpcUrls];\n if (!rpcUrl) {\n return jsonResponse({ error: `No RPC URL configured for chain: ${chain}` }, 500);\n }\n\n // Create sponsor account from private key\n const sponsorAccount = privateKeyToAccount(gasSponsorPrivateKey as Hex);\n\n // Create wallet client for signing/sending transactions\n const walletClient = createWalletClient({\n account: sponsorAccount,\n chain: viemChain,\n transport: http(rpcUrl),\n });\n\n // Create public client for waiting on receipts\n const publicClient = createPublicClient({\n chain: viemChain,\n transport: http(rpcUrl),\n });\n\n // Get create account transaction from API with sponsor as sender\n const response = await client.earn.earnCreateAccount({\n chain: chain as any,\n owner: owner as `0x${string}`,\n sender: sponsorAccount.address,\n estimateGas: false,\n });\n\n const earnAccountAddress = response.earnAccountAddress;\n\n // If no transaction needed (account may already exist)\n if (!response.transaction) {\n return jsonResponse({\n earnAccountAddress,\n success: true,\n alreadyExists: true,\n });\n }\n\n // Send the transaction from the sponsor wallet\n const transaction = response.transaction;\n const txHash = await walletClient.sendTransaction({\n to: transaction.to as Hex,\n data: transaction.data as Hex,\n value: transaction.value ? BigInt(transaction.value) : 0n,\n gas: transaction.gas ? BigInt(transaction.gas) : undefined,\n });\n\n // Wait for the transaction receipt\n const receipt = await publicClient.waitForTransactionReceipt({\n hash: txHash,\n });\n\n // Check if transaction reverted\n if (receipt.status === 'reverted') {\n return jsonResponse({ error: 'Account creation transaction reverted' }, 500);\n }\n\n return jsonResponse({\n earnAccountAddress,\n txHash,\n success: true,\n });\n}\n\n// --- Manage (Deposit/Withdraw) Handlers ---\n\ninterface ManagePrepareBody {\n amount: string;\n token: string;\n owner: string;\n chain: string;\n venueType: 'VAULT' | 'AAVE' | 'PENDLE_PT';\n vaultAddress?: string;\n marketAddress?: string;\n maxSlippagePercent?: number;\n}\n\ntype ManageAction = 'DEPOSIT' | 'WITHDRAW';\n\nasync function handleManagePrepare(\n client: CompassApiSDK,\n body: ManagePrepareBody,\n action: ManageAction\n): Promise<Response> {\n const { amount, token, owner, chain, venueType, vaultAddress, marketAddress, maxSlippagePercent } = body;\n\n // Build venue based on type\n let venue: any;\n\n if (venueType === 'VAULT' && vaultAddress) {\n venue = {\n type: 'VAULT' as const,\n vaultAddress,\n };\n } else if (venueType === 'AAVE') {\n venue = {\n type: 'AAVE' as const,\n token,\n };\n } else if (venueType === 'PENDLE_PT' && marketAddress) {\n venue = {\n type: 'PENDLE_PT' as const,\n marketAddress,\n token: action === 'DEPOSIT' ? token : undefined,\n maxSlippagePercent: maxSlippagePercent ?? 1.0,\n };\n } else {\n return jsonResponse({ error: 'Invalid venue type or missing address' }, 400);\n }\n\n const response = await client.earn.earnManage({\n owner,\n chain: chain as any,\n venue,\n action,\n amount,\n gasSponsorship: true,\n });\n\n // Extract EIP-712 data for signing\n const eip712 = (response as any).eip712;\n if (!eip712) {\n return jsonResponse({ error: 'No EIP-712 data returned from API' }, 500);\n }\n\n // Normalize types for wallet signing\n // SDK returns camelCase keys (safeTx, eip712Domain) but wallets expect PascalCase (SafeTx)\n // to match the primaryType\n const types = eip712.types as any;\n const normalizedTypes = {\n EIP712Domain: types.eip712Domain,\n SafeTx: types.safeTx,\n };\n\n return jsonResponse({\n eip712,\n normalizedTypes,\n domain: eip712.domain,\n message: eip712.message,\n });\n}\n\n// --- Execute Handler (shared for all operations) ---\n\ninterface ExecuteBody {\n owner: string;\n eip712: any;\n signature: string;\n chain: string;\n}\n\nasync function handleExecute(\n client: CompassApiSDK,\n body: ExecuteBody,\n config: CompassHandlerConfig\n): Promise<Response> {\n const { owner, eip712, signature, chain } = body;\n const { gasSponsorPrivateKey, rpcUrls } = config;\n\n // Validate gas sponsor config\n if (!gasSponsorPrivateKey) {\n return jsonResponse(\n { error: 'Gas sponsor not configured. Set gasSponsorPrivateKey in handler config.' },\n 500\n );\n }\n\n // Get chain config\n const viemChain = CHAIN_MAP[chain.toLowerCase()];\n if (!viemChain) {\n return jsonResponse({ error: `Unsupported chain: ${chain}` }, 500);\n }\n\n // Get RPC URL for the chain\n const rpcUrl = rpcUrls?.[chain.toLowerCase() as keyof typeof rpcUrls];\n if (!rpcUrl) {\n return jsonResponse({ error: `No RPC URL configured for chain: ${chain}` }, 500);\n }\n\n // Create sponsor account from private key\n const sponsorAccount = privateKeyToAccount(gasSponsorPrivateKey as Hex);\n\n // Create wallet client for signing/sending transactions\n const walletClient = createWalletClient({\n account: sponsorAccount,\n chain: viemChain,\n transport: http(rpcUrl),\n });\n\n // Create public client for waiting on receipts\n const publicClient = createPublicClient({\n chain: viemChain,\n transport: http(rpcUrl),\n });\n\n // Call gas sponsorship prepare with sponsor as sender\n const response = await client.gasSponsorship.gasSponsorshipPrepare({\n chain: chain as any,\n owner,\n sender: sponsorAccount.address,\n eip712,\n signature,\n });\n\n // Extract the unsigned transaction from the response\n const transaction = (response as any).transaction;\n if (!transaction) {\n return jsonResponse(\n { error: 'No transaction returned from gas sponsorship prepare' },\n 500\n );\n }\n\n // Send the transaction from the sponsor wallet\n const txHash = await walletClient.sendTransaction({\n to: transaction.to as Hex,\n data: transaction.data as Hex,\n value: transaction.value ? BigInt(transaction.value) : 0n,\n gas: transaction.gas ? BigInt(transaction.gas) : undefined,\n });\n\n // Wait for the transaction receipt\n const receipt = await publicClient.waitForTransactionReceipt({\n hash: txHash,\n });\n\n // Check if transaction reverted\n if (receipt.status === 'reverted') {\n return jsonResponse({ error: 'Transaction reverted' }, 500);\n }\n\n return jsonResponse({ txHash, success: true });\n}\n"]}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { CompassApiSDK } from '@compass-labs/api-sdk';
|
|
2
|
+
import { createWalletClient, http, createPublicClient } from 'viem';
|
|
3
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
4
|
+
import { arbitrum, base, mainnet } from 'viem/chains';
|
|
5
|
+
|
|
6
|
+
// src/server/handler.ts
|
|
7
|
+
var CHAIN_MAP = {
|
|
8
|
+
ethereum: mainnet,
|
|
9
|
+
base,
|
|
10
|
+
arbitrum
|
|
11
|
+
};
|
|
12
|
+
function createCompassHandler(config) {
|
|
13
|
+
const { apiKey, serverUrl = "https://api.compasslabs.ai" } = config;
|
|
14
|
+
const client = new CompassApiSDK({
|
|
15
|
+
apiKeyAuth: apiKey,
|
|
16
|
+
serverURL: serverUrl
|
|
17
|
+
});
|
|
18
|
+
return async function handler(request, context) {
|
|
19
|
+
try {
|
|
20
|
+
const { path } = await context.params;
|
|
21
|
+
const route = path.join("/");
|
|
22
|
+
const method = request.method;
|
|
23
|
+
if (method === "GET") {
|
|
24
|
+
const url = new URL(request.url);
|
|
25
|
+
const searchParams = Object.fromEntries(url.searchParams.entries());
|
|
26
|
+
switch (route) {
|
|
27
|
+
case "earn-account/check":
|
|
28
|
+
return await handleEarnAccountCheck(client, searchParams);
|
|
29
|
+
default:
|
|
30
|
+
return jsonResponse({ error: `Unknown GET route: ${route}` }, 404);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (method === "POST") {
|
|
34
|
+
const body = await request.json();
|
|
35
|
+
switch (route) {
|
|
36
|
+
case "create-account":
|
|
37
|
+
return await handleCreateAccount(client, body, config);
|
|
38
|
+
case "deposit/prepare":
|
|
39
|
+
return await handleManagePrepare(client, body, "DEPOSIT");
|
|
40
|
+
case "deposit/execute":
|
|
41
|
+
return await handleExecute(client, body, config);
|
|
42
|
+
case "withdraw/prepare":
|
|
43
|
+
return await handleManagePrepare(client, body, "WITHDRAW");
|
|
44
|
+
case "withdraw/execute":
|
|
45
|
+
return await handleExecute(client, body, config);
|
|
46
|
+
default:
|
|
47
|
+
return jsonResponse({ error: `Unknown POST route: ${route}` }, 404);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return jsonResponse({ error: `Method ${method} not allowed` }, 405);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("[Compass Handler Error]", error);
|
|
53
|
+
const message = error instanceof Error ? error.message : "Internal server error";
|
|
54
|
+
return jsonResponse({ error: message }, 500);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function jsonResponse(data, status = 200) {
|
|
59
|
+
return new Response(JSON.stringify(data), {
|
|
60
|
+
status,
|
|
61
|
+
headers: { "Content-Type": "application/json" }
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async function handleEarnAccountCheck(client, params) {
|
|
65
|
+
const { owner, chain = "base" } = params;
|
|
66
|
+
if (!owner) {
|
|
67
|
+
return jsonResponse({ error: "Missing owner parameter" }, 400);
|
|
68
|
+
}
|
|
69
|
+
const response = await client.earn.earnCreateAccount({
|
|
70
|
+
chain,
|
|
71
|
+
owner,
|
|
72
|
+
sender: owner,
|
|
73
|
+
estimateGas: false
|
|
74
|
+
});
|
|
75
|
+
const earnAccountAddress = response.earnAccountAddress;
|
|
76
|
+
const hasTransaction = !!response.transaction;
|
|
77
|
+
return jsonResponse({
|
|
78
|
+
earnAccountAddress,
|
|
79
|
+
isDeployed: !hasTransaction,
|
|
80
|
+
needsCreation: hasTransaction
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async function handleCreateAccount(client, body, config) {
|
|
84
|
+
const { owner, chain = "base" } = body;
|
|
85
|
+
const { gasSponsorPrivateKey, rpcUrls } = config;
|
|
86
|
+
if (!owner) {
|
|
87
|
+
return jsonResponse({ error: "Missing owner parameter" }, 400);
|
|
88
|
+
}
|
|
89
|
+
if (!gasSponsorPrivateKey) {
|
|
90
|
+
return jsonResponse(
|
|
91
|
+
{ error: "Gas sponsor not configured. Set gasSponsorPrivateKey in handler config." },
|
|
92
|
+
500
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const viemChain = CHAIN_MAP[chain.toLowerCase()];
|
|
96
|
+
if (!viemChain) {
|
|
97
|
+
return jsonResponse({ error: `Unsupported chain: ${chain}` }, 500);
|
|
98
|
+
}
|
|
99
|
+
const rpcUrl = rpcUrls?.[chain.toLowerCase()];
|
|
100
|
+
if (!rpcUrl) {
|
|
101
|
+
return jsonResponse({ error: `No RPC URL configured for chain: ${chain}` }, 500);
|
|
102
|
+
}
|
|
103
|
+
const sponsorAccount = privateKeyToAccount(gasSponsorPrivateKey);
|
|
104
|
+
const walletClient = createWalletClient({
|
|
105
|
+
account: sponsorAccount,
|
|
106
|
+
chain: viemChain,
|
|
107
|
+
transport: http(rpcUrl)
|
|
108
|
+
});
|
|
109
|
+
const publicClient = createPublicClient({
|
|
110
|
+
chain: viemChain,
|
|
111
|
+
transport: http(rpcUrl)
|
|
112
|
+
});
|
|
113
|
+
const response = await client.earn.earnCreateAccount({
|
|
114
|
+
chain,
|
|
115
|
+
owner,
|
|
116
|
+
sender: sponsorAccount.address,
|
|
117
|
+
estimateGas: false
|
|
118
|
+
});
|
|
119
|
+
const earnAccountAddress = response.earnAccountAddress;
|
|
120
|
+
if (!response.transaction) {
|
|
121
|
+
return jsonResponse({
|
|
122
|
+
earnAccountAddress,
|
|
123
|
+
success: true,
|
|
124
|
+
alreadyExists: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const transaction = response.transaction;
|
|
128
|
+
const txHash = await walletClient.sendTransaction({
|
|
129
|
+
to: transaction.to,
|
|
130
|
+
data: transaction.data,
|
|
131
|
+
value: transaction.value ? BigInt(transaction.value) : 0n,
|
|
132
|
+
gas: transaction.gas ? BigInt(transaction.gas) : void 0
|
|
133
|
+
});
|
|
134
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
135
|
+
hash: txHash
|
|
136
|
+
});
|
|
137
|
+
if (receipt.status === "reverted") {
|
|
138
|
+
return jsonResponse({ error: "Account creation transaction reverted" }, 500);
|
|
139
|
+
}
|
|
140
|
+
return jsonResponse({
|
|
141
|
+
earnAccountAddress,
|
|
142
|
+
txHash,
|
|
143
|
+
success: true
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async function handleManagePrepare(client, body, action) {
|
|
147
|
+
const { amount, token, owner, chain, venueType, vaultAddress, marketAddress, maxSlippagePercent } = body;
|
|
148
|
+
let venue;
|
|
149
|
+
if (venueType === "VAULT" && vaultAddress) {
|
|
150
|
+
venue = {
|
|
151
|
+
type: "VAULT",
|
|
152
|
+
vaultAddress
|
|
153
|
+
};
|
|
154
|
+
} else if (venueType === "AAVE") {
|
|
155
|
+
venue = {
|
|
156
|
+
type: "AAVE",
|
|
157
|
+
token
|
|
158
|
+
};
|
|
159
|
+
} else if (venueType === "PENDLE_PT" && marketAddress) {
|
|
160
|
+
venue = {
|
|
161
|
+
type: "PENDLE_PT",
|
|
162
|
+
marketAddress,
|
|
163
|
+
token: action === "DEPOSIT" ? token : void 0,
|
|
164
|
+
maxSlippagePercent: maxSlippagePercent ?? 1
|
|
165
|
+
};
|
|
166
|
+
} else {
|
|
167
|
+
return jsonResponse({ error: "Invalid venue type or missing address" }, 400);
|
|
168
|
+
}
|
|
169
|
+
const response = await client.earn.earnManage({
|
|
170
|
+
owner,
|
|
171
|
+
chain,
|
|
172
|
+
venue,
|
|
173
|
+
action,
|
|
174
|
+
amount,
|
|
175
|
+
gasSponsorship: true
|
|
176
|
+
});
|
|
177
|
+
const eip712 = response.eip712;
|
|
178
|
+
if (!eip712) {
|
|
179
|
+
return jsonResponse({ error: "No EIP-712 data returned from API" }, 500);
|
|
180
|
+
}
|
|
181
|
+
const types = eip712.types;
|
|
182
|
+
const normalizedTypes = {
|
|
183
|
+
EIP712Domain: types.eip712Domain,
|
|
184
|
+
SafeTx: types.safeTx
|
|
185
|
+
};
|
|
186
|
+
return jsonResponse({
|
|
187
|
+
eip712,
|
|
188
|
+
normalizedTypes,
|
|
189
|
+
domain: eip712.domain,
|
|
190
|
+
message: eip712.message
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function handleExecute(client, body, config) {
|
|
194
|
+
const { owner, eip712, signature, chain } = body;
|
|
195
|
+
const { gasSponsorPrivateKey, rpcUrls } = config;
|
|
196
|
+
if (!gasSponsorPrivateKey) {
|
|
197
|
+
return jsonResponse(
|
|
198
|
+
{ error: "Gas sponsor not configured. Set gasSponsorPrivateKey in handler config." },
|
|
199
|
+
500
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
const viemChain = CHAIN_MAP[chain.toLowerCase()];
|
|
203
|
+
if (!viemChain) {
|
|
204
|
+
return jsonResponse({ error: `Unsupported chain: ${chain}` }, 500);
|
|
205
|
+
}
|
|
206
|
+
const rpcUrl = rpcUrls?.[chain.toLowerCase()];
|
|
207
|
+
if (!rpcUrl) {
|
|
208
|
+
return jsonResponse({ error: `No RPC URL configured for chain: ${chain}` }, 500);
|
|
209
|
+
}
|
|
210
|
+
const sponsorAccount = privateKeyToAccount(gasSponsorPrivateKey);
|
|
211
|
+
const walletClient = createWalletClient({
|
|
212
|
+
account: sponsorAccount,
|
|
213
|
+
chain: viemChain,
|
|
214
|
+
transport: http(rpcUrl)
|
|
215
|
+
});
|
|
216
|
+
const publicClient = createPublicClient({
|
|
217
|
+
chain: viemChain,
|
|
218
|
+
transport: http(rpcUrl)
|
|
219
|
+
});
|
|
220
|
+
const response = await client.gasSponsorship.gasSponsorshipPrepare({
|
|
221
|
+
chain,
|
|
222
|
+
owner,
|
|
223
|
+
sender: sponsorAccount.address,
|
|
224
|
+
eip712,
|
|
225
|
+
signature
|
|
226
|
+
});
|
|
227
|
+
const transaction = response.transaction;
|
|
228
|
+
if (!transaction) {
|
|
229
|
+
return jsonResponse(
|
|
230
|
+
{ error: "No transaction returned from gas sponsorship prepare" },
|
|
231
|
+
500
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
const txHash = await walletClient.sendTransaction({
|
|
235
|
+
to: transaction.to,
|
|
236
|
+
data: transaction.data,
|
|
237
|
+
value: transaction.value ? BigInt(transaction.value) : 0n,
|
|
238
|
+
gas: transaction.gas ? BigInt(transaction.gas) : void 0
|
|
239
|
+
});
|
|
240
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
241
|
+
hash: txHash
|
|
242
|
+
});
|
|
243
|
+
if (receipt.status === "reverted") {
|
|
244
|
+
return jsonResponse({ error: "Transaction reverted" }, 500);
|
|
245
|
+
}
|
|
246
|
+
return jsonResponse({ txHash, success: true });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export { createCompassHandler };
|
|
250
|
+
//# sourceMappingURL=index.mjs.map
|
|
251
|
+
//# sourceMappingURL=index.mjs.map
|