项目架构分析

项目链接:https://github.com/OutrunFinance/Outrun-App

根据提供的代码片段和项目结构,我可以对这个项目进行以下分析:

  1. 技术栈:
    • 前端框架: Next.js (React)
    • UI组件库: NextUI
    • 状态管理: React Query
    • 区块链交互: wagmi, viem
    • 样式: Tailwind CSS
    • 类型检查: TypeScript
  2. 项目结构: 这是一个去中心化金融(DeFi)应用,主要功能包括:
    • 代币交换(Swap)
    • 流动性提供(Liquidity)
    • 质押(Staking)
    • 代币发行(FFLaunch)
  3. 核心功能:
    • 代币交换:实现了类似Uniswap V2的自动做市商(AMM)机制。
    • 流动性管理:允许用户添加和移除流动性。
    • 路由:实现了最优交易路径的计算。
  public static swapCallParameters(
    inputCurrency: Currency,
    outputCurrency: Currency,
    trade: Trade<Currency, Currency, TradeType>,
    options: TradeOptions | TradeOptionsDeadline,
  ): SwapParameters {
    const etherIn = inputCurrency.isNative;
    const etherOut = outputCurrency.isNative;
    // the router does not support both ether in and out
    invariant(!(etherIn && etherOut), "ETHER_IN_OUT");
    invariant(!("ttl" in options) || options.ttl > 0, "TTL");

    const to: string = validateAndParseAddress(options.recipient);
    const amountIn: string = toHex(trade.maximumAmountIn(options.allowedSlippage));
    const amountOut: string = toHex(trade.minimumAmountOut(options.allowedSlippage));

    const path: string[] = trade.route.path.map((token: Token) => token.address);
    const deadline =
      "ttl" in options
        ? `0x${(Math.floor(new Date().getTime() / 1000) + options.ttl).toString(16)}`
        : `0x${options.deadline.toString(16)}`;

    const useFeeOnTransfer = Boolean(options.feeOnTransfer);

    let methodName: string;
    let args: (string | string[])[];
    let value: string;
    switch (trade.tradeType) {
      case TradeType.EXACT_INPUT:
        if (etherIn) {
          if (outputCurrency.equals(USDB[outputCurrency.chainId])) {
            methodName = useFeeOnTransfer ? "swapExactETHForUSDBSupportingFeeOnTransferTokens" : "swapExactETHForUSDB";
          } else {
            methodName = useFeeOnTransfer
              ? "swapExactETHForTokensSupportingFeeOnTransferTokens"
              : "swapExactETHForTokens";
          }

          // (uint amountOutMin, address[] calldata path, address to, uint deadline)
          args = [amountOut, path, to, deadline];
          value = amountIn;
        } else if (etherOut) {
          if (inputCurrency.equals(USDB[inputCurrency.chainId])) {
            methodName = useFeeOnTransfer ? "swapExactUSDBForETHSupportingFeeOnTransferTokens" : "swapExactUSDBForETH";
          } else {
            methodName = useFeeOnTransfer
              ? "swapExactTokensForETHSupportingFeeOnTransferTokens"
              : "swapExactTokensForETH";
          }

          // (uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
          args = [amountIn, amountOut, path, to, deadline];
          value = ZERO_HEX;
        } else if (inputCurrency.equals(USDB[inputCurrency.chainId])) {
          methodName = useFeeOnTransfer
            ? "swapExactUSDBForTokensSupportingFeeOnTransferTokens"
            : "swapExactUSDBForTokens";
          // (uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
          args = [amountIn, amountOut, path, to, deadline];
          value = ZERO_HEX;
        } else if (outputCurrency.equals(USDB[outputCurrency.chainId])) {
          methodName = useFeeOnTransfer
            ? "swapExactTokensForUSDBSupportingFeeOnTransferTokens"
            : "swapExactTokensForUSDB";
          // (uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
          args = [amountIn, amountOut, path, to, deadline];
          value = ZERO_HEX;
        } else {
          methodName = useFeeOnTransfer
            ? "swapExactTokensForTokensSupportingFeeOnTransferTokens"
            : "swapExactTokensForTokens";
          // (uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
          args = [amountIn, amountOut, path, to, deadline];
          value = ZERO_HEX;
        }
  1. 用户界面:
    • 使用NextUI组件库构建现代化的UI界面。
    • 实现了代币选择器等自定义组件。
import { ArrowLeft, Search } from "lucide-react";
  1. 区块链交互:
    • 使用wagmi和viem库与以太坊兼容链进行交互。
    • 实现了代币数据和交易对数据的获取。
export abstract class Fetcher {
  /**
   * Cannot be constructed.
   */
  private constructor() {}

  /**
   * Fetch information for a given token on the given chain, using the given viem provider.
   * @param chainId chain of the token
   * @param address address of the token on the chain
   * @param provider provider used to fetch the token
   * @param symbol symbol of the token
   * @param name optional name of the token
   */
  public static async fetchTokenData(
    chainId: ChainId,
    address: Address,
    publicClient: PublicClient,
    symbol?: string,
    name?: string,
  ): Promise<Token> {
    const erc20 = getContract({
      abi: erc20ABI,
      address,
      client: publicClient,
    });
    const parsedDecimals =
      typeof TOKEN_DECIMALS_CACHE?.[chainId]?.[address] === "number"
        ? TOKEN_DECIMALS_CACHE[chainId][address]
        : await erc20.read.decimals([]).then((decimals: any) => {
            // @ts-ignore
            TOKEN_DECIMALS_CACHE = {
              ...TOKEN_DECIMALS_CACHE,
              [chainId]: {
                ...TOKEN_DECIMALS_CACHE?.[chainId],
                [address]: decimals,
              },
            };
            // @ts-ignore
            return decimals;
          });
    let _symbol = "";
    if (!symbol) {
      _symbol = (await erc20.read.symbol().catch(() => address)) as string;
    }
    return new Token(chainId, address, parsedDecimals, symbol || _symbol, name);
  }

  /**
   * Fetches information about a pair and constructs a pair from the given two tokens.
   * @param tokenA first token
   * @param tokenB second token
   * @param provider the provider to use to fetch the data
   */
  public static async fetchPairData(tokenA: Token, tokenB: Token, publicClient: PublicClient): Promise<Pair> {
    invariant(tokenA.chainId === tokenB.chainId, "CHAIN_ID");
    const address = Pair.getAddress(tokenA, tokenB) as Address;
    // console.log(address);

    const pairContract = getContract({
      abi: PairABI,
      address,
      client: publicClient,
    });
    const [reserves0, reserves1] = await pairContract.read.getReserves();
    const balances = tokenA.sortsBefore(tokenB) ? [reserves0, reserves1] : [reserves1, reserves0];
    return new Pair(
      CurrencyAmount.fromRawAmount(tokenA, balances[0].toString()),
      CurrencyAmount.fromRawAmount(tokenB, balances[1].toString()),
    );
  }
}
  1. 项目特点:
    • 支持多链:项目似乎支持多个区块链网络,包括Blast和Blast Sepolia测试网。
    • 费用on-transfer:考虑了带有转账费用的代币。
    • 自定义路由:实现了针对特定代币(如USDB)的自定义交换路由。
  2. 开发工具:
    • 使用pnpm作为包管理器
    • 使用ESLint进行代码质量控制
    • 使用GraphQL客户端(可能用于数据查询)

总的来说,这是一个功能丰富的DeFi应用,实现了代币交换、流动性提供、质押等核心功能。项目使用了现代化的前端技术栈,并且考虑了多链支持和特殊代币(如带有转账费用的代币)的处理。项目结构清晰,分离了核心逻辑(SDK)和用户界面,有利于代码的维护和扩展。