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

No hashing, paymaster script #1

Merged
merged 3 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions contracts/ZeekSecretMessages.sol → contracts/ZeekMessages.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ZeekSecretMessages {
bytes32[] private messages;
contract ZeekMessages {
string[] private messages;

// Event to acknowledge a new message
event MessageReceived(string);
Expand All @@ -12,10 +12,8 @@ contract ZeekSecretMessages {
emit MessageReceived("Zeek welcomes you to zkSync!");
}

// Function to send a "secret" message to Zeek
function sendMessage(string memory _message) public {
bytes32 hashedMessage = keccak256(abi.encodePacked(_message));
messages.push(hashedMessage);
messages.push(_message);

// Acknowledge the message receipt with Zeek's reply
emit MessageReceived("ZK is the endgame - Message received!");
Expand All @@ -25,4 +23,10 @@ contract ZeekSecretMessages {
function getTotalMessages() public view returns (uint) {
return messages.length;
}

// Function to return the last message sent to Zeek
function getLastMessage() public view returns (string memory) {
require(messages.length > 0, "No messages sent to Zeek yet!");
return messages[messages.length - 1];
}
}
123 changes: 103 additions & 20 deletions scripts/paymaster-transaction.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,126 @@

import {ethers} from "ethers";
import {Provider, Contract, utils, Signer} from "zksync-ethers";

// Address of the ZeekMessages contract
const ZEEK_MESSAGES_CONTRACT_ADDRESS = "";
// Address of the ERC20 token contract
const TOKEN_CONTRACT_ADDRESS = ""
// Wallet that will receive tokens
const RECEIVER_WALLET = "";
// Amount of tokens to mint in ETH format, e.g. 1.23
const TOKEN_AMOUNT = "";
const TOKEN_CONTRACT_ADDRESS = "";
// Message to be sent to the contract
const NEW_MESSAGE = "This tx cost me no ETH!";

(async () => {
try {

// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/TestToken.json` // Change this for different path
console.log(artifactsPath)
const messagesContractArtifactsPath = `browser/contracts/artifacts/ZeekSecretMessages.json`;
const tokenContractArtifactsPath = `browser/contracts/artifacts/TestToken.json`;

const messagesContractABI = JSON.parse(await remix.call('fileManager', 'getFile', messagesContractArtifactsPath));
const tokenContractABI = JSON.parse(await remix.call('fileManager', 'getFile', tokenContractArtifactsPath));

console.log('Sending a transaction via the testnet paymaster')

const browserProvider = new ethers.providers.Web3Provider(web3Provider)

const zkProvider = new Provider("https://sepolia.era.zksync.dev");

// const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(0)
const zkSigner = Signer.from(browserProvider.getSigner(), zkProvider);

// const walletAddress = await signer.getAddress();
const walletAddress = await zkSigner.getAddress();

console.log(walletAddress)

// initialise messages and token contracts with address, abi and signer
const messagesContract= new Contract(ZEEK_MESSAGES_CONTRACT_ADDRESS, messagesContractABI.abi, zkSigner);
const tokenContract= new Contract(TOKEN_CONTRACT_ADDRESS, tokenContractABI.abi, zkSigner);

// retrieve and print the current balance of the wallet
let ethBalance = await zkProvider.getBalance(walletAddress)
console.log(`Account ${walletAddress} has ${ethers.utils.formatEther(ethBalance)} ETH`);
let tokenBalance = await tokenContract.balanceOf(walletAddress)
console.log(`Account ${walletAddress} has ${ethers.utils.formatUnits(tokenBalance, 18)} tokens`);

const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))

// retrieve the testnet paymaster address
const testnetPaymasterAddress = await zkProvider.getTestnetPaymasterAddress();

const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(0)
console.log(`Testnet paymaster address is ${testnetPaymasterAddress}`);

const gasPrice = await zkProvider.getGasPrice();

// initialise token contract with address, abi and signer
const tokenContract= new ethers.Contract(TOKEN_CONTRACT_ADDRESS, metadata.abi, signer);
console.log("gasPrice >> ", gasPrice)

// define paymaster parameters for gas estimation
const paramsForFeeEstimation = utils.getPaymasterParams(testnetPaymasterAddress, {
type: "ApprovalBased",
token: TOKEN_CONTRACT_ADDRESS,
// set minimalAllowance to 1 for estimation
minimalAllowance: ethers.BigNumber.from(1),
// empty bytes as testnet paymaster does not use innerInput
innerInput: new Uint8Array(0),
});

// estimate gasLimit via paymaster
const gasLimit = await messagesContract.estimateGas.sendMessage(NEW_MESSAGE, {
customData: {
gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT,
paymasterParams: paramsForFeeEstimation,
},
});

console.log("gasLimit >> ", gasLimit)


// fee calculated in ETH will be the same in
// ERC20 token using the testnet paymaster
const fee = gasPrice * gasLimit;

console.log("Fee >>", fee);

// new paymaster params with fee as minimalAllowance
const paymasterParams = utils.getPaymasterParams(testnetPaymasterAddress, {
type: "ApprovalBased",
token: TOKEN_CONTRACT_ADDRESS,
// provide estimated fee as allowance
minimalAllowance: fee,
// empty bytes as testnet paymaster does not use innerInput
innerInput: new Uint8Array(0),
});

// full overrides object including maxFeePerGas and maxPriorityFeePerGas
const txOverrides = {
maxFeePerGas: gasPrice,
maxPriorityFeePerGas: "1",
gasLimit,
customData: {
gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT,
paymasterParams,
}
}

console.log("Minting tokens...");
const tx = await tokenContract.mint(RECEIVER_WALLET, ethers.utils.parseEther(TOKEN_AMOUNT));
console.log(`Mint transaction is ${tx.hash}`)
await tx.wait();
console.log("Success!");
console.log("overrides >> ", txOverrides);

console.log(`Sign the transaction in your wallet`);

const balance = await tokenContract.balanceOf(RECEIVER_WALLET)
// send transaction with additional paymaster params as overrides
const txHandle = await messagesContract.sendMessage(NEW_MESSAGE, txOverrides);
console.log(`Transaction ${txHandle.hash} with fee ${ethers.utils.formatUnits(fee, 18)} ERC20 tokens, sent via paymaster ${testnetPaymasterAddress}`);
await txHandle.wait();
console.log(`Transaction processed`)

ethBalance = await zkProvider.getBalance(walletAddress)
tokenBalance = await tokenContract.balanceOf(walletAddress)
console.log(`Account ${walletAddress} now has ${ethers.utils.formatEther(ethBalance)} ETH`);
console.log(`Account ${walletAddress} now has ${ethers.utils.formatUnits(tokenBalance, 18)} tokens`);

console.log(`The account ${RECEIVER_WALLET} now has ${balance} tokens`)
console.log(`Done!`);

} catch (e) {
console.log(e.message)
console.error('Error in script!')
console.error(e.message)
console.error(e)
}
})()