-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Protocol3] Support DEX upgradability be default #353
Comments
BTW, we are of course assuming DAOs are not ready. The objective is not to prevent DEX owners from upgrading to a malicious contract, but allows users of a DEX to have enough time to respond during the waiting period. |
After talking to @Brechtpd and @letsgoustc, a better way to offer transparent upgradability is to create a VersionManager that always maintain the latest version and force each DEX's proxy to point to the lastest version. |
Here is the basic idea. Right now we have ILoopring3, but in the future, we may have ILoopring4, ILoopring5. And we have to assume they are not compatible. For Loopring3, the current design allows the creation of multiple exchange instances (each with its own id), but if we use a proxy instead for each DEX, we only need to have one single exchange instance behind all the proxies. First, we can ask interface ILoopring {
function getProtocolInfo() returns (uint16 version, address dexInstanceAddress);
function registerProxy(address proxy) returns (uint dexId); // this will also initialize the proxy's exchange ID
} When ILoopring3 is deployed, its constructor will automatically calls the We then create an contract IVersionManager {
struct Protocol {
address loopring;
address instance;
}
mapping (uint16 => Protocol) protocols;
function getProtocol(uint16 version) returns (address loopring, address instance) {
Protocol protocol = protocols[version];
loopring = protocol.loopring;
instance = protocol.instance;
}
function registerProtocol(address loopring) {
ILoopring loopring = ILoopring(loopring);
(ver, instance) = loopring.getProtocolInfo();
protocols[ver] = Protocol(loopring, instance);
}
function createExchange(uint16 ver) {
Protocol protocol = protocols[ver];
ILoopring loopring = ILoopring(protocol.loopring);
ExchangeProxy proxy = new ExchangeProxy(ver, address(this));
loopring.registerProxy(proxy);
}
} The contract ExchangeProxy {
constructor(uint16 _version, address _versionManager) public {
setVersion(_version);
setVersionManager(_versionManager);
}
function implementation() view public returns (address impl) {
IVersionManager vm = IVersionManager(versionManager());
(_, impl) = vm.getProtocol(version());
assert(impl != address(0));
}
} @Brechtpd please comment. |
Looks good. How would we do small backwards compatible changes to the Exchange implementation? It seems like currently it's a single Exchange implementation / Loopring version (a single call to |
Update: We can create the contract IProtocalRegistry is Claimable {
struct Protocol {
address instance;
string version;
}
mapping (address => Protocol) protocols; // map keys are ILoopring contract addresses
function getProtocol(address loopring) returns (address instance, string version) {
Protocol protocol = protocols[loopring];
instance = protocol.instance;
version = protocol.version;
}
function registerProtocol(address loopring, address instance, string version) {
protocols[loopring] = Protocol(instance, version);
}
function createExchange(address loopring) returns (address exchangeProxy, uint exchangeId) {
// assert loopring is a valid address
ExchangeProxy proxy = new ExchangeProxy(address(this), loopring);
exchangeProxy = address(proxy);
exchangeId = ILoopring(loopring).registerProxy(exchangeProxy);
}
} By doing this, we allow the owner to replace an instance for an existing loopring protocol version, and the instance does not have to deployed by the loopring contract. The contract ExchangeProxy {
constructor(address _registry, address loopring) public {
setRegistry(_registry);
setProtocol(_loopring);
}
function implementation() view public returns (address instance) {
IProtocolRegistry registry = IProtocolRegistry(registry());
(instance, _) = registry.getProtocol(loopring());
assert(instance != address(0));
}
function version() view public returns (string version) {
IProtocalRegistry registry = IProtocalRegistry(registry());
(, version) = registry.getProtocol(loopring());
}
} |
I think this is a good solution! Something that doesn't really matter and is just an implementation detail: Because storing data in a proxy contract is done in a way that is a bit strange, we can choose to not store the loopring address there. In |
I like your recommendation. Will change it later that way in the PR. |
I created these images to illustrate our upgradability solution. 1. Only one protocol version is registered: 2. After we deploy and register a compatible implementation: @kongliangzhong @letsgoustc please take a look |
Will we use a proxy for the Loopring contracts as well? There's not that much logic in it, but I guess it could be useful to be able to update it as well when the Exchange <-> Loopring interaction changes a bit when updating Exchange implementations. |
We can call ILoopringVxxx represents the major versions, and IExchangeVxxx represents the minor versions. 1. Multiple exchange instances using the same major version (LoopringV3):2. Exchanges will be upgraded automatically to a new default minor version:3. Exchanges will NOT be able to upgrade to any major version:4. Upgradability can be disabledThe upgrade is triggered by the owner of the function forgeExchange(
address protocol,
bool supportUpgradability,
bool onchainDataAvailability
)
external
nonReentrant
returns (
address exchangeAddress,
uint exchangeId
); 5. DEX owners can also manage their own customized proxies for upgradability |
Implemented by PR #384 |
DONE |
OwnedUpgradeabilityProxy is very convenient to make sure a DEX is upgradable. The issue is that user assets are at risk -- the owner of the proxy can always upgrade the proxy to point to another contract that may move user fund.
My idea is to disable instant proxy upgrade to arbitrary contract and only allow the proxy owner to schedule an upgrade to a declared address in advance, for example, 30 days. When a scheduled upgrade is still pending, the owner can cancel it or schedule a new one to replace it, and the waiting period shall restart.
@letsgoustc @Brechtpd @hosschao please let me know what you think.
The text was updated successfully, but these errors were encountered: