diff --git a/api/.gcloudignore b/api/.gcloudignore index 4d7d6938..971a225c 100644 --- a/api/.gcloudignore +++ b/api/.gcloudignore @@ -12,6 +12,7 @@ # below: .git .gitignore +.env.local # Node.js dependencies: node_modules/ \ No newline at end of file diff --git a/api/.gitignore b/api/.gitignore new file mode 100644 index 00000000..ef5eb9cb --- /dev/null +++ b/api/.gitignore @@ -0,0 +1,2 @@ +.env.local +.env.yaml diff --git a/api/app.yaml b/api/app.yaml index 078c8125..b0949a97 100644 --- a/api/app.yaml +++ b/api/app.yaml @@ -1,3 +1,3 @@ runtime: nodejs20 -env_variables: - SOLANA_RPC: "https://api.devnet.solana.com" +includes: + - .env.yaml diff --git a/api/src/main.ts b/api/src/main.ts index cac1b8f7..6ec91188 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -19,18 +19,32 @@ import { priceHistory, fundPerformance } from "./prices"; import { openfunds } from "./openfunds"; import { marinadeDelayedUnstakeTx, marinadeDelayedUnstakeClaimTx } from "./tx"; +if (process.env.NODE_ENV !== "production") { + require("dotenv").config({ path: ".env.local", override: true }); +} + const BASE_URL = "https://api.glam.systems"; const SOLANA_RPC = process.env.SOLANA_RPC || "http://localhost:8899"; +const SOLANA_MAINNET_KEY = process.env.SOLANA_MAINNET_KEY || ""; -const app: Express = express(); -app.use(cors({ origin: "*", methods: "GET" })); -app.use(bodyParser.json()); +/* GlamClient for devnet and testnet */ -const connection = new Connection(SOLANA_RPC, "confirmed"); -const provider = new AnchorProvider(connection, null, { +const devnetConnection = new Connection(SOLANA_RPC, "confirmed"); +const devnetProvider = new AnchorProvider(devnetConnection, null, { commitment: "confirmed" }); -const client = new GlamClient({ provider }); +const devnetClient = new GlamClient({ provider: devnetProvider }); + +const mainnetConnection = new Connection( + `https://mainnet.helius-rpc.com/?api-key=${SOLANA_MAINNET_KEY}`, + "confirmed" +); +const mainnetProvider = new AnchorProvider(mainnetConnection, null, { + commitment: "confirmed" +}); +const mainnetClient = new GlamClient({ provider: mainnetProvider }); + +/* Pyth client */ const PYTHNET_CLUSTER_NAME: PythCluster = "pythnet"; const pythClient = new PythHttpClient( @@ -39,12 +53,23 @@ const pythClient = new PythHttpClient( "confirmed" ); -app.use("/assets", express.static(path.join(__dirname, "assets"))); +/* Express app */ -app.get("/api", (req: Request, res: Response) => { - res.send({ message: "Welcome to Glam!" }); +const app: Express = express(); +app.use(cors({ origin: "*", methods: "GET" })); +app.use(bodyParser.json()); + +app.use((req, res, next) => { + if (req.hostname === "api.glam.systems") { + req.client = mainnetClient; + } else { + req.client = devnetClient; + } + next(); }); +app.use("/assets", express.static(path.join(__dirname, "assets"))); + /* * Openfunds */ @@ -54,17 +79,23 @@ app.get("/openfunds", async (req, res) => { req.query.funds.split(","), req.query.template, req.query.format, - client, + req.client, res ); }); app.get("/openfunds/:pubkey.:ext", async (req, res) => { - return openfunds([req.params.pubkey], "auto", req.params.ext, client, res); + return openfunds( + [req.params.pubkey], + "auto", + req.params.ext, + req.client, + res + ); }); app.get("/openfunds/:pubkey", async (req, res) => { - return openfunds([req.params.pubkey], "auto", "json", client, res); + return openfunds([req.params.pubkey], "auto", "json", req.client, res); }); /* @@ -77,17 +108,26 @@ app.post("/tx/jupiter/swap", async (req, res) => { }); app.post("/tx/marinade/unstake", async (req, res) => { - return marinadeDelayedUnstakeTx(client, req, res); + return marinadeDelayedUnstakeTx(req.client, req, res); }); app.post("/tx/marinade/unstake/claim", async (req, res) => { - return marinadeDelayedUnstakeClaimTx(client, req, res); + return marinadeDelayedUnstakeClaimTx(req.client, req, res); }); /* * Other */ +app.get("/api", (req: Request, res: Response) => { + res.send({ message: "Welcome to Glam!" }); +}); + +app.get("/genesis", async (req: Request, res: Response) => { + const genesis = await mainnetClient.provider.connection.getGenesisHash(); + res.send({ genesis }); +}); + app.get("/prices", async (req, res) => { const data = await pythClient.getData(); res.set("content-type", "application/json"); @@ -216,7 +256,7 @@ app.get("/image/:pubkey.png", async (req, res) => { const port = process.env.PORT || 8080; const server = app.listen(port, () => { - console.log(`Listening at http://localhost:${port}/api`); + console.log(`Api ${process.env.NODE_ENV} at http://localhost:${port}`); }); server.on("error", console.error); diff --git a/package.json b/package.json index a4956fe8..dd4c5d10 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "d3-cloud": "^1.2.7", "d3-sankey": "^0.12.3", "daisyui": "3.9.3", + "dotenv": "^16.4.5", "exceljs": "^4.4.0", "express": "^4.18.1", "jotai": "2.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00fc9af4..83dc2dbd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,6 +131,9 @@ importers: daisyui: specifier: 3.9.3 version: 3.9.3(ts-node@10.9.2(@swc/core@1.5.3(@swc/helpers@0.5.11))(@types/node@18.19.32)(typescript@5.4.5)) + dotenv: + specifier: ^16.4.5 + version: 16.4.5 exceljs: specifier: ^4.4.0 version: 4.4.0 @@ -4579,6 +4582,10 @@ packages: resolution: {integrity: sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==} engines: {node: '>=12'} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + downshift@8.5.0: resolution: {integrity: sha512-BAr/KAZX8GGARwWl4aER6ABv8aAaRXZcVKP0m1oFPKpSIXCGuoqnhi6nRf87glHhYDd/CCPp9RVUK27JKJD/Fw==} peerDependencies: @@ -12481,7 +12488,7 @@ snapshots: dependencies: '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) bs58: 5.0.0 - dotenv: 16.3.2 + dotenv: 16.4.5 transitivePeerDependencies: - bufferutil - encoding @@ -15191,6 +15198,8 @@ snapshots: dotenv@16.3.2: {} + dotenv@16.4.5: {} + downshift@8.5.0(react@18.2.0): dependencies: '@babel/runtime': 7.24.5