目前,我们已经实现了最终用户授权功能,以便与我们的合约进行交互。 现在,让我们将 DApp 与第 5 章中部署的实际合约连接起来。 您需要注意一些要求,以便在本课和下一课中正确使用我们的步骤: 此时,我假设你已经将第 4 章/第 5 课中的合约部署到了 testnet,并知道了它的地址。 这是我的合约地址: - EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu' v( g) Q! D- k# O) h/ i. Q' s
复制代码大家还记得,我们在编写测试时已经与我们的合约进行了一些交互。我们必须创建一个包装文件,它可以帮助我们向合约发送内部信息,还可以运行 getter 方法。令人惊喜的是,我们可以使用同一个包装文件:) 我们 DApp 的合约包装文件现在位于 src/contracts/MainContract.ts. 现在我们有了一个合适的合约包装,但如何才能真正从浏览器与链上合约建立连接呢?在第 3 章中,我们检测了合约是否已部署,并使用了 TON 库中的 TonClient 实例。它使用 TON access RPC 提供者从链上获取信息,并向我们的合约发送请求。我们将在本课中使用相同的逻辑。 在开始之前,让我们做一件 "非常 React "的事情,我们将实现一个通用的 React 钩子,它将帮助我们初始化异步对象。我们将用它来连接 TonClient。 创建包含以下内容的文件 src/hooks/useAsyncInitialize.ts: - import { useEffect, useState } from "react";
" g7 O8 b. e: a y - 2 r% W3 {0 U# m- M. i
- export function useAsyncInitialize<T>(
9 u+ J! O+ f# W5 p/ w2 Z4 n0 p - func: () => Promise<T>,7 m# ]" F: z/ {% |, ~9 | a
- deps: any[] = []- T$ J$ C9 g6 n; Z
- ) {
+ [, r- r9 H5 K8 q - const [state, setState] = useState<T | undefined>();
: F4 W) Q+ G* j( Q - useEffect(() => {* @: z. t# Z$ m3 @) `. b F. R
- (async () => {) d! ?; R2 S* U7 t8 J" G8 C
- setState(await func());
+ d0 W3 f1 E, ^! K% S - })();* Q# [' o* p4 h. M ?- K I1 J
- }, deps);
8 ^ \7 o; m# A2 J. o
[; y2 k' Q: g) m/ g( _5 K Q- return state;
6 i& E- [ B a - }
复制代码接下来,我们将创建另一个依赖于 useAsyncInitialize 的 React 钩子,并在应用程序中初始化 RPC 客户端。如上所述,我们将使用 TON Access,它将免费为我们提供无节流的 API 访问。它也是去中心化的,是访问网络的首选方式。 创建包含以下内容的文件 src/hooks/useTonClient.ts: - import { getHttpEndpoint } from '@orbs-network/ton-access';& U- d! r* \- Y) A& z6 _8 R) ?
- import { TonClient } from 'ton';
# Q L% L' F; S" g6 Y# y% o - import { useAsyncInitialize } from './useAsyncInitialize';
1 U7 c& v" n9 W8 ?
/ p2 f0 G9 `3 {' c4 m3 y$ _- export function useTonClient() {
0 N' W, \( C, @& a N5 V* n - return useAsyncInitialize(0 L" ~% }* p# g
- async () =>3 S& m2 j9 S; _7 y+ f3 I3 Q: ]% b
- new TonClient({
6 N' `0 I: V6 R2 T& m* ^" u- T - endpoint: await getHttpEndpoint({ network: 'testnet' }),
1 A6 p/ ^: b: t* |$ e; \2 P' ] - })
+ K2 t, F! W# E2 K: @1 J - );/ g. f6 d3 F w( t* W( D$ `$ X
- }
复制代码让我们再创建一个名为 useMainContract() 的钩子,它将接受已部署合约的链上地址,并运行我们合约的getter方法(借助包装器的 .getData() 方法)。 该钩子的代码将如下所示: 请务必将合约地址替换为已部署合约的地址 - import { useEffect, useState } from "react";
2 s' K& d+ U& U' w- C9 f - import { MainContract } from "../contracts/MainContract";
7 r3 F; c4 I @. P( a - import { useTonClient } from "./useTonClient";1 t5 _& l: r7 m) E, i
- import { useAsyncInitialize } from "./useAsyncInitialize";$ `6 [9 D" z$ A0 U/ q. m( `
- import { Address, OpenedContract } from "ton-core";; u+ f' Z5 k1 p
- ) p t% e5 E2 p( U: F* T* D
- export function useMainContract() {
% P% v- g/ b( e - const client = useTonClient();
: g# S: g) c2 m m - const [contractData, setContractData] = useState<null | { c/ V' e/ c- z3 z9 g
- counter_value: number;
0 d% ^' R0 M# D - recent_sender: Address;* f }% b- h. x
- owner_address: Address;
. h; ~; z9 A6 n! C9 S - }>();, Z4 r. F, r- y( e3 p8 _) E
- # F$ B y$ W$ v
- const mainContract = useAsyncInitialize(async () => {
; Y& r6 d7 ^3 S' Y - if (!client) return;
+ d$ _/ c3 {9 w- d' a, Y( x - const contract = new MainContract(/ Z8 Q4 W K" X
- Address.parse("EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu") // replace with your address from tutorial 2 step 8: y' y) L0 I8 ^
- );) f6 U) C% p; G5 [+ w! b" n3 J
- return client.open(contract) as OpenedContract<MainContract>;9 F) O; S& D) L# F5 ?: ^ f0 G& u
- }, [client]);/ j7 ]$ S8 d2 h! Z0 ]
- ' @8 p/ }+ \; ], Z3 Z
- useEffect(() => {+ L1 R4 t* j! F3 k
- async function getValue() {- C: i6 `+ }( U% v
- if (!mainContract) return; P g) m7 }& V A# Y5 A3 N! p; _& k
- setContractData(null);' { I* Z# R4 Z1 e
- const val = await mainContract.getData();& N) @* A3 n" r
- setContractData({
* M# |1 c! A5 w0 G, ` - counter_value: val.number,
- H9 i/ c+ |' d6 \! l - recent_sender: val.recent_sender,! n: l, A- H( f9 \$ x
- owner_address: val.owner_address,* X0 {$ K! B1 j) C: ~2 |: w2 Y8 _
- });
8 [* w# B% o5 O) S( T/ p7 ` - }8 C# I( V! ?: P% F; q, f$ B
- getValue();
& w8 l- z4 Z+ J. M/ K8 g5 C) q& A - }, [mainContract]);, I, m' O9 N" _% O3 ^
- " E$ A4 O1 g3 R& G z
- return {
- b7 D$ g' @, C" ~$ _0 B - contract_address: mainContract?.address.toString(),
9 \9 @) J# T& Z - ...contractData,1 ~6 D& C4 f6 V% I6 D. u
- };
% w) }+ O- w/ D) _ - }
复制代码至此,我们已准备好将从链中读取的数据呈现在我们的界面上。我们用以下代码更新 src/App.tsx: - import "./App.css";3 z, r' q; U6 w" e
- import { TonConnectButton } from "@tonconnect/ui-react";3 V0 a$ V; C, g" P9 E; m3 j
- import { useMainContract } from "./hooks/useMainContract"; O" Q( s& {! H. g9 o2 C% i5 r# o4 X
- " u7 s+ e1 G( p/ b- l
- function App() {, b3 Y4 D- H( F& o" j: o' U
- const {
) H# Y4 ?& F" F% ]2 _9 m1 X( C& u - contract_address,
' O/ k" [- d" K3 a1 l1 w. U4 O% ]9 p - counter_value,
# E: S0 @. c% A2 p, F9 b& o& p O - recent_sender,
, y/ f/ ^' B5 Q5 f2 v - owner_address,7 _# H" t5 X1 R; K' S" a3 F( z( v
- contract_balance,
2 F7 P$ V: {5 |. _+ K - } = useMainContract();
3 J+ B4 ~9 ?0 f0 S9 d7 J - return (2 K5 x% n: z1 P. [
- <div>
) i- o% O9 h$ [ - <div>
4 x+ [# `0 `" q S - <TonConnectButton />
! \9 G7 W c& O# f5 ~; w; n2 C* h - </div>
5 E& T: H' W' j: O+ |7 U' k - <div>/ D( _1 d! j9 m5 E+ l
- <div className='Card'>1 z1 ]; L- B( L# v
- <b>Our contract Address</b>
- @& R X/ `$ G6 |; F - <div className='Hint'>{contract_address?.slice(0, 30) + "..."}</div>
- c9 \' b; Q( I- [& b - <b>Our contract Balance</b>
" `, V, P( ]' Y& v2 h6 J0 H - <div className='Hint'>{contract_balance}</div>
( ^2 m" k) H, j, T' `. [ E5 B - </div>
% D9 v' j9 y, l$ O& j5 L - * M" q$ ^5 c5 R" R6 G
- <div className='Card'>
$ _/ o8 s$ o' W2 R2 v - <b>Counter Value</b>
5 Z- c8 z+ o# S; j" g2 @ - <div>{counter_value ?? "Loading..."}</div>( g+ d7 @8 F6 |3 Q6 `: U
- </div>
3 f t' W# ^. w+ D9 H - </div>( l0 n N6 X$ _/ H, F
- </div>
1 a0 ]2 H9 ^! R/ h. f+ D - );/ N2 V* j8 t. `" v1 H2 h4 v+ w
- }
% V! Y1 d, A2 x! `) g. s3 [, T* q - ; t/ T+ ], A7 J# J# w% S7 i
- export default App;
复制代码很好,至此我们已经准备就绪,可以轻松从链中读取合约数据了。请注意,即使我们没有通过 TON Connect 授权,也可以读取数据,因为这是只读数据,只有当我们要执行 "写 "操作时才需要授权。 在下一课中,我们将执行这些交互式操作,发送增量、存款和取款交易。
9 [% u0 Q$ F" N0 @. y* q |