TRON Developer Hub CN

TRON开发者中心

欢迎来到TRON开发者中心。您将找到全面的指南和文档,以帮助您尽快开始使用TRON Developer Hub,并在遇到困难时提供支持。让我们跳进去吧!

Get Started    

自定义Oracle执行

智能合约能够运行算法计算,并存储和检索数据。 由于合同仅限于区块链,因此无法从 Tron 合同中提出网络请求。 Oracles 解决这个问题是通过观察的区块链上的事件以及将网络请求的结果发布返回给合同的方式。 通过这种方式, 合约可以与链下交互。

源代码可以在[这里](https://github.com/tronprotocol/custom-oracle)找到。

Developing the Smart Contract

以下为有 oracle 监听发出的事件的合约。 oracle 将根据一个事件,请求获取 Tron 的价格数据,然后将其写回智能合约。

pragma solidity ^0.4.24;

contract PriceOracle {
	// Address of oracle set in the contract constructor
 address oracleAddress;
 
 // Price data
 string price;
 string rank;
 string marketCap;
 string vol24H;
 string perChange1H;
 string perChange1D;
 string perChange7D;
 
 // Function called on creation of contract, sets oracle address
 constructor (address _oracleAddress) public {
 oracleAddress = _oracleAddress;
 }
 
 // Event emitted when price is updated from oracle
 event PriceUpdate (
 string price,
 string rank,
 string marketCap,
 string vol24H,
 string perChange1H,
 string perChange1D,
 string perChange7D
 );
 
 // Event emitted when a price update is initiated
 event InitUpdate();
 
 // Function called to return price data
 function getPriceData() public returns (string, string, string, string, string, string, string) {
 return (price, rank, marketCap, vol24H, perChange1H, perChange1D, perChange7D);
 }
 
 // Function called to initate price update
 function initUpdate() public {
 emit InitUpdate();
 }
 
 // function called by oracle to update price data within contract
 function updatePrice (
 string _price,
 string _rank,
 string _marketCap,
 string _vol24H,
 string _perChange1H,
 string _perChange1D,
 string _perChange7D
 )
 public
 {
 require(msg.sender == oracleAddress);
 price = _price;
 rank = _rank;
 marketCap = _marketCap;
 vol24H = _vol24H;
 perChange1H = _perChange1H;
 perChange1D = _perChange1D;
 perChange7D = _perChange7D;
 
 emit PriceUpdate (
 price,
 rank,
 marketCap,
 vol24H,
 perChange1H,
 perChange1D,
 perChange7D
 );
 }
}

部署智能合约

现在我们已经编写了智能合约,我们将使用 Tron Studio将其部署到 Shasta 测试网上。 将 PriceOracle.sol 合约复制并粘贴到 Tron Studio 中。

将你现有的设置配置指向 Shasta 测试网 。 确保将你的网络环境也切换到测试网。

开发及运行Oracle

合约已编译完成,Tron Studio已指向Shasta测试网,现在一起开发可以监听事件的oracle,从外部API获取价格信息并写回给合约。

运行 mkdir PriceOracle 并通过在终端中运行 cd PriceOracle 进入项目目录。

接下来,运行 npm init 以启动Node.js实例,配置package.json属性,并在文本编辑器中打开项目目录。

继续,在项目目录中运行 npm install tr​​onweb*npm install request ,以向oracle添加必要的依赖项。

最后,创建一个名为 index.js 的新javascript文件,并将以下代码复制到该文件中。

// Initiate request object
const request = require("request");
// Initiate TronWeb object
const TronWeb = require('TronWeb');
const HttpProvider = TronWeb.providers.HttpProvider;
// Full node http endpoint
const fullNode = new HttpProvider("https://api.shasta.trongrid.io");
// Solidity node http endpoint
const solidityNode = new HttpProvider("https://api.shasta.trongrid.io");
// Contract events http endpoint
const eventServer = "https://api.shasta.trongrid.io";
// Private key of oracle
const privateKey = '';

// Create instance of TronWeb
const tronWeb = new TronWeb(
 fullNode,
 solidityNode,
 eventServer,
 privateKey
);


// Create instance of contract object
const contract = {
 "PriceOracle.sol:PriceOracle": {
 "address": "",
 "abi":
 }
};

// Initiate PriceOracle contract object 
const priceOracle = tronWeb.contract(contract["PriceOracle.sol:PriceOracle"].abi, contract["PriceOracle.sol:PriceOracle"].address);

// Function to watch for events on PriceOracle contract
function startEventListener() {
 // Watch for InitUpdate event
 priceOracle.InitUpdate().watch((err, {result}) => {
 if (err) return console.error('Failed to bind event listener:', err);
 
 // Make request to fetch price data from https://api.coinmarketcap.com
 request("https://api.coinmarketcap.com/v2/ticker/1958/", function (err, response, body) {
 if (err) return;
 
 // Parse price data in API response
 const json = JSON.parse(body);
 const data = json.data;
 const rank = data.rank;
 const price = data.quotes.USD.price;
 const marketCap = data.quotes.USD.market_cap;
 const vol24H = data.quotes.USD.volume_24h;
 const perChange1H = data.quotes.USD.percent_change_1h;
 const perChange1D = data.quotes.USD.percent_change_24h;
 const perChange7D = data.quotes.USD.percent_change_7d;
 
// Call state changing function updatePrice within PriceOracle contract to write price data
 priceOracle.updatePrice(price.toString(), rank.toString(), marketCap.toString(),
 vol24H.toString(), perChange1H.toString(), perChange1D.toString(), perChange7D.toString()).send({
 shouldPollResponse: true,
 callValue: 0
 }).catch(function (err) {
 console.log(err)
 });
 })
 });

// Watch for PriceUpdate event
 priceOracle.PriceUpdate().watch((err, {result}) => {
 if (err) return console.error('Failed to bind event listener:', err);
 console.log(result);
 });
}

// Call startEventListener function to watch for events in PriceOracle contract
startEventListener();

用您希望用作 oracle 的波场账户的私钥指定 privateKey 变量。

在 Tron Studio 中复制构造函数参数框中的 Oracle 的公共地址(私钥变量所对应的公钥在 index.js 文件中)并在 Shasta 测试网中部署合约。

注意:如果 Oracle 的波场帐户没有任何测试网 TRX,部署将失败。 你可在这里接受 TRX 测试币。

复制已部署的合约地址并用此工具将其转化为十六进制的 string。

复制该 16 进制的 string 并在 index.js 文件中合约对象内指定其至“地址”变量。

下一步,从 Tron Studio 中复制已部署的合约 ABI,并将其指定至 index.js 文件中合约对象内的 “abi” 变量。

让我们通过运行在项目指令集终端的 node index.js 开启 oracle。

Oracle 现在正在监听部署的 PriceOracle 合约上的事件。 在 Tron Studio 中,调用 initUpdate 函数来更新合同中的价格数据。

一旦成功调用 initUpdate 函数,initUpdate 事件将发出,oracle 将接受此事件,调用并解析价格数据的api回应,调用 PriceOracle updatePrice 合约函数并通过 oracle index.js 文件中的代码来书写价格数据。

一旦通过 updatePrice 函数成功写出价格数据, PriceUpdate 事件将发出。 Oracle 将接收该发出事件,并通过此代码显示合同中的价格数据。 您将在项目的控制台中看到。

恭喜您,您已在波场区块链上成功开发了自定义的oracle。

Full Code示例

pragma solidity ^0.4.24;

contract PriceOracle {
 address oracleAddress;
 
 string price;
 string rank;
 string marketCap;
 string vol24H;
 string perChange1H;
 string perChange1D;
 string perChange7D;
 
 constructor (address _oracleAddress) public {
 oracleAddress = _oracleAddress;
 }
 
 event PriceUpdate (
 string price,
 string rank,
 string marketCap,
 string vol24H,
 string perChange1H,
 string perChange1D,
 string perChange7D
 );
 
 event InitUpdate();
 
 function getPriceData() public returns (string, string, string, string, string, string, string) {
 return (price, rank, marketCap, vol24H, perChange1H, perChange1D, perChange7D);
 }
 
 function initUpdate() public {
 emit InitUpdate();
 }
 
 function updatePrice (
 string _price,
 string _rank,
 string _marketCap,
 string _vol24H,
 string _perChange1H,
 string _perChange1D,
 string _perChange7D
 )
 public
 {
 require(msg.sender == oracleAddress);
 price = _price;
 rank = _rank;
 marketCap = _marketCap;
 vol24H = _vol24H;
 perChange1H = _perChange1H;
 perChange1D = _perChange1D;
 perChange7D = _perChange7D;
 
 emit PriceUpdate (
 price,
 rank,
 marketCap,
 vol24H,
 perChange1H,
 perChange1D,
 perChange7D
 );
 }
}
// Initiate request object
const request = require("request");
// Initiate TronWeb object
const TronWeb = require('TronWeb');
const HttpProvider = TronWeb.providers.HttpProvider;
// Full node http endpoint
const fullNode = new HttpProvider("https://api.shasta.trongrid.io");
// Solidity node http endpoint
const solidityNode = new HttpProvider("https://api.shasta.trongrid.io");
// Contract events http endpoint
const eventServer = "https://api.shasta.trongrid.io";
// Private key of oracle
const privateKey = 'b815adfd6ef133d5a878869cb3a2b31f32d4c1481132a71300c3e125be0ab1a1';

// Create instance of TronWeb
const tronWeb = new TronWeb(
 fullNode,
 solidityNode,
 eventServer,
 privateKey
);


// Create instance of contract object
const contract = {
 "PriceOracle.sol:PriceOracle": {
 "address": "4100CB5944750274A723EB22064201BF2BA1089F55",
 "abi": [{
 "constant": false,
 "inputs": [],
 "name": "initUpdate",
 "outputs": [],
 "payable": false,
 "stateMutability": "nonpayable",
 "type": "function"
 }, {
 "constant": false,
 "inputs": [],
 "name": "getPriceData",
 "outputs": [{"name": "", "type": "string"}, {"name": "", "type": "string"}, {
 "name": "",
 "type": "string"
 }, {"name": "", "type": "string"}, {"name": "", "type": "string"}, {
 "name": "",
 "type": "string"
 }, {"name": "", "type": "string"}],
 "payable": false,
 "stateMutability": "nonpayable",
 "type": "function"
 }, {
 "constant": false,
 "inputs": [{"name": "_price", "type": "string"}, {"name": "_rank", "type": "string"}, {
 "name": "_marketCap",
 "type": "string"
 }, {"name": "_vol24H", "type": "string"}, {
 "name": "_perChange1H",
 "type": "string"
 }, {"name": "_perChange1D", "type": "string"}, {"name": "_perChange7D", "type": "string"}],
 "name": "updatePrice",
 "outputs": [],
 "payable": false,
 "stateMutability": "nonpayable",
 "type": "function"
 }, {
 "inputs": [{"name": "_oracleAddress", "type": "address"}],
 "payable": false,
 "stateMutability": "nonpayable",
 "type": "constructor"
 }, {
 "anonymous": false,
 "inputs": [{"indexed": false, "name": "price", "type": "string"}, {
 "indexed": false,
 "name": "rank",
 "type": "string"
 }, {"indexed": false, "name": "marketCap", "type": "string"}, {
 "indexed": false,
 "name": "vol24H",
 "type": "string"
 }, {"indexed": false, "name": "perChange1H", "type": "string"}, {
 "indexed": false,
 "name": "perChange1D",
 "type": "string"
 }, {"indexed": false, "name": "perChange7D", "type": "string"}],
 "name": "PriceUpdate",
 "type": "event"
 }, {"anonymous": false, "inputs": [], "name": "InitUpdate", "type": "event"}]
 }
};

// Initiate PriceOracle contract object 
const priceOracle = tronWeb.contract(contract["PriceOracle.sol:PriceOracle"].abi, contract["PriceOracle.sol:PriceOracle"].address);

// Function to watch for events on PriceOracle contract
function startEventListener() {
 // Watch for InitUpdate event
 priceOracle.InitUpdate().watch((err, {result}) => {
 if (err) return console.error('Failed to bind event listener:', err);
 
 // Make request to fetch price data from https://api.coinmarketcap.com
 request("https://api.coinmarketcap.com/v2/ticker/1958/", function (err, response, body) {
 if (err) return;
 
 // Parse price data in API response
 const json = JSON.parse(body);
 const data = json.data;
 const rank = data.rank;
 const price = data.quotes.USD.price;
 const marketCap = data.quotes.USD.market_cap;
 const vol24H = data.quotes.USD.volume_24h;
 const perChange1H = data.quotes.USD.percent_change_1h;
 const perChange1D = data.quotes.USD.percent_change_24h;
 const perChange7D = data.quotes.USD.percent_change_7d;
 
// Call state changing function updatePrice within PriceOracle contract to write price data
 priceOracle.updatePrice(price.toString(), rank.toString(), marketCap.toString(),
 vol24H.toString(), perChange1H.toString(), perChange1D.toString(), perChange7D.toString()).send({
 shouldPollResponse: true,
 callValue: 0
 }).catch(function (err) {
 console.log(err)
 });
 })
 });

// Watch for PriceUpdate event
 priceOracle.PriceUpdate().watch((err, {result}) => {
 if (err) return console.error('Failed to bind event listener:', err);
 console.log(result);
 });
}

// Call startEventListener function to watch for events in PriceOracle contract
startEventListener();