mirror of
https://github.com/embarklabs/embark.git
synced 2025-01-22 03:29:43 +00:00
feat(@embark/specialconfigs): introduce dynamic addresses (#1873)
* fix embarkjs generation fix ens setProvider fix embarkjs objects fix generated embarkjs provider generate contracts fix embarkjs-ens * address some of the issues in the code review * feat(@embark/specialconfigs): introduce dynamic addresses This commit introduces a new Smart Contract configuration addressHandler that lets users define a function to "lazily" compute the address of the Smart Contract in question. This is useful when a third-party takes care of deploying a dependency Smart Contract, but the address of that Smart Contract only being available at run-time. Example: ``` deploy: { SimpleStorage: { fromIndex: 0, args: [100], }, OtherContract: { deps: ['SimpleStorage'], address: async (deps) => { // use `deps.contracts.SimpleStorage` to determine address }, abiDefinition: ABI }, } ``` In the example above, OtherContract will be deployed after SimpleStorage because it uses the deps property to define Smart Contracts that it depends on. All dependencies are exposed on the addressHandler function parameter similar to deployment hooks. Closes #1690
This commit is contained in:
parent
022a3c11ec
commit
86ee867689
@ -323,7 +323,10 @@ class ContractsManager {
|
||||
contract.type = 'file';
|
||||
contract.className = className;
|
||||
|
||||
if (contract.address) {
|
||||
if (contract.address && typeof contract.address === 'function') {
|
||||
contract.addressHandler = contract.address;
|
||||
delete contract.addres;
|
||||
} else if (contract.address && typeof contract.address === 'string') {
|
||||
contract.deployedAddress = contract.address;
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,19 @@ class Deployment {
|
||||
Object.values(contracts).forEach((contract) => {
|
||||
function deploy(result, callback) {
|
||||
if (typeof result === 'function') callback = result;
|
||||
if (contract.addressHandler) {
|
||||
return self.events.request('deployment:contract:address', {contract}, (err, address) => {
|
||||
if (err) {
|
||||
errors.push(err);
|
||||
} else {
|
||||
contract.address = address;
|
||||
contract.deployedAddress = address;
|
||||
self.logger.info(__('{{contractName}} already deployed at {{address}}', {contractName: contract.className.bold.cyan, address: contract.address.bold.cyan}));
|
||||
self.events.emit("deployment:contract:deployed", contract);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
self.deployContract(contract, (err) => {
|
||||
if (err) {
|
||||
errors.push(err);
|
||||
|
@ -9,6 +9,17 @@ class FunctionConfigs {
|
||||
this.config = embark.config;
|
||||
}
|
||||
|
||||
async executeContractAddressHandler(contract, cb) {
|
||||
try {
|
||||
const logger = Utils.createLoggerWithPrefix(this.logger, 'addressHandler >');
|
||||
const dependencies = await this.getDependenciesObject(logger);
|
||||
const address = await contract.addressHandler(dependencies);
|
||||
cb(null, address);
|
||||
} catch (err) {
|
||||
cb(new Error(`Error running addressHandler for ${contract.className}: ${err.message}`));
|
||||
}
|
||||
}
|
||||
|
||||
async beforeAllDeployAction(cb) {
|
||||
try {
|
||||
const beforeDeployFn = this.config.contractsConfig.beforeDeploy;
|
||||
@ -76,6 +87,10 @@ class FunctionConfigs {
|
||||
// TODO: for this to work correctly we need to add a default from address to the contract
|
||||
if (contract.deploy === false) continue;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const contractRegisteredInVM = await this.checkContractRegisteredInVM(contract);
|
||||
if (!contractRegisteredInVM) {
|
||||
await this.events.request2("embarkjs:contract:runInVM", contract);
|
||||
}
|
||||
let contractInstance = await this.events.request2("runcode:eval", contract.className);
|
||||
args.contracts[contract.className] = contractInstance;
|
||||
}
|
||||
@ -90,6 +105,12 @@ class FunctionConfigs {
|
||||
});
|
||||
}
|
||||
|
||||
async checkContractRegisteredInVM(contract) {
|
||||
const checkContract = `
|
||||
return typeof ${contract.className} !== 'undefined';
|
||||
`;
|
||||
return await this.events.request2('runcode:eval', checkContract);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FunctionConfigs;
|
||||
|
@ -15,6 +15,7 @@ class SpecialConfigs {
|
||||
this.listConfigs = new ListConfigs(embark);
|
||||
this.functionConfigs = new FunctionConfigs(embark);
|
||||
|
||||
this.events.setCommandHandler('deployment:contract:address', this.executeAddressHandlerForContract.bind(this));
|
||||
this.embark.registerActionForEvent('deployment:deployContracts:beforeAll', this.beforeAllDeployAction.bind(this));
|
||||
this.embark.registerActionForEvent('deployment:deployContracts:afterAll', this.afterAllDeployAction.bind(this));
|
||||
this.embark.registerActionForEvent("deployment:contract:deployed", this.doOnDeployAction.bind(this));
|
||||
@ -22,6 +23,10 @@ class SpecialConfigs {
|
||||
this.embark.registerActionForEvent('deployment:contract:beforeDeploy', this.beforeDeployAction.bind(this));
|
||||
}
|
||||
|
||||
async executeAddressHandlerForContract(params, cb) {
|
||||
return this.functionConfigs.executeContractAddressHandler(params.contract, cb);
|
||||
}
|
||||
|
||||
async beforeAllDeployAction(_params, cb) {
|
||||
if (typeof this.config.contractsConfig.beforeDeploy !== 'function') {
|
||||
return this.listConfigs.beforeAllDeployAction(cb);
|
||||
|
@ -183,7 +183,7 @@ function prepareContractsConfig(config) {
|
||||
config.contracts[contractName].gasPrice = getWeiBalanceFromString(gasPrice);
|
||||
}
|
||||
|
||||
if (address) {
|
||||
if (address && typeof address === 'string') {
|
||||
config.contracts[contractName].address = extendZeroAddressShorthand(address);
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ production: {
|
||||
|
||||
In order to give users full control over which Smart Contracts should be deployed, Embark comes with a configuration feature called "deployment strategies". Deployment strategies tell Embark whether it should deploy all of the user's Smart Contracts (and its (3rd-party) dependencies, or just deploy individual Smart Contracts.
|
||||
|
||||
There are two possible strategy options:
|
||||
There are two possible strategy options:
|
||||
|
||||
- **implicit** - This is the default. Using the `implicit` strategy, Embark tries to deploy all Smart Contracts configured in the `deploy` configuration, including its (3rd-party) dependencies.
|
||||
- **explicit** - Setting this option to `explicit` tells Embark to deploy the Smart Contracts specified in the `deploy` configuration without their dependencies. This can be combined with [disabling deployment](#Disabling-deployment) of individual Smart Contracts for fine control.
|
||||
@ -202,6 +202,29 @@ module.exports = {
|
||||
}
|
||||
```
|
||||
|
||||
## Dynamic Addresses
|
||||
|
||||
There are scenarios in which we want to configure a Smart Contract that is already deployed by a third-party, but its address can only be computed at run-time. For such cases, Embark supports specifying a function as `address`, which returns the address we're interested in. Usually, other Smart Contract instances are needed to perform that computation, so the [`deps` configuration](#Deployment-hooks) comes in handy as well:
|
||||
|
||||
```
|
||||
deploy: {
|
||||
SimpleStorage: {
|
||||
fromIndex: 0,
|
||||
args: [100],
|
||||
},
|
||||
OtherContract: {
|
||||
deps: ['SimpleStorage'],
|
||||
address: async (deps) => {
|
||||
// use `deps.contracts.SimpleStorage` to determine and return address
|
||||
},
|
||||
abiDefinition: ABI
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, `OtherContract` will be deployed after `SimpleStorage` because it uses the `deps` property to define Smart Contracts that it depends on. All dependencies are exposed on the `address` function parameter similar to [deployment hooks](#Deployment-hooks). Also, notice that the `abiDefinition` is only needed if the Smart Contracts bytecode isn't already on disk.
|
||||
|
||||
|
||||
## Configuring source files
|
||||
|
||||
By default Embark will look for Smart Contracts inside the folder that's configured in the application's [embark.json](configuration.html#contracts), the default being the `contracts` folder. However, if we want to change the location to look for a single Smart Contract's source, or need to compile a third-party Smart Contract to get hold of its ABI, we can do so by using the `file` property.
|
||||
@ -541,7 +564,7 @@ deploy: {
|
||||
}
|
||||
```
|
||||
|
||||
Which will result in
|
||||
Which will result in
|
||||
|
||||
```
|
||||
SimpleStorage > onDeploy > Hello from onDeploy!
|
||||
|
Loading…
x
Reference in New Issue
Block a user