# 3种方式集成Redstone预言机

By [kekec@Crypto](https://paragraph.com/@889908) · 2024-03-06

---

在了解Redstone预言机集成之前，需要先了解清楚三种集成方式的概念。

1.  \*\*RedStone核心模型：\*\*RedStone核心模型是它们最基本的运作方式，数据在用户的交易过程中被自动添加。在开发者的应用程序（dApp）中，它允许您使用按需提供的数据流。通过实现EVM-connector库和扩展您的Ethers.js，您的dApp可以附加带有时间戳的已签名数据包至用户的交易调用数据。使用RedStone核心进行集成你需要改变两个东西：调整您的dApp的Javascript代码以将额外的带有签名数据流的有效载荷注入您用户的交易调用数据并进行优化，同时也需要调整您的智能合约包括负责从调用数据提取数据和进行签名验证的库。这种模型在生产环境中得以实践，已经用于保护多个主网上的DeFi协议的价值，并已被注入到超过50,000个交易中。
    
2.  \*\*RedStone经典模型：\*\*尽管（RedStone核心）实际上是一种更有效和可扩展的模型，但我们承认一些协议可能更倾向于坚持传统的设计，即在需要时将数据推送到链单一节点上。适合三种情况：已经存在一个经过良好审计的代码库并且团队不愿做任何微小修改的话、在私有网络或网络费用微小的链上部署协议、数据不需要太频繁的更新。RedStone经典对传统推送型预言机有了显著的优势。我们的模块化设计让您更加自由在何时何地更新价格（对于其他预言机，只能接受已经被决定的参数）。
    
3.  **RedStone X模型**：RedStone X是一种全新的集成选项，目前仍在开发中。该模型采用了延迟执行模式，交易处理分为两步：用户通过在链上记录与协议交互的意图来启动交易而不知道交易将在何种上下文中执行，从而消除了任何通过预测预言机从协议中进行套利的可能性，价格仅在第二阶段推送到链上，这通常发生在用户交互后的下一个区块。这种模型被永久协议等大量使用，并正在开启一波超高效的DeFi项目，尽管熊市内仍在快速增长。
    

接下来，我将为您详述如何操作这三种集成方式:

**RedStone核心模型集成：**
-------------------

> \*\*第一步是安装:\*\*你需要从NPM仓库中安装@redstone-finance/evm-connector具体的安装方式分为Hardhat方式和Foundry方式。这两种安装方式都需要合适的代码来完成，如yarn或npm命令，或者forge命令来安装。同时，你可能也需要添加对应的库到你的remappings.txt文件中。

你需要从NPM仓库安装@redstone-finance/evm-connector。你可以使用以下命令进行安装：

    yarn add @redstone-finance/evm-connector
    

或者

    npm install @redstone-finance/evm-connector
    

_如果你是在Hardhat的环境下进行安装，你将需要在项目的hardhat.config.js文件中添加@redstone-finance/evm-connector。Foundry环境下需要在remappings.txt中添加“@redstone-finance/evm-connector=./node\_modules/@redstone-finance/evm-connector/”._

> \*\*第二步是使用:\*\*简单来说，你需要做两件事情：
> 
> *   调整你的智能合约以包含负责数据提取和验证的库
>     
> *   调整你的dApp的Javascript代码以注入额外的带有数据源的有效载荷。
>     

请参考以下的修改，这是一个使用RedStone的合约例子：

**solidity**

![](https://storage.googleapis.com/papyrus_images/144fb2f063f7400cea1b3ca9c4ba30dff2e3de4bcf1634bec2cab060a88d8214.png)

    import "@redstone-finance/evm-connector/contracts/DataAuthenticatable.sol";
    
    contract YourSmartContract is DataAuthenticatable {
        function yourFunctionName(address[] calldata assets, uint[] calldata amounts, bytes calldata signature) external onlyDataAuthenticatable {
            // Your smart contract code here // 
        }
    }
    

然后，你需要修改你的dApp的JavaScript代码。这是一个使用ethers.js和RedStone集成的例子：

**javascript**

![](https://storage.googleapis.com/papyrus_images/6bccc855432e59d8d908c298265095a4dc9696ba66cdd3233ea9502673ef13a8.png)

    const { buildContractInterfaces } = require("@redstone-finance/evm-connector");
    const ethers = require("ethers");
    
    const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545");
    const signer = provider.getSigner();
    const yourSmartContractAddress = "0xYourSmartContractAddress";
    const yourSmartContractABI = {/* Your Smart Contract ABI */};
    
    // Use Redstone to wrap your ethers contract
    const contracts = buildContractInterfaces(yourSmartContractABI,yourSmartContractAddress, {
      defaultSigners: ["AAVE"], // Array of RedStone price identifiers
      signer, 
      providerUrl: "https://cache.redstone.finance", // URL of the RedStone Cache Node
    })(signer);
    
    // Now you can use your function like regular ethers contract but now with appended data from RedStone
    const result = await contracts.yourFunctionName(["0xYourAssetAddress"], ["1000"], {gasLimit: 250000});
    

**RedStone经典模型：**
-----------------

> **红石经典模型（RedStone Classic Model）是一个完整的数据收集和存储模型，适用于某些希望按照传统设计将数据推送到区块链的协议，该模型适合在以下情况:**

*   _已有一份经过深度审核的代码库，且开发者团队更倾向于不进行任何改动；_
    
*   _协议部署在私有网络或者手续费用最少的区块链上；_
    
*   _不需要过于频繁地更新价格。_
    

相较于传统的价格发布Oracle，红石经典模型（RedStone Classic Model）有一个显著的优势：其模块化设计允许用户自主决定何时以及如何更新价格（某些其他Oracle会过度干预这些参数的设定）。该模型主要由两部分组成：链下中继器和链上合约。

![](https://storage.googleapis.com/papyrus_images/724f6860aa786789c9ec4e61168b2bebf7c683f7d2d70a8c53369f81984ee938.png)

**链下中继器**的工作方式可以根据环境变量进行自定义，它会定期检查一组预定的条件，一旦条件满足，就会更新价格。目前实现的条件有两种：时间条件（UPDATE\_PRICE\_INTERVAL，表示多久更新一次价格）和价值偏差条件（MIN\_DEVIATION\_PERCENTAGE，指价格变动多少将触发价格更新）。

**链上合约部分**主要是基于PriceFeedsAdapter合约，该合约负责：

*   存储所有价格订阅的符号（映射到RedStone dataFeedId）；
    
*   存储价格订阅值；
    
*   批量更新价格订阅值；
    
*   存储关于最后一次更新的轮次和时间戳的信息；
    
*   一次获取多个订阅值的价格。
    

如果协议希望能100%兼容Chainlink PriceFeed的架构，那么可以部署额外的PriceFeed合约来模拟这个解决方案。

此外，需要配置以下环境变量以满足实际需要：

    RELAYER_ITERATION_INTERVAL：更新价格的尝试间隔
    
    UPDATE_CONDITIONS：描述决定价格是否可以更新的参数数组
    
    UPDATE_PRICE_INTERVAL：描述多久更新一次价格的时间间隔
    
    MIN_DEVIATION_PERCENTAGE：描述价格变动多少将触发价格更新的最小偏差百分比
    
    RPC_URL：与区块链交互的RPC的URL
    
    CHAIN_NAME：所在的区块链的名称
    
    CHAIN_ID：所在区块链的ID
    
    PRIVATE_KEY：在对应网络上拥有充足资金以推送价格至适配器合约的钱包的私钥
    
    ADAPTER_CONTRACT_ADDRESS：在对应网络上部署的适配器合约的地址
    
    DATA_SERVICE_ID：描述应用哪些数据服务来获取价格的RedStone包装器参数
    
    UNIQUE_SIGNERS_COUNT：描述应签名价格数据的独特签名者数量的RedStone包装器参数
    
    DATA_FEEDS：描述将使用哪些代币的RedStone包装器参数。
    
    CACHE_SERVICE_URLS：描述将使用哪些缓存服务URL来获取价格的RedStone包装器参数。
    
    GAS_LIMIT：推送数据至价格定阅合约的Gas limit 限制。
    

总的来说，这个模型运作在红石核心模型之上，同时保持了对数据提供者和时间戳进行链上验证的安全性。

**RedStoneX模型：**
----------------

> **RedStone X模型还在开发中，目的是在保护以太坊上的交易执行免于前插交易方面，提供了极强的保护。这一模型借鉴了被延迟执行模式，每笔交易的处理分为两步。**

首先，用户发起交易时，记录在链上一个他打算与协议进行交互的愿望（例如打开一个永续其他资产的仓位），但是并不知道交易执行时的具体环境（例如价格）。这种做法可以避免任何尝试通过预先将价格输送到Oracles从而进行套利的行为。

其次，价格在第二步中被推送到链上，这通常发生在下一个区块之后。任何人（包括用户自己）都可以推送价格，因为价格的完整性是在链上基于协议限制进行验证的。此价格将用于最终结算交易。

这种模型借鉴于像GMX这样的永续协议，并且无论是否熊市，都可以启动新的DeFi项目。

![](https://storage.googleapis.com/papyrus_images/4f16f1db46f606497f47259e5ea734d19a35fb7e1319af3c652294cc50278bf3.png)

**需要完成两件事：**

1.  _调整合约以便可以在请求和执行的两个阶段中执行对价格敏感的交易。_
    
2.  _部署一个可以自动获取价格并触发执行的keeper服务。_
    

> **更新智能合约代码分为两个阶段：**

**阶段1 - 请求**当一个用户想要执行一个价格敏感的交易时，我们需要收集一些抵押品，记录请求参数，并请求keeper提供价格数据。这是示例代码：

**solidity**

    function changeEthToUsdc() external payable {
            bytes32 requestHash = calculateHashForSwapRequest(
                msg.value,
                msg.sender,
                block.number );
            requestedSwaps[requestHash] = true;
            emit NewOracleDataRequest(msg.value, msg.sender, block.number);
        }
    

**阶段2 - 执行**在这个阶段，用户的请求会在接收到来自keeper网络的数据后执行。以下是一个在我们的eth -> usdc冲换示例中进行必要步骤分析的代码：

**solidity**

![](https://storage.googleapis.com/papyrus_images/6549746c372989a50fabdcf3ee4597ebd52b8112c152e87e112a25b04e5d3fba.png)

    function executeWithOracleData(uint256 ethToSwap, address requestedBy, uint256 requestedAtBlock) external payable {
        // Check if the request actually exists
        bytes32 requestHash = calculateHashForSwapRequest(avaxToSwap, requestedBy, requestedAtBlock);
        require(requestedSwaps[requestHash],"Can not find swap request with the given params");
        delete requestedSwaps[requestHash];
        // We need to validate the timestamp (block.number)
        uint256 dataPackagesBlockNumber = extractTimestampsAndAssertAllAreEqual();
        require(dataPackagesBlockNumber == requestedAtBlock, "Block number mismatch in payload and request");
        // Transfer USDC to the user
        uint256 usdcAmount = getExpectedUsdAmount(ethToSwap);
        usdc.transfer(requestedBy, usdcAmount);
    }

---

*Originally published on [kekec@Crypto](https://paragraph.com/@889908/3-redstone)*
