¿Pueden los contratos pagar el gas en lugar del remitente del mensaje?

¿Es posible que un contrato pague los gastos de gas (o parte de ellos) que resultan de la convocatoria del contrato? ¿O el remitente de un mensaje siempre paga los costos de gasolina resultantes sin importar qué?

Más o menos sí, mira mi respuesta aquí
> Acabo de publicar una pequeña biblioteca para agregar la capacidad de delegar > creación de transacciones (pago de tarifas): > github.com/bitclave/Feeless ¿Cómo evita este método el gasto doble?
Lea esta migración perezosa de bitclave . Estaba pensando en usar esto como parte del mecanismo de actualización, pero no dejar que el usuario pague. Creo que su biblioteca ayuda.

Respuestas (9)

Actualmente el usuario que inicia la transacción debe pagar la tarifa. Sin embargo, es probable que Serenity habilite esquemas de 'contrato paga'. (Vea la publicación del blog de Vitalik aquí ) Una posible solución es un contrato que reembolsa al remitente; sin embargo, esto aún requeriría que el usuario tenga algo de Ether y podría permitir que partes malintencionadas llamen repetidamente al contrato para agotar sus fondos.

Hay mejores soluciones que eso. Intentaré publicar una respuesta cuando tenga la oportunidad.
Mirando hacia adelante a sus soluciones
Todavía no he tenido tiempo de hacer esto, así que en lugar de una respuesta completa, solo señalaré que puede usar un mecanismo similar al reloj despertador Ethereum para que las aplicaciones puedan enviar sus transacciones a personas aleatorias que luego " prestarles" el costo de éter del contrato y se les reembolsa por un contrato especial financiado por el desarrollador del contrato de destino. El resultado es una aplicación que no requiere que los usuarios tengan éter. Obviamente, uno querría agregar algún tipo de elemento anti-sybil a la aplicación para evitar DoS. Una forma sencilla sería un pequeño PoW en la aplicación.
@JeffColeman esperó un poco tarde en este caso, pero ¿por casualidad escribió más detalles sobre este? Estaría realmente interesado en obtener la idea completa del mecanismo, especialmente cómo funcionaría la primera parte.

No, un remitente con cero éter no puede "pedir" un contrato para pagar los costos del gas. Un remitente con cero éter ni siquiera puede enviar una transacción.

Más detalles: El remitente de una transacción debe tener suficiente gas para cubrir la ejecución de la transacción. Ese gas se requiere incluso antes de que se pueda llamar a un contrato. Una vez que se cumple el requisito de gas (para que la transacción se ejecute completamente sin quedarse sin gas), el contrato llamado puede enviar al remitente cualquier cantidad de fondos que tenga el contrato: el comportamiento neto es que el remitente puede terminar con más éter de lo que necesita. comenzó, pero eso es diferente del contrato que paga el gas.

Es como si pudieras conducir hasta el banco para obtener dinero, pero primero necesitas combustible para poder conducir hasta el banco: el banco no puede enviarte dinero para combustible antes de que manejes hasta el banco.

Hay una discusión en curso aquí para una versión futura (Serenity) que puede cambiar este comportamiento.


Solidity tiene gas()sintaxis, como la siguiente mencionada en una de las respuestas aquí:

contract Gracious {
  function runMe() {
    this.realWork.gas(1000000)();
  }
}

gas()no significa usar el éter del contrato para pagar el gas. gas()limita la cantidad de gas que recibe la subllamada ( realWork). Si runMese proporciona con 3.000.000 de gas, entonces realWorkconsumirá como máximo 1.000.000 de gas, por lo que runMese garantizará que cualquier función llamada cuando finalice tendrá 2.000.000 de gas. Si realWorkconsume más de 1,000,000 de gas, se generará una excepción de inmediato, se pagarán los 3,000,000 de gas al minero y se revertirá la transacción.

Copiando mi respuesta de aquí ¿Cómo hacer que otra persona pague por Gas?

Hay 2 soluciones con sus pros y sus contras:

  1. Usar firmas

    • Cada función en su contrato inteligente debe tener un signatureparámetro.
    • Las personas que deseen interactuar con el contrato inteligente deben firmar los parámetros de la función con la clave privada de su cuenta y enviarla al propietario del contrato inteligente (a través de cualquier canal de comunicación).
    • Luego, el propietario envía los parámetros junto con la firma a la cadena de bloques y paga la gasolina. La firma garantiza que el mensaje fue aprobado por el usuario.
  2. Reembolso del gas usado al final de la transacción. Se puede usar un modificador para esto (ver más abajo).

A continuación se presentan más detalles para cada opción:


Uso de firmas

Aquí hay un contrato simple ReceiverPaysque permite hacer que el receptor del pago pague el gas:

pragma solidity ^0.4.20;

contract ReceiverPays {
    address owner = msg.sender;

    mapping(uint256 => bool) usedNonces;

    // Funds are sent at deployment time.
    function ReceiverPays() public payable { }


    function claimPayment(uint256 amount, uint256 nonce, bytes sig) public {
        require(!usedNonces[nonce]);
        usedNonces[nonce] = true;

        // This recreates the message that was signed on the client.
        bytes32 message = prefixed(keccak256(msg.sender, amount, nonce, this));

        require(recoverSigner(message, sig) == owner);

        msg.sender.transfer(amount);
    }

    // Destroy contract and reclaim leftover funds.
    function kill() public {
        require(msg.sender == owner);
        selfdestruct(msg.sender);
    }


    // Signature methods

    function splitSignature(bytes sig)
        internal
        pure
        returns (uint8, bytes32, bytes32)
    {
        require(sig.length == 65);

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }

        return (v, r, s);
    }

    function recoverSigner(bytes32 message, bytes sig)
        internal
        pure
        returns (address)
    {
        uint8 v;
        bytes32 r;
        bytes32 s;

        (v, r, s) = splitSignature(sig);

        return ecrecover(message, v, r, s);
    }

    // Builds a prefixed hash to mimic the behavior of eth_sign.
    function prefixed(bytes32 hash) internal pure returns (bytes32) {
        return keccak256("\x19Ethereum Signed Message:\n32", hash);
    }
}

Se pueden encontrar más detalles en este artículo https://programtheblockchain.com/posts/2018/02/17/signing-and-verifying-messages-in-ethereum/

La limitación de este enfoque es que si su contrato inteligente necesita interactuar con otros contratos, también deben implementar el mismo patrón agregando firmas a cada método.


Reembolso de gas usado al remitente de la transacción

No es una solución ideal, pero puede reembolsar el costo del combustible al remitente de la transacción. Puedes hacer esto con un modificador:

pragma solidity^0.4.11;

contract SomeContract {

    event SomeEvent(address sender);

    // Need to allow depositing ether to the contract
    function() public payable {
    }

    modifier refundGasCost()
    {
        uint remainingGasStart = msg.gas;

        _;

        uint remainingGasEnd = msg.gas;
        uint usedGas = remainingGasStart - remainingGasEnd;
        // Add intrinsic gas and transfer gas. Need to account for gas stipend as well.
        usedGas += 21000 + 9700;
        // Possibly need to check max gasprice and usedGas here to limit possibility for abuse.
        uint gasCost = usedGas * tx.gasprice;
        // Refund gas cost
        tx.origin.transfer(gasCost);
    }

    function doSomething() external refundGasCost {
        SomeEvent(msg.sender);  
    }
}

Reembolsar de esta manera implica algunos gastos generales: al menos 9700 de gas deben pagarse extra por el modificador transferinterno de llamada de función . También se debe agregar refundGasCostgas para otros códigos de operación en .refundGasCostusedGas


https://github.com/ethereum/wiki/wiki/Design-Rationale

Requerir que los remitentes de transacciones paguen por el gas en lugar de contratos aumenta sustancialmente la usabilidad del desarrollador. Las versiones muy tempranas de Ethereum tenían contratos que pagaban el gas, pero esto llevó al problema bastante feo de que cada contrato tenía que implementar un código de "protección" que aseguraría que cada mensaje entrante compensara el contrato con suficiente éter para pagar el gas que consumado.

...

ORIGEN: el uso principal del código de operación ORIGEN, que proporciona el remitente de una transacción, es permitir que los contratos realicen pagos de reembolso por gas.

Esto debería recibir más votos, ya que actualmente es la única forma de pagar las tarifas para la persona que llama.

No todas las llamadas en un contrato necesitan gas. Es el llamado "ejecución en seco". https://github.com/ethereum/go-ethereum/wiki/Contratos-y-Transacciones#interactuando-con-contratos

Ahora todas las llamadas a funciones especificadas en abi están disponibles en la instancia del contrato. Simplemente puede llamar a esos métodos en la instancia del contrato y encadenar sendTransaction(3, {from: address}) o call(3) a él. La diferencia entre los dos es que call realiza una "ejecución en seco" localmente, en su computadora, mientras que sendTransaction en realidad enviaría su transacción para incluirla en la cadena de bloques y los resultados de su ejecución eventualmente se convertirán en parte del consenso global. En otras palabras, use call, si solo está interesado en el valor de retorno y use sendTransaction si solo le interesan los "efectos secundarios" en el estado del contrato.

Por lo tanto, es posible proporcionar un método en un contrato a los usuarios por costos de gas cero; en el ejemplo anterior, es la función de multiplicación.

Tiene razón, hay un simulacro y la pregunta usó "remitente de un mensaje" en lugar de "remitente de una transacción", que sería más preciso.

Acabo de publicar una pequeña biblioteca para agregar la capacidad de delegar la creación de transacciones (pago de tarifas): https://github.com/bitclave/Feeless

Solo necesitas:

  1. Herede su contrato inteligente del Feelesscontrato inteligente
  2. Agregue feelessmodificador para cualquier método que desee permitir llamar indirectamente
  3. Usar msgSenderen lugar de msg.senderen estos métodos y métodos llamados internamente por ellos

Y cualquiera podrá pagar cuotas por cualquier otro de forma totalmente desconfiada. Por ejemplo, el servicio puede pagar tarifas por transacciones de usuarios y compensar estas tarifas con tarifas de tokens propios.

Para que quede claro, ¿esto funciona reembolsando las tarifas a la persona que llama? He mirado su código y tengo problemas para averiguar dónde sucede esto. Las líneas 24-28 verifican la firma y nonce, la línea 30 llama a la función deseada, pero ¿dónde ocurre el cálculo del monto de la devolución y la devolución en sí?
Este contrato permite crear un subcontrato en el que cualquiera puede ejecutar algo para cualquier persona en función de la verificación de la firma. Parece que GSN se basa en el mismo enfoque.

Echa un vistazo al proveedor de Fuel Web3 : https://github.com/ahmb84/fuel-web3-provider

"Fuel permite a los desarrolladores agregar un sistema de financiación a su Dapp y así hacer que las transacciones sean gratuitas para el usuario final".

Aquí hay una implementación de referencia: https://github.com/ahmb84/fuel-node-example

¿Como funciona? No puedo decir desde el sitio de github.

Estas respuestas son buenas, pero noté que son un poco antiguas y hay una forma de enviar una transacción incluso cuando no tienen éter. Solo necesitan un poco de ayuda: https://medium.com/@andreafspeziale/understanding-ethereum-meta-transaction-d0d632da4eb2

Es complicado, pero no depende de una actualización de protocolo.

Un proceso groseramente resumido funciona así:

  1. El firmante firma una transacción para el contrato/cadena de bloques pero no la envía de la forma habitual. Eso costaría gasolina.
  2. El firmante reenvía el mensaje (fuera de la cadena) a un repetidor. El repetidor es un servidor en algún lugar que paga la gasolina. Tiene un mensaje firmado que sería válido si se enviara a Ethereum.
  3. El relé envía la transacción firmada y paga la gasolina.

Espero eso ayude.

Actualmente no, pero actualmente se está discutiendo con un EIP. Gavin Wood dice que hay una manera de hacerlo actualmente, pero según tengo entendido, es algo así como un truco.

¿Algún consejo sobre EIP?

En primer lugar, quiero agradecer a @eth y @medvedev1088 por sus respuestas. @eth afirma que

Es como si pudieras conducir hasta el banco para obtener dinero, pero primero necesitas combustible para poder conducir hasta el banco: el banco no puede enviarte dinero para combustible antes de que manejes hasta el banco.

Creo que todos los que trabajan con tokens de utilidad se enfrentan a este problema inicial. Me importa como desarrollador porque no es fácil de usar preguntar a los usuarios que compran Ethereum en intercambios o locales. Por lo tanto, busco posibles soluciones que solucionen el "problema del primer token". La respuesta de @ medvedev1088 parece una solución a primera vista. No me di cuenta antes de probarlo, pero después de la prueba que he hecho, puedo decir fácilmente

El destinatario reclama su pago presentando el mensaje firmado en el contrato inteligente. El contrato inteligente verifica su autenticidad y luego libera los fondos.

de su blog es un poco engañoso. Porque cualquiera que quiera interactuar con un contrato inteligente por primera vez necesita gasolina.

Si tiene su propia red privada, puede probar con el siguiente código. (Antes de ejecutarlo, debe implementar un contrato de al menos 6 ether e implementarlo desde su cuenta de coinbase)

El código se puede encontrar en este violín .

Espero no haber entendido mal el primer enfoque de @ medvedev1088.