Skip to content

Commit

Permalink
Add Saddle, the Best Darn Ethereum Framework.
Browse files Browse the repository at this point in the history
  • Loading branch information
hayesgm committed Jun 23, 2019
1 parent 271adab commit 6d132f3
Show file tree
Hide file tree
Showing 19 changed files with 272 additions and 506 deletions.
1 change: 0 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ jobs:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
- run: ~/repo/node_modules/.bin/tsc
- run: mkdir ~/junit-oracle
- run: JEST_JUNIT_OUTPUT=~/junit-oracle/test-results.xml yarn run test --ci --reporters=default --reporters=jest-junit
- store_test_results:
Expand Down
22 changes: 0 additions & 22 deletions config/development.js

This file was deleted.

37 changes: 0 additions & 37 deletions config/rinkeby.js

This file was deleted.

27 changes: 0 additions & 27 deletions config/test.js

This file was deleted.

17 changes: 8 additions & 9 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = {
// collectCoverageFrom: null,

// The directory where Jest should output its coverage files
// coverageDirectory: "coverage",
// coverageDirectory: null,

// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
Expand Down Expand Up @@ -52,7 +52,7 @@ module.exports = {
// forceCoverageMatch: [],

// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: './.tsbuilt/test.js',
// globalSetup: null,

// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: null,
Expand Down Expand Up @@ -123,9 +123,7 @@ module.exports = {
// setupFiles: [],

// A list of paths to modules that run some code to configure or set up the testing framework before each test
setupFilesAfterEnv: [
'./.tsbuilt/test.js'
],
setupFilesAfterEnv: ["<rootDir>/tests/Matchers.js"],

// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
Expand All @@ -140,9 +138,10 @@ module.exports = {
// testLocationInResults: false,

// The glob patterns Jest uses to detect test files
testMatch: [
"**/tests/**/*.[jt]s?(x)"
],
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],

// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
Expand Down Expand Up @@ -176,7 +175,7 @@ module.exports = {
// unmockedModulePathPatterns: undefined,

// Indicates whether each individual test should be reported during the run
verbose: true,
// verbose: null,

// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
Expand Down
11 changes: 4 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,18 @@
"author": "Compound Labs, Inc.",
"license": "MIT",
"dependencies": {
"eth-saddle": "^0.0.5",
"truffle-hdwallet-provider": "^1.0.10",
"web3": "1.0.0-beta.55",
"yargs": "^13.2.4"
},
"devDependencies": {
"ganache-core": "^2.5.5",
"jest": "^24.8.0",
"jest-junit": "^6.4.0",
"typescript": "^3.5.1"
"jest-cli": "^24.8.0",
"jest-junit": "^6.4.0"
},
"scripts": {
"saddle:compile": "mkdir -p .build && solc --combined-json bin,abi --optimize contracts/*.sol > .build/contracts.json",
"saddle:test:run": "node --stack-trace-limit=1000 ./node_modules/jest-cli/bin/jest.js",
"saddle:deploy": "yarn run saddle:compile && node ./.tsbuilt/deploy.js $@",
"compile": "tsc && yarn run saddle:compile",
"test": "yarn run saddle:compile && yarn run saddle:test:run"
"test": "npx saddle compile && npx saddle test"
}
}
83 changes: 83 additions & 0 deletions saddle.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@

module.exports = {
// solc: "solc", // Solc command to run
// solc_args: [], // Extra solc args
// build_dir: ".build", // Directory to place built contracts
// contracts: "contracts/*.sol", // Glob to match contract files
tests: ['**/tests/*Test.js'], // Glob to match test files
// networks: { // Define configuration for each network
// development: {
// providers: [ // How to load provider (processed in order)
// {env: "PROVIDER"}, // Try to load Http provider from `PROVIDER` env variable (e.g. env PROVIDER=http://...)
// {http: "http://127.0.0.1:8545"} // Fallback to localhost provider
// ],
// web3: { // Web3 options for immediate confirmation in development mode
// gas: [
// {env: "GAS"},
// {default: "4600000"}
// ],
// gas_price: [
// {env: "GAS_PRICE"},
// {default: "12000000000"}
// ],
// options: {
// transactionConfirmationBlocks: 1,
// transactionBlockTimeout: 5
// }
// },
// accounts: [ // How to load default account for transactions
// {env: "ACCOUNT"}, // Load from `ACCOUNT` env variable (e.g. env ACCOUNT=0x...)
// {unlocked: 0} // Else, try to grab first "unlocked" account from provider
// ]
// },
// test: {
// providers: [
// {env: "PROVIDER"},
// {ganache: {}}, // In test mode, connect to a new ganache provider. Any options will be passed to ganache
// ],
// web3: {
// gas: [
// {env: "GAS"},
// {default: "4600000"}
// ],
// gas_price: [
// {env: "GAS_PRICE"},
// {default: "12000000000"}
// ],
// options: {
// transactionConfirmationBlocks: 1,
// transactionBlockTimeout: 5
// }
// },
// accounts: [
// {env: "ACCOUNT"},
// {unlocked: 0}
// ]
// },
// rinkeby: {
// providers: [
// {env: "PROVIDER"},
// {file: "~/.ethereum/rinkeby-url"}, // Load from given file with contents as the URL (e.g. https://infura.io/api-key)
// {http: "https://rinkeby.infura.io"}
// ],
// web3: {
// gas: [
// {env: "GAS"},
// {default: "4600000"}
// ],
// gas_price: [
// {env: "GAS_PRICE"},
// {default: "12000000000"}
// ],
// options: {
// transactionConfirmationBlocks: 1,
// transactionBlockTimeout: 5
// }
// },
// accounts: [
// {env: "ACCOUNT"},
// {file: "~/.ethereum/rinkeby"} // Load from given file with contents as the private key (e.g. 0x...)
// ]
// }
// }
}
29 changes: 15 additions & 14 deletions tests/DelFiPriceTest.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
const {
address,
bytes,
encode,
sign,
uint256
} = require('./Helpers');

describe('DelFiPrice', () => {
it('sanity checks the delfi price view', async () => {
const {
address,
deploy,
encode,
sign,
web3
} = saddle; // XXX this kinda sucks

const sources = [
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf10',
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf11',
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf12',
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf13',
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf14',
].map(web3.eth.accounts.privateKeyToAccount);

const nonSources = [
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf15',
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf16',
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf17',
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf18',
'0x177ee777e72b8c042e05ef41d1db0f17f1fcb0e8150b37cfad6993e4373bdf19'
].map(web3.eth.accounts.privateKeyToAccount);

const priceData = await deploy('OpenOraclePriceData', []);
const delfi = await deploy('DelFiPrice', [priceData.address, sources.map(a => a.address)]);
const now = new Date - 0;

// Reads a price of an asset that doesn't exist yet
expect(await delfi.methods.prices('ETH').call()).numEquals(0);
expect(await call(delfi.methods.prices('ETH'))).numEquals(0);

async function postPrices(timestamp, priceses, symbols, signers = sources) {
const messages = [], signatures = [];
Expand All @@ -40,15 +42,14 @@ describe('DelFiPrice', () => {
expect(signatory).toEqual(signers[i].address);
messages.push(message);
signatures.push(signature);
})
return delfi.methods.postPrices(messages, signatures, symbols).send({gas: 5000000});
});
return send(delfi.methods.postPrices(messages, signatures, symbols), {gas: 5000000});
}

async function getPrice(symbol) {
return delfi.methods.prices(symbol).call()
return call(delfi.methods.prices(symbol))
}


/** Posts nothing **/

const post0 = await postPrices(now, [], ['ETH'])
Expand Down Expand Up @@ -149,4 +150,4 @@ describe('DelFiPrice', () => {
expect(await getPrice('ETH')).numEquals(256);

}, 30000);
});
});
38 changes: 38 additions & 0 deletions tests/Helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const Web3 = require('web3');

const web3 = new Web3(); // no provider, since we won't make any calls

function address(n) {
return `0x${(n).toString(16).padStart(40, '0')}`;
}

function bytes(str) {
return web3.eth.abi.encodeParameter('string', str);
}

function uint256(int) {
return web3.eth.abi.encodeParameter('uint256', int);
}

function encode(timestamp, pairs) {
return web3.eth.abi.encodeParameters(['uint256', 'bytes[]'], [timestamp, pairs.map(([k, v]) => {
return web3.eth.abi.encodeParameters(['string', 'uint'], [k, v])
})]);
}

// XXX maybe want to import signing here, shared with sdk, define there or at top level?
function sign(message, privateKey) {
const hash = web3.utils.keccak256(message);
const {r, s, v} = web3.eth.accounts.sign(hash, privateKey);
const signature = web3.eth.abi.encodeParameters(['bytes32', 'bytes32', 'uint8'], [r, s, v]);
const signatory = web3.eth.accounts.recover(hash, v, r, s);
return {hash, message, signature, signatory};
}

module.exports = {
address,
bytes,
uint256,
encode,
sign
};
18 changes: 18 additions & 0 deletions tests/Matchers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

expect.extend({
numEquals(actual, expected) {
return {
pass: actual.toString() == expected.toString(),
message: () => `expected ${JSON.stringify(actual)} == ${JSON.stringify(expected)}`
}
}
});

expect.extend({
toRevert(actual, msg='revert') {
return {
pass: !!actual['message'] && actual.message === `VM Exception while processing transaction: ${msg}`,
message: () => `expected revert, got: ${JSON.stringify(actual)}`
}
}
});
Loading

0 comments on commit 6d132f3

Please sign in to comment.