教程:实时前端数据与 Embark 的 Subspace 和 Infura

在关于前端开发的上一篇文章中,我们最后提供了一个网站。用户可通过 MetaMask 连接该网站,它将显示用户的以太币账户余额。但是,有许多以太坊用例(例如 DeFi)涉及到 ERC20 令牌的发送和接收,其跟踪方式与原生以太币不同。在本指南中,我们将介绍如何跟踪已部署合约的交易,以及当它们在每个新确认的区块中进行更新时,如何在前端显示和更新这些数字。我们以跟踪 Uniswap 上的 Dai<>Eth 交易为例来进行说明。

教程:实时前端数据与 Embark 的 Subspace 和 Infura

在关于前端开发的上一篇文章中,我们最后提供了一个网站。用户可通过 MetaMask 连接该网站,它将显示用户的以太币账户余额。但是,有许多以太坊用例(例如 DeFi)涉及到 ERC20 令牌的发送和接收,其跟踪方式与原生以太币不同。在本指南中,我们将介绍如何跟踪已部署合约的交易,以及当它们在每个新确认的区块中进行更新时,如何在前端显示和更新这些数字。我们以跟踪 Uniswap 上的 Dai<>Eth 交易为例来进行说明。

Uniswap

为此,我们将使用来自 Status Embark 团队的一个库,名为 Subspace。我们首选使用 React Hooks 跟踪实时数据。因此,我们将通过在这里找到的 Embark 示例代码,使用以太坊数据流设置前端。总的来说,该前端使用了 Infura、React(含助手库)和 Subspace。

  • 本教程是一个单独的网站,与上一个前端教程相互独立。我们将检查代码的重要部分,而非全部代码,因此建议复制示例库,然后运行根目录(或者使用 yarn)中的 npm install 和 npm start,网站将进入 localhost:3000。从这里可以看到,使用 Hooks 和 useEffect() 添加更多数据跟踪非常轻松。

我们只需要检查 3 个重要文件。首先检查 src/index.js 的短文件,其中的 <SubspaceProvider> 包装整个 App,从而为每个组件提供对 web3 对象的访问权限,该对象即我们通过 web3 提供商 Infura 与以太坊建立的连接。我们将稍后在 App.js 中设置为使用 Infura 进行 web3 连接。

const rootElement = document.getElementById('root')
ReactDOM.render(
  <SubspaceProvider web3={web3}>
    <App />
  </SubspaceProvider>,
  rootElement
);

在 contracts/exchange_abi.json 中有 Uniswap 的 ABI,它是我们要求 Subspace 跟踪的每个函数的已部署 Uniswap 合约的规范。ABI 在 JSON 中指定,我们将对 web3 的合约对象使用它来在去中心化应用中与 Uniswap 进行交互。每个在以太坊上部署的合约都有 ABI,因此您可以将任何现有合约添加到该前端,知道它的 ABI 和已部署合约的地址就能跟踪它的交易。


    {
        "name": "TokenPurchase",
        "inputs": [
            {
                "type": "address",
                "name": "buyer",
                "indexed": true
            },
            {
                "type": "uint256",
                "name": "eth_sold",
                "indexed": true
            },
  ... and a lot more

在 App.js 中,我们首先使用 Infura 连接对 web3 对象进行初始化。如果您没有 API 密钥,可以注册并免费获取。然后,通过将 ABI 与该 ABI 的合约地址相组合来创建合约对象。该地址是 Uniswap 将 Dai 保存在流动性池所使用的合约。

  • 请注意,您现在可以返回 index.js,在那里将该相同 Infura URL 添加为 Web3 提供商。
const web3 = new Web3("wss://mainnet.infura.io/ws/v3/806ce35b64344f04a9a7e47379d9ca41");
const dai = new web3.eth.Contract(exchangeABI, '0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667');

在下一部分,我们将设置 Subspace 将会使用的 React 状态变量。Subspace 对象从 useSubspace() 进行创建,并且我们将它传递到我们刚刚创建的 Contract 对象中。然后进行一些定义,以帮助处理来自交易的 wei 值。

function App(props) {

    const subspace = useSubspace();
    const daiContract = subspace.contract(dai);

    const [txnObserver, setObservable] = useState();
    const [last5Observable, setlast5Observable] = useState();
    const [latestBlock, setBlock] = useState();
    const [last5, setLast5] = useState([]);

    //Trade details object for calculating exchange rate
    function TradeDetails(tokensSold, ethBought) {
        this.tokensSold = web3.utils.fromWei(tokensSold);
        this.ethBought = web3.utils.fromWei(ethBought);
        this.exchangeRate = this.tokensSold / this.ethBought;
    }

下面 3 个代码区块是去中心化应用中非常重要的 Hooks,为我们提供我们需要的实时流。我们将对它进行设置,使我们能够查看最新挖出的 50 个区块,并显示在这些区块中发生的最近 5 次 Eth->Dai 交易。随着不断有新的区块挖出和交易发生,这些信息将持续更新。

useEffect(() => {
        web3.eth.getBlockNumber().then((block) => setBlock(block));
        if (typeof(latestBlock) != "number") 
            return;

        const EthPurchased$ = daiContract.events.EthPurchase.track({
            fromBlock: latestBlock - 50
        });
        const last5$ = EthPurchased$.pipe($latest(5));
        setObservable(EthPurchased$);
        setlast5Observable(last5$)
    },[latestBlock])

上面的 useEffect() 会将 Hook 设置为从最新的 50 个以太坊区块获取最近的 5 个 EthPurchase 事件。重要的是,setObservable(EthPurchased$) 是我们跟踪的每个交易事件,我们通过使用管道操作符(从 RxJS 导入)并创建 5 个事件的 Observable 来限制前端仅显示 5 个事件。

接下来,我们还有一个 useEffect() Hook,它订阅符合我们在上面为 EthPurchase 定义的要求的所有交易,并将它们放到 console.log 中。

   useEffect(() => {
        if ((txnObserver === undefined) || (typeof latestBlock != "number")) {
            return;
        }
        txnObserver.subscribe((trade) => {
            console.log(trade);
        });
    
        return () => { txnObserver.unsubscribe(); }
    }, [txnObserver, latestBlock]);

从我们在第一个 Hook 中创建的 last5Observable,我们获得了它们的交易详情,如下:

  useEffect(() => {
        if (last5Observable === undefined) {
            return;
        }
        last5Observable.subscribe((fiveTrades) => {
            const prices = fiveTrades.map(trade => {
                const txnDetails = new TradeDetails(trade.tokens_sold, trade.eth_bought);
                return {'block': trade.blockNumber, 'rate': txnDetails.exchangeRate}
            });
            setLast5(prices);
        });
    
        return () => { last5Observable.unsubscribe(); }
    }, [last5Observable]);

最后,我们有一些 React UI 代码,然后就能看到前端!这是 gif 图。它刚开始不久,目前有 3 次交易。在开发者控制台中,我们可以看到每个交易的更多交易详情。随着新的交易涌入,较早的交易将被挤出堆栈。

希望这些内容清晰地解释了什么是 Subspace 库。它让去中心化应用前端开发变得非常简单轻松,尤其是使用 Infura 处理 web3 数据非常实用!复制这里的样板库并使用 Subspace。如果您对此有任何问题,请在这里与我们讨论。有关更多教程,请访问我们的社区中的教程部分