Skip to content
This repository has been archived by the owner on Jan 24, 2022. It is now read-only.

Commit

Permalink
New lib model for supporting multiple deps
Browse files Browse the repository at this point in the history
App contract now has multiple providers, instead of having a single
one which could have a fallback (stdlib). Now the app explicitly manages
its dependencies. App is still offered in two flavours: unversioned
(which accepts directories) and versioned (which accepts packages with a
pinned version).

All contract wrapper models were also updated. In particular, App no
longer acts as a facade, but is instead an actual wrapper of the App
contract. A new abstraction, Project, is introduced to be used as a
facade by the CLI (or lib users). Project is subclassed by LibProject
and AppProject.
  • Loading branch information
spalladino committed Sep 24, 2018
1 parent f5c850a commit beb7afd
Show file tree
Hide file tree
Showing 55 changed files with 1,878 additions and 2,212 deletions.
53 changes: 0 additions & 53 deletions packages/lib/contracts/application/AppDirectory.sol

This file was deleted.

34 changes: 21 additions & 13 deletions packages/lib/contracts/application/BaseApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,22 @@ contract BaseApp is Ownable {
}

/**
* @dev Abstract function to return the implementation provider.
* @return The implementation provider.
* @dev Abstract function to return the implementation provider for a given package name.
* @param packageName Name of the package to be retrieved.
* @return The implementation provider for the package.
*/
function getProvider() internal view returns (ImplementationProvider);
function getProvider(string packageName) public view returns (ImplementationProvider);

/**
* @dev Returns the implementation address for a given contract name, provided by the `ImplementationProvider`.
* @param packageName Name of the package where the contract is contained.
* @param contractName Name of the contract.
* @return Address where the contract is implemented.
*/
function getImplementation(string contractName) public view returns (address) {
return getProvider().getImplementation(contractName);
function getImplementation(string packageName, string contractName) public view returns (address) {
ImplementationProvider provider = getProvider(packageName);
if (address(provider) == address(0)) return address(0);
return provider.getImplementation(contractName);
}

/**
Expand Down Expand Up @@ -66,49 +70,53 @@ contract BaseApp is Ownable {

/**
* @dev Creates a new proxy for the given contract.
* @param packageName Name of the package where the contract is contained.
* @param contractName Name of the contract.
* @return Address of the new proxy.
*/
function create(string contractName) public returns (AdminUpgradeabilityProxy) {
address implementation = getImplementation(contractName);
function create(string packageName, string contractName) public returns (AdminUpgradeabilityProxy) {
address implementation = getImplementation(packageName, contractName);
return factory.createProxy(this, implementation);
}

/**
* @dev Creates a new proxy for the given contract and forwards a function call to it.
* This is useful to initialize the proxied contract.
* @param packageName Name of the package where the contract is contained.
* @param contractName Name of the contract.
* @param data Data to send as msg.data in the low level call.
* It should include the signature and the parameters of the function to be called, as described in
* https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
* @return Address of the new proxy.
*/
function createAndCall(string contractName, bytes data) payable public returns (AdminUpgradeabilityProxy) {
address implementation = getImplementation(contractName);
function createAndCall(string packageName, string contractName, bytes data) payable public returns (AdminUpgradeabilityProxy) {
address implementation = getImplementation(packageName, contractName);
return factory.createProxyAndCall.value(msg.value)(this, implementation, data);
}

/**
* @dev Upgrades a proxy to the newest implementation of a contract.
* @param proxy Proxy to be upgraded.
* @param packageName Name of the package where the contract is contained.
* @param contractName Name of the contract.
*/
function upgrade(AdminUpgradeabilityProxy proxy, string contractName) public onlyOwner {
address implementation = getImplementation(contractName);
function upgrade(AdminUpgradeabilityProxy proxy, string packageName, string contractName) public onlyOwner {
address implementation = getImplementation(packageName, contractName);
proxy.upgradeTo(implementation);
}

/**
* @dev Upgrades a proxy to the newest implementation of a contract and forwards a function call to it.
* This is useful to initialize the proxied contract.
* @param proxy Proxy to be upgraded.
* @param packageName Name of the package where the contract is contained.
* @param contractName Name of the contract.
* @param data Data to send as msg.data in the low level call.
* It should include the signature and the parameters of the function to be called, as described in
* https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
*/
function upgradeAndCall(AdminUpgradeabilityProxy proxy, string contractName, bytes data) payable public onlyOwner {
address implementation = getImplementation(contractName);
function upgradeAndCall(AdminUpgradeabilityProxy proxy, string packageName, string contractName, bytes data) payable public onlyOwner {
address implementation = getImplementation(packageName, contractName);
proxy.upgradeToAndCall.value(msg.value)(implementation, data);
}
}
48 changes: 0 additions & 48 deletions packages/lib/contracts/application/PackagedApp.sol

This file was deleted.

41 changes: 29 additions & 12 deletions packages/lib/contracts/application/UnversionedApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,50 @@ import "../upgradeability/UpgradeabilityProxyFactory.sol";
*/
contract UnversionedApp is BaseApp {
/*
* @dev Provider that stores the contract implementation addresses.
* @dev Providers for contract implementation addresses.
*/
ImplementationProvider internal provider;
mapping(string => ImplementationProvider) internal providers;

/**
* @dev Emitted when a provider dependency is changed in the application.
* @param providerName Name of the provider that changed.
* @param implementation Address of the provider associated to the name.
*/
event ProviderChanged(string providerName, address implementation);

/**
* @dev Constructor function.
* @param _provider Implementation provider.
* @param _factory Proxy factory.
*/
constructor(ImplementationProvider _provider, UpgradeabilityProxyFactory _factory) BaseApp(_factory) public {
setProvider(_provider);
}
constructor(UpgradeabilityProxyFactory _factory) BaseApp(_factory) public { }

/**
* @dev Returns the provider used by the app.
* @dev Returns the provider for a given package name, or zero if not set.
* @param packageName Name of the package to be retrieved.
* @return The provider.
*/
function getProvider() internal view returns (ImplementationProvider) {
return provider;
function getProvider(string packageName) public view returns (ImplementationProvider) {
return providers[packageName];
}

/**
* @dev Sets a new implementation provider.
* @param _provider New implementation provider
* @param packageName Name under which the provider is to be registered in the app.
* @param _provider New implementation provider.
*/
function setProvider(ImplementationProvider _provider) public onlyOwner {
function setProvider(string packageName, ImplementationProvider _provider) public onlyOwner {
require(address(_provider) != address(0), "Cannot set the implementation provider of an app to the zero address");
provider = _provider;
providers[packageName] = _provider;
emit ProviderChanged(packageName, _provider);
}

/**
* @dev Unsets an existing provider. Reverts if the provider does not exist.
* @param packageName Name of the provider to be removed.
*/
function unsetProvider(string packageName) public onlyOwner {
require(providers[packageName] != address(0), "Provider to unset not found");
delete providers[packageName];
emit ProviderChanged(packageName, address(0));
}
}
78 changes: 78 additions & 0 deletions packages/lib/contracts/application/VersionedApp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
pragma solidity ^0.4.24;

import "./BaseApp.sol";
import "./versioning/Package.sol";
import "../upgradeability/UpgradeabilityProxyFactory.sol";

/**
* @title VersionedApp
* @dev App for an upgradeable project that can use different versions from packages.
* This is the standard entry point for an upgradeable app.
*/
contract VersionedApp is BaseApp {

struct ProviderInfo {
Package package;
string version;
}

mapping(string => ProviderInfo) internal providers;

/**
* @dev Emitted when a package dependency is changed in the application.
* @param providerName Name of the package that changed.
* @param package Address of the package associated to the name.
* @param version Version of the package in use.
*/
event PackageChanged(string providerName, address package, string version);

/**
* @dev Constructor function.
* @param _factory Proxy factory.
*/
constructor(UpgradeabilityProxyFactory _factory) BaseApp(_factory) public { }

/**
* @dev Returns the provider for a given package name, or zero if not set.
* @param packageName Name of the package to be retrieved.
* @return The provider.
*/
function getProvider(string packageName) public view returns (ImplementationProvider) {
ProviderInfo storage info = providers[packageName];
if (address(info.package) == address(0)) return ImplementationProvider(0);
return info.package.getVersion(info.version);
}

/**
* @dev Returns information on a package given its name.
* @param packageName Name of the package to be queried.
* @return A tuple with the package address and pinned version given a package name, or zero if not set
*/
function getPackage(string packageName) public view returns (Package, string) {
ProviderInfo storage info = providers[packageName];
return (info.package, info.version);
}

/**
* @dev Sets a package in a specific version as a dependency for this application.
* Requires the version to be present in the package.
* @param packageName Name of the package to set or overwrite.
* @param package Address of the package to register.
* @param version Version of the package to use in this application.
*/
function setPackage(string packageName, Package package, string version) public onlyOwner {
require(package.hasVersion(version), "The requested version must be registered in the given package");
providers[packageName] = ProviderInfo(package, version);
emit PackageChanged(packageName, package, version);
}

/**
* @dev Unsets a package given its name.
* Reverts if the package is not set in the application.
* @param packageName Name of the package to remove.
*/
function unsetPackage(string packageName) public onlyOwner {
require(address(providers[packageName].package) != address(0), "Package to unset not found");
delete providers[packageName];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ interface ImplementationProvider {
*/
function getImplementation(string contractName) public view returns (address);
}

4 changes: 2 additions & 2 deletions packages/lib/contracts/mocks/DummyImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ contract DummyImplementation {
string public text;
uint256[] public values;

function initialize(uint256 _value) public {
function initialize(uint256 _value) payable public {
value = _value;
}

Expand All @@ -33,7 +33,7 @@ contract DummyImplementation {
}

contract DummyImplementationV2 is DummyImplementation {
function migrate(uint256 newVal) public {
function migrate(uint256 newVal) payable public {
value = newVal;
}

Expand Down
Loading

0 comments on commit beb7afd

Please sign in to comment.