目前,我们已经实现了最终用户授权功能,以便与我们的合约进行交互。 现在,让我们将 DApp 与第 5 章中部署的实际合约连接起来。 您需要注意一些要求,以便在本课和下一课中正确使用我们的步骤: 此时,我假设你已经将第 4 章/第 5 课中的合约部署到了 testnet,并知道了它的地址。 这是我的合约地址: - EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu
! v+ d% w6 }! o2 G" W
复制代码大家还记得,我们在编写测试时已经与我们的合约进行了一些交互。我们必须创建一个包装文件,它可以帮助我们向合约发送内部信息,还可以运行 getter 方法。令人惊喜的是,我们可以使用同一个包装文件:) 我们 DApp 的合约包装文件现在位于 src/contracts/MainContract.ts. 现在我们有了一个合适的合约包装,但如何才能真正从浏览器与链上合约建立连接呢?在第 3 章中,我们检测了合约是否已部署,并使用了 TON 库中的 TonClient 实例。它使用 TON access RPC 提供者从链上获取信息,并向我们的合约发送请求。我们将在本课中使用相同的逻辑。 在开始之前,让我们做一件 "非常 React "的事情,我们将实现一个通用的 React 钩子,它将帮助我们初始化异步对象。我们将用它来连接 TonClient。 创建包含以下内容的文件 src/hooks/useAsyncInitialize.ts: - import { useEffect, useState } from "react";
/ p1 P2 Z; A" t: n. y/ G# S - 7 C- T; P: `0 u1 I
- export function useAsyncInitialize<T>(
7 e3 x% F3 I' ~! T) F W9 D1 A - func: () => Promise<T>,
" l( f# q0 I4 r' \ - deps: any[] = []" R2 H: q, w* G
- ) {
8 |0 [& V' Y3 t6 ^+ q$ h - const [state, setState] = useState<T | undefined>();0 B3 ^) l. k/ n7 \
- useEffect(() => {
0 [. W6 z! v$ Y4 a6 l) `2 e - (async () => {
+ k7 o; s1 d& W' ^ - setState(await func());
: T$ n$ r+ [, n1 |6 c3 R! v9 B - })();
/ E! n. E+ k+ R - }, deps);
4 b8 g& ?% K I+ F/ b4 i6 W8 i - ' B2 p5 w4 ~# i7 G7 y
- return state;
) \% Z# b7 K4 h$ c1 ? - }
复制代码接下来,我们将创建另一个依赖于 useAsyncInitialize 的 React 钩子,并在应用程序中初始化 RPC 客户端。如上所述,我们将使用 TON Access,它将免费为我们提供无节流的 API 访问。它也是去中心化的,是访问网络的首选方式。 创建包含以下内容的文件 src/hooks/useTonClient.ts: - import { getHttpEndpoint } from '@orbs-network/ton-access';4 V9 X g, ]6 i* x8 j
- import { TonClient } from 'ton';
T$ z% {4 d2 u3 T# V - import { useAsyncInitialize } from './useAsyncInitialize';; W4 [! r; {4 y
$ D& _: N M* z! ^" ^- export function useTonClient() {
% d6 L# F! w0 W( U0 \ - return useAsyncInitialize(
: N7 A2 p# x# w } - async () =>7 }% r" @: h0 P4 i4 t' f
- new TonClient({
0 S5 ]( L; n6 D, Z2 Y - endpoint: await getHttpEndpoint({ network: 'testnet' }),
: q) I& Q2 ?) W; z2 R$ T; n u - })
# `+ \- k( y, _8 B" ~ - );/ z$ u" v- `' _
- }
复制代码让我们再创建一个名为 useMainContract() 的钩子,它将接受已部署合约的链上地址,并运行我们合约的getter方法(借助包装器的 .getData() 方法)。 该钩子的代码将如下所示: 请务必将合约地址替换为已部署合约的地址 - import { useEffect, useState } from "react";7 {6 K! ?1 H, l! | Q5 P
- import { MainContract } from "../contracts/MainContract";
- T8 b+ x# r2 s5 J - import { useTonClient } from "./useTonClient";6 b3 T- B, p' m; z
- import { useAsyncInitialize } from "./useAsyncInitialize";
: v7 ^9 f' [& W5 A - import { Address, OpenedContract } from "ton-core";+ O, P- I5 a* f5 d+ F' {/ H5 D
O' Y, I8 g* Y C1 ?- export function useMainContract() {
7 W" ]2 N7 c8 Z - const client = useTonClient();: d; k! y, c, e; z. a0 O7 r$ B9 M" w
- const [contractData, setContractData] = useState<null | {% V1 L* y( n7 E. ?
- counter_value: number;: J$ b) j" Q3 A0 E9 q: H
- recent_sender: Address;
* g/ c6 k) o. `: J* k6 o - owner_address: Address;0 W& @6 E) J/ u% d$ h0 e' s1 ]
- }>();! D' x, h% p4 P+ }
- 6 a! ^0 c% U" N9 c$ @ U- w- v
- const mainContract = useAsyncInitialize(async () => {9 Y8 c: t- j7 H! x+ b6 h
- if (!client) return;
/ | Z8 z1 B5 d0 F5 j, ] - const contract = new MainContract(6 y0 j, i, S3 g1 H' s
- Address.parse("EQCS7PUYXVFI-4uvP1_vZsMVqLDmzwuimhEPtsyQKIcdeNPu") // replace with your address from tutorial 2 step 8
! R% {$ k; V4 a/ q6 x5 Q - );
. n# o# x; ?9 o) Z - return client.open(contract) as OpenedContract<MainContract>;
: b- z) s' v* O - }, [client]);8 v- G( Q/ a% \
- ! T& e' b0 X' r" r x! }
- useEffect(() => {9 J- v6 [ n% @
- async function getValue() {
+ j+ |6 y* y9 C; U* Y9 ^/ z9 c+ P; l3 B - if (!mainContract) return;/ o% i d3 Z/ ~* o0 {! [0 P- Z( y
- setContractData(null);- M# }8 p: ?* T& V2 K, [
- const val = await mainContract.getData();9 e: s; x, a2 ^ b$ Y9 o
- setContractData({
2 D7 g% x8 ]: W- H1 D) t - counter_value: val.number,( G5 @) q5 W) `3 F& X! ~
- recent_sender: val.recent_sender,: \ n) f9 w+ Q- C9 r% X, A" W9 V
- owner_address: val.owner_address,
3 G T' q/ f1 ~6 \* i+ M - });
* e3 u8 ?4 n$ d+ g& p% Q - }
0 f, Q( H# q. } - getValue();9 N( d6 c, v3 G7 i/ f3 K
- }, [mainContract]);5 T; y$ e9 G: @1 [" y: I
7 f- m* g; {; g- R! |- return {
% j9 T# } D+ B8 B0 N U7 p - contract_address: mainContract?.address.toString(),
7 I# `% }% e2 \# a3 E+ ~ - ...contractData,- p3 U2 b. d, }! X3 q# n
- }; A: w$ h; n, N" X2 O# t: Z/ y8 ~
- }
复制代码至此,我们已准备好将从链中读取的数据呈现在我们的界面上。我们用以下代码更新 src/App.tsx: - import "./App.css";8 ?# p6 @; ~9 t6 H0 ~6 K
- import { TonConnectButton } from "@tonconnect/ui-react";: N$ t, B8 l& C0 o7 y/ |0 w
- import { useMainContract } from "./hooks/useMainContract";
( e/ G4 `" ]! F7 V9 @4 h0 ]. h
4 \- @( z4 T- Y+ l! a1 [# `- function App() {
8 m6 Y/ Q! Y2 M - const {
2 O' F. }& i# @0 o- K5 y. u - contract_address,
c0 q# g5 s' W1 e - counter_value,) g9 ?" @6 D' j7 g% U9 I( {
- recent_sender,
9 o( v" h$ g1 ` - owner_address,! u2 V% W) j J0 ^ l4 X1 O
- contract_balance,
; A. o$ I! z+ c, k8 l" h- V - } = useMainContract();
' k$ P; Q" W8 b, K- z9 c/ j - return (% x& \8 V& i2 ^# c) m% [* L
- <div>- |! R! \- Q5 Z" Y
- <div>
; k# N$ H" G0 U1 \9 w - <TonConnectButton />
5 U4 C! a6 `& A5 @6 m8 A - </div>" d- d1 c" c: k, a- q3 Q
- <div>6 R- b; E9 q: L" l* L; ?
- <div className='Card'>
3 p9 ~0 F4 p! }) N9 S - <b>Our contract Address</b>
1 e* o; n+ Y7 y; G8 G5 q - <div className='Hint'>{contract_address?.slice(0, 30) + "..."}</div>3 ]0 P/ R! k, L. Q
- <b>Our contract Balance</b>
9 `6 d) u& ?4 L" ~ k- v0 N - <div className='Hint'>{contract_balance}</div>0 m! V: O: j; K
- </div>
) q8 k, T, t& v# j$ W, g4 T - 2 u" a4 a' _! E; Z9 s$ @
- <div className='Card'>
& t# D- x! F- F$ x% v: }/ Q! s - <b>Counter Value</b>/ W/ d# Q C* V& i, V
- <div>{counter_value ?? "Loading..."}</div>1 N! B5 p. H4 K ?
- </div>7 x. }; f( S0 |
- </div>8 _+ D& a7 I; q0 @- v
- </div>
5 s( l- [5 t0 K$ Z - );6 S4 D- c! z- }
- }; F$ j7 j' Y/ E! D
- / F: K- t/ F( {0 G3 ], v5 f
- export default App;
复制代码很好,至此我们已经准备就绪,可以轻松从链中读取合约数据了。请注意,即使我们没有通过 TON Connect 授权,也可以读取数据,因为这是只读数据,只有当我们要执行 "写 "操作时才需要授权。 在下一课中,我们将执行这些交互式操作,发送增量、存款和取款交易。 7 Z' u0 k. M( }# T: {" G3 i) w
|