跳转至

dapp 核心

原文:https://docs.elrond.com/sdk-and-tools/dapp-core

用于在Elrond网络上构建 React dApps 的库。

重要

以下文档基于 dapp-core 2.0.0+

dapp-core 是一个包含核心功能逻辑的库,可用于在Elrond网络上创建 dapp。

它是为使用 React 的应用程序而构建的。

GitHub 库可以在这里找到:https://github.com/ElrondNetwork/dapp-core/

npmjs

dapp-core 也可以在 npmjs 上找到:https://www.npmjs.com/package/@elrondnetwork/dapp-core

现场演示:dapp-template

中使用的 dapp 模板在中 15 分钟构建一个 dapp 是基于dapp-core

dapp 模板的现场演示可在https://dapp-template.elrond.com/获得。

要求

  • Node.js 版本 12.16.2+
  • Npm 版本 6.14.4+

从 dapp-core 1.x 迁移

如果您正在从 dapp-core 1.x 过渡到 dapp-core 2.0,请阅读迁移指南

安装

该库可以通过 npm 或 yarn 安装。

npm install @elrondnetwork/dapp-core 

或者

yarn add @elrondnetwork/dapp-core 

如果您只需要 dapp-core 基本逻辑,而不需要额外的 UI,可以考虑使用--no-optional标志。这不会安装可选 UI 组件所需的软件包。

npm install @elrondnetwork/dapp-core --no-optional 

或者

yarn add @elrondnetwork/dapp-core --no-optional 

用法

dapp-core 旨在抽象和简化与用户钱包和Elrond网络的交互过程,允许开发者轻松开始新的应用程序或将 dapp-core 集成到现有应用程序中。

这个库涵盖了两个主要领域:用户身份交易。用于与库的逻辑交互的 API 是通过钩子和方法公开的,可以调用这些钩子和方法来登录用户、获取用户状态或发送交易。

然而,为了进一步简化使用,这个库还附带了一个默认的 UI,它已经使用了这些钩子和方法。这些 UI 元素可以很容易地用定制 css 类来定制。

import * as DappUI from "@elrondnetwork/dapp-core/UI";

请注意,这种导入方式也可能会导入未使用的代码。为了减少死代码的数量,您可以为每个组件使用命名导入,比如

import { UnlockPage } from "@elrondnetwork/dapp-core/UI/pages";
or
import { UnlockPage } from "@elrondnetwork/dapp-core/UI/pages/UnlockPage"; 

以下是更多相关信息。

先决条件

要使应用程序正常工作,需要满足几个要求。

反应过来

反应

这个库是为使用 React 的应用程序构建的,它可能不适合与其他库或框架一起使用。

数据供应商

DappProvider

您需要用库导出的 DappProvider 组件包装您的应用程序,因为我们需要创建一个全局上下文来操作数据。

  • 导入提供程序:
import { DappProvider } from '@elrondnetwork/dapp-core/wrappers/DappProvider';
or
import { DappProvider } from '@elrondnetwork/dapp-core/wrappers'; 
  • 用这个供应商包装你的应用程序。
<DappProvider
    environment="devnet"
    customNetworkConfig={customNetworkConfig}
> 

environment是为特定环境配置应用程序端点所需的必需密钥。可接受的值有testnetdevnetmainnet(也在EnvironmentsEnum中配置)。

DappProvider 还接受一个可选的带有几个键的customNetworkConfig对象。这允许使用不同的 API 和不同的连接供应商来配置您的网络配置。

所有键都是可选的

{
  id?: string;
  name?: string;
  egldLabel?: string;
  decimals?: string;
  digits?: string;
  gasPerDataByte?: string;
  walletConnectDeepLink?: string; - a string that will create a deeplink for an application that is used on a mobile phone, instead of generating the login QR code.
  walletConnectBridgeAddresses?: string; - a string that is used to establish the connection to walletConnect library;
  walletAddress?: string;
  apiAddress?: string;
  explorerAddress?: string;
  apiTimeout?: 4000;
} 
UI 糖纸

UI 包装器

该库公开了几个连接到 redux store 的组件,用于在应用程序内部发生某些事情时显示各种元素:

  • 将在屏幕下方的祝酒词中显示新的交易。这个组件是完全可定制的。
 import {TransactionsToastList} from "@elrondnetwork/dapp-core/UI/TransactionsToastList";

  <App>
    <TransactionsToastList
    toastId?: string,
    title: string,
    className?: string
    />
    <Content/>
  </App> 
  • SignTransactionsModals提交新交易时会显示一个模式,提示用户验证并签名。

重要!这是使交易工作所必需的,除非您使用钩子来手工签署交易(下面将详细介绍)。

 import {SignTransactionsModals} from "@elrondnetwork/dapp-core/UI/SignTransactionsModals";

  <App>
    <SignTransactionsModals />
    <Content/>
  </App> 

NotificationModal将向用户显示带有各种警告和错误的模态。

 import {NotificationModal} from "@elrondnetwork/dapp-core/UI/NotificationModal";

  <App>
    <NotificationModal />
    <Content/>
  </App> 

如果你想显示定制的通知,你可以使用useGetNotification钩子来获得通知(比如资金不足,错误等等)。

用户身份

Dapp-core 使得登录和持久化用户会话变得简单和容易。

一个方便的组件是 AuthenticatedRoutesWrapper,它可用于保护某些路由,并在用户未经身份验证的情况下将用户重定向到登录页面。

从 dapp-core 导入:

import { AuthenticatedRoutesWrapper } from '@elrondnetwork/dapp-core/wrappers/AuthenticatedRoutesWrapper';
or
import { AuthenticatedRoutesWrapper } from '@elrondnetwork/dapp-core/wrappers'; 

与路线一起使用:

 <AuthenticatedRoutesWrapper
    routes={routes} unlockRoute="/unlock"
  > {appContent} </AuthenticatedRoutesWrapper> 

路线应该是一个数组,其对象的签名如下:

{
    path: "/dashboard",
    title: "Dashboard",
    component: Dashboard,
    authenticatedRoute: true,
  } 

使这个组件工作的重要部分是标志 authenticatedRoute: true 和关键的路径,这意味着这个路由应该只允许经过身份验证的用户访问。

登录 UI

登录 UI

有几个非常方便的 React 组件可用于登录用户,并在用户未登录时保护某些路由。

在上面提到的DappUI对象下,您可以找到 4 个按钮(每个供应商一个),它们抽象出用户登录的所有逻辑,并呈现默认 UI。这些按钮可以很容易地用自定义 css 类进行自定义。导出的按钮有:

  • 分机登录按钮
  • walletconnectploginbutton
  • ledger 登录按钮
  • webwalletploginbutton

示例:

<ExtensionLoginButton
  callbackRoute="/dashboard"
  buttonClassName="extension-login"
  loginButtonText="Extension login"
/> 

它们也可以用于儿童

<ExtensionLoginButton
  callbackRoute="/dashboard"
  buttonClassName="extension-login"
  loginButtonText="Extension login"
>
  <>
    <icon/>
    <p>Login text</p>
  <>
</ExtensionLoginButton 

WalletConnectLoginButtonLedgerLoginButton将分别触发带有二维码的模态和账本登录 UI。这些都是由按钮自动触发的。

但是,如果您想在没有按钮的情况下访问这些容器,您可以轻松地导入和使用它们。

<WalletConnectLoginContainer
  callbackRoute={callbackRoute}
  loginButtonText="Login with Maiar"
  title='Maiar Login',
  logoutRoute='/unlock',
  className='wallect-connect-login-modal',
  lead='Scan the QR code using Maiar',
  wrapContentInsideModal={wrapContentInsideModal}
  redirectAfterLogin={redirectAfterLogin}
  token={token}
  onLoginRedirect={onLoginRedirect}
  onClose={onClose}
  /> 
<LedgerLoginContainer
  callbackRoute={callbackRoute}
  className='ledger-login-modal',
  wrapContentInsideModal={wrapContentInsideModal}
  redirectAfterLogin={redirectAfterLogin}
  token={token}
  onClose={onClose}
  onLoginRedirect={onLoginRedirect}
  /> 

所有登录按钮和挂钩都接受一个名为redirectAfterLogin的属性,该属性指定用户在登录后应该被自动重定向。这个布尔值的默认值是 false,因为大多数应用程序监听“isLoggedIn”布尔值,并以编程方式重定向。

另一个方便的组件是 AuthenticatedRoutesWrapper,它可以用来保护某些路由,并在用户未经身份验证的情况下将用户重定向到登录页面。

从 dapp-core 导入:

import { AuthenticatedRoutesWrapper } from '@elrondnetwork/dapp-core/wrappers/AuthenticatedRoutesWrapper'; 

与路线一起使用:

<AuthenticatedRoutesWrapper
    routes={routes} unlockRoute={routeNames.unlock} > {appContent} </AuthenticatedRoutesWrapper> 

路线应该是一个数组,其对象的签名如下:

{
    path: "/dashboard",
    title: "Dashboard",
    component: Dashboard,
    authenticatedRoute: true,
  } 

使这个组件工作的重要部分是标志 authenticatedRoute: true 和关键的路径,这意味着这个路由应该只允许经过身份验证的用户访问。

登录挂钩

登录挂钩

这个区域包含了登录钩子,它公开了一个触发函数和登录数据,可以随时呈现。

这些钩子被公开为命名导出,可以从 dapp-core 导入:

import { useExtensionLogin, useWalletConnectLogin, useLedgerLogin, useWebWalletLogin } from '@elrondnetwork/dapp-core/hooks';
or
import { useExtensionLogin } from '@elrondnetwork/dapp-core/hooks/login/useExtensionLogin';
import { useWalletConnectLogin } from '@elrondnetwork/dapp-core/hooks/login/useWebWalletLogin';
import { useLedgerLogin } from '@elrondnetwork/dapp-core/hooks/login/useLedgerLogin';
import { useWebWalletLogin } from '@elrondnetwork/dapp-core/hooks/login/useWebWalletLogin';` 

有 4 个可用挂钩:

  • useExtensionLogin
  • usewalletconnectplogin
  • useLedgerLogin
  • usewebwalletplogin

所有挂钩都有相同的响应签名:

返回类型如下:

const [initiateLogin, genericLoginReturnType, customLoginReturnType] = useLoginHook({
 callbackRoute,
 logoutRoute,
 onLoginRedirect,
  }); 
  • initiateLogin 是启动登录流程需要调用的函数;
  • genericLoginReturnType 是一个对所有钩子都完全一样的对象:
{
  error: string,
  loginFailed: boolean,
  isLoading: boolean,
  isLoggedIn: boolean
} 
  • customLoginReturnType 是为每个挂钩定制的对象,并返回该登录的特定数据:

    • useExtensionLogin 为 null

    • useWebWalletConnect 为空;

    • { uriDeepLink: string, qrCodeSvg: svgElement }为 useWalletConnectLogin

{
  accounts: string[];
  showAddressList: boolean;
  startIndex: number;
  selectedAddress: SelectedAddress | null;
  onGoToPrevPage: () => void;
  onGoToNextPage: () => void;
  onSelectAddress: (address: SelectedAddress | null) => void;
  onConfirmSelectedAddress: () => void;
} 

对于 useLedgerLogin

读取用户状态

读取用户状态

一旦登录,用户的会话将被持久化,并可以通过几个方便的函数读取和删除。

为了注销,这个库公开了一个简单的函数叫做 logout ,可以调用这个函数来清除用户数据。

该函数接受两个参数:

  • callbackUrl: string (optional)用户注销后重定向到的 url
  • 将被调用而不是重定向用户的功能。这允许您控制如何完成重定向,例如,使用 react-router-dom,而不是 window.location.href 赋值。重要提示此功能不会在网络钱包注销时调用

您可以选择使用useIdleTimer挂钩,在一段时间不活动后注销用户(默认设置为 10 分钟)。可选地,它接受一个满足您的 dapp 的特定注销业务逻辑的onLogout函数。确保在这个onLogout回调函数中调用上面的logout函数。

有两种读取用户当前状态的方法:钩子(在组件内部使用,用于对数据变化做出反应)和简单函数(用于在 React 组件外部或处理程序内部读取数据)。

  • 钩子:useGetLoginInfo, useGetAccountInfo, useGetNetworkConfig
  • 功能:getAccount, getAccountBalance, getAccountShard, getAddress, getIsLoggedIn;

交易

dapp-core 库公开了一种发送交易和跟踪交易状态的直接方式,并提供了一些方便的 UI 组件;

发送交易

发送交易

用于发送交易的 API 是一个名为 sendTransactions 的函数:

import { sendTransactions } from "@elrondnetwork/dapp-core";

它可用于发送包含最少信息的交易:

const { sessionId, error } = await sendTransactions({
    transactions: [
        {
          value: '1000000000000000000',
          data: 'ping',
          receiver: contractAddress
        },
      ],
    callbackRoute?: string // (optional, defaults to window.location.pathname) the route to be redirected to after signing. Will not redirect if the user is already on the specified route;
    transactionsDisplayInfo: TransactionsDisplayInfoType // (optional, default to null) custom message for toasts texts;
    minGasLimit?: number (optional, defaults to 50_000);
    sessionInformation?: any (optional, defaults to null) extra sessionInformation that will be passed back to you via getSignedTransactions hook;
    signWithoutSending?: boolean // (optional, defaults to false), the transaction will be signed without being sent to the blockchain;
    completedTransactionsDelay?: number // delay the transaction status from going into "successful" state;
    redirectAfterSigning?: boolean // (optional, defaults to true), whether to redirect to the provided callbackRoute;
    }); 

它返回一个将通过{error?: string; sessionId: string | null;}实现的承诺

sessionId是交易的批处理 id,可用于跟踪交易的状态并对其做出反应。

重要!对于要签署的交易,您必须使用上面定义的Prerequisites部分中的SignTransactionsModals,或者下面定义的useSignTransactions钩子。如果你不使用其中的一个,交易就不会被签署

交易签约流程

交易签约流程

一旦提交了交易,您必须使用SignTransactionsModalsuseSignTransactions钩子,让用户在其供应商(扩展、Maiar 等)中被提示签署交易。

如果您不想使用在签名过程发生时显示给用户的默认模态,您必须使用useSignTransactions钩子来签名这些交易。

 const {
 callbackRoute,
 transactions,
 error,
 sessionId,
 onAbort,
 hasTransactions,
    canceledTransactionsMessage
  } = useSignTransactions(); 

这个钩子将让您知道是否有任何交易,并且您可以通过编程中止签名过程。

我们建议在屏幕上显示一条消息,确认需要签名的交易。

您也可以通过以下方式获得提供商

 const { providerType, provider } = useGetAccountProvider(); 

并使用它向用户显示适当的消息。

对于 ledger,如果您使用的是SignTransactionsModal组件,签署交易就很简单。

它是完全可定制的,将负责引导用户完成签名流程。

然而,如果你想实现不同的体验,你必须使用useSignTransactionsWithLedger钩子。

它接受以下属性:

{
  onCancel: () => void;
} 

并返回具有以下键的对象:

{
  onSignTransaction: () => void;
  onNext: () => void;
  onPrev: () => void;
  waitingForDevice: boolean;
  onAbort: (e: React.MouseEvent) => void;
  isLastTransaction: boolean;
  currentStep: number;
  signedTransactions?: Record<string, Transaction>;
  currentTransaction: {
      transaction: Transaction;
      transactionTokenInfo: {
          tokenId: string;
          amount: string;
          receiver: string;
          type?: string;
          nonce?: string;
          multiTxData?: string;
      };
      isTokenTransaction: boolean;
      tokenDecimals: number;
      dataField: string;
  };
  } 
跟踪一笔交易

跟踪一笔交易

该库公开了一个名为 useTrackTransactionStatus 的挂钩;

import {useTrackTransactionStatus} from @elrondnetwork/dapp-core/hooks;

const transactionStatus = useTrackTransactionStatus({
  transactionId: sessionId,
  onSuccess,
  onFail,
  onCancelled,
}); 

transactionStatus 具有关于交易的以下信息:

{
 isPending,
 isSuccessful,
 isFailed,
 isCancelled,
 errorMessage,
 status,
  transactions
} 

null作为 sessionId 传入是安全的,因此如果交易尚未发送,钩子将返回一个空对象。

跟踪交易状态

跟踪交易状态

Dapp-core 还提供了许多方便的钩子来跟踪所有的、挂起的、失败的、成功的和超时的交易。

使用:

  • useGetPendingTransactions获取所有未决交易的列表。
  • useGetSuccessfulTransactions获取所有成功交易的列表。
  • useGetFailedTransactions获取所有未决交易的列表。

一个特别有用的钩子叫做useGetActiveTransactionsStatus,它可以让你及时了解某一时刻所有交易的状态。

它的回复签名是

{
  pending: boolean - at least one transaction is pending;
  hasPendingTransactions: boolean - the user has at least 1 pending transaction active;
  timedOut: boolean = there are no pending transactions and at least one has timed out;
  fail: boolean - there are no pending and no timedOut transactions and at least one has failed;
  success: boolean - all transactions are successful and all smart contract calls have been processed successfully;
} 
交易祝酒 UI

交易祝酒 UI

dapp-core 还公开了一个用于跟踪交易的 toast 组件,它使用上面提到的钩子并显示带有交易状态的 toast。

祝酒列表通过 TransactionsToastList UI 组件公开,只需在应用程序中呈现即可使用。组件还呈现自定义的祝酒词。可以使用 util 函数:addNewCustomToast添加自定义吐司,也可以使用deleteCustomToast删除

TransactionToastList也用于显示自定义祝酒词时,只需调用addNewCustomToast就可以将新的自定义祝酒词添加到列表中;

<App>
  <Router/>
  <TransactionsToastList />
</App> 

重要的:这个必须在<DappProvider/>孩子里面。

如果您不想使用TransactionToastList而只是显示一个自定义的 toast,那么您必须导入CustomToast组件

const customToast = addNewCustomToast(
  {
    toastId: 'toast-id',
    message: '',
    type: 'custom',
    duration: 2000
  }
);
<CustomToast
  {...customToast}
  onDelete: () => deleteCustomToast(toastId)
 /> 
手动删除交易

手动删除交易

Dapp-core 负责更改交易的状态,并在需要时删除它们,但是如果您需要手动执行此操作,您可以使用以下公开的函数:

 removeTransactionsToSign(sessionId);
  removeSignedTransaction(sessionId);
  removeAllTransactionsToSign();
  removeAllSignedTransactions(); 

单元测试用笑话

dapp-core 库公开了 CommonJS 和 ESModules 的包,但是,在某些环境中,Jest 可能需要手工映射 CommonJS 输出。要实现它,请在 jest 配置文件中添加以下代码片段。

moduleNameMapper: {
    '@elrondnetwork/dapp-core/(.*)':
      '<rootDir>/node_modules/@elrondnetwork/dapp-core/__commonjs/$1.js'
} 

dapp 核心出口

从 2.0 版开始,dapp-core 没有默认的导出对象。你必须从单独的模块中导入所有的东西。下面你可以找到所有的出口。

你可以从一个模块中导入任何东西,或者如果你真的想确保你没有导入任何没有被使用的东西,你可以从它自己的文件中导入任何东西。

您可以进入模块中的特定文件夹进行额外的修整,或者一起导入所有内容。

例如,这两个导入都有效:

import { useExtensionLogin, useGetAccountInfo } from '@elrondnetwork/dapp-core/hooks'; 

import { useExtensionLogin } from '@elrondnetwork/dapp-core/hooks/login';
import { useGetAccountInfo } from '@elrondnetwork/dapp-core/hooks/account'; 

常量出口

import {
 GAS_PRICE_MODIFIER,
 GAS_PER_DATA_BYTE,
 GAS_LIMIT,
 GAS_PRICE,
 DECIMALS,
 DIGITS,
 mnemonicWords,
 ledgerErrorCodes,
   fallbackNetworkConfigurations
 } from '@elrondnetwork/dapp-core/constants'; 

挂钩出口

登录

import {
 useExtensionLogin,
 useLedgerLogin,
 useWalletConnectLogin,
  useWebWalletLogin
} from '@elrondnetwork/dapp-core/hooks/login'; 

账号

import {
  useGetAccountInfo,
  useGetAccountProvider,
  useGetLoginInfo
 } from '@elrondnetwork/dapp-core/hooks/accounts'; 

交易

import {
 useCheckTransactionStatus,
 useGetActiveTransactionsStatus,
 useGetFailedTransactions,
 useGetPendingTransactions,
 useGetSignedTransactions,
 useGetSignTransactionsError,
 useGetSuccessfulTransactions,
 useGetTokenDetails,
 useGetTransactionDisplayInfo,
 useParseMultiEsdtTransferData,
 useParseSignedTransactions,
 useSignMultipleTransactions,
 useSignTransactions,
 useSignTransactionsWithDevice,
 useSignTransactionsWithLedger,
} from '@elrondnetwork/dapp-core/hooks/transactions'; 

杂项

import {
 useDebounce,
 useGetNetworkConfig,
 useGetNotification,
  useUpdateEffect
} from '@elrondnetwork/dapp-core/hooks'; 

服务出口

import {
 removeTransactionsToSign,
 removeSignedTransaction,
 removeAllSignedTransactions,
 removeAllTransactionsToSign,
 isCrossShardTransaction,
 sendTransactions,
 signTransactions,
  calcTotalFee
} from '@elrondnetwork/dapp-core/services'; 

utils 出口

账号

import {
 addressIsValid,
 getAccount,
 getAccountBalance,
 getAccountShard,
 getAddress,
 getLatestNonce,
 getShardOfAddress,
 refreshAccount,
 setNonce,
  signMessage
} from '@elrondnetwork/dapp-core/utils/account'; 

操作

import {
 calculateFeeLimit,
 formatAmount,
 nominate,
 getUsdValue,
} from '@elrondnetwork/dapp-core/utils/operations'; 

交易

import {
 getTokenFromData,
 isTokenTransfer,
 parseMultiEsdtTransferData,
 parseTransactionAfterSigning,
} from '@elrondnetwork/dapp-core/utils/transactions'; 

验证

import {
 getIdentifierType,
 stringIsFloat,
 stringIsInteger,
 isContract,
 isStringBase64,
} from '@elrondnetwork/dapp-core/utils'; 

杂项

import {
 encodeToBase64,
 decodeBase64,
 logout,
 getTokenFromData,
 getIsLoggedIn,
 isSelfESDTContract,
 getAddressFromDataField,
} from '@elrondnetwork/dapp-core/utils'; 
import {
 DappProvider,
 AuthenticatedRoutesWrapper,
 AppInitializer,
} from '@elrondnetwork/dapp-core/wrappers'; 

网络专用进口

import {
  useIdleTimer
} from '@elrondnetwork/dapp-core/web'; 
import {
 CopyButton,
 FormatAmount,
 ExplorerLink,
 ExtensionLoginButton,
 LedgerLoginButton,
 LedgerLoginContainer,
 NotificationModal,
 PageState,
 ProgressSteps,
 SignTransactionsModals,
 SignWithDeviceModal,
 SignWithExtensionModal,
 SignWithLedgerModal,
 TransactionsToastList,
 TransactionToast,
 Trim,
 UsdValue,
 WalletConnectLoginButton,
 WalletConnectLoginContainer,
} from '@elrondnetwork/dapp-core/UI'; 

或者

import { CopyButton } from '@elrondnetwork/dapp-core/UI/CopyButton';
import { FormatAmount } from '@elrondnetwork/dapp-core/UI/FormatAmount';
import { ExplorerLink } from '@elrondnetwork/dapp-core/UI/ExplorerLink';

etc 

重要 : shouldRenderDefaultCss已从所有组件中移除。

反应原生支持

我们知道有一些项目希望使用这个库来允许用户无缝地通过 Maiar 进行身份验证。

您可以使用这个库的实用功能,如“格式数量,语法数量”,助记单词列表或其常量。

然而,我们做出的某些架构决策并不适合 React Native runtime(无论是 Metro 还是 Re.pack)。因此,您还不能在 React 本机应用程序中使用 DappProvider 包装逻辑。

我们已经有了几个解决方案,并且正在积极探索克服这些限制的方法。在此之前,您可以使用@elrondnetwork/erdjs 库和@walletconnect 来连接 Maiar。在社区也有这样做的指南



回到顶部