Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paymaster for user gasless transactions #100

Open
alrevuelta opened this issue Aug 12, 2024 · 2 comments
Open

Paymaster for user gasless transactions #100

alrevuelta opened this issue Aug 12, 2024 · 2 comments

Comments

@alrevuelta
Copy link
Contributor

One of the frictions with RLN is that users need to register an RLN membership, and this requires to pay some fees (transaction cost + price of the membership). Meaning that the user has to have funds in their wallet. This makes it difficult to dogfood the feature, since having funds acts as an entry barrier.

With accounts abstraction, some L2s already support features such as the zkSync Paymaster. This allows to sponsor gas for users, where the "paymaster" account pays the gas fees. In other words, a user can register a RLN membership without having to pay gas, since the paymaster pays for it. While this may not make sense for mainnet (it defeats the purpose of RLN, since if RLN memberships can be registered at no cost, we would lose the economic rate-limiting of RLN). But it may be interesting for testnet and dogfooding.

After having spent some time with this, these are the findings:

  • After multiple attempts, I haven't been able to deploy our RLN contract to zkSync. Seems that the Posseidon hashing function we use (which contains some assembly code) can't be deployed. More specifically, I can't even compile it. Takes multiple hours and then errors.
  • Experimented with the paymaster feature, see code below. If we had RLN deployed in zksync it could be used almost as it is to sponsor gas tx fees for RLN registration. Coolest thing about this, is that no changes are needed in RLN. Registering is a "normal" transaction and you only have to add they paymaster as a parameter.

Paymaster code in Golang

package main

import (
	"context"
	"io/ioutil"
	"math/big"
	"strings"

	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
	log "github.com/sirupsen/logrus"
	"github.com/zksync-sdk/zksync2-go/accounts"
	"github.com/zksync-sdk/zksync2-go/clients"
	zkTypes "github.com/zksync-sdk/zksync2-go/types"
	"github.com/zksync-sdk/zksync2-go/utils"
)

func main() {

	// Public endpoints for Sepolia (L1 and L2 zkSync)
	ZkSyncProvider := "https://sepolia.era.zksync.dev"
	EthereumProvider := "https://rpc.ankr.com/eth_sepolia"

	// Connect to ZKsync network
	client, err := clients.Dial(ZkSyncProvider)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// Connect to Ethereum network
	ethClient, err := ethclient.Dial(EthereumProvider)
	if err != nil {
		log.Fatal(err)
	}
	defer ethClient.Close()

	// Load the ABI JSON file. This would be the RLN contract abi
	abiFilePath := "abi.abi"
	abiFile, err := ioutil.ReadFile(abiFilePath)
	if err != nil {
		log.Fatal("Failed to read ABI file: ", err)
	}

	// Parse the ABI
	parsedABI, err := abi.JSON(strings.NewReader(string(abiFile)))
	if err != nil {
		log.Fatal("Failed to parse ABI: ", err)
	}

	// Method from the contract we want to call
	methodName := "register"

	// First field. This would be the commitment
	arg1 := big.NewInt(0)

	// Second field. This would be the userMessageLimit
	arg2 := uint32(100)

	// Pack the method call
	data, err := parsedABI.Pack(methodName, arg1, arg2)
	if err != nil {
		log.Fatalf("Failed to pack ABI data: %v", err)
	}

	log.Info("Packed data:", data)

	// https://sepolia.explorer.zksync.io/address/0x3cb2b87d10ac01736a65688f3e0fb1b070b3eea3#transactions
	// 0xaEaD05FD4986e6128587eB2E714B6fe1751B41C3 (taken from https://www.youtube.com/watch?v=7rC9rjPtxBs)
	// 0x3cb2b87d10ac01736a65688f3e0fb1b070b3eea3 (official paymaster, not working)
	// Note that a custom paymaster can be also deployed and used
	Paymaster := common.HexToAddress("0xaEaD05FD4986e6128587eB2E714B6fe1751B41C3")

	// RLN contract address
	RlnContract := common.HexToAddress("0xaEaD05FD4986e6128587eB2E714B6fe1751B41C4")

	// Any account works. No need to have funds in it. Paymaster covers them
	privateKey := "TODO: YOUR PRIVATE KEY"
	wallet, err := accounts.NewWallet(common.Hex2Bytes(privateKey), &client, ethClient)
	if err != nil {
		log.Fatal("error creating new wallet ", err)
	}

	paymasterParams, err := utils.GetPaymasterParams(
		Paymaster,
		&zkTypes.GeneralPaymasterInput{})
	if err != nil {
		log.Fatal("error getting paymaster params: ", err)
	}

	hash, err := wallet.SendTransaction(context.Background(), &accounts.Transaction{
		Data: data,
		To:   &RlnContract,
		Meta: &zkTypes.Eip712Meta{
			PaymasterParams: paymasterParams,
		},
	})
	if err != nil {
		log.Fatal("error sending tx: ", err)
	}

	log.Info("Tx hash: ", hash)
}
@s-tikhomirov
Copy link
Contributor

Does a paymaster change our security assumptions in any way? As cool as it sounds, a paymaster is another external dependency. If we use zkSync's paymaster, what are we trusting them with? To not censor our transactions? Can / should Waku (or Status?) deploy their own paymaster instead?

@alrevuelta
Copy link
Contributor Author

Does a paymaster change our security assumptions in any way? As cool as it sounds, a paymaster is another external dependency. If we use zkSync's paymaster, what are we trusting them with? To not censor our transactions? Can / should Waku (or Status?) deploy their own paymaster instead?

  • No change on our side. Paymasters are optional and not part of the waku protocol.
  • Anyone can deploy their own Paymaster.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants