const axios = require('axios')
const BN = require('bn.js')
const common = require('./utils/common.js')
const SLEEP_INTERVAL = process.env.SLEEP_INTERVAL || 2000
const PRIVATE_KEY_FILE_NAME = process.env.PRIVATE_KEY_FILE || './oracle/oracle_private_key'
const CHUNK_SIZE = process.env.CHUNK_SIZE || 3
const MAX_RETRIES = process.env.MAX_RETRIES || 5
const OracleJSON = require('./oracle/build/contracts/EthPriceOracle.json')
var pendingRequests = []
async function getOracleContract (web3js) {
const networkId = await web3js.eth.net.getId()
return new web3js.eth.Contract(OracleJSON.abi, OracleJSON.networks[networkId].address)
}
async function retrieveLatestEthPrice () {
const resp = await axios({
url: ' https://api.binance.com/api/v3/ticker/price',
params: {
symbol: 'ETHUSDT'
},
method: 'get'
})
return resp.data.price
}
async function filterEvents (oracleContract, web3js) {
oracleContract.events.GetLatestEthPriceEvent(async (err, event) => {
if (err) {
console.error('Error on event', err)
return
}
await addRequestToQueue(event)
})
oracleContract.events.SetLatestEthPriceEvent(async (err, event) => {
if (err) console.error('Error on event', err)
// Do something
})
}
async function addRequestToQueue (event) {
const callerAddress = event.returnValues.callerAddress
const id = event.returnValues.id
pendingRequests.push({ callerAddress, id })
}
async function processQueue (oracleContract, ownerAddress) {
let processedRequests = 0
while (pendingRequests.length > 0 && processedRequests < CHUNK_SIZE) {
const req = pendingRequests.shift()
await processRequest(oracleContract, ownerAddress, req.id, req.callerAddress)
processedRequests++
}
}
async function processRequest (oracleContract, ownerAddress, id, callerAddress) {
let retries = 0
while (retries < MAX_RETRIES) {
try {
const ethPrice = await retrieveLatestEthPrice()
await setLatestEthPrice(oracleContract, callerAddress, ownerAddress, ethPrice, id)
return
} catch (error) {
if (retries === MAX_RETRIES - 1) {
await setLatestEthPrice(oracleContract, callerAddress, ownerAddress, '0', id)
return
}
retries++
}
}
}
async function setLatestEthPrice (oracleContract, callerAddress, ownerAddress, ethPrice, id) {
// Start here
const ethPriceInt = (new BN(parseInt(ethPrice), 10)).mul(multiplier)
const idInt = new BN(parseInt(id))
try {
await oracleContract.methods.setLatestEthPrice(ethPriceInt.toString(), callerAddress, idInt.toString()).send({ from: ownerAddress })
} catch (error) {
console.log('Error encountered while calling setLatestEthPrice.')
// Do some error handling
}
}
|
const fs = require('fs')
const Web3 = require('web3')
const { Client, NonceTxMiddleware, SignedTxMiddleware, LocalAddress, CryptoUtils, LoomProvider } = require('loom-js')
function loadAccount (privateKeyFileName) {
const extdevChainId = 'extdev-plasma-us1'
const privateKeyStr = fs.readFileSync(privateKeyFileName, 'utf-8')
const privateKey = CryptoUtils.B64ToUint8Array(privateKeyStr)
const publicKey = CryptoUtils.publicKeyFromPrivateKey(privateKey)
const client = new Client(
extdevChainId,
'wss://extdev-plasma-us1.dappchains.com/websocket',
'wss://extdev-plasma-us1.dappchains.com/queryws'
)
client.txMiddleware = [
new NonceTxMiddleware(publicKey, client),
new SignedTxMiddleware(privateKey)
]
client.on('error', msg => {
console.error('Connection error', msg)
})
return {
ownerAddress: LocalAddress.fromPublicKey(publicKey).toString(),
web3js: new Web3(new LoomProvider(client, privateKey)),
client
}
}
module.exports = {
loadAccount,
};
|
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle is Ownable {
uint private randNonce = 0;
uint private modulus = 1000;
mapping(uint256=>bool) pendingRequests;
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public onlyOwner {
require(pendingRequests[_id], "This request is not in my pending list.");
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
|
pragma solidity 0.5.0;
import "./EthPriceOracleInterface.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
contract CallerContract is Ownable {
uint256 private ethPrice;
EthPriceOracleInterface private oracleInstance;
address private oracleAddress;
mapping(uint256=>bool) myRequests;
event newOracleAddressEvent(address oracleAddress);
event ReceivedNewRequestIdEvent(uint256 id);
event PriceUpdatedEvent(uint256 ethPrice, uint256 id);
function setOracleInstanceAddress (address _oracleInstanceAddress) public onlyOwner {
oracleAddress = _oracleInstanceAddress;
oracleInstance = EthPriceOracleInterface(oracleAddress);
emit newOracleAddressEvent(oracleAddress);
}
function updateEthPrice() public {
uint256 id = oracleInstance.getLatestEthPrice();
myRequests[id] = true;
emit ReceivedNewRequestIdEvent(id);
}
function callback(uint256 _ethPrice, uint256 _id) public onlyOracle {
require(myRequests[_id], "This request is not in my pending list.");
ethPrice = _ethPrice;
delete myRequests[_id];
emit PriceUpdatedEvent(_ethPrice, _id);
}
modifier onlyOracle() {
require(msg.sender == oracleAddress, "You are not authorized to call this function.");
_;
}
}
|
pragma solidity 0.5.0;
contract EthPriceOracleInterface {
function getLatestEthPrice() public returns (uint256);
}
|
pragma solidity 0.5.0;
contract CallerContractInterface {
function callback(uint256 _ethPrice, uint256 id) public;
}
|