dapp 核心
用于在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
是为特定环境配置应用程序端点所需的必需密钥。可接受的值有testnet
、devnet
和mainnet
(也在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
WalletConnectLoginButton
和LedgerLoginButton
将分别触发带有二维码的模态和账本登录 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
钩子。如果你不使用其中的一个,交易就不会被签署
交易签约流程
交易签约流程
一旦提交了交易,您必须使用SignTransactionsModals
或useSignTransactions
钩子,让用户在其供应商(扩展、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。在社区也有这样做的指南