Вызов функции из развернутого контракта

Я хочу знать, как создать экземпляр существующего контракта, развернутого в блокчейне, используя его адрес. например:

contract A {
    function f1()
    {}
}

Aразвернут в блокчейне, и в контракте Bя хочу вызвать функцию f1()и получить ее результат. например:

contract B {
    address contrac_A=0x123456;

    //call f1 from A
}

Как я должен назвать это, используя адрес A?

Ответы (4)

Если развернутый контракт не соответствует ABI, но вы знаете подпись контракта (имя и типы аргументов), вы можете использовать:

contract_address.call(bytes4(sha3("function_name(types)")),parameters_values)

например: contrac_A.call(bytes4(sha3("f()"))пока в вашем примере нет входных параметров.

замените адрес_контракта, имя_функции, значения_параметров своими учетными данными.

Редактировать: поскольку sha3 устарел, лучше использовать вместо него keccak256 следующим образом:bytes4(keccak256("f()")).

Кроме того, начиная с Solidity 0.4.22, глобальные функции abi.encode(), abi.encodePacked(), abi.encodeWithSelector() and abi.encodeWithSignature()были определены для кодирования структурированных данных и, следовательно, помогают нам построить правильный вызов (они возвращают необходимые 4 байта) следующим образом:

contract_address.call.value(1 ether).gas(10)(abi.encodeWithSignature("register(string)", "MyName"));

Документация :

https://github.com/ethereum/wiki/wiki/Solidity-Features#общий-вызов-метод

https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#селектор функций

Я видел следующую функцию. передача функции (адрес _to, uint _value, байты _data, строка _custom_fallback) возвращает (логический успех) { ContractReceiver Receiver = ContractReceiver (_to); Receiver.call.value(0)(bytes4(sha3(_custom_fallback)), msg.sender, _value, _data); вернуть истину; } ..... При чем здесь значение (0)??
f.call.value(X) вызывает f с X, проверяя ethereum.stackexchange.com/questions/6707/…

Используйте абстрактный контракт (предпочтительно)

Дополнительные разъяснения к ответу @Edmund:

contract A { // This doesn't have to match the real contract name. Call it what you like.
   function f1(bool arg1, uint arg2) returns(uint); // No implementation, just the function signature. This is just so Solidity can work out how to call it.
}

contract YourContract {
  function doYourThing(address addressOfA) returns(uint) {
    A my_a = A(addressOfA);
    return my_a.f1(true, 3);
  }
}

Это показывает использование возвращаемого значения из f1.

Кроме того, если f1встречается исключение (представьте, что его реализация — function f1(bool arg1, uint arg2) returns(uint) { throw; }), исключение распространяется, а my_a.f1также throwотменяет транзакцию, вызвавшую doYourThing.

На практике у вас будет 3 файла.

AbstractA.sol содержит:

contract A {
   function f1(bool arg1, uint arg2) returns(uint); // No implementation, just the function signature. This is just so Solidity can work out how to call it.
}

YourContract.sol содержит:

import "AbstractA.sol"

contract YourContract {
  function doYourThing(address addressOfA) returns(uint) {
    A my_a = A(addressOfA);
    return my_a.f1(true, 3);
  }
}

A.sol содержит:

contract A {
   // implementation of f1
   function f1(bool arg1, uint arg2) returns(uint) {
       if (arg1) {
           throw;
       } else {
           return arg2;
       }
   }
}

Ограничения использованияcall

callпредлагается в ответе @Badr, но его следует использовать очень осторожно. Документы Solidity гласят :

Все три функции callи являются функциями очень низкого уровня delegatecallи callcodeдолжны использоваться только в крайнем случае, поскольку они нарушают безопасность типов Solidity.

Кроме того, возвращаемое значение f1не может быть получено с помощью функции calllike, addressOfA.call(bytes4(keccak256("f1(bool, uint256)")), true, 3)поскольку callвозвращает a bool( только в falseтом случае, если вызов встречает исключение).

Это означает, что исключение должно распространяться вручную, например:

if (!addressOfA.call(bytes4(keccak256("f1(bool, uint256)")), true, 3)) {
    throw;
}
Должны ли мы включать видимость и модификаторы при указании сигнатуры функции?
@DigitalJedi Да, с 2017 года в Solidity были внесены улучшения, в том числе четкое указание видимости.
contract A { // This doesn't have to match the real contract name. Call it what you like.
   function f1(){} // No implementation, just the function signature. This is just so Solidity can work out how to call it.
}

contract YourContract 
  function doYourThing() {
    A my_a = A(contract_A);
    my_a.f1();
  }
}
они не находятся в одном и том же файле контракта, компилятор не распознает контракт A, поэтому нет A my_a = A (contract_A);
Поместите их в один файл. Вам не нужна полная реализация, только сигнатуры функций.
Я спрашиваю об уже развернутом контракте.
Вам не нужен полный исходный код. Вы должны знать имя метода, который вы вызываете, и какие параметры он принимает, поэтому придерживайтесь этого в определении контракта с именем A, оставьте тело функции пустым и поместите его над контрактом, который будет его вызывать.
Отредактировал мой ответ, чтобы предоставить контекст. Это действительно все, что вам нужно, не имеет значения, развернут контракт уже или нет. Если исходный код не скомпилируется, проверьте правильность написания слова «функция».
@EdmundEdgar Я видел следующую функцию. передача функции (адрес _to, uint _value, байты _data, строка _custom_fallback) возвращает (логический успех) { ContractReceiver Receiver = ContractReceiver (_to); Receiver.call.value(0)(bytes4(sha3(_custom_fallback)), msg.sender, _value, _data); вернуть истину; } ..... При чем здесь значение (0)??
Это должно быть так. Хорошее решение!

Вот код Solidity для Нового контракта , который может вызывать функцию f1() из вашего Старого контракта . Надеюсь это поможет. Я старался сделать это как можно проще.

    import "./Old.sol";    // You import the existing contract. 
        
        
            /**
            * The NEW contract will call the Old one
            */
        
          contract New {
          
               Old OLD; // Intitilize old contract variable (empty)
        
            /**
             * Set the address for Old contract (We call this function and enter the address of the OLD contract)
             */
            function setOldContractAddress(address addr) public {
                OLD = Old(addr);
            }
        
        
             /**
             * Function that allows us to call f1() from the Old contract
             */
            function callOLDcontract() public {
                OLD.f1();
             
                }
            
            }