Безгазовые транзакции - отправка транзакции вместо подписания сообщения. Как это работает?

Я следую этому руководству: https://docs.openzeppelin.com/learn/sending-gasless-transactions

Но на самом деле это не объясняет четко, как это работает в деталях. Стандартная транзакция довольно проста для понимания. Пользователь подписывает сообщение, отправляет его, оплачивает и оно отправляется в блокчейн.

Если в основной сети или тестовой сети есть безгазовая транзакция, такая как ropsten, пользователь не отправляет транзакцию в блокчейн, а только подписывает ее. Что будет дальше? Должно быть что-то, что захватывает личное подписанное сообщение пользователя (потому что, насколько я знаю, это сообщение "частное", оно не выходит в глобальную сеть) и помещает его в сеть и платит за это, но как именно это происходит? работа?

На мгновение я подумал, что если я создам контракт, расширяющий GSNRecipient, любая пользовательская транзакция превратится в запрос подписи, и этот контракт будет выполнять транзакции в сети. Поэтому я воссоздал простой контракт Counter, который расширяет GSNRecipientUpgradeSafe.

pragma solidity ^0.5.0;
import "./GSNRecipient.sol";
contract Counter is GSNRecipientUpgradeSafe
{
    int public value;
    
    function addValue()
        external
    {
        value++;
    }
    
    function start()
        external
    {
        __GSNRecipient_init();
    }
    
    function acceptRelayedCall(
        address relay,
        address from,
        bytes calldata encodedFunction,
        uint256 transactionFee,
        uint256 gasPrice,
        uint256 gasLimit,
        uint256 nonce,
        bytes calldata approvalData,
        uint256 maxPossibleCharge
    )
        external
        view
        returns (uint256, bytes memory)
    {
        return _approveRelayedCall();
    }

    function _preRelayedCall(bytes memory context) internal returns (bytes32)
    {

    }

    function _postRelayedCall(bytes memory context, bool success, uint256 actualCharge, bytes32 preRetVal) internal
    {
        
    }
}

Я развернул его и вызвал start()

Я не хотел использовать какие-либо сценарии реагирования. Я решил использовать обычную библиотеку web3js и очень простой простой веб-сайт.

<span id="cnt">COUNTER</span>
<button onclick="getContract().methods.addValue().send({from:account})">click</button>
<script>
    let account;
    let web3;
    window.addEventListener('load', async () => {
        if(window.ethereum)
        {
            try
            {
                await ethereum.enable();
                web3 = new Web3(ethereum);
                web3.eth.defaultAccount = (await web3.eth.getAccounts())[0];
                account = web3.eth.defaultAccount;
                let c = getContract();
                $("#cnt").html(await c.methods.value().call());
                
            }
            catch(e)
            {
                console.log(e.toString());
            }
        }
    });
    
    function getContract()
    {
        return new web3.eth.Contract(abi, "0xaa9f0c1AC580EFA7A0e6d64d3eEBF62B4F970701");
    }
</script>

Я проверил демонстрацию gsn: https://metacoin.opengsn.org/ После нажатия «отправить метакойн» я получаю запрос на подписание сообщения. В исходном коде есть обычные contract.methods......send(). Поэтому я решил воссоздать его.

Но, к сожалению:

<button onclick="getCounterContract().methods.addValue().send({from:account})">click</button>

Это просто вызывает обычную транзакцию вместо запроса на подпись.

web3.версия: "1.2.6"

Я почти уверен, что я что-то неправильно понял или не понял правильно. Как я могу заставить это работать? Может быть, есть что-то лучше, чем сетевой код OpenZeppelin GSN?

Я использую метамаску.

Ответы (1)

Идея «безгазовых» транзакций заключается в том, что пользователь подписывает сообщение и отправляет это сообщение ретранслятору газа . Ретранслятор — это отдельный объект, который собирает сообщения от пользователей и отправляет транзакции. Таким образом, пользователю не нужно платить за транзакцию, это делает только ретранслятор. Затем, при желании, пользователь может заплатить ретранслятору токенами или другими платежными средствами.

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

Для отправки транзакций в сеть АЗС можно использовать их библиотеки JavaScript, например:

const { RelayProvider } = require('@opengsn/gsn')

const provider = new RelayProvider(web3.currentProvider, ...);
const web3 = new Web3(provider);

const counterContract = new web3.eth.Contract(abi, ...);
await counterContract.methods.addValue().send({ ... });

Это позволит RelayProviderподписать транзакцию и отправить ее ретранслятору газа, который затем отправит транзакцию в ваш контракт от имени пользователя.

Более подробное руководство по использованию OpenGSN можно найти здесь: https://docs.opengsn.org/gsn-provider/getting-started.html.