I've written a very basic oracle that calls a graphQL API because I couldn't find any services that supported graphQL. I have 2 questions about testing these oracles
- How do you test whether data has been set via transaction? My first test
'has no vote data to start'
succeeds, but my second test'saves vote data after a call to the API'
fails - Is there anything else I need to write to build trust in this oracle? It seems pretty obvious the magic here isn't happening on-chain, so I don't quite see how this flies in a trustless environment
- Any other feedback is welcome! This is my first contract
// VoteOracle.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract VoteOracle {
// Contract owner
address public owner;
string[] public voters;
// Callback function
event CallbackGetVotes();
constructor() {
owner = msg.sender;
}
// This is called first, which emits an event
// that fetches new data
function updateVotes() public {
emit CallbackGetVotes();
}
// Once the data is fetched by the service,
// setVotes is called to set that data
function setVotes(string[] memory _voters) public {
require(msg.sender == owner);
voters = _voters;
}
// Finally, getVotes is called to get the new data
function getVotes() public view returns (string[] memory) {
return voters;
}
}
// voteOracleTests.js
const {expect, use} = require('chai');
const {Contract} = require('ethers');
const {deployContract, MockProvider, solidity} = require('ethereum-waffle');
const VoteOracle = require('../build/contracts/VoteOracle.json');
const { ApolloClient, NormalizedCacheObject, HttpLink, InMemoryCache, gql } = require("@apollo/client");
const fetch = require('cross-fetch');
use(solidity);
describe('VoteOracle', () => {
const provider = new MockProvider()
const [wallet] = provider.getWallets()
let voteOracle
let sharedData
const apolloClient = new ApolloClient({
link: new HttpLink({
uri: `my_url/graphql?`,
fetch
}),
cache: new InMemoryCache()
})
GET_VOTES = gql`
query Votes() {
votes (
orderBy: "created",
orderDirection: desc
) {
id
voter
created
proposal {
id
}
choice
space {
id
}
}
}
`
beforeEach(async () => {
// Grab the data from Apollo Client
const { data } = await apolloClient.query({
query: GET_VOTES,
})
sharedData = data
// Set up the contract itself
voteOracle = await deployContract(wallet, VoteOracle)
})
it('has no vote data to start', async () => {
expect(await voteOracle.getVotes().length).to.equal(undefined)
})
it('saves vote data after a call to the API', async() => {
const txn = voteOracle.setVotes(sharedData.votes.map(v => v.voter))
// The next line fails! See my question #1
expect(await voteOracle.getVotes().length).to.exist()
})
});