@zerodev/smart-recipes 0.2.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/dist/index.js ADDED
@@ -0,0 +1,274 @@
1
+ // src/errors.ts
2
+ var SmartRecipeError = class extends Error {
3
+ constructor(code, message, details) {
4
+ super(message);
5
+ this.code = code;
6
+ this.details = details;
7
+ this.name = "SmartRecipeError";
8
+ }
9
+ code;
10
+ details;
11
+ };
12
+ var ERROR_MAP = /* @__PURE__ */ new Map();
13
+ function defineError(name, code) {
14
+ const ctor = class extends SmartRecipeError {
15
+ constructor(message, details) {
16
+ super(code, message, details);
17
+ this.name = name;
18
+ }
19
+ };
20
+ Object.defineProperty(ctor, "name", { value: name });
21
+ ERROR_MAP.set(code, ctor);
22
+ return ctor;
23
+ }
24
+ var InvalidRequestError = defineError("InvalidRequestError", "INVALID_REQUEST");
25
+ var ServiceUnavailableError = defineError(
26
+ "ServiceUnavailableError",
27
+ "SERVICE_UNAVAILABLE"
28
+ );
29
+ var InternalError = defineError("InternalError", "INTERNAL_ERROR");
30
+ var SraNotFoundError = defineError("SraNotFoundError", "SRA_NOT_FOUND");
31
+ var SraUnavailableError = defineError("SraUnavailableError", "SRA_UNAVAILABLE");
32
+ var ChainNotSupportedError = defineError("ChainNotSupportedError", "CHAIN_NOT_SUPPORTED");
33
+ var VaultCapExceededError = defineError("VaultCapExceededError", "VAULT_CAP_EXCEEDED");
34
+ var AssetMismatchError = defineError("AssetMismatchError", "ASSET_MISMATCH");
35
+ var UnknownProtocolError = defineError("UnknownProtocolError", "UNKNOWN_PROTOCOL");
36
+ var VaultTypeMismatchError = defineError("VaultTypeMismatchError", "VAULT_TYPE_MISMATCH");
37
+ var RpcUnavailableError = defineError("RpcUnavailableError", "RPC_UNAVAILABLE");
38
+ var QuoterUnavailableError = defineError("QuoterUnavailableError", "QUOTER_UNAVAILABLE");
39
+ var UnsupportedTokenError = defineError("UnsupportedTokenError", "UNSUPPORTED_TOKEN");
40
+ var InsufficientAmountError = defineError(
41
+ "InsufficientAmountError",
42
+ "INSUFFICIENT_AMOUNT"
43
+ );
44
+ var SwapRouteNotFoundError = defineError("SwapRouteNotFoundError", "SWAP_ROUTE_NOT_FOUND");
45
+ var QuoteExpiredError = defineError("QuoteExpiredError", "QUOTE_EXPIRED");
46
+ var SanctionedAddressError = defineError("SanctionedAddressError", "SANCTIONED_ADDRESS");
47
+ var VaultBlockedError = defineError("VaultBlockedError", "VAULT_BLOCKED");
48
+ function mapErrorCode(code, message, details) {
49
+ const Ctor = ERROR_MAP.get(code);
50
+ if (Ctor) return new Ctor(message, details);
51
+ return new SmartRecipeError(code, message, details);
52
+ }
53
+
54
+ // src/client.ts
55
+ var DEFAULT_WATCH_INTERVAL = 4e3;
56
+ function createSmartRecipes(config) {
57
+ const { serverUrl, projectId } = config;
58
+ const fetchFn = config.fetch ?? globalThis.fetch;
59
+ const base = serverUrl.replace(/\/$/, "");
60
+ async function handleError(res) {
61
+ const errBody = await res.json().catch(() => ({ error: { code: "INTERNAL_ERROR", message: "Unknown error" } }));
62
+ throw mapErrorCode(errBody.error.code, errBody.error.message, errBody.error.details);
63
+ }
64
+ async function post(path, body) {
65
+ const res = await fetchFn(`${base}${path}`, {
66
+ method: "POST",
67
+ headers: { "content-type": "application/json", "x-project-id": projectId },
68
+ body: JSON.stringify(body)
69
+ });
70
+ if (!res.ok) return handleError(res);
71
+ return await res.json();
72
+ }
73
+ async function get(path) {
74
+ const res = await fetchFn(`${base}${path}`, {
75
+ method: "GET",
76
+ headers: { "x-project-id": projectId }
77
+ });
78
+ if (!res.ok) return handleError(res);
79
+ return await res.json();
80
+ }
81
+ function normalizeInto(into) {
82
+ if (into == null) return void 0;
83
+ return typeof into === "string" ? into : into.id;
84
+ }
85
+ function resolveDestChainId(p) {
86
+ if (p.into != null && typeof p.into !== "string") {
87
+ if (p.destChainId != null && p.destChainId !== p.into.chainId) {
88
+ throw new InvalidRequestError(
89
+ `destChainId ${p.destChainId} conflicts with vault.chainId ${p.into.chainId}`
90
+ );
91
+ }
92
+ return p.into.chainId;
93
+ }
94
+ if (p.destChainId == null) {
95
+ throw new InvalidRequestError(
96
+ "destChainId is required unless `into` is a Vault object (which carries its own chainId)"
97
+ );
98
+ }
99
+ return p.destChainId;
100
+ }
101
+ function toDepositBody(p, destChainId, protocol) {
102
+ return {
103
+ owner: p.owner,
104
+ amount: p.amount,
105
+ token: p.token,
106
+ srcChainId: p.srcChainId,
107
+ destChainId,
108
+ into: normalizeInto(p.into),
109
+ slippage: p.slippage,
110
+ protocol
111
+ };
112
+ }
113
+ function deposit(p, protocol) {
114
+ const destChainId = resolveDestChainId(p);
115
+ const path = p.srcChainId === destChainId ? "/api/v1/recipes/deposit-into-vault" : "/api/v1/recipes/bridge-and-deposit";
116
+ return post(path, toDepositBody(p, destChainId, protocol));
117
+ }
118
+ const aave = {
119
+ /** Aave V3 supply. Optional `into` = an Aave Earn listing selecting the reserve to supply. */
120
+ deposit: (p) => deposit(p, "aave")
121
+ };
122
+ const morpho = { deposit: (p) => deposit(p, "morpho") };
123
+ const fluid = { deposit: (p) => deposit(p, "fluid") };
124
+ const yearn = { deposit: (p) => deposit(p, "yearn") };
125
+ const erc4626 = { deposit: (p) => deposit(p, "erc4626") };
126
+ function depositIntoVault(p) {
127
+ return deposit(p, p.protocol);
128
+ }
129
+ function bridgeAndSwap(p) {
130
+ const body = {
131
+ owner: p.owner,
132
+ amount: p.amount,
133
+ token: p.token,
134
+ toToken: p.toToken,
135
+ srcChainId: p.srcChainId,
136
+ destChainId: p.destChainId,
137
+ slippage: p.slippage
138
+ };
139
+ return post("/api/v1/recipes/bridge-and-swap", body);
140
+ }
141
+ async function listVaults(params = {}) {
142
+ const q = new URLSearchParams();
143
+ if (params.asset) q.set("asset", params.asset);
144
+ if (params.protocol) q.set("protocol", params.protocol);
145
+ if (params.chains?.length) q.set("chains", params.chains.join(","));
146
+ const qs = q.toString();
147
+ const data = await get(`/api/v1/vaults${qs ? `?${qs}` : ""}`);
148
+ return data.vaults;
149
+ }
150
+ function getVault(vaultId) {
151
+ return get(`/api/v1/vaults/${vaultId}`);
152
+ }
153
+ function getStatus(sra) {
154
+ return get(`/api/v1/recipes/${sra}/status`);
155
+ }
156
+ function watchStatus(sra, opts) {
157
+ const interval = opts.interval ?? DEFAULT_WATCH_INTERVAL;
158
+ let lastState;
159
+ let stopped = false;
160
+ const tick = async () => {
161
+ if (stopped) return;
162
+ try {
163
+ const status = await getStatus(sra);
164
+ if (status.state !== lastState) {
165
+ lastState = status.state;
166
+ opts.onStatusChange(status);
167
+ }
168
+ if (status.state === "COMPLETED" || status.state === "FAILED") stop();
169
+ } catch {
170
+ }
171
+ };
172
+ const handle = setInterval(tick, interval);
173
+ void tick();
174
+ function stop() {
175
+ stopped = true;
176
+ clearInterval(handle);
177
+ }
178
+ return stop;
179
+ }
180
+ function getSraInfo(params) {
181
+ return post("/api/v1/recipes/sra/info", params);
182
+ }
183
+ function getWithdrawCalls(params) {
184
+ return post("/api/v1/recipes/sra/withdraw", params);
185
+ }
186
+ function getSraFeeEstimates(params) {
187
+ return post("/api/v1/recipes/sra/fee-estimates", params);
188
+ }
189
+ function getDepositStatus(params) {
190
+ return post("/api/v1/recipes/sra/deposit-status", params);
191
+ }
192
+ function preflight(params) {
193
+ return post("/api/v1/recipes/preflight", params);
194
+ }
195
+ function getBalances(params) {
196
+ return post("/api/v1/recipes/balances", params);
197
+ }
198
+ async function getChains() {
199
+ const data = await get("/api/v1/discovery/chains");
200
+ return data.chains;
201
+ }
202
+ async function getTokens(params) {
203
+ const data = await post("/api/v1/discovery/tokens", params ?? {});
204
+ return data.tokens;
205
+ }
206
+ function listOpportunities(params = {}) {
207
+ return post("/api/v1/recipes/earn/opportunities", params);
208
+ }
209
+ return {
210
+ aave,
211
+ morpho,
212
+ fluid,
213
+ yearn,
214
+ erc4626,
215
+ depositIntoVault,
216
+ bridgeAndSwap,
217
+ listVaults,
218
+ getVault,
219
+ getStatus,
220
+ watchStatus,
221
+ getSraInfo,
222
+ getWithdrawCalls,
223
+ getSraFeeEstimates,
224
+ getDepositStatus,
225
+ preflight,
226
+ getBalances,
227
+ getChains,
228
+ getTokens,
229
+ listOpportunities
230
+ };
231
+ }
232
+
233
+ // src/errors-codes.ts
234
+ var ERROR_CODES = [
235
+ "INVALID_REQUEST",
236
+ "SERVICE_UNAVAILABLE",
237
+ "QUOTER_UNAVAILABLE",
238
+ "SRA_UNAVAILABLE",
239
+ "SRA_NOT_FOUND",
240
+ "VAULT_NOT_ALLOWLISTED",
241
+ "CHAIN_NOT_SUPPORTED",
242
+ "VAULT_CAP_EXCEEDED",
243
+ "ASSET_MISMATCH",
244
+ "UNKNOWN_PROTOCOL",
245
+ "VAULT_TYPE_MISMATCH",
246
+ "RPC_UNAVAILABLE",
247
+ "INTERNAL_ERROR",
248
+ // Doc-locked codes (SMART_RECIPES_SDK.md § Server error contract)
249
+ "UNSUPPORTED_TOKEN",
250
+ "INSUFFICIENT_AMOUNT",
251
+ "SWAP_ROUTE_NOT_FOUND",
252
+ "QUOTE_EXPIRED",
253
+ "SANCTIONED_ADDRESS",
254
+ "VAULT_BLOCKED"
255
+ ];
256
+ function isErrorCode(code) {
257
+ return ERROR_CODES.includes(code);
258
+ }
259
+
260
+ // src/tokens.ts
261
+ var TOKENS = {
262
+ USDC: "USDC",
263
+ USDT: "USDT",
264
+ DAI: "DAI",
265
+ WETH: "WETH",
266
+ WBTC: "WBTC",
267
+ EURC: "EURC",
268
+ NATIVE: "NATIVE"
269
+ };
270
+ function isTokenSymbol(value) {
271
+ return value in TOKENS;
272
+ }
273
+
274
+ export { AssetMismatchError, ChainNotSupportedError, ERROR_CODES, InsufficientAmountError, InternalError, InvalidRequestError, QuoteExpiredError, QuoterUnavailableError, RpcUnavailableError, SanctionedAddressError, ServiceUnavailableError, SmartRecipeError, SraNotFoundError, SraUnavailableError, SwapRouteNotFoundError, TOKENS, UnknownProtocolError, UnsupportedTokenError, VaultBlockedError, VaultCapExceededError, VaultTypeMismatchError, createSmartRecipes, isErrorCode, isTokenSymbol, mapErrorCode };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@zerodev/smart-recipes",
3
+ "version": "0.2.0",
4
+ "description": "client sdk",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "type": "module",
9
+ "main": "./dist/index.cjs",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "dependencies": {
23
+ "viem": "^2.21.40"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20.16.11",
27
+ "tsup": "^8.3.5",
28
+ "typescript": "^5.6.3",
29
+ "vitest": "3.2.6"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "dev": "tsup --watch",
34
+ "lint": "biome check src",
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "vitest run"
37
+ }
38
+ }