在 Web3 函数任务之间共享存储

  • gelato
  • 发布于 2023-08-04 17:17
  • 阅读 29

本文介绍了Gelato Web3 Functions如何利用Polybase实现不同任务之间的存储共享。

博客 — 用例

在 Web3 Functions 任务之间共享存储

什么是 Gelato 的 Web3 Functions?

将智能合约与现有的 web2 API 连接是 web3 开发中的一个大问题,这为关键的链下资源集成设置了重大障碍。

Gelato Web3 Functions 已经成为应对这一挑战的解决方案。通过充当去中心化的云函数,它们促进了智能合约与链下数据(API、子图等)之间的连接,而无需单独的基础设施。这释放了混合应用程序的潜力,允许基于链下数据执行链上交易,并且非常简单。

Web3 Functions 的状态/存储

Gelato 的 Web3 Functions 使你能够使用智能合约执行复杂的任务,例如从 API 检索数据、执行计算以及在满足条件时将数据推送到链上。

它们被设计成无状态的,这意味着它们不会保留过去执行的内存。但是,在某些情况下,你可能需要在不同的运行之间管理一些状态变量。这时,一个简单的键/值存储就派上用场了,可以从 Web3 Function 上下文中访问。

让我们看下面的例子:

  • 检索先前的状态:使用 await storage.get() 检索先前的块编号。存储的值始终是字符串,因此必须将其解析为数字。

  • 检查并更新存储:如果新的块编号大于最后一个块,则使用新的值更新存储。

import {
  Web3Function,
  Web3FunctionContext,
} from "@gelatonetwork/web3-functions-sdk";

Web3Function.onRun(async (context: Web3FunctionContext) => {
  const { storage, multiChainProvider } = context;
  const provider = multiChainProvider.default();

  // Use storage to retrieve previous state (stored values are always string)
  // 使用存储来检索先前的状态(存储的值始终是字符串)
  const lastBlockStr = (await storage.get("lastBlockNumber")) ?? "0";
  const lastBlock = parseInt(lastBlockStr);
  console.log(`Last block: ${lastBlock}`);

  const newBlock = await provider.getBlockNumber();
  console.log(`New block: ${newBlock}`);
  if (newBlock > lastBlock) {
    // Update storage to persist your current state (values must be cast to string)
    // 更新存储以保持你当前的状态(值必须转换为字符串)
    await storage.set("lastBlockNumber", newBlock.toString());
  }

  return {
    canExec: false,
    message: `Updated block number: ${newBlock.toString()}`,
  };});

要在测试中填充存储值,请在与 web3 函数相同的目录中创建一个 storage.json 文件并填写存储值。

{
"lastBlockNumber": "1000"
}

在 Web3 Functions 之间共享存储

有时,需要在不同的 Web3 Functions 之间共享状态或存储。这可以使用 Polybase 作为共享存储来实现。出于演示目的,创建了以下结构:

Lottery Contract (彩票合约)
  • addName():用户可以请求参与。此方法发出“AddParticipant”事件。
  • updateWinner():仅由更新获胜者的 Web3 Function 调用。
  • getLastWinner():返回当前的获胜者。
Web3 Functions
  • readlogs:获取“AddParticipant”事件,并在需要时更新 Polybase 数据库。
  • lottery:从 Polybase 数据库查询 Participants 集合,随机选择一个获胜者,并更新 Lottery 合约。
Polybase 数据库

Polybase 是一个使用零知识证明的去中心化数据库。它使用 Participants 集合创建数据库。为了限制对 Polybase 数据库的访问,只有 web3 函数可以访问它。为此,我们生成一个公钥/私钥对,并将其安全地存储在 web3 Functions 的密钥中。

代码

智能合约 (Lottery.sol)-

彩票合约有三个公共方法:

  • addName():任何人都可以调用此方法来添加他们的名字作为彩票的参与者。该函数发出一个“AddParticipant”事件,其中包含发送者的地址和姓名。

  • updateWinner():此方法用于声明彩票的获胜者。但是,它有一个 onlyDedicatedMsgSender() 修饰符,该修饰符仅允许特定地址(在合约部署期间设置)调用此方法。此地址是与 Gelato Web3 Function 对应的地址,允许该任务更新彩票的获胜者。

  • getLastWinner():顾名思义,此方法在被调用时返回上次声明的彩票获胜者的姓名。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

contract Lottery {
    string public winner;

    address public immutable dedicatedMsgSender;

    event AddParticipant(address participant, string name);

    modifier onlyDedicatedMsgSender() {
        require(
            msg.sender == dedicatedMsgSender,
            "LensGelatoGPT.onlyDedicatedMsgSender"
        );
        _;
    }

    constructor(address _dedicatedMsgSender) {
        dedicatedMsgSender = _dedicatedMsgSender;
    }

    function addName(string memory _name) external {
        emit AddParticipant(msg.sender, _name);
    }

    function updateWinner(string memory _name) external onlyDedicatedMsgSender {
        winner = _name;
    }

    function getLastWinner() external view returns (string memory) {
        return winner;
    }
}
Polybase 数据库 (createDb())

此代码设置 Polybase 数据库,这是一个去中心化的存储平台。它用于存储彩票参与者的数据。它创建一个新的数据库,其中包含 Participants 集合和三个方法:

  • constructor():用于在 Participants 集合中创建新记录。
  • updateName():更新现有参与者的姓名。
  • del():删除参与者的记录(注意:它没有在此项目中使用)。
@public
  collection Participants {
    id: string;
    name: string;
    constructor (id: string, name: string) {
      this.id = id;
      this.name = name;
    }
    updateName(name: string) {
      this.name = name;
    }
    del () {
      selfdestruct();
    }
  }
Gelato Web3 Function (readlogs)

readlogs Web3 Function 的任务是管理彩票游戏中的参与者。它从指定的彩票智能合约中读取“AddParticipant”事件,处理新的参与者,并在去中心化数据库 Polybase 中更新他们。

  1. 初始化和配置:
import {
  Web3Function,
  Web3FunctionContext,
} from "@gelatonetwork/web3-functions-sdk";
import { Contract } from "ethers";
import { lotteryAbi } from "../utils/abis/abis";
import { initDb } from "../utils/db.js";
const MAX_RANGE = 100;
interface record {
  name: string;
  participant: string;
}
const PRIVATE_KEY = (await secrets.get("PRIVATE_KEY_POLYBASE")) as string;
const PUBLIC_KEY = (await secrets.get("PUBLIC_KEY_POLYBASE")) as string;
const lotteryAddress = userArgs.lotteryAddress;
if (!lotteryAddress) throw new Error("Missing userArgs.lotteryAddress");
const lottery = new Contract(lotteryAddress as string, lotteryAbi, provider);
  1. 获取和处理新的参与者:

此部分从区块链中检索当前块编号,并迭代未处理的块以查找特定的事件日志。它解析这些日志以提取参与者信息,例如姓名和地址,并将它们添加到 newParticipants 数组中。最后,它使用上次处理的块编号更新存储,确保将来的迭代从它离开的地方开始。

const currentBlock = await provider.getBlockNumber();
  const lastBlockStr = await storage.get("lastBlockNumber");
  let lastBlock: number = +(lastBlockStr
    ? parseInt(lastBlockStr)
    : (userArgs.genesisBlock as number));
  console.log(`Last processed block: ${lastBlock}, ${lastBlock}`);

  // Retrieve new mint event
  // 检索新的铸币事件
  let nbRequests = 0;
  const topics = [lottery.interface.getEventTopic("AddParticipant")];
  // Fetch historical events in batch without exceeding runtime limits
  // 在批处理中获取历史事件,而不超过运行时限制
  const newParticipants: Array<record> = [];
  while (lastBlock < currentBlock) {
    nbRequests++;
    const fromBlock = lastBlock;
    const toBlock = Math.min(fromBlock + MAX_RANGE, currentBlock);
    try {
      console.log(`Fetching log events from blocks ${fromBlock} to ${toBlock}`);
      // 从块 ${fromBlock} 到 ${toBlock} 获取日志事件
      const eventFilter = {
        address: lottery.address,
        topics,
        fromBlock,
        toBlock,
      };
const transferLogs = await provider.getLogs(eventFilter);
for (const transferLog of transferLogs) {
        const transferEvent = lottery.interface.parseLog(transferLog);
        const [participant, name] = transferEvent.args;
        newParticipants.push({ name, participant });
      }
      lastBlock = toBlock;
    } catch (err) {
      return {
        canExec: false,
        message: `Rpc call failed: ${(err as Error).message}`,
      };
    }  } await storage.set("lastBlockNumber", lastBlock.toString());
  1. 与 Polybase 的数据库交互:

此部分与 Polybase 数据库交互,检查记录是否存在,并根据需要进行更新或创建。

const db = await initDb(PRIVATE_KEY, PUBLIC_KEY);
const coll = db.collection("Participants");
for (const participant of newParticipants) {
  const record = await coll.record(participant.participant).get();
  if (record.exists()) {
    await record.call("updateName", [participant.name]);
  } else {
    await coll.create([participant.participant, participant.name]);
  }
}
Gelato Web3 Function (lottery)

lottery Web3 function 与另一个名为 "readLogs" 的 Web3 function 紧密合作。它们共同创建了一个综合系统来管理去中心化的彩票。readLogs 函数持续监控区块链上彩票中的新参与者条目,并更新 Polybase 中的 "Participants" 集合。更新参与者后,lottery Web3 function 从 Polybase 获取此数据。如果存在参与者,它会随机选择一个获胜者,并准备一个函数调用来更新彩票智能合约本身中的获胜者。

Polybase 的作用在这里至关重要,它充当存储和检索重要信息的去中心化数据库。它确保数据在两个 Web3 function 之间保持一致且可访问,从而使它们能够协同工作。

总而言之,readLogs 函数更新参与者,lottery Web3 function 选择获胜者,而 Polybase 则为共享存储提供便利。这种集成带来了以去中心化方式运行彩票系统所需的效率和功能,并将获胜者的信息正确记录在区块链上。

import {
  Web3Function,
  Web3FunctionContext,
} from "@gelatonetwork/web3-functions-sdk";
import { Contract } from "ethers";

import { lotteryAbi } from "../utils/abis/abis";

import { initDb } from "../utils/db.js";

Web3Function.onRun(async (context: Web3FunctionContext) => {
  const { userArgs, secrets, multiChainProvider } = context;

  const provider = multiChainProvider.default();

  // User Secrets
  // 用户密钥
  const PRIVATE_KEY = (await secrets.get("PRIVATE_KEY_POLYBASE")) as string;
  const PUBLIC_KEY = (await secrets.get("PUBLIC_KEY_POLYBASE")) as string;

  const lotteryAddress = userArgs.lotteryAddress as string;
  if (!lotteryAddress) throw new Error("Missing userArgs.lotteryAddress");

  const lottery = new Contract(lotteryAddress as string, lotteryAbi, provider);
  const db = await initDb(PRIVATE_KEY, PUBLIC_KEY);

  const coll = db.collection("Participants");

  let res = await coll.get();

  if (res.data.length == 0) {
    return { canExec: false, message: `There are no participants yet` };
    // 还没有参与者
  }

  const winnerIndex = Math.floor(Math.random() * res.data.length);
  const winner = res.data[winnerIndex].data.name;

  console.log(`Winner is ${winner}`);
  // 获胜者是 ${winner}
  return {
    canExec: true,
    callData: [\
      {\
        to: lotteryAddress,\
        data: lottery.interface.encodeFunctionData("updateWinner", [winner]),\
      },\
    ],
  };
});

进一步阅读

Github 代码

Polybase

Gelato Web3 Function

Web3 Function 网站

结论

Gelato Web3 Functions 通过集成 Polybase,实现了 Web3 Functions 任务之间的共享存储。"ReadLogs" 函数处理参与者更新,"lottery" 函数管理获胜者选择,而 Polybase 为这些函数提供必要的去中心化数据库,以实现共享和一致的数据存储。

关于 Gelato

准备好见证 web3 的新时代,Gelato 受到 400 多个 web3 项目的信赖,为 DeFi、NFT 和游戏中的数百万笔交易提供支持。

Gelato 目前提供四项服务:

  • Web3 Functions: 通过运行去中心化的云函数,将你的智能合约连接到链下数据和计算。

  • Automate: 通过以可靠、对开发者友好和去中心化的方式自动执行交易来自动化你的智能合约

  • Relay: 通过一个易于使用的 API,让你的用户可以访问可靠、强大和可扩展的无 gas 交易

  • Gasless Wallet: 一个强大的 SDK,使开发人员能够通过结合 Gelato Relay + Safe 的智能合约钱包来提供世界一流的 UX,从而实现账户抽象

见证 Gelato 引领的迈向去中心化未来的持续旅程!

  • 原文链接: gelato.cloud/blog/sharin...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
gelato
gelato
The Web3 Developer Cloud. Launch your own chain via our #1 Rollup-As-A-Service platform.