Контракт, не использующий обновленные прокси-контракты

Там 2 контракта Aи B, Aimports B, где Bнаходится библиотека, которую можно обновить. Чтобы добиться этого, я попытался заменить Bконтракт прокси и контракт делегата.

Теперь у нас есть контракты Foo, которые импортируют прокси-контракты Bar, указывающие на делегированный контракт ZeroDelegate.

Foo.sol

pragma solidity ^0.4.18;

import './Bar.sol';

contract Foo {
    uint storageData;
    Bar bar;
    address barContractAddress;

    constructor(address _barContractAddress) public {
        barContractAddress = _barContractAddress;
    }

    function set(uint x) public {
        storageData = x;
    }

    function get() view public returns (uint) {
        return storageData;
    }

    function baz() public returns (uint) {
        bar = Bar(barContractAddress);
        storageData = bar.baz(storageData);
    }

}

Бар.соль

pragma solidity ^0.4.18;

import './Proxy.sol';

contract Bar is Proxy {
    function baz(uint x) public returns (uint) {
        return  x * x;
    }
}

ZeroDelegate.sol

pragma solidity ^0.4.18;

contract ZeroDelegate {
    function baz(uint x) public returns (uint) {
        return x * 0;
    }
}

Прокси.sol

pragma solidity ^0.4.18;

import "zeppelin-solidity/contracts/ownership/Ownable.sol";

contract Proxy is Ownable {

    event Upgraded(address indexed implementation);

    address internal _implementation;

    function implementation() public view returns (address) {
        return _implementation;
    }

    function upgradeTo(address impl) public onlyOwner {
        require(_implementation != impl);
        _implementation = impl;
        emit Upgraded(impl);
    }

    function () payable public {
        address _impl = implementation();
        require(_impl != address(0));
        bytes memory data = msg.data;

        assembly {
            let result := delegatecall(gas, _impl, add(data, 0x20), mload(data), 0, 0)
            let size := returndatasize
            let ptr := mload(0x40)
            returndatacopy(ptr, 0, size)
            switch result
            case 0 { revert(ptr, size) }
            default { return(ptr, size) }
        }
    }
}

Теперь мы сначала развертываем контракты Foo, Bar, ZeroDelegate.

bar = await Bar.new()
foo = await Foo.new(bar.address)
zeroDelegate = await ZeroDelegate.new()

И foo.baz()возводит число 2 в квадрат 4.

x = await foo.baz()
console.log(x.toNumber())  // 4

Далее мы улучшаем Barконтракт до ZeroDelegate, но foo.baz()все еще возводим число 4 в 16.

await bar.upgradeTo(zeroDelegate.address)
bar = _.extend(bar, ZeroDelegate.at(bar.address))
await foo.baz()
x = await foo.get()
console.log(x.toNumber())  // 16, but expects 0

Однако, если бы мы повторно развернули Fooконтракт, он использовал бы обновленный файл Bar. Почему это так и как мы можем позволить Fooиспользовать обновленные Barфункции без повторного развертывания Foo, что противоречит цели использования обновляемого контракта.

foo = await Foo.new(bar.address)
await foo.baz()
x = await foo.get()
console.log(x.toNumber())  // 0

Ответы (1)

Вам нужно сделать резервную функцию с именем baz(), а затем обращаться с Bar.sol так же, как с ZeroDelegate (как с ненаследуемым внешним контрактом — возможно, на самом деле библиотекой). Делегированный вызов будет отправлен этому внешнему контракту (на который в данный момент указывает _imp) и будет использовать логику внутри этого контракта. При обновлении _imp на адрес Bar или ZeroDelegate, чтобы переключить его поведение.

Вот рабочий пример с обновляемой функцией TokenURI:
https://github.com/clovers-network/clovers-contracts/blob/master/contracts/Clovers.sol
, указывающий на:
https://github.com/clovers-network/ клеверы-контракты/blob/мастер/контракты/CloversMetadata.sol