目前,我们已经实现了最终用户授权功能,以便与我们的合约进行交互。 现在,让我们将 DApp 与第 5 章中部署的实际合约连接起来。 您需要注意一些要求,以便在本课和下一课中正确使用我们的步骤: 此时,我假设你已经将第 4 章/第 5 课中的合约部署到了 testnet,并知道了它的地址。 这是我的合约地址: - EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu
/ ^2 s$ h1 M# T* M, X/ H
复制代码大家还记得,我们在编写测试时已经与我们的合约进行了一些交互。我们必须创建一个包装文件,它可以帮助我们向合约发送内部信息,还可以运行 getter 方法。令人惊喜的是,我们可以使用同一个包装文件:) 我们 DApp 的合约包装文件现在位于 src/contracts/MainContract.ts. 现在我们有了一个合适的合约包装,但如何才能真正从浏览器与链上合约建立连接呢?在第 3 章中,我们检测了合约是否已部署,并使用了 TON 库中的 TonClient 实例。它使用 TON access RPC 提供者从链上获取信息,并向我们的合约发送请求。我们将在本课中使用相同的逻辑。 在开始之前,让我们做一件 "非常 React "的事情,我们将实现一个通用的 React 钩子,它将帮助我们初始化异步对象。我们将用它来连接 TonClient。 创建包含以下内容的文件 src/hooks/useAsyncInitialize.ts: - import { useEffect, useState } from "react";7 H. |, E3 [: T& u7 Y
- 3 o0 M$ T- `, A3 T5 o
- export function useAsyncInitialize<T>(+ D, }1 M4 @ {2 _$ r! f8 p$ G
- func: () => Promise<T>,
! Z' }6 D" z9 N& U P' Y0 o/ G" |" P* i - deps: any[] = []) P: w) B" ?- Y4 c) c- q
- ) {& w$ t# f% H; e1 K
- const [state, setState] = useState<T | undefined>();8 x8 \3 a. @7 G# b
- useEffect(() => {" T; r+ s8 m5 P" U ]/ H
- (async () => {
' p2 |( G5 o K: F$ b - setState(await func());3 s+ a9 c( g! d8 ?! p3 p2 y0 c
- })();
8 B1 g: t8 v; o" C) K/ U - }, deps);' z8 M6 A0 m" `$ D
6 X0 k1 _4 H s; C" x& N& a- return state;% G4 A1 _: g; h1 d# M, k5 }5 I R
- }
复制代码接下来,我们将创建另一个依赖于 useAsyncInitialize 的 React 钩子,并在应用程序中初始化 RPC 客户端。如上所述,我们将使用 TON Access,它将免费为我们提供无节流的 API 访问。它也是去中心化的,是访问网络的首选方式。 创建包含以下内容的文件 src/hooks/useTonClient.ts: - import { getHttpEndpoint } from '@orbs-network/ton-access';( U6 ]3 g& R, ~0 b3 F+ m+ Z* @
- import { TonClient } from 'ton';
4 w! C" P( C. ` - import { useAsyncInitialize } from './useAsyncInitialize';# c& K2 x y# O# ^& u! Y. H
7 U- y8 y% n6 ?% A- export function useTonClient() {
) U" `5 t- J0 ~ - return useAsyncInitialize(
+ h, `5 x0 V3 J" x - async () =>* `* d9 r! w$ @
- new TonClient({$ Z9 w% X# X2 T+ i. o3 r3 s6 q
- endpoint: await getHttpEndpoint({ network: 'testnet' }),9 j' s- }$ P; G) u9 v" ^
- })
# |1 c) T7 ^. u/ A8 T - );
- d; r7 m2 u' C, ` - }
复制代码让我们再创建一个名为 useMainContract() 的钩子,它将接受已部署合约的链上地址,并运行我们合约的getter方法(借助包装器的 .getData() 方法)。 该钩子的代码将如下所示: 请务必将合约地址替换为已部署合约的地址 - import { useEffect, useState } from "react"; U- s5 m6 W t! ?9 g
- import { MainContract } from "../contracts/MainContract";
$ W; p5 m- e E7 ~. {2 N, t. \; ? - import { useTonClient } from "./useTonClient";
* A; ~9 j8 g0 P - import { useAsyncInitialize } from "./useAsyncInitialize";
, i) J8 _. q; l+ Z: h( g% H! Q - import { Address, OpenedContract } from "ton-core";
8 p/ L B Z# v) v% k4 X" t
$ W* W, F( V0 b+ @3 @( S$ }! j- export function useMainContract() {: F; H( ^9 w- d4 s6 O
- const client = useTonClient();. S3 S) D7 f8 M5 K
- const [contractData, setContractData] = useState<null | {
% }& \- u4 p b) r- O# I8 _. H - counter_value: number;
3 H; z+ Y$ j! ?5 R: ~ - recent_sender: Address;
m7 ?1 j7 {0 J. l+ G+ S; Y - owner_address: Address;9 y1 y# _5 S7 ]
- }>();
) Q( Q5 H) J: I9 H* Y0 C - 1 m$ S3 L- A$ f! n) [# ?% H- P, O
- const mainContract = useAsyncInitialize(async () => {
- W1 ~+ K/ J( L- o - if (!client) return;% |* i; o1 [, t! T6 u
- const contract = new MainContract(
! q- p2 A. |7 f z) N3 n6 W - Address.parse("EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu") // replace with your address from tutorial 2 step 8) P1 x2 {8 X* H x& ~2 B3 E3 Y' W
- );1 V( }* L3 [6 z4 p2 [- O, v" p' R
- return client.open(contract) as OpenedContract<MainContract>;( a9 O+ @( e) A0 N6 Q0 S
- }, [client]);
]! t" ?5 L1 t/ N2 V# m4 e - 4 I+ V/ }" r' E& E9 C" w( j
- useEffect(() => {& K3 k6 Q. Q3 Q- N$ _& Z+ _
- async function getValue() {
6 D/ z/ } n3 P& K: ]* }7 f - if (!mainContract) return;9 [ C. [2 h' a
- setContractData(null);
2 c4 [5 N. V' C9 P - const val = await mainContract.getData();* p) u" e9 `6 J! Q/ v' |, ?/ l" S# F
- setContractData({9 E/ f. l9 E9 V: U0 m1 Z
- counter_value: val.number,
; g# h6 e- X0 H- I! V* t8 D - recent_sender: val.recent_sender,0 }" [9 Z, Q, a. Z; @2 o: A6 n
- owner_address: val.owner_address,
" T8 w' M, ^& r, O* i - });
( @+ _. I2 P2 i% h: y' h+ H( o - }9 R0 B. x1 f, W3 \. f/ n
- getValue(); o# ]8 L5 p/ g- C4 ^9 u
- }, [mainContract]);# K) K7 S; y( j* D2 C
- 0 y0 q' N( w n7 a- m/ i
- return {
/ Q M5 U% z1 W/ I - contract_address: mainContract?.address.toString(), P' i! G4 q- n
- ...contractData,
( n8 X8 H8 I: \3 C m& k% B - };
7 _0 e# P1 M/ y - }
复制代码至此,我们已准备好将从链中读取的数据呈现在我们的界面上。我们用以下代码更新 src/App.tsx: - import "./App.css";
' g& D& o1 S+ A- d - import { TonConnectButton } from "@tonconnect/ui-react";7 h( `) ^: @: h( n+ y. K
- import { useMainContract } from "./hooks/useMainContract";* T7 E% @# b u2 w
- $ e) G7 g! ~6 g
- function App() {9 j( r: [+ q' H9 p
- const {* R4 X+ ~3 Z6 k+ _3 x
- contract_address,% G& V0 r& F/ {9 d* p" n1 Z* F
- counter_value,
$ v+ a3 o" k% M! Y$ ^ - recent_sender,
# g- x9 t2 d' I& f% s - owner_address,
0 w& I, W* C2 h - contract_balance,
: M+ D% y$ B1 `# ~$ b - } = useMainContract();$ a* D' ^4 \9 b m
- return (% E4 S5 l9 U5 }7 e+ @
- <div>
9 u! A4 n0 x& k5 G - <div>
4 i: {! r% E# U& A+ W; q/ p: F5 p - <TonConnectButton />- {7 b9 \& F5 Y9 f; j, a
- </div>) q( i) w, K! F; B2 R$ ~
- <div>
2 L8 I. f9 C; [; e; E. N - <div className='Card'>( Z- I" K1 G( v! |% G; ]
- <b>Our contract Address</b>: W, h3 F" p' e/ e" {
- <div className='Hint'>{contract_address?.slice(0, 30) + "..."}</div>5 W7 {$ l( ~; D0 E/ s n2 n4 y
- <b>Our contract Balance</b>
' t4 y3 n* Z0 R5 b& f - <div className='Hint'>{contract_balance}</div>& l2 G5 v' o3 V) }$ p! [# ]; e( H
- </div>
- G% x0 @6 s2 Q/ _3 F3 n/ F - 2 L& x& \& O \, y# `* t
- <div className='Card'>
C" x* u! s0 s) K3 p, ~6 _1 c. E - <b>Counter Value</b>
! z6 ?$ k' ^9 [! W) H; k9 e( C - <div>{counter_value ?? "Loading..."}</div>$ K! p3 X o: R% P8 H& H! P
- </div>
5 N8 `( R F# ~: U, D - </div>
5 q! m* v8 G2 w - </div>$ u( x+ w" n# p! y- ]! b
- );8 p+ [ a5 A( A" G
- }, p- U7 S) [/ D7 M3 K; v1 Q
: R% {7 E- ]1 \6 K- export default App;
复制代码很好,至此我们已经准备就绪,可以轻松从链中读取合约数据了。请注意,即使我们没有通过 TON Connect 授权,也可以读取数据,因为这是只读数据,只有当我们要执行 "写 "操作时才需要授权。 在下一课中,我们将执行这些交互式操作,发送增量、存款和取款交易。
: u$ H) h( b& u, N3 A/ V |