Skip to content

Commit

Permalink
feat(@embark/contracts): add proxyFor property for contracts
Browse files Browse the repository at this point in the history
Requested here #1689
Adds proxyFor to contracts that merges the ABI of the parent
contract to the child (proxy)  contract so that the proxy can use
the methods of the parent but is deployed as itself
  • Loading branch information
jrainville committed Feb 14, 2020
1 parent 414de52 commit 2e8b255
Showing 1 changed file with 63 additions and 42 deletions.
105 changes: 63 additions & 42 deletions packages/stack/contracts-manager/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ export default class ContractsManager {

if (contract.address && typeof contract.address === 'function') {
contract.addressHandler = contract.address;
delete contract.addres;
delete contract.address;
} else if (contract.address && typeof contract.address === 'string') {
contract.deployedAddress = contract.address;
}
Expand Down Expand Up @@ -385,64 +385,63 @@ export default class ContractsManager {
}
callback();
},
/*eslint complexity: ["error", 11]*/
// eslint-disable-next-line complexity
function dealWithSpecialConfigs(callback) {
let className, contract, parentContractName, parentContract;
let dictionary = Object.keys(self.contracts);

for (className in self.contracts) {
contract = self.contracts[className];

if (contract.instanceOf === undefined) {
if (!contract.instanceOf && !contract.proxyFor) {
continue;
}

parentContractName = contract.instanceOf;
parentContract = self.contracts[parentContractName];

if (parentContract === className) {
self.logger.error(__("%s : instanceOf is set to itself", className));
continue;
}
if (contract.instanceOf) {
parentContractName = contract.instanceOf;
parentContract = self.contracts[parentContractName];
if (!self._isParentContractDependencyCorrect(className, parentContract, 'instanceOf', dictionary)) {
continue;
}

if (parentContract === undefined) {
self.logger.error(__("{{className}}: couldn't find instanceOf contract {{parentContractName}}", {
className: className,
parentContractName: parentContractName
}));
let suggestion = proposeAlternative(parentContractName, dictionary, [className, parentContractName]);
if (suggestion) {
self.logger.warn(__('did you mean "%s"?', suggestion));
// If the contract has no args and the parent has them, use the parent's args in its place
if (parentContract.args?.length > 0 && contract.args?.length === 0) {
contract.args = parentContract.args;
}
continue;
}

if (parentContract.args && parentContract.args.length > 0 && ((contract.args && contract.args.length === 0) || contract.args === undefined)) {
contract.args = parentContract.args;
}
if (!contract.code) {
self.logger.error(__("{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}", {
className,
parentContractName
}));
}

if (contract.code !== undefined) {
self.logger.error(__("{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}", {
className: className,
parentContractName: parentContractName
}));
contract.path = parentContract.path;
contract.originalFilename = parentContract.originalFilename;
contract.filename = parentContract.filename;
contract.code = parentContract.code;
contract.runtimeBytecode = parentContract.runtimeBytecode;
contract.realRuntimeBytecode = (parentContract.realRuntimeBytecode || parentContract.runtimeBytecode);
contract.gasEstimates = parentContract.gasEstimates;
contract.functionHashes = parentContract.functionHashes;
contract.abiDefinition = parentContract.abiDefinition;
contract.linkReferences = parentContract.linkReferences;

contract.gas = contract.gas || parentContract.gas;
contract.gasPrice = contract.gasPrice || parentContract.gasPrice;
contract.type = 'instance';
}

contract.path = parentContract.path;
contract.originalFilename = parentContract.originalFilename;
contract.filename = parentContract.filename;
contract.code = parentContract.code;
contract.runtimeBytecode = parentContract.runtimeBytecode;
contract.realRuntimeBytecode = (parentContract.realRuntimeBytecode || parentContract.runtimeBytecode);
contract.gasEstimates = parentContract.gasEstimates;
contract.functionHashes = parentContract.functionHashes;
contract.abiDefinition = parentContract.abiDefinition;
contract.linkReferences = parentContract.linkReferences;

contract.gas = contract.gas || parentContract.gas;
contract.gasPrice = contract.gasPrice || parentContract.gasPrice;
contract.type = 'instance';
if (contract.proxyFor) {
parentContractName = contract.proxyFor;
parentContract = self.contracts[parentContractName];
if (!self._isParentContractDependencyCorrect(className, parentContract, 'proxyFor', dictionary)) {
continue;
}

// Merge ABI of contract and proxy so that the contract shares both ABIs, but remove the constructor
contract.abiDefinition = contract.abiDefinition.concat(parentContract.abiDefinition.filter(def => def.type !== 'constructor'));
}
}
callback();
},
Expand Down Expand Up @@ -552,6 +551,28 @@ export default class ContractsManager {
});
}

_isParentContractDependencyCorrect(className, parentContract, typeOfInheritance, dictionary) {
const parentContractName = parentContract.className;
if (parentContract === className) {
this.logger.error(__("{{className}} : {{typeOfInheritance}} is set to itself", {className, typeOfInheritance}));
return false;
}

if (parentContract === undefined) {
this.logger.error(__("{{className}}: couldn't find {{typeOfInheritance}} contract {{parentContractName}}", {
className,
parentContractName,
typeOfInheritance
}));
let suggestion = proposeAlternative(parentContractName, dictionary, [className, parentContractName]);
if (suggestion) {
this.logger.warn(__('did you mean "%s"?', suggestion));
}
return false;
}
return true;
}

_contractsForApi() {
const contracts = this.formatContracts();
contracts.forEach((contract) => {
Expand Down

0 comments on commit 2e8b255

Please sign in to comment.