Skip to content

Commit

Permalink
Merge pull request #2 from identity-com/feature
Browse files Browse the repository at this point in the history
Support importing contract artifacts by url
  • Loading branch information
dankelleher authored Oct 4, 2018
2 parents e8d8724 + 5bd52c8 commit 4dc6058
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 21 deletions.
2 changes: 1 addition & 1 deletion src/support/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function mapError(error) {

module.exports = {
mapError,
CvcError: CvcError,
CvcError,
InvalidNonceError,
FailedTxChainError,
NotDeployedError,
Expand Down
59 changes: 40 additions & 19 deletions src/support/tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,27 @@ const fallbackToAutodetectDeployedContract = (contract, contractName) =>
})
.then(assertCodeAtAddress);

/**
* Returns the contract artifact
* @param {string} contractName - The contact name.
* @returns {Promise<object>} The contract artifact.
*/
// eslint-disable-next-line consistent-return
const getContractArtifact = contractName => {
if (config.contracts.url) {
// For frontend apps you can pass the url of the contracts
// eslint-disable-next-line no-undef
return fetch(`${config.contracts.url}/${contractName}.json`)
.then(res => res.json())
.then(data => Promise.resolve(data))
.catch(err => Promise.reject(err));
} else if (config.contracts.dir) {
// For backend servers you can pass the path of the contracts
// eslint-disable-next-line import/no-dynamic-require, global-require
return Promise.resolve(require(`./${path.join(config.contracts.dir, `${contractName}.json`)}`));
}
};

/**
* Returns the contract instance by name.
* @type {Function}
Expand All @@ -106,26 +127,27 @@ tx.contractInstance = _.memoize(contractName => {
throw new Error(`Invalid contract name "${contractName}"`);
}
// Load contract artifact file.
// eslint-disable-next-line import/no-dynamic-require, global-require
const contractArtifact = require(`./${path.join(config.contracts.dir, `${contractName}.json`)}`);
// Create contract object.
const contract = truffleContract(contractArtifact);
contract.setProvider(tx.web3.currentProvider);

if (_.has(config, ['contracts', 'addresses', contractName])) {
const contractAddress = config.contracts.addresses[contractName];
try {
return contract.at(contractAddress).then(assertCodeAtAddress);
} catch (e) {
logger.debug(
`Contract '${contractName}' could not be found at configured '${contractAddress}'. Falling back to autodetect`
);
return getContractArtifact(contractName).then(contractArtifact => {
// Create contract object.
const contract = truffleContract(contractArtifact);
contract.setProvider(tx.web3.currentProvider);

if (_.has(config, ['contracts', 'addresses', contractName])) {
const contractAddress = config.contracts.addresses[contractName];
try {
return contract.at(contractAddress).then(assertCodeAtAddress);
} catch (e) {
logger.debug(
`Contract '${contractName}' could not be found at configured '${contractAddress}'.
Falling back to autodetect`
);
return fallbackToAutodetectDeployedContract(contract, contractName);
}
} else {
logger.debug(`Address not configured for '${contractName}' contract. Using autodetect...`);
return fallbackToAutodetectDeployedContract(contract, contractName);
}
} else {
logger.debug(`Address not configured for '${contractName}' contract. Using autodetect...`);
return fallbackToAutodetectDeployedContract(contract, contractName);
}
});
} catch (error) {
logger.error(`Error loading contract ${contractName}`, error);
return Promise.reject(new CvcError(`Error loading contract: ${contractName}`, error));
Expand All @@ -146,7 +168,6 @@ tx.contractInstance = _.memoize(contractName => {
*/
tx.contractInstances = function(...contractNames) {
const contractInstancePromises = contractNames.map(tx.contractInstance);

return Promise.all(contractInstancePromises).then(contractInstances => _.zipObject(contractNames, contractInstances));
};

Expand Down
3 changes: 3 additions & 0 deletions test/assets/contracts/mockContract.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"contractName": "CvcValidatorRegistry"
}
64 changes: 63 additions & 1 deletion test/tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ require('longjohn');
const chai = require('chai');
const sandbox = require('sinon').createSandbox();
const nonce = require('../src/support/nonce');
const tx = require('../src/support/tx');
const fetchMock = require('fetch-mock');
const proxyquire = require('proxyquire');

const { expect } = chai;
chai.use(require('chai-as-promised'));

describe('tx.js', () => {
describe('when createTx throws', () => {
// eslint-disable-next-line global-require
const tx = require('../src/support/tx');
let releaseNonceSpy;
beforeEach('stub', () => {
const getDataApplyStub = sandbox.stub().throws('Error', 'Invalid number of arguments to Solidity function');
Expand Down Expand Up @@ -46,6 +49,8 @@ describe('tx.js', () => {
});

describe('waitForMine', () => {
// eslint-disable-next-line global-require
const tx = require('../src/support/tx');
before('Mining never resolves to a tx receipt', () => {
sandbox.stub(tx, 'getTransactionReceipt').resolves(null);
});
Expand All @@ -59,4 +64,61 @@ describe('tx.js', () => {
/getTransactionReceiptMined timeout/
)).timeout(4000);
});

describe('When we pass contract.url to contractInstance', () => {
const tx = proxyquire('../src/support/tx', {
'../../config/index': () => ({
contracts: { url: './contracts' }
}),
'truffle-contract': () => ({
setProvider: () => {},
deployed: () => ({
catch: () => ({
then: () => Promise.resolve({ foo: 'bar' })
})
})
})
});
before('stub', () => {
tx.web3 = sandbox.stub().returns({});
fetchMock.mock('./contracts/CvcEscrow.json', { contractName: 'CvcEscrow' });
});

after(() => {
sandbox.restore();
});

it('should fetch the contract', async () => {
const result = await tx.contractInstance('CvcEscrow');
expect(result).to.deep.equal({ foo: 'bar' });
});
});

describe('When we pass contract.dir to contractInstance', () => {
const tx = proxyquire('../src/support/tx', {
'../../config/index': () => ({
contracts: { dir: '../../test/assets/contracts' }
}),
'truffle-contract': () => ({
setProvider: () => {},
deployed: () => ({
catch: () => ({
then: () => Promise.resolve({ foo: 'bar' })
})
})
})
});
before('stub', () => {
tx.web3 = sandbox.stub().returns({});
});

after(() => {
sandbox.restore();
});

it('should fetch the contract', async () => {
const result = await tx.contractInstance('mockContract');
expect(result).to.deep.equal({ foo: 'bar' });
});
});
});

0 comments on commit 4dc6058

Please sign in to comment.