# Technical Details

### 1. What paymaster data would the signer key sign on?

* Paymaster validates upon EIP-712 type signatures.&#x20;

```
(_domainSeparator +
hash(
    SIGNATURE_TYPEHASH,
    _from,
    _to,
    _expirationTime,
    _maxNonce,
    _maxFeePerGas,
    _gasLimit
))
```

**\_from :** The user address the signer wants to sponsor.

**\_to:** The target contract user address is interacting i.e. Dapp's contract.

**\_expirationTime :** Timestamp post which the signature expires.

**\_maxNonce :** Nonce of the **user(\_from)** post which signature cannot be replayed.

**\_maxFeePerGas :** Current gas price returned by the provider.

**\_gasLimit :** Gas limit required by the transaction.\
Paymaster cost **60K** gas overhead. Hence, should be added while setting close gasLimit.&#x20;

* Following code represents what the exact values the are required to be signed by the signer(point 3 & 4 as per previous integration flow diagram):

```javascript
// Example code
// ethers v5
import {BigNumber, Contract, Wallet} from "zksync-ethers";
export async function getSignature(
  from: string, to: string, expirationTime: BigNumber, maxNonce: BigNumber, maxFeePerGas: BigNumber, gasLimit: BigNumber, paymaster: Contract
){
  const signer = new Wallet(process.env.SIGNER_PRIVATE_KEY, provider);
// EIP-712 domain from the paymaster
  const eip712Domain = await paymaster.eip712Domain();
  const domain = {
    name: eip712Domain[1],
    version: eip712Domain[2],
    chainId: eip712Domain[3],
    verifyingContract: eip712Domain[4],
  }
  const types = {
    PermissionLessPaymaster: [
      { name: "from", type: "address"},
      { name: "to", type: "address"},
      { name: "expirationTime", type: "uint256"},
      { name: "maxNonce", type: "uint256"},
      { name: "maxFeePerGas", type: "uint256"},
      { name: "gasLimit", type: "uint256"}
    ]
  };
// -------------------- IMPORTANT --------------------
  const values = {
    from,  // User address
    to, // Your dapp contract address which the user will interact
    expirationTime, // Expiration time post which the signature expires
    maxNonce, // Max nonce of user after which signature becomes invalid
    maxFeePerGas, // Current max gas price
    gasLimit // Max gas limit you want to allow to your user. Ensure to add 60K gas for paymaster overhead.
  }
// Note: MaxNonce allows the signature to be replayed.
// For eg: If currentNonce of user is 5, maxNonce is set to 10. Signature will allowed to replayed for nonce 6,7,8,9,10 on the same `to` address by the same user.
// This is to provide flexibility to Dapps to ensure signature works if users have multiple transactions running. 
// Important: Signers are recommended to set maxNonce as current nonce of the user or as close as possible to ensure safety of gas funds.
// Important : Signers should set expirationTime is close enough to ensure safety of funds.

// Signer wallet will already defined in the code
  return [(await signer._signTypedData(domain, types, values)), signer.address];
}
```

### 2. What extra data will be send with transaction for paymaster data?

* Once you get the signature, you simply need to add custom data to the user transaction as below(point 5 as per previous integration diagram) :

```javascript
// This is example code. Direct copy/paste won't work
import {utils, provider, Contract, BigNumber} from "zksync-ethers";

const paymasterAddress = "0x1fc6AAd6FFc4b26229a29432FbC4b65d5A5e462b";
const paymasterAbi = ["function eip712Domain() external view returns (bytes1 fields,string memory name,string memory version,uint256 chainId,address verifyingContract,bytes32 salt,uint256[] memory extensions);"];
const paymasterContract = new Contract(paymasterAddress, paymasterAbi, provider);
// Below part can be managed in getSignature() as well.
// ------------------------------------------------------------------------------------
// Note: Do not set maxNonce too high than current to avoid unwanted signature replay.
// Consider maxNonce is as replayLimit. And setting maxNonce to currentNonce means 0 replay.
// Get the maxNonce allowed to user. Here we ensure it's currentNonce.
const maxNonce = await provider.getNonce(userAddress);
// You can also check for min Nonce from the NonceHolder System contract to fully ensure as ZKsync support arbitrary nonce.
// -----------------
// const nonceHolderAddress = "0x0000000000000000000000000000000000008003";
// const nonceHolderAbi = ["function getMinNonce(address _address) external view returns (uint256)"];
// const nonceHolderContract = new Contract(nonceHolderAddress, nonceHolderAbi, provider);
// const maxNonce = await nonceHolderContract.callStatic.getMinNonce(userAddress);
// -----------------
// Get the expiration time. Here signature will be valid upto 60 sec. 
const expirationTime = BigNumber.from((await provider.getBlock).timestamp + 60);
// Get the current gas price.
const maxFeePerGas = await provider.getGasPrice();
// Set the gasLimit. Here, Dapp would know range of gas a function could cost and add 60K top up for paymaster overhead.. 
// Setting 215K (For eg: 150K function gas cost + 65K paymaster overhead)
// It will refunded anyways, so not an issue if Dapps set more.
const gasLimit = 215_000;
// ------------------------------------------------------------------------------------

const [signature, signerAddress] = await getSignature(userAddress,DappContract.address,expirationTime, maxNonce, maxFeePerGas, gasLimit, paymasterContract);
// We encode the extra data to be sent to paymaster
// Notice how it's not required to provide from, to, maxFeePerGas and gasLimit as per signature above. 
// That's because paymaster will get it from the transaction struct directly to ensure it's the correct user.
const innerInput = ethers.utils.arrayify(
      abiCoder.encode(
["uint256","uint256","address","bytes"], 
[expirationTime, // As used in above signature 
 maxNonce, // As used in above signature
 signerAddress, // The signer address
 signature]), // Signature created in the above snippet. get from API server
    );
   // getPaymasterParams function is available in zksync-ethers
const paymasterParams = utils.getPaymasterParams(
            paymasterAddress, // Paymaster address
            {
                type: "General",
                innerInput: innerInputs
            });
// Send the transaction with paymaster data. 
// Users will get transaction signature pop-up
const tx = await DappContract.<function>([args..],{
    maxFeePerGas, // Ensure it's same as used for signature
    gasLimit, // Ensure it's same as used for signature
    customData:{
       paymasterParams, // Paymaster address + paymaster data with signature.
       gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT,
    },
  });

```

> Further documentation on this paymaster will be available soon [here](https://docs.zyfi.org/) & on [https://code.zksync.io](https://code.zksync.io/) .\
> Zyfi is also working on an SDK to reduces the above code into 5 lines of code.

### Miscellaneous

1. `_maxNonce` allows flexibility to Dapps by allowing signature replay in a secure constrained way. Signer should ensure maxNonce is not too big from the *current nonce* of the user and `_expirationTime` is not too far from the *current timestamp*. If `_maxNonce` is set to *current nonce of the user,* then signature cannot be replayed at all.<br>

   Check [here](https://github.com/ondefy/permissionless-multisigner-paymaster/blob/875a28b75f7f0c888b7b1ab61116cca0f49f0e46/contracts/paymasters/PermissionlessPaymaster.sol#L199-L203):

   <pre data-title="PermissionlessPaymaster.sol"><code>        // Validate that the transaction generated by the API is not expired
           if (block.timestamp > expirationTime)
               revert Errors.PM_SignatureExpired();
           // Validate that the nonce is not higher than the maximum allowed
           if (_transaction.nonce > maxNonce) revert Errors.PM_InvalidNonce();
   </code></pre>

   &#x20;
2. ZKsync might allow [arbitary nonce ordering ](https://docs.zksync.io/zk-stack/components/zksync-evm/bootloader#nonce-ordering)in future. To ensure surety over nonce of a user, you can add one more check by calling `getMinNonce` on the [NonceHolder system contract of ZKsync](https://github.com/matter-labs/era-contracts/blob/f4ae6a1b90e2c269542848ada44de669a5009290/system-contracts/contracts/interfaces/INonceHolder.sol#L17). For more details, check docs [here](https://docs.zksync.io/build/developer-reference/era-contracts/system-contracts#nonceholder) & [here](https://docs.zksync.io/sdk/js/ethers/api/v5/types#accountnonceordering).
3. This [paymaster has gas overhead of **51K-60K** gas](https://github.com/ondefy/permissionless-multisigner-paymaster/blob/main/technical_specs.md#gas), which is quite nominal compare to other paymaster gas overhead. Signer should ensure to add this overhead i.e. **60K** in the `_gasLimit`, if there are setting it close to the actual required gas.
4. Do not worry to set the `_gasLimit` high. All extra ETH spent from the manager's gas funds are refunded back to the manager.

### Markup charge

Zyfi Dao will have the ability to set the markup fee percent similar to Uniswap fee switch. Currently, markup is set to 0 percent.

Once activated, a markup percent will be charged on each transaction on the required ETH *(gasPrice\*gasLimit)* of the transaction and will be deducted from the respective manager's balance.

This markup will incentivise the DAO to promote paymaster evolution.

### FAQs

**1. Are there any fees for interacting with this paymaster other than potential markup fee set by the DAO?**\
\&#xNAN;**—** No, this paymaster is meant for common good. No fees on deposit, withdrawal, adding signers.

**2. What if the private key of our signer gets leaked?**\
\&#xNAN;**—** You will need to quickly remove/replace the leaked signer address from the paymaster. A leaked signer private key can drain gas funds of the related manager.

**3. As a manager(Dapp), are my funds at risk if private key of un-related signer address is leaked?**\
\&#xNAN;**—** Only the manager’s gas funds related to the leaked signer address will be at risk. Rest all the manager funds will be safe.

**4. ZKsync processes refunds for extra gas fees paid in each transaction. As a manager, would I be receiving those refunds that is ideally deducted from my balance?**\
\&#xNAN;**—** Yes, this paymaster solves the refund issue innovatively. Each manager’s balance will be updated with exact refund amount during the **next paymaster interaction.** Hence, all refunds are added back to the respective manager's balance.
