Función de llamada desde el contrato desplegado

Quiero saber cómo instanciar un contrato existente implementado en la cadena de bloques usando su dirección. p.ej:

contract A {
    function f1()
    {}
}

Ase implementa en la cadena de bloques y, por contrato B, quiero llamar a la función f1()y obtener su devolución. p.ej:

contract B {
    address contrac_A=0x123456;

    //call f1 from A
}

¿Cómo debo llamarlo usando la dirección de A?

Respuestas (4)

Si el contrato implementado no se adhiere a la ABI, pero conoce la firma del contrato (tipos de nombre y argumento), podría usar:

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

por ejemplo: contrac_A.call(bytes4(sha3("f()"))si bien no hay entrada, no hay parámetros en su ejemplo.

reemplace contract_address,function_name,parameters_values ​​por sus credenciales.

Editar: como sha3 ha quedado obsoleto, es mejor usar keccak256 en su lugar de la siguiente manera:bytes4(keccak256("f()")).

Además, desde solidity 0.4.22, las funciones globales abi.encode(), abi.encodePacked(), abi.encodeWithSelector() and abi.encodeWithSignature()se han definido para codificar datos estructurados y, por lo tanto, nos ayudan a crear llamadas válidas (devuelven los 4 bytes necesarios) de la siguiente manera:

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

Documentación :

https://github.com/ethereum/wiki/wiki/Solidity-Features#generic-call-method

https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#function-selector

He visto una función de la siguiente manera. función de transferencia (dirección _a, uint _valor, bytes _datos, cadena _personalizado_retroceso) devuelve (bool éxito) { ContractReceiver receptor = ContractReceiver(_to); receptor.llamada.valor(0)(bytes4(sha3(_custom_fallback)), mensaje.remitente, _valor, _datos); devolver verdadero; } ..... ¿Cuál es el uso de valor (0) aquí?
f.call.value(X) está llamando a f con X wei check ethereum.stackexchange.com/questions/6707/…

Usar un contrato abstracto (preferido)

Más aclaraciones a la respuesta de @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);
  }
}

Esto muestra el uso del valor de retorno de f1.

Además, si f1encuentra una excepción (imagine que su implementación es function f1(bool arg1, uint arg2) returns(uint) { throw; }), la excepción se propaga y my_a.f1también throwrevertirá una transacción que invocó doYourThing.

En la práctica, tendrás 3 archivos.

AbstractA.sol contiene:

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 contiene:

import "AbstractA.sol"

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

A.sol contiene:

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

Limitaciones de usocall

callse sugiere en la respuesta de @Badr, pero debe usarse con mucho cuidado. Los documentos de Solidity afirman:

Las tres funciones son funciones callde muy bajo nivel delegatecally callcodesolo deben usarse como último recurso, ya que rompen la seguridad de tipos de Solidity.

Además, el valor de retorno de f1no se puede obtener usando calllike addressOfA.call(bytes4(keccak256("f1(bool, uint256)")), true, 3)porque callsolo devuelve un bool( falsesi la llamada encuentra una excepción).

Esto significa que la excepción debe propagarse manualmente como:

if (!addressOfA.call(bytes4(keccak256("f1(bool, uint256)")), true, 3)) {
    throw;
}
¿Tenemos que incluir visibilidad y modificadores al especificar la firma de la función?
@DigitalJedi Sí, ha habido mejoras en Solidity desde 2017, e incluyen ser explícito sobre la visibilidad.
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();
  }
}
no están en el mismo archivo de contrato, el compilador no reconocerá el contrato A, por lo que no A my_a = A (contrato_A);
Póngalos en el mismo archivo. No necesita la implementación completa, solo las firmas de función.
Estoy preguntando acerca de un contrato ya implementado.
No necesita el código fuente completo. Debe saber el nombre del método que está llamando y qué parámetros toma, así que péguelo en una definición de contrato llamada A, deje el cuerpo de la función vacío y colóquelo sobre el contrato que lo va a llamar.
Edité mi respuesta para proporcionar contexto. Eso es realmente todo lo que necesita, no importa si el contrato ya está implementado o no. Si su código original no se compila, verifique la ortografía de "función".
@EdmundEdgar He visto una función de la siguiente manera. función de transferencia (dirección _a, uint _valor, bytes _datos, cadena _personalizado_retroceso) devuelve (bool éxito) { ContractReceiver receptor = ContractReceiver(_to); receptor.llamada.valor(0)(bytes4(sha3(_custom_fallback)), mensaje.remitente, _valor, _datos); devolver verdadero; } ..... ¿Cuál es el uso de valor (0) aquí?
Este debería ser el caso. ¡Buena solución!

Aquí hay un código de Solidity para el nuevo contrato que puede llamar a la función f1() desde su antiguo contrato . Espero que esto ayude. Traté de mantenerlo lo más simple posible.

    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();
             
                }
            
            }