Как заставить другого платить за газ?

Насколько я понимаю, msg.senderэто оплата за газ. Можно ли написать смарт-контракт таким образом, чтобы комиссию всегда платил владелец контракта?

Например, в токене ERC20, когда кто-то передает токены, он платит комиссию за газ, но что, если плата за газ за каждого transferдолжна быть оплачена владельцем смарт-контракта?

В одном из ближайших обновлений Эфириума вы сможете позволить своему контракту оплачивать газ.
@Орри, прошло 3 года после вашего комментария, это обновление уже появилось?

Ответы (5)

Есть 2 варианта со своими плюсами и минусами:

  1. Используйте подписи

    • Каждая функция в вашем смарт-контракте должна иметь signatureпараметр.
    • Люди, которые хотят взаимодействовать со смарт-контрактом, должны подписать параметры функции закрытым ключом своей учетной записи и отправить его владельцу смарт-контракта (по любому каналу связи).
    • Затем владелец отправляет параметры вместе с подписью в блокчейн, оплачивая газ. Подпись гарантирует, что сообщение было одобрено пользователем.
  2. Возврат использованного газа в конце сделки. refundGasCostДля этого можно использовать модификатор (см. ниже).

Ниже приведены более подробные сведения о каждом варианте:


Использование подписей

Вот простой ReceiverPaysдоговор, который позволяет заставить получателя платежа платить за газ:

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

Более подробную информацию можно найти в этой статье https://programtheblockchain.com/posts/2018/02/17/signing-and-verifying-messages-in-ethereum/

Ограничение этого подхода заключается в том, что если ваш смарт-контракт должен взаимодействовать с другими контрактами, то они также должны реализовать один и тот же шаблон с добавлением подписей к каждому методу.


Возврат использованного газа отправителю транзакции

Это не идеальное решение, но вы можете возместить стоимость газа отправителю транзакции. Вы можете сделать это с помощью модификатора:

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

Возврат таким образом подразумевает некоторые накладные расходы: как минимум 9700 газа нужно доплатить за transferвызов функции внутри refundGasCostмодификатора. refundGasCostТакже следует добавить газ для других опкодов в usedGas.

Также приведенный выше код потенциально уязвим для повторного входа и других атак. Я привел его только в качестве примера и не проверял его полностью.


https://github.com/ethereum/wiki/wiki/Дизайн-Обоснование

Требование к отправителям транзакций платить за газ вместо контрактов существенно повышает удобство использования для разработчиков. В очень ранних версиях Ethereum контракты оплачивали газ, но это привело к довольно уродливой проблеме, заключавшейся в том, что каждый контракт должен был реализовывать «защитный» код, который гарантировал бы, что каждое входящее сообщение компенсирует контракт достаточным количеством эфира для оплаты газа, который он потребляется.

...

ORIGIN: основное использование кода операции ORIGIN, который указывает отправителя транзакции, заключается в том, чтобы разрешить контрактам производить возврат платежей за газ.

Спасибо! Я искал что-то, что в первую очередь не требовало бы оплаты газа пользователем смарт-контракта. Сказав это, ваше решение действительно интересно :)
Интересно. Кто-нибудь пробовал это?
Пробовал только в ремиксе. Рассчитанный используемый газ не совсем равен фактически использованному газу, но довольно близок к нему.
С некоторых пор полагаться на tx.origin не рекомендуется ethereum.stackexchange.com/a/200
@Исмаэль, это настоящий Виталик Бутерин или тролль? Я не обнаружил, что на tx.origin нельзя полагаться (за исключением того, что на него нельзя полагаться для авторизации) нигде в официальных документах.
@Ismael Интересно, что только что нашел это в Обосновании дизайна: ORIGIN: основное использование кода операции ORIGIN, который предоставляет отправителя транзакции, — разрешить контрактам производить возврат платежей за газ. github.com/ethereum/wiki/wiki/Обоснование дизайна
@medvedev1088 Он настоящий. Был эксплойт, когда вредоносный контракт может атаковать контракт, полагающийся на tx.origin, чтобы выдать себя за неосторожного пользователя.

Только что опубликовал крошечную библиотеку, чтобы добавить возможность делегировать создание транзакций (оплата комиссий): https://github.com/bitclave/Feeless

Вам нужно всего лишь:

  1. Наследуйте свой смарт-контракт от Feelessсмарт-контракта
  2. Добавьте feelessмодификатор для любых методов, которые вы хотите разрешить косвенно вызывать.
  3. Используйте msgSenderвместо msg.senderэтих методов и методов, вызываемых ими внутри

И любой сможет оплачивать комиссию за любого другого совершенно ненадежным способом.

Очень элегантно! Попробуйте также опубликовать свой ответ здесь ethereum.stackexchange.com/questions/144/… . Он может получить больше охвата.

Вопрос был задан в феврале 2018 года, что похоже на древнюю историю пространства Etheruem.


Ключевые термины «метатранзакции» и «сеть АЗС»:

Недавние примеры отправки токенов без газа:

USDC

https://medium.com/centre-blog/centre-consortium-announces-release-of-usd-coin-version-2-0-37ee8b27e09b

USDC 2.0 вводит «безгазовые отправки», которые позволяют разработчикам кошельков и приложений абстрагироваться от сложности платы за газ (и необходимости для клиента держать баланс эфира) и вместо этого делегировать оплату платы за газ на другой адрес. Это позволяет разработчикам либо предоставлять эту услугу самостоятельно, либо позволяет сторонней службе оплачивать соответствующие сборы.

ANTv2

https://aragon.org/blog/antv2

В мире токенов Ethereum произошли и другие удивительные события, в том числе безгазовые переводы. Безгазовые переводы позволяют ретрансляторам или другим третьим сторонам субсидировать переводы токенов для пользователей.

EIP-3009, который они используют: https://eips.ethereum.org/EIPS/eip-3009 .

К сожалению, нет, другой пользователь, кроме пользователя, инициировавшего транзакцию, не может оплатить газ. О msg.senderсм. редактирование ниже.

Было некоторое обсуждение, чтобы абстрагировать подпись от протокола и позволить затем иметь самоокупаемые смарт-контракты.

Однако это были только обсуждения, и эта функция даже не заложена в техническую реализацию.

Вы можете найти начало обсуждения здесь и текущее продолжение обсуждения там .

РЕДАКТИРОВАТЬ :

Обратите внимание, что msg.senderэто не обязательно пользователь, инициировавший транзакцию.

Давайте возьмем два контракта A и B и представим, что A вызывает метод в контракте B. Тогда метод, вызванный в B, будет иметь msg.senderадрес контракта A.

Первоначальный адрес в основном упоминается как tx.originадрес, который инициировал транзакцию, где бы мы ни указывали в транзакции.

Также обратите внимание, что при кодировании смарт-контракта не рекомендуется использовать tx.originдля определения того, кто отправил транзакцию, а использовать параметр в подписи метода для идентификации пользователя.

Ознакомьтесь с провайдером Fuel Web3 : https://github.com/ahmb84/fuel-web3-provider .

«Fuel позволяет разработчикам добавить систему финансирования в свое Dapp и, таким образом, сделать транзакции бесплатными для конечного пользователя».

Вот эталонная реализация: https://github.com/ahmb84/fuel-node-example .