Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ECO-2436] Add Aptos API keys to the Aptos client anywhere we use it #374

Merged
merged 14 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/docker/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ services:
NEXT_PUBLIC_BROKER_URL: 'ws://host.docker.internal:${BROKER_PORT}'
NEXT_PUBLIC_REWARDS_MODULE_ADDRESS: '${EMOJICOIN_REWARDS_MODULE_ADDRESS}'
REVALIDATION_TIME: '${REVALIDATION_TIME}'
NEXT_PUBLIC_LOCAL_APTOS_API_KEY: '${FRONTEND_LOCAL_APTOS_API_KEY}'
NEXT_PUBLIC_CUSTOM_APTOS_API_KEY: '${FRONTEND_CUSTOM_APTOS_API_KEY}'
NEXT_PUBLIC_DEVNET_APTOS_API_KEY: '${FRONTEND_DEVNET_APTOS_API_KEY}'
NEXT_PUBLIC_TESTNET_APTOS_API_KEY: '${FRONTEND_TESTNET_APTOS_API_KEY}'
NEXT_PUBLIC_MAINNET_APTOS_API_KEY: '${FRONTEND_MAINNET_APTOS_API_KEY}'
healthcheck:
test: 'curl -f http://localhost:3001/ || exit 1'
interval: '30s'
Expand Down
7 changes: 7 additions & 0 deletions src/docker/example.local.env
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,10 @@ FEE_RATE_BPS="100"

# Secret hash seed.
HASH_SEED="some random string that is not public"

# Set the API keys for the various frontend networks. It is okay if these are exposed publicly.
FRONTEND_LOCAL_APTOS_API_KEY=""
FRONTEND_CUSTOM_APTOS_API_KEY=""
FRONTEND_DEVNET_APTOS_API_KEY="AG-MSPVZN9BNPNB6KWSPZN7GHSETJU2TFLHJ" # cspell:disable-line
FRONTEND_TESTNET_APTOS_API_KEY="AG-FRGKQEVCNY5PDJKZBAVTVCPGEQ6YFUBBX" # cspell:disable-line
FRONTEND_MAINNET_APTOS_API_KEY="AG-BAGXRD2QME5WFTBZVAR4BPA7M5EGTBRHQ" # cspell:disable-line
7 changes: 7 additions & 0 deletions src/docker/example.testnet.env
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,10 @@ FEE_RATE_BPS="100"

# Secret hash seed.
HASH_SEED="some random string that is not public"

# Set the API keys for the various frontend networks. It is okay if these are exposed publicly.
FRONTEND_LOCAL_APTOS_API_KEY=""
FRONTEND_CUSTOM_APTOS_API_KEY=""
FRONTEND_DEVNET_APTOS_API_KEY="AG-MSPVZN9BNPNB6KWSPZN7GHSETJU2TFLHJ" # cspell:disable-line
FRONTEND_TESTNET_APTOS_API_KEY="AG-FRGKQEVCNY5PDJKZBAVTVCPGEQ6YFUBBX" # cspell:disable-line
FRONTEND_MAINNET_APTOS_API_KEY="AG-BAGXRD2QME5WFTBZVAR4BPA7M5EGTBRHQ" # cspell:disable-line
18 changes: 14 additions & 4 deletions src/docker/frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ ARG HASH_SEED \
NEXT_PUBLIC_REWARDS_MODULE_ADDRESS \
NEXT_PUBLIC_BROKER_URL \
REVALIDATION_TIME \
EMOJICOIN_INDEXER_URL
EMOJICOIN_INDEXER_URL \
NEXT_PUBLIC_LOCAL_APTOS_API_KEY \
NEXT_PUBLIC_CUSTOM_APTOS_API_KEY \
NEXT_PUBLIC_DEVNET_APTOS_API_KEY \
NEXT_PUBLIC_TESTNET_APTOS_API_KEY \
NEXT_PUBLIC_MAINNET_APTOS_API_KEY
ENV HASH_SEED=$HASH_SEED \
NEXT_PUBLIC_APTOS_NETWORK=$NEXT_PUBLIC_APTOS_NETWORK \
NEXT_PUBLIC_INTEGRATOR_ADDRESS=$NEXT_PUBLIC_INTEGRATOR_ADDRESS \
Expand All @@ -29,8 +34,13 @@ ENV HASH_SEED=$HASH_SEED \
NEXT_PUBLIC_REWARDS_MODULE_ADDRESS=$NEXT_PUBLIC_REWARDS_MODULE_ADDRESS \
NEXT_PUBLIC_BROKER_URL=$NEXT_PUBLIC_BROKER_URL \
REVALIDATION_TIME=$REVALIDATION_TIME \
EMOJICOIN_INDEXER_URL=$EMOJICOIN_INDEXER_URL

RUN ["bash", "-c", "pnpm install && pnpm run build:no-checks"]
EMOJICOIN_INDEXER_URL=$EMOJICOIN_INDEXER_URL \
NEXT_PUBLIC_LOCAL_APTOS_API_KEY=$FRONTEND_LOCAL_APTOS_API_KEY \
NEXT_PUBLIC_CUSTOM_APTOS_API_KEY=$FRONTEND_CUSTOM_APTOS_API_KEY \
NEXT_PUBLIC_DEVNET_APTOS_API_KEY=$FRONTEND_DEVNET_APTOS_API_KEY \
NEXT_PUBLIC_TESTNET_APTOS_API_KEY=$FRONTEND_TESTNET_APTOS_API_KEY \
NEXT_PUBLIC_MAINNET_APTOS_API_KEY=$FRONTEND_MAINNET_APTOS_API_KEY

RUN ["bash", "-c", "pnpm install && pnpm run build:test"]

CMD ["bash", "-c", "pnpm run start -- -H 0.0.0.0"]
1 change: 1 addition & 0 deletions src/typescript/frontend/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist/
.next/
public/static/
tests/e2e/global.*.ts
playwright-report/
3 changes: 3 additions & 0 deletions src/typescript/frontend/src/app/test/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export const revalidate = 2;
export const fetchCache = "default-cache";

export async function GET() {
if (process.env.NODE_ENV !== "test") {
return new NextResponse("-1");
}
const aptos = getAptos();
try {
const version = await aptos.getLedgerInfo().then((res) => res.ledger_version);
Expand Down
25 changes: 25 additions & 0 deletions src/typescript/frontend/src/app/verify_api_keys/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { AccountAddress } from "@aptos-labs/ts-sdk";
import { APTOS_API_KEY } from "@sdk/const";
import { getAptosClient } from "@sdk/utils/aptos-client";

export const dynamic = "force-static";
export const revalidate = 600;
export const runtime = "nodejs";

const VerifyApiKeys = async () => {
const { aptos } = getAptosClient();
const accountAddress = AccountAddress.ONE;
let balance = 0;
try {
balance = await aptos.account.getAccountAPTAmount({ accountAddress });
} catch (e) {
const msg = `\n\tLikely an invalid API key. APTOS_API_KEY: ${APTOS_API_KEY}`;
throw new Error(`Couldn't fetch ${accountAddress}'s balance. ${msg}`);
}

return (
<div className="bg-black w-full h-full m-auto pixel-heading-2">{`Balance: ${balance}`}</div>
);
};

export default VerifyApiKeys;
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@ import path from "path";
import { emojisToName } from "lib/utils/emojis-to-name-or-symbol";
import { useEventStore } from "context/event-store-context";
import { getPeriodStartTimeFromTime } from "@sdk/utils";
import { getAptosConfig } from "lib/utils/aptos-client";
import { getAptos } from "lib/utils/aptos-client";
import { getSymbolEmojisInString, symbolToEmojis, toMarketEmojiData } from "@sdk/emoji_data";
import { type PeriodicStateEventModel, type MarketMetadataModel } from "@sdk/indexer-v2/types";
import { getMarketResource } from "@sdk/markets";
import { Aptos } from "@aptos-labs/ts-sdk";
import { periodEnumToRawDuration, Trigger } from "@sdk/const";
import {
type LatestBar,
Expand Down Expand Up @@ -184,7 +183,7 @@ export const Chart = (props: ChartContainerProps) => {
// Also, we specifically call this client-side because the server will get rate-limited if we call the
// fullnode from the server for each client.
const marketResource = await getMarketResource({
aptos: new Aptos(getAptosConfig()),
aptos: getAptos(),
marketAddress: props.marketAddress,
});

Expand Down
6 changes: 5 additions & 1 deletion src/typescript/frontend/src/context/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { RiseWallet } from "@rise-wallet/wallet-adapter";
import { MartianWallet } from "@martianwallet/aptos-wallet-adapter";
import { EmojiPickerProvider } from "./emoji-picker-context/EmojiPickerContextProvider";
import { isMobile, isTablet } from "react-device-detect";
import { APTOS_API_KEY } from "@sdk/const";

enableMapSet();

Expand All @@ -50,7 +51,10 @@ const ThemedApp: React.FC<{ children: React.ReactNode }> = ({ children }) => {
<AptosWalletAdapterProvider
plugins={wallets}
autoConnect={false}
dappConfig={{ network: APTOS_NETWORK }}
dappConfig={{
aptosApiKey: APTOS_API_KEY,
network: APTOS_NETWORK,
}}
>
<WalletModalContextProvider>
<AptosContextProvider>
Expand Down
23 changes: 10 additions & 13 deletions src/typescript/frontend/src/lib/env.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Network } from "@aptos-labs/wallet-adapter-react";
import packageInfo from "../../package.json";
import { parse } from "semver";
import { type AccountAddressString } from "@sdk/emojicoin_dot_fun";
import { type Network } from "@aptos-labs/ts-sdk";

export type Links = {
x: string;
Expand All @@ -10,7 +10,15 @@ export type Links = {
tos: string;
};

let APTOS_NETWORK: Network;
const network = process.env.NEXT_PUBLIC_APTOS_NETWORK;
const APTOS_NETWORK = network as Network;
// NOTE: We must check it this way instead of with `NetworkToNetworkName[APTOS_NETWORK]` because
// otherwise the @aptos-labs/ts-sdk package is included in the middleware.ts function and the edge
// runtime won't build properly.
if (!["local", "devnet", "testnet", "mainnet", "custom"].includes(APTOS_NETWORK)) {
throw new Error(`Invalid network: ${network}`);
}

let INTEGRATOR_ADDRESS: AccountAddressString;
let INTEGRATOR_FEE_RATE_BPS: number;
let BROKER_URL: string;
Expand All @@ -22,17 +30,6 @@ export const LINKS: Links | undefined =

const IS_ALLOWLIST_ENABLED: boolean = process.env.NEXT_PUBLIC_IS_ALLOWLIST_ENABLED === "true";

if (process.env.NEXT_PUBLIC_APTOS_NETWORK) {
const network = process.env.NEXT_PUBLIC_APTOS_NETWORK;
if (["mainnet", "testnet", "devnet", "local", "custom", "docker"].includes(network)) {
APTOS_NETWORK = network as Network;
} else {
throw new Error(`Invalid network: ${network}`);
}
} else {
throw new Error("Environment variable NEXT_PUBLIC_APTOS_NETWORK is undefined.");
}

if (process.env.NEXT_PUBLIC_INTEGRATOR_ADDRESS) {
INTEGRATOR_ADDRESS = process.env.NEXT_PUBLIC_INTEGRATOR_ADDRESS as AccountAddressString;
} else {
Expand Down
3 changes: 1 addition & 2 deletions src/typescript/frontend/src/lib/server-env.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import "server-only";
import { APTOS_NETWORK, IS_ALLOWLIST_ENABLED } from "./env";
import { Network } from "@aptos-labs/ts-sdk";
import { EMOJICOIN_INDEXER_URL } from "@sdk/server/env";

if (typeof process.env.REVALIDATION_TIME === "undefined") {
Expand Down Expand Up @@ -45,7 +44,7 @@ export const VPNAPI_IO_API_KEY: string = process.env.VPNAPI_IO_API_KEY!;
export const PRE_LAUNCH_TEASER: boolean = process.env.PRE_LAUNCH_TEASER === "true";

if (
APTOS_NETWORK === Network.LOCAL &&
APTOS_NETWORK.toString() === "local" &&
!EMOJICOIN_INDEXER_URL.includes("localhost") &&
!EMOJICOIN_INDEXER_URL.includes("docker")
) {
Expand Down
8 changes: 6 additions & 2 deletions src/typescript/frontend/src/lib/utils/aptos-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { NetworkToFaucetAPI, NetworkToIndexerAPI, NetworkToNodeAPI } from "@aptos-labs/ts-sdk";
import { Aptos, AptosConfig, NetworkToNetworkName } from "@aptos-labs/ts-sdk";
import { APTOS_CONFIG } from "@sdk/utils/aptos-client";
import { APTOS_NETWORK } from "lib/env";

const toDockerUrl = (url: string) => url.replace("127.0.0.1", "host.docker.internal");

// Get an Aptos config based off of the network environment variables.
// Get an Aptos config based off of the network environment variables and the default APTOS_CONFIG
// client configuration/settings.
export const getAptosConfig = (): AptosConfig => {
const networkString = APTOS_NETWORK;
const clientConfig = APTOS_CONFIG;
if (networkString === "local" && typeof window === "undefined") {
const fs = require("node:fs");
if (fs.existsSync("/.dockerenv")) {
Expand All @@ -17,6 +20,7 @@ export const getAptosConfig = (): AptosConfig => {
fullnode: toDockerUrl(NetworkToNodeAPI["local"]),
indexer: toDockerUrl(NetworkToIndexerAPI["local"]),
faucet: toDockerUrl(NetworkToFaucetAPI["local"]),
clientConfig,
});
}
}
Expand All @@ -27,7 +31,7 @@ export const getAptosConfig = (): AptosConfig => {
if (!network) {
throw new Error(`Invalid network: ${networkString}`);
}
return new AptosConfig({ network, fullnode, indexer, faucet });
return new AptosConfig({ network, fullnode, indexer, faucet, clientConfig });
};

// Get an Aptos client based off of the network environment variables.
Expand Down
7 changes: 4 additions & 3 deletions src/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
"keyv": "npm:@keyvhq/[email protected]"
},
"scripts": {
"build": "pnpm i && pnpm load-env:test -- turbo run build",
"build:debug": "pnpm i && pnpm load-env:test -- turbo run build:debug",
"build:no-checks": "pnpm i && pnpm load-env:test -- turbo run build:no-checks",
"build": "pnpm i && pnpm load-env -- turbo run build",
"build:debug": "pnpm i && pnpm load-env -- turbo run build:debug",
"build:no-checks": "pnpm i && pnpm load-env -- turbo run build:no-checks",
"build:test": "pnpm i && pnpm load-env:test -- turbo run build:no-checks",
"check": "turbo run check",
"clean": "turbo run clean --no-cache --force && rm -rf .turbo",
"clean:full": "pnpm run clean && rm -rf node_modules && rm -rf sdk/node_modules && rm -rf frontend/node_modules",
Expand Down
12 changes: 9 additions & 3 deletions src/typescript/sdk/src/client/emojicoin-client.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {
AccountAddress,
Aptos,
type Account,
type UserTransactionResponse,
type AccountAddressInput,
type Aptos,
type TypeTag,
type InputGenerateTransactionOptions,
type WaitForTransactionOptions,
AptosConfig,
} from "@aptos-labs/ts-sdk";
import { type ChatEmoji, type SymbolEmoji } from "../emoji_data/types";
import { EmojicoinDotFun, getEvents } from "../emojicoin_dot_fun";
Expand All @@ -21,7 +22,7 @@ import {
import { type Events } from "../emojicoin_dot_fun/events";
import { getEmojicoinMarketAddressAndTypeTags } from "../markets";
import { type EventsModels, getEventsAsProcessorModelsFromResponse } from "../mini-processor";
import { getAptosClient } from "../utils/aptos-client";
import { APTOS_CONFIG, getAptosClient } from "../utils/aptos-client";
import { toChatMessageEntryFunctionArgs } from "../emoji_data";
import customExpect from "./expect";
import { DEFAULT_REGISTER_MARKET_GAS_OPTIONS, INTEGRATOR_ADDRESS } from "../const";
Expand Down Expand Up @@ -145,7 +146,12 @@ export class EmojicoinClient {
integratorFeeRateBPs = 0,
minOutputAmount = 1n,
} = args ?? {};
this.aptos = aptos;
// Create a client that always uses the static API_KEY config options.
const hardCodedConfig = new AptosConfig({
...aptos.config,
clientConfig: { ...aptos.config.clientConfig, ...APTOS_CONFIG },
});
this.aptos = new Aptos(hardCodedConfig);
this.integrator = AccountAddress.from(integrator);
this.integratorFeeRateBPs = Number(integratorFeeRateBPs);
this.minOutputAmount = BigInt(minOutputAmount);
Expand Down
41 changes: 39 additions & 2 deletions src/typescript/sdk/src/const.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { AccountAddress, APTOS_COIN, parseTypeTag } from "@aptos-labs/ts-sdk";
import {
AccountAddress,
APTOS_COIN,
Network,
NetworkToNetworkName,
parseTypeTag,
} from "@aptos-labs/ts-sdk";
import Big from "big.js";
import { type ValueOf } from "./utils/utility-types";
import { type DatabaseStructType } from "./indexer-v2/types/json-types";
Expand All @@ -8,13 +14,15 @@ if (
!process.env.NEXT_PUBLIC_MODULE_ADDRESS ||
!process.env.NEXT_PUBLIC_REWARDS_MODULE_ADDRESS ||
!process.env.NEXT_PUBLIC_INTEGRATOR_ADDRESS ||
!process.env.NEXT_PUBLIC_INTEGRATOR_FEE_RATE_BPS
!process.env.NEXT_PUBLIC_INTEGRATOR_FEE_RATE_BPS ||
!process.env.NEXT_PUBLIC_APTOS_NETWORK
) {
const missing = [
["NEXT_PUBLIC_MODULE_ADDRESS", process.env.NEXT_PUBLIC_MODULE_ADDRESS],
["NEXT_PUBLIC_REWARDS_MODULE_ADDRESS", process.env.NEXT_PUBLIC_REWARDS_MODULE_ADDRESS],
["NEXT_PUBLIC_INTEGRATOR_ADDRESS", process.env.NEXT_PUBLIC_INTEGRATOR_ADDRESS],
["NEXT_PUBLIC_INTEGRATOR_FEE_RATE_BPS", process.env.NEXT_PUBLIC_INTEGRATOR_FEE_RATE_BPS],
["NEXT_PUBLIC_APTOS_NETWORK", process.env.NEXT_PUBLIC_APTOS_NETWORK],
].filter(([_, value]) => !value);
missing.forEach(([key, _]) => {
console.error(`Missing environment variables ${key}`);
Expand All @@ -26,6 +34,35 @@ if (
);
}

const network = process.env.NEXT_PUBLIC_APTOS_NETWORK;
export const APTOS_NETWORK = NetworkToNetworkName[network];
if (!APTOS_NETWORK) {
throw new Error(`Invalid network: ${network}`);
}

const allAPIKeys: Record<Network, string | undefined> = {
[Network.LOCAL]: process.env.NEXT_PUBLIC_LOCAL_APTOS_API_KEY,
[Network.CUSTOM]: process.env.NEXT_PUBLIC_CUSTOM_APTOS_API_KEY,
[Network.DEVNET]: process.env.NEXT_PUBLIC_DEVNET_APTOS_API_KEY,
[Network.TESTNET]: process.env.NEXT_PUBLIC_TESTNET_APTOS_API_KEY,
[Network.MAINNET]: process.env.NEXT_PUBLIC_MAINNET_APTOS_API_KEY,
};

const apiKey = allAPIKeys[APTOS_NETWORK];
if (typeof apiKey === "undefined") {
// Do nothing if we're on a local network, because we don't need an API key for it.
if (APTOS_NETWORK !== "local") {
if (APTOS_NETWORK === "custom") {
console.warn(`No API key set. Ignoring because we're on the \`${APTOS_NETWORK}\` network.`);
} else {
throw new Error(`Invalid API key set for the network: ${APTOS_NETWORK}: ${apiKey}`);
}
}
}
// Select the API key from the list of env API keys. This means we don't have to change the env
// var for API keys when changing environments- we just need to provide them all every time, which
// is much simpler.
export const APTOS_API_KEY = apiKey;
export const MODULE_ADDRESS = (() => AccountAddress.from(process.env.NEXT_PUBLIC_MODULE_ADDRESS))();
export const REWARDS_MODULE_ADDRESS = (() =>
AccountAddress.from(process.env.NEXT_PUBLIC_REWARDS_MODULE_ADDRESS))();
Expand Down
Loading
Loading