1

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

  1. 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
  2. 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
  3. 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()
  })
});

1 Answer 1

1

Looks cool. You could use Tellor to put random data onchain, so you could definitely bring on graphQL data for votes also. I think the big problem obviously here is just that you have an admin key that controls everything

3
  • Right - just so I understand (this is my first oracle) - you're saying that the admin key (i.e., the address of the person deploying the contract) has total control over the oracle and its upgrades. So, if you don't trust me, you can't trust the oracle? Dec 23, 2021 at 2:21
  • not even upgrades, just that setVotes function. If whoever that owner address is dies, you're SOL
    – thefett
    Dec 24, 2021 at 3:10
  • Right - makes a ton of sense, thanks Dec 25, 2021 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.