Skip to content

Commit f9178a8

Browse files
committed
Load ServerConfig Env Var
1 parent 61e8c68 commit f9178a8

13 files changed

Lines changed: 2402 additions & 268 deletions

File tree

.env.example

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
1-
BITTE_API_KEY=
2-
NEAR_PK=
31
NEXT_PUBLIC_ACCOUNT_ID=
4-
CRON_SECRET=
2+
DEPLOYMENT_CONFIG=

app/api/withdraw/route.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ import { NextRequest, NextResponse } from "next/server";
22
import { BALANCE_UPDATE_DELAY, USDC_CONTRACT } from "@/lib/utils";
33
import {
44
initializeNearAccount,
5-
depositUSDC,
6-
getUSDCBalance,
7-
intentsUSDCBalance,
8-
withdrawUSDC,
95
withdrawToken,
106
intentsBalance,
117
} from "@/lib/near";
@@ -46,7 +42,7 @@ export async function GET(request: NextRequest) {
4642
const account = await initializeNearAccount(accountId);
4743

4844
const usdcBalance = await intentsBalance(account, token);
49-
let withdrawAmount = bigIntMin(requestedWithdrawAmount, usdcBalance);
45+
const withdrawAmount = bigIntMin(requestedWithdrawAmount, usdcBalance);
5046

5147
if (withdrawAmount == ZERO) {
5248
return NextResponse.json(

jest.config.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// eslint-disable-next-line @typescript-eslint/no-require-imports
2+
const nextJest = require("next/jest");
3+
4+
const createJestConfig = nextJest({
5+
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
6+
dir: "./",
7+
});
8+
9+
// Add any custom config to be passed to Jest
10+
const customJestConfig = {
11+
testEnvironment: "node",
12+
};
13+
14+
process.env.TZ = "UTC";
15+
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
16+
module.exports = createJestConfig(customJestConfig);

lib/agent-context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
import { getCurrentPositions } from "./api-helpers";
88
import type { AgentContext, PositionWithPnL } from "./types";
99
import { TOKEN_LIST } from "./utils";
10-
import { getEnvStrategy } from "./strategies";
10+
import { loadConfig } from "./config";
1111

1212
export async function buildAgentContext(
1313
accountId: string,
@@ -93,7 +93,7 @@ function generateSystemPrompt(
9393
positionsWithPnl: PositionWithPnL[],
9494
marketOverviewData: string,
9595
): string {
96-
const strategy = getEnvStrategy();
96+
const strategy = loadConfig().strategy;
9797
return `
9898
9999
=== PORTFOLIO DATA ===

lib/api-auth.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { NextRequest, NextResponse } from "next/server";
2-
import { getEnvVar } from "./env";
2+
import { loadConfig } from "./config";
33

44
type AuthenticatedCallHandler = (req: NextRequest) => Promise<NextResponse>;
55

66
export function withCronSecret(handler: AuthenticatedCallHandler) {
77
return async (req: NextRequest): Promise<NextResponse> => {
8+
const { cronSecret } = loadConfig();
89
const authHeader = req.headers.get("Authorization");
9-
if (authHeader !== `Bearer ${getEnvVar("CRON_SECRET")}`) {
10+
if (authHeader !== `Bearer ${cronSecret}`) {
1011
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
1112
}
1213

lib/api-helpers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getEnvVar } from "./env";
1+
import { loadConfig } from "./config";
22
import { Quote, PositionWithPnL, CurrentPosition } from "./types";
33
import { TOKEN_LIST } from "./utils";
44

@@ -10,9 +10,10 @@ interface ApiCallOptions {
1010
}
1111

1212
async function makeApiCall(endpoint: string, options: ApiCallOptions) {
13+
const { bitteKey } = loadConfig();
1314
const headers: Record<string, string> = {
1415
"Content-Type": "application/json",
15-
Authorization: `Bearer ${getEnvVar("BITTE_API_KEY")}`,
16+
Authorization: `Bearer ${bitteKey}`,
1617
...options.headers,
1718
};
1819

lib/config.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { z } from "zod";
2+
import { getEnvVar } from "./env";
3+
4+
const StrategyConfigSchema = z.object({
5+
overview: z.string(),
6+
riskParams: z.object({
7+
profitTarget: z.number(),
8+
stopLoss: z.number(),
9+
maxPositions: z.number(),
10+
positionSize: z.string(),
11+
}),
12+
step1Rules: z.string(),
13+
step2Rules: z.string(),
14+
step3Rules: z.string(),
15+
});
16+
17+
export const DEFAULT_STRATEGY: StrategyConfig = {
18+
overview:
19+
"Wall Street 3-Step: Data-driven day trading with clear profit/loss targets and risk management",
20+
riskParams: {
21+
profitTarget: 2,
22+
stopLoss: -1.5,
23+
maxPositions: 4,
24+
positionSize: "5-15% of USDC",
25+
},
26+
step1Rules:
27+
"Risk targets: SELL at +2% profit OR -1.5% loss. Close losing positions faster than winners (cut losses, let profits run). Don't close positions with raw balance below 1000.",
28+
step2Rules:
29+
"Screen for high-probability setups: Price momentum >3% with volume confirmation, Fear/Greed extremes, Order book imbalances. Use 1 analysis tool only if market data insufficient. Only trade clear directional moves.",
30+
step3Rules:
31+
"Dynamic sizing: 5-15% per trade (scales with account). Size calculation: Min($10, Max($5, USDC_balance * 0.10)). Account for slippage: Minimum $8 positions. Max 3-4 open positions at once.",
32+
};
33+
34+
const ed25519Pattern = /^ed25519:[1-9A-HJ-NP-Za-km-z]{43,87}$/;
35+
36+
export const ed25519String = z
37+
.string()
38+
.regex(ed25519Pattern, "Invalid NEAR private key: must be ed25519:<base58>")
39+
.transform((val) => val as `ed25519:${string}`);
40+
41+
export const ServerConfigSchema = z.object({
42+
strategy: StrategyConfigSchema.optional().default(DEFAULT_STRATEGY),
43+
cronSecret: z.string(),
44+
bitteKey: z.string(),
45+
nearPk: ed25519String,
46+
});
47+
48+
export type StrategyConfig = z.infer<typeof StrategyConfigSchema>;
49+
export type ServerConfig = z.infer<typeof ServerConfigSchema>;
50+
51+
export function parseConfig(configString: string): ServerConfig {
52+
return ServerConfigSchema.parse(JSON.parse(configString));
53+
}
54+
55+
export function loadConfig(): ServerConfig {
56+
try {
57+
return parseConfig(getEnvVar("DEPLOYMENT_CONFIG"));
58+
} catch (error: unknown) {
59+
console.warn(
60+
"Failed to parse DEPLOYMENT_CONFIG, trying individual env vars",
61+
String(error),
62+
);
63+
return {
64+
strategy: JSON.parse(
65+
getEnvVar("STRATEGY", JSON.stringify(DEFAULT_STRATEGY)),
66+
),
67+
cronSecret: getEnvVar("CRON_SECRET"),
68+
bitteKey: getEnvVar("BITTE_API_KEY"),
69+
nearPk: getEnvVar("NEAR_PK") as `ed25519:${string}`,
70+
};
71+
}
72+
}

lib/near.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
TGas,
88
USDC_CONTRACT,
99
} from "./utils";
10-
import { getEnvVar } from "./env";
10+
import { loadConfig } from "./config";
1111

1212
const FIFTY_TGAS = BigInt(TGas * 50);
1313
const ONE_YOCTO = BigInt(1);
@@ -32,9 +32,8 @@ export async function getTokenBalance(
3232
export async function initializeNearAccount(
3333
accountId: string,
3434
): Promise<Account> {
35-
const keyPair = KeyPair.fromString(
36-
getEnvVar("NEAR_PK") as `ed25519:${string}`,
37-
);
35+
const { nearPk } = loadConfig();
36+
const keyPair = KeyPair.fromString(nearPk);
3837
const keyStore = new keyStores.InMemoryKeyStore();
3938
keyStore.setKey("mainnet", accountId, keyPair);
4039

lib/strategies/index.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

lib/strategies/types.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)