Skip to content

Commit b9edc94

Browse files
committed
Load ServerConfig Env Var
1 parent 65fc4c0 commit b9edc94

12 files changed

Lines changed: 1551 additions & 237 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=

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
@@ -3,7 +3,7 @@ import { calculatePositionsPnL, fetchMarketPrices, fetchPortfolioBalances } from
33
import { getCurrentPositions } from './api-helpers'
44
import type { AgentContext, PositionWithPnL } from './types'
55
import { TOKEN_LIST } from './utils'
6-
import { getEnvStrategy } from './strategies'
6+
import { loadConfig } from './config'
77

88
export async function buildAgentContext(
99
accountId: string,
@@ -84,7 +84,7 @@ function generateSystemPrompt(
8484
marketOverviewData: string,
8585
accountId: string
8686
): string {
87-
const strategy = getEnvStrategy()
87+
const strategy = loadConfig().strategy
8888

8989
const tradingPositions = positionsWithPnl.filter(
9090
(pos) => pos.symbol !== 'USDC' && Number(pos.rawBalance) >= 1000

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: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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('Failed to parse DEPLOYMENT_CONFIG, trying individual env vars', String(error))
60+
return {
61+
strategy: JSON.parse(getEnvVar('STRATEGY', JSON.stringify(DEFAULT_STRATEGY))),
62+
cronSecret: getEnvVar('CRON_SECRET'),
63+
bitteKey: getEnvVar('BITTE_API_KEY'),
64+
nearPk: getEnvVar('NEAR_PK') as `ed25519:${string}`,
65+
}
66+
}
67+
}

lib/near.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { actionCreators } from '@near-js/transactions'
22
import { Account, KeyPair, keyStores, Near } from 'near-api-js'
33
import type { Quote } from './types'
44
import { INTENTS_CONTRACT_ID, NEAR_RPC_URL, TGas, USDC_CONTRACT } from './utils'
5-
import { getEnvVar } from './env'
5+
import { loadConfig } from './config'
66

77
const FIFTY_TGAS = BigInt(TGas * 50)
88
const ONE_YOCTO = BigInt(1)
@@ -22,7 +22,8 @@ export async function getTokenBalance(account: Account, assetId: string): Promis
2222
}
2323

2424
export async function initializeNearAccount(accountId: string): Promise<Account> {
25-
const keyPair = KeyPair.fromString(getEnvVar('NEAR_PK') as `ed25519:${string}`)
25+
const { nearPk } = loadConfig()
26+
const keyPair = KeyPair.fromString(nearPk)
2627
const keyStore = new keyStores.InMemoryKeyStore()
2728
keyStore.setKey('mainnet', accountId, keyPair)
2829

lib/strategies/index.ts

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

lib/strategies/types.ts

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

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"format": "next lint --fix && prettier . --write",
1010
"fmt": "pnpm format",
1111
"start": "next start",
12-
"types": "tsc --noEmit"
12+
"types": "tsc --noEmit",
13+
"test": "jest"
1314
},
1415
"dependencies": {
1516
"@ai-sdk/openai": "^1.3.24",
@@ -21,15 +22,18 @@
2122
"near-api-js": "^5.1.0",
2223
"next": "15.2.4",
2324
"react": "^19",
24-
"react-dom": "^19"
25+
"react-dom": "^19",
26+
"zod": "^4.1.12"
2527
},
2628
"devDependencies": {
2729
"@eslint/eslintrc": "^3.3.1",
30+
"@types/jest": "^30.0.0",
2831
"@types/node": "^22",
2932
"@types/react": "^19",
3033
"@types/react-dom": "^19",
3134
"eslint": "^9",
3235
"eslint-config-next": "15.5.4",
36+
"jest": "^30.2.0",
3337
"prettier": "^3.6.2",
3438
"typescript": "^5"
3539
}

0 commit comments

Comments
 (0)