Add paymaster allowlist tutorial (#632)
* init commit * implement feedback * make global limit to the second step * add admonition for limit cycles * swap out PK * use reference style links * add danger callouts for private keys
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 914 KiB |
|
After Width: | Height: | Size: 1005 KiB |
|
After Width: | Height: | Size: 886 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 827 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 950 KiB |
|
After Width: | Height: | Size: 932 KiB |
|
After Width: | Height: | Size: 524 KiB |
@@ -1,5 +1,27 @@
|
||||
{
|
||||
"deploy-with-foundry": {
|
||||
"0_deploy-with-fleek": {
|
||||
"title": "Deploy an Onchain App with Fleek",
|
||||
"slug": "/deploy-with-fleek",
|
||||
"description": "Learn how to deploy an onchain app using Fleek.",
|
||||
"author": "briandoyle81",
|
||||
"keywords": [
|
||||
"deploy",
|
||||
"fleek",
|
||||
"onchain",
|
||||
"dapp",
|
||||
"onchain app"
|
||||
],
|
||||
"tags": [
|
||||
"frontend"
|
||||
],
|
||||
"difficulty": "hard",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "9 min read",
|
||||
"checksum": "1155994e8aedab6a77e03bed61e33b04004828f11b05b6b7b3b58422292f48fc"
|
||||
},
|
||||
"0_deploy-with-foundry": {
|
||||
"title": "Deploying a smart contract using Foundry",
|
||||
"slug": "/deploy-with-foundry",
|
||||
"description": "A tutorial that teaches how to deploy a smart contract on the Base test network using Foundry. Includes instructions for setting up the environment, compiling, and deploying the smart contract.",
|
||||
@@ -24,11 +46,11 @@
|
||||
],
|
||||
"difficulty": "beginner",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jun 4, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "17 min read",
|
||||
"checksum": "b6ba5851a908389eb12dae91ff7d742f3e188eba2322b8550661b05f6731296c"
|
||||
},
|
||||
"deploy-with-hardhat": {
|
||||
"0_deploy-with-hardhat": {
|
||||
"title": "Deploying a smart contract using Hardhat",
|
||||
"slug": "/deploy-with-hardhat",
|
||||
"description": "A tutorial that teaches how to deploy a smart contract on the Base test network using Hardhat. Includes instructions for setting up the environment, compiling, and deploying the smart contract.",
|
||||
@@ -53,11 +75,11 @@
|
||||
],
|
||||
"difficulty": "beginner",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Feb 9, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "15 min read",
|
||||
"checksum": "e2a66b9b034f2fd97a898c5137aba8e29446c4e73433ee5b2dce3b4af0935bce"
|
||||
"checksum": "b9b4a675c58d4cdfb99f3b97ddcf1b223a51dfc5c2414d184cd5d4fb9f988b55"
|
||||
},
|
||||
"deploy-with-remix": {
|
||||
"0_deploy-with-remix": {
|
||||
"title": "Deploying a smart contract using Remix",
|
||||
"slug": "/deploy-with-remix",
|
||||
"description": "A tutorial that teaches how to deploy a smart contract on the Base test network using Remix IDE. Includes instructions for setting up the environment, compiling, and deploying the smart contract.",
|
||||
@@ -83,11 +105,11 @@
|
||||
],
|
||||
"difficulty": "beginner",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 18, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "18 min read",
|
||||
"checksum": "039bc3fe2c561bc45a6069fbf704b2b29203fb0ce49e1d0dee97a8f0fada11df"
|
||||
},
|
||||
"deploy-with-tenderly": {
|
||||
"0_deploy-with-tenderly": {
|
||||
"title": "Deploying a smart contract using Tenderly",
|
||||
"slug": "/deploy-with-tenderly",
|
||||
"description": "A tutorial that teaches how to deploy smart contracts using Tenderly DevNets. This page covers setup, debugging, transaction simulations, and continuous integration for smart contract development on Base Network.",
|
||||
@@ -114,11 +136,11 @@
|
||||
],
|
||||
"difficulty": "beginner",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Oct 12, 2023",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "19 min read",
|
||||
"checksum": "6b93f84f6431fcf064adbb8b51ee2fb162a97516d4fbac2f982af1eddba87b6b"
|
||||
},
|
||||
"deploy-with-thirdweb": {
|
||||
"0_deploy-with-thirdweb": {
|
||||
"title": "Deploying a smart contract using thirdweb",
|
||||
"slug": "/deploy-with-thirdweb",
|
||||
"description": "A tutorial that teaches how to deploy and interact with smart contracts using the thirdweb CLI and SDK. Includes instructions for project creation, contract deployment on the Base test network.",
|
||||
@@ -148,11 +170,44 @@
|
||||
],
|
||||
"difficulty": "beginner",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 11, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "10 min read",
|
||||
"checksum": "f4b7fa789b2c65e00e467c4851857ab2e629f6d8dbed7f8c2d625abf668a124b"
|
||||
},
|
||||
"run-a-base-node": {
|
||||
"0_gassless-transactions-with-paymaster": {
|
||||
"title": "Gasless Transactions on Base using Coinbase Paymaster",
|
||||
"slug": "/gasless-transaction-on-base-using-a-paymaster",
|
||||
"description": "Learn how to leverage Coinbase Paymaster for seamless, gasless transactions on the Coinbase Cloud Developer Platform.",
|
||||
"author": "hughescoin",
|
||||
"keywords": [
|
||||
"Gas",
|
||||
"Gasless",
|
||||
"Transactions",
|
||||
"Paymaster",
|
||||
"Sponsor",
|
||||
"Sponsored Transactions",
|
||||
"Onchain",
|
||||
"Coinbase",
|
||||
"Base",
|
||||
"Crypto",
|
||||
"Cloud Platform"
|
||||
],
|
||||
"tags": [
|
||||
"Gasless",
|
||||
"Crypto",
|
||||
"Gas",
|
||||
"Sponsor",
|
||||
"Coinbase",
|
||||
"Base"
|
||||
],
|
||||
"difficulty": "easy",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "25 min read",
|
||||
"checksum": "bb4faf87f8d0f36abf18b2f2440d2111af2c6f31552be1cede40f2dc2162c9e4"
|
||||
},
|
||||
"0_run-a-base-node": {
|
||||
"title": "Running a Base Node",
|
||||
"slug": "/run-a-base-node",
|
||||
"description": "A tutorial that teaches how to set up and run a Base Node.",
|
||||
@@ -176,11 +231,11 @@
|
||||
],
|
||||
"difficulty": "beginner",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jun 10, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "6 min read",
|
||||
"checksum": "9349abde020a7f705710c3e214ccddafaceef0e8bb92692ca2e58ac64e5aa7a4"
|
||||
"checksum": "2effa6656ddc3da5f719b560d44007e317de85bc0e4bf133ef521be9db3b2665"
|
||||
},
|
||||
"build-with-thirdweb": {
|
||||
"1_build-with-thirdweb": {
|
||||
"title": "Building an onchain app using thirdweb",
|
||||
"slug": "/build-with-thirdweb",
|
||||
"description": "A tutorial that teaches how to build an NFT gallery app using thirdweb, including steps for creating an NFT collection, minting NFTs, and configuring the app for the Base testnet.",
|
||||
@@ -209,11 +264,36 @@
|
||||
],
|
||||
"difficulty": "beginner",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 11, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "8 min read",
|
||||
"checksum": "527200e33d612b0c76e5eaacddfc23633b57eaa4577f389d4429b8f862c45462"
|
||||
},
|
||||
"thirdweb-unreal-nft-items": {
|
||||
"1_event-poap-nouns": {
|
||||
"title": "Event POAPs with Nouns",
|
||||
"slug": "/event-poaps-with-nouns",
|
||||
"description": "Learn how to give attendees of an in-person event a Nouns-based POAP/PFT, even if they're not onchain yet.",
|
||||
"author": "briandoyle81",
|
||||
"keywords": [
|
||||
"Solidity",
|
||||
"ERC-721",
|
||||
"token",
|
||||
"NFT",
|
||||
"POAP",
|
||||
"Nouns",
|
||||
"PFP"
|
||||
],
|
||||
"tags": [
|
||||
"nft",
|
||||
"smart contracts"
|
||||
],
|
||||
"difficulty": "hard",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "4 min read",
|
||||
"checksum": "a96c8d7eca808c7f6a7b0b55ff4ece251bfe565d57aa8921896cf49b3c64b3bb"
|
||||
},
|
||||
"1_thirdweb-unreal-nft-items": {
|
||||
"title": "Thirdweb and Unreal - NFT Items",
|
||||
"slug": "/thirdweb-unreal-nft-items",
|
||||
"description": "Learn how to use NFTs as in-game items using Thirdweb and Unreal.",
|
||||
@@ -230,16 +310,17 @@
|
||||
"onchain games"
|
||||
],
|
||||
"tags": [
|
||||
"nft"
|
||||
"nft",
|
||||
"smart contracts"
|
||||
],
|
||||
"difficulty": "hard",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jun 21, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "40 min read",
|
||||
"checksum": "cfd81fd73a2952f32fd63557915cc864aee013dca9beb255b47cde01b205b32e"
|
||||
"checksum": "dac639cda3aa56bf5ec7041a5152d1b90abef96d80d44a85e1a142551ac635fe"
|
||||
},
|
||||
"account-abstraction-with-biconomy": {
|
||||
"2_account-abstraction-with-biconomy": {
|
||||
"title": "Account Abstraction on Base using Biconomy",
|
||||
"slug": "/account-abstraction-with-biconomy",
|
||||
"description": "A tutorial that teaches how to implement Account Abstraction into a Base project using Biconomy paymasters, bundlers, and smart accounts.",
|
||||
@@ -262,11 +343,11 @@
|
||||
],
|
||||
"difficulty": "intermediate",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Feb 5, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "29 min read",
|
||||
"checksum": "8c8189d19e874c08dd05549d06c1eb2e45ce924aa2b0e1239f0adf04c8ee4879"
|
||||
},
|
||||
"account-abstraction-with-particle-network": {
|
||||
"2_account-abstraction-with-particle-network": {
|
||||
"title": "Account Abstraction on Base using Particle Network",
|
||||
"slug": "/account-abstraction-with-particle-network",
|
||||
"description": "A walkthrough on Particle Network's Modular Smart Wallet-as-a-Service, leveraging account abstraction and social logins across various providers.",
|
||||
@@ -288,11 +369,11 @@
|
||||
],
|
||||
"difficulty": "intermediate",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Apr 11, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "34 min read",
|
||||
"checksum": "f15915f4b8a607c68e5ae57dc1b3e3519f581a089a7f25669e3530112a1d42ae"
|
||||
"checksum": "d307c0834f745648d2c7004595a7c60544381a0d90d4f3a3d6337f6152c4c856"
|
||||
},
|
||||
"account-abstraction-with-privy-and-base-paymaster": {
|
||||
"2_account-abstraction-with-privy-and-base-paymaster": {
|
||||
"title": "Account Abstraction on Base using Privy and the Base Paymaster",
|
||||
"slug": "/account-abstraction-with-privy-and-base-paymaster",
|
||||
"description": "A tutorial that teaches how to implement Account Abstraction into a Base project using Privy and the Base paymaster.",
|
||||
@@ -316,11 +397,11 @@
|
||||
"difficulty": "intermediate",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Dec 23, 2023",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "46 min read",
|
||||
"checksum": "f7985912c4b36e3e0dcd33ad58d83a59403242e0f8eb6160660132b70c597cc2"
|
||||
"checksum": "fcd90aad54c93b59ab947c2954597b9b861204b885ed599754f7e50cca303fd9"
|
||||
},
|
||||
"coinbase-smart-wallet": {
|
||||
"2_coinbase-smart-wallet": {
|
||||
"title": "Coinbase Smart Wallet",
|
||||
"slug": "/coinbase-smart-wallet",
|
||||
"description": "Learn to create an app that uses the Coinbase Smart Wallet and effectively manages assets and permissions for both native and new users of onchain apps",
|
||||
@@ -341,11 +422,11 @@
|
||||
"difficulty": "intermediate",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jun 10, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "32 min read",
|
||||
"checksum": "44f84716dcfa1f181186af7369098b3f02485680e4c26c707915129361b92672"
|
||||
"checksum": "f8d2be1153df419e835c3afd8ea2714c2560f50a4e967eb303323693eb7777fe"
|
||||
},
|
||||
"cross-chain-with-ccip": {
|
||||
"2_cross-chain-with-ccip": {
|
||||
"title": "Sending messages and tokens from Base to other chains using Chainlink CCIP",
|
||||
"slug": "/cross-chain-with-ccip",
|
||||
"description": "A tutorial that teaches how to use Chainlink CCIP to perform cross-chain messaging and token transfers from Base Goerli testnet to Optimism Goerli testnet.",
|
||||
@@ -366,11 +447,11 @@
|
||||
],
|
||||
"difficulty": "intermediate",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 12, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "32 min read",
|
||||
"checksum": "b613b074cbb8f4c8eb55a13651f6371a8a3211afe65b8e2190e5b9df18c1d98f"
|
||||
},
|
||||
"cross-chain-with-layerzero": {
|
||||
"2_cross-chain-with-layerzero": {
|
||||
"title": "Sending messages from Base to other chains using LayerZero V2",
|
||||
"slug": "/cross-chain-with-layerzero",
|
||||
"description": "A tutorial that teaches how to use LayerZero V2 to perform cross-chain messaging from Base Goerli testnet to Optimism Goerli testnet.",
|
||||
@@ -391,11 +472,11 @@
|
||||
],
|
||||
"difficulty": "intermediate",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Feb 4, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "34 min read",
|
||||
"checksum": "a0ce01aa1c3097efa7582a74bc73f9ccad03222b25af451be1fde4fcd45c7dd7"
|
||||
},
|
||||
"dynamic-nfts": {
|
||||
"2_dynamic-nfts": {
|
||||
"title": "Building dynamic NFTs",
|
||||
"slug": "/dynamic-nfts",
|
||||
"description": "A tutorial that teaches how to make dynamic NFTs that evolve based on onchain or offchain actions.",
|
||||
@@ -419,11 +500,36 @@
|
||||
"difficulty": "intermediate",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jun 25, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "14 min read",
|
||||
"checksum": "0b1498d415cda6e83031231cd05d42edacd2faa82d68471b2ed09670481a3259"
|
||||
},
|
||||
"complex-onchain-nfts": {
|
||||
"2_nft-minting-with-zora": {
|
||||
"title": "How to Mint on Zora with an App",
|
||||
"slug": "/minting-nfts-with-zora",
|
||||
"description": "Learn to use Zora contracts inside your app to create secure, efficient, and feature-rich minting experiences for your users.",
|
||||
"author": "briandoyle81",
|
||||
"keywords": [
|
||||
"Solidity",
|
||||
"ERC-1155",
|
||||
"token",
|
||||
"NFT",
|
||||
"wagmi",
|
||||
"viem",
|
||||
"Zora"
|
||||
],
|
||||
"tags": [
|
||||
"nft",
|
||||
"smart wallet"
|
||||
],
|
||||
"difficulty": "intermediate",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "18 min read",
|
||||
"checksum": "fc55c7e07decb6053d9b906445c93002374e4e50120c804b7c04e36af4e1779b"
|
||||
},
|
||||
"2_onchain-nfts": {
|
||||
"title": "Building onchain NFTs",
|
||||
"slug": "/complex-onchain-nfts",
|
||||
"description": "A tutorial that teaches how to make complex nfts that are procedurally generated and have onchain metadata and images.",
|
||||
@@ -448,11 +554,11 @@
|
||||
"difficulty": "intermediate",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 31, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "39 min read",
|
||||
"checksum": "aa4782a6b0ef93073915e99e6ae6a244063422f14c8021f4dc0eb2d5a0c71b6a"
|
||||
"checksum": "108ca52fb9d1242e529c225e6f9e9cdde63f8f0725bddd876bc5ea572db1ea59"
|
||||
},
|
||||
"oracles-chainlink-price-feeds": {
|
||||
"2_oracles-chainlink-price-feeds": {
|
||||
"title": "Accessing real-world data using Chainlink Data Feeds",
|
||||
"slug": "/oracles-chainlink-price-feeds",
|
||||
"description": "A tutorial that teaches how to use Chainlink Data Feeds to access real-world data, such as asset prices, directly from your smart contracts on the Base testnet.",
|
||||
@@ -477,11 +583,11 @@
|
||||
],
|
||||
"difficulty": "intermediate",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Dec 19, 2023",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "11 min read",
|
||||
"checksum": "df42e4091a128a0bd3e7dc9fcc585f9a711a56027152d57553a54e8040a4001f"
|
||||
},
|
||||
"oracles-pyth-price-feeds": {
|
||||
"2_oracles-pyth-price-feeds": {
|
||||
"title": "Accessing real-time asset data using Pyth Price Feeds",
|
||||
"slug": "/oracles-pyth-price-feeds",
|
||||
"description": "A tutorial that teaches how to use Pyth Price Feeds to access real-time asset data, directly from your smart contracts on the Base testnet.",
|
||||
@@ -507,11 +613,11 @@
|
||||
],
|
||||
"difficulty": "intermediate",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Dec 19, 2023",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "13 min read",
|
||||
"checksum": "983e7175434206be7c996758b09db9c33aac454ecb9b4b3da8f301f8140b7c52"
|
||||
},
|
||||
"oracles-supra-vrf": {
|
||||
"2_oracles-supra-vrf": {
|
||||
"title": "Generating random numbers contracts using Supra dVRF",
|
||||
"slug": "/oracles-supra-vrf",
|
||||
"description": "A tutorial that teaches how to use Supra dVRF to serve random numbers using an onchain randomness generation mechanism directly within your smart contracts on the Base testnet.",
|
||||
@@ -545,11 +651,11 @@
|
||||
],
|
||||
"difficulty": "intermediate",
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Dec 19, 2023",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "19 min read",
|
||||
"checksum": "260e40333e386593cf6f211c06218042d4b93ecac34f56a0decbd398f1e3ddb1"
|
||||
},
|
||||
"signature-mint-nft": {
|
||||
"2_signature-mint": {
|
||||
"title": "Signature Mint NFT",
|
||||
"slug": "/signature-mint-nft",
|
||||
"description": "A tutorial that teaches how to create a signature mint, in which minters pay their own gas, but must first be given a valid signed authorization.",
|
||||
@@ -569,11 +675,11 @@
|
||||
"difficulty": "intermediate",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "May 9, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "19 min read",
|
||||
"checksum": "9429b07496c1e0b22041d913a16d46bb1850b0cebd72a45aaf55bc585c35bb91"
|
||||
"checksum": "bb0152d2ccf76eb7db67a995f5a35590df22bcacfbaf98ff930510e3d5b21704"
|
||||
},
|
||||
"farcaster-frames-deploy-to-vercel": {
|
||||
"3_farcaster-frames-deploy-to-vercel": {
|
||||
"title": "Farcaster Frames: Deploying to Vercel",
|
||||
"slug": "/farcaster-frames-deploy-to-vercel",
|
||||
"description": "A tutorial that teaches how to deploy a Farcaster Frame using Vercel.",
|
||||
@@ -592,11 +698,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Feb 6, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "9 min read",
|
||||
"checksum": "3174ebe066db1100e389cf2b0ec1451c2711145fb645dc540f4c08c5a2e48540"
|
||||
},
|
||||
"farcaster-frames-gating-and-redirects": {
|
||||
"3_farcaster-frames-gating-and-redirects": {
|
||||
"title": "Farcaster Frames: Gating content and creating redirects",
|
||||
"slug": "/farcaster-frames-gating-and-redirects",
|
||||
"description": "A tutorial that teaches how to create Frames with more advanced behaviors such as gating content based on a user's follows, likes, or recasts, and creating redirect buttons.",
|
||||
@@ -612,11 +718,11 @@
|
||||
"difficulty": "intermediate",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Feb 23, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "12 min read",
|
||||
"checksum": "6767682f0995d5f873b46aa615f6fb952a70d8b0487823fa32a41734df6048ba"
|
||||
"checksum": "81672fdd81867b1ac78f474fb670c073da4132fcccd961b809a88f84580b7fca"
|
||||
},
|
||||
"farcaster-frames-hyperframes": {
|
||||
"3_farcaster-frames-hyperframes": {
|
||||
"title": "Farcaster Frames: Building HyperFrames",
|
||||
"slug": "/farcaster-frames-hyperframes",
|
||||
"description": "A tutorial that teaches how to make cross-linked hyperframes in an organized manner.",
|
||||
@@ -637,11 +743,11 @@
|
||||
"tags": [
|
||||
"frames"
|
||||
],
|
||||
"last_updated": "Feb 20, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "16 min read",
|
||||
"checksum": "9497478eb1ba7a6bfb3b2e668b34b3a57107e24fdaae76673d832163eb0561b0"
|
||||
"checksum": "dca5d7ffcf59c834221a9585628ba88e58842be3ed2c10705e1185e6c5d30715"
|
||||
},
|
||||
"farcaster-frames-nft-minting": {
|
||||
"3_farcaster-frames-nft-minting": {
|
||||
"title": "Farcaster Frames: Building an NFT airdrop Frame",
|
||||
"slug": "/farcaster-frames-nft-minting",
|
||||
"description": "A tutorial that teaches how to make a Farcaster Frame that allows you to mint and airdrop NFTs to users.",
|
||||
@@ -662,11 +768,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "May 23",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "22 min read",
|
||||
"checksum": "8f1ced0b8fe2f7b5ae69b21c16dfdc433fb5fc09c88dffc1d32e9267cb4c0aef"
|
||||
"checksum": "4f2e23920817a1ef138ce594423b25d57080ecb1f954940cc91ef133f1050fa4"
|
||||
},
|
||||
"farcaster-frames-nocode-minting": {
|
||||
"3_farcaster-frames-nocode-minting": {
|
||||
"title": "Farcaster Frames: Building a no-code minting Frame",
|
||||
"slug": "/farcaster-frames-nocode-minting",
|
||||
"description": "A tutorial that teaches how to make a Farcaster Frame with an outbound link to an NFT minting website.",
|
||||
@@ -688,11 +794,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Feb 23, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "7 min read",
|
||||
"checksum": "fed0c4e553f10ac5ab0d90354fad93ac44e3e34306ac310eb07bcb7ad390600d"
|
||||
"checksum": "2201cf6ca7cf13fc8940de15b834076090671161a37e97a3146fd207b79b2033"
|
||||
},
|
||||
"farcaster-frames-transactions": {
|
||||
"3_farcaster-frames-transactions": {
|
||||
"title": "Farcaster Frames: Making transactions",
|
||||
"slug": "/farcaster-frames-transactions",
|
||||
"description": "A tutorial that teaches how to invoke a wallet transaction from a Farcaster Frame.",
|
||||
@@ -711,11 +817,11 @@
|
||||
"difficulty": "intermediate",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Mar 8, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "17 min read",
|
||||
"checksum": "50bc1e1efc66e0a22536cc73c7d0e04daebd552db8538d0ffde59a2a6b75bab3"
|
||||
"checksum": "f039e4e2b33d73e803fbc7785395ca283dc0abe35c46ead630e1dfaa0d7ab275"
|
||||
},
|
||||
"hardhat-debugging": {
|
||||
"4_hardhat-debugging": {
|
||||
"title": "Hardhat: Debugging smart contracts",
|
||||
"slug": "/hardhat-debugging",
|
||||
"description": "A tutorial that teaches how to debug your smart contracts using Hardhat.",
|
||||
@@ -734,11 +840,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 18, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "15 min read",
|
||||
"checksum": "c4f0c5cc94e04b85501323d6992a014308db14900c069ac5a7639d5b17703db6"
|
||||
},
|
||||
"hardhat-profiling-gas": {
|
||||
"4_hardhat-profiling-gas": {
|
||||
"title": "Hardhat: Optimizing the gas usage of smart contracts",
|
||||
"slug": "/hardhat-profiling-gas",
|
||||
"description": "A tutorial that teaches how to optimize the gas usage of your smart contracts using Hardhat.",
|
||||
@@ -760,11 +866,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 18, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "16 min read",
|
||||
"checksum": "20233dab9a1cda1a19de6ef9597d2dba37c853477b1474660fc22d302a815eb8"
|
||||
"checksum": "7462ea26fdaafac994b2ccbeaaa0fcc08f708debcda1193f8131f99c201005cc"
|
||||
},
|
||||
"hardhat-profiling-size": {
|
||||
"4_hardhat-profiling-size": {
|
||||
"title": "Hardhat: Optimizing the size of smart contracts",
|
||||
"slug": "/hardhat-profiling-size",
|
||||
"description": "A tutorial that teaches how to optimize the size of your smart contracts using Hardhat.",
|
||||
@@ -787,11 +893,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 18, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "22 min read",
|
||||
"checksum": "213edb66a4dc7845730cf4b72015d01c30cee0528cea6832714baf5bb451d9a8"
|
||||
"checksum": "38c40aa53b9359a9b8716e7f56f68ebcc910da9ae6d34b055bde866d51da007b"
|
||||
},
|
||||
"hardhat-test-coverage": {
|
||||
"4_hardhat-test-coverage": {
|
||||
"title": "Hardhat: Analyzing the test coverage of smart contracts",
|
||||
"slug": "/hardhat-test-coverage",
|
||||
"description": "A tutorial that teaches how to profile the test coverage of your smart contracts using Hardhat and the Solidity Coverage plugin.",
|
||||
@@ -814,11 +920,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 18, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "9 min read",
|
||||
"checksum": "54b377b97554eea4e09520fbf6fdacff9978d840248ec69d82ab79c0bebb0846"
|
||||
},
|
||||
"intro-to-foundry-setup": {
|
||||
"4_intro-to-foundry-setup": {
|
||||
"title": "Foundry: Setting up Foundry with Base",
|
||||
"slug": "/intro-to-foundry-setup",
|
||||
"description": "A tutorial that teaches how to set up your development environment to work with Foundry.",
|
||||
@@ -836,11 +942,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 18, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "6 min read",
|
||||
"checksum": "0b50de1197db8c50cbf02405a904f99ec7e32159f86ae884993aa6cc36b10fcb"
|
||||
},
|
||||
"intro-to-foundry-testing": {
|
||||
"4_intro-to-foundry-testing": {
|
||||
"title": "Foundry: Testing smart contracts",
|
||||
"slug": "/intro-to-foundry-testing",
|
||||
"author": "Edson Alcala",
|
||||
@@ -860,11 +966,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 18, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "9 min read",
|
||||
"checksum": "2ced64394c425e5749001cb21cca5a3b0043fe6ab013be45e98299e16f337d23"
|
||||
},
|
||||
"intro-to-providers": {
|
||||
"4_intro-to-providers": {
|
||||
"title": "Introduction to Providers",
|
||||
"slug": "/intro-to-providers",
|
||||
"description": "A tutorial that teaches what providers are, why you need one, and how to configure several providers and use them to connect to the blockchain.",
|
||||
@@ -910,11 +1016,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jan 18, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "21 min read",
|
||||
"checksum": "0ce947aec9f1cab196e9489b78509392a625b0d12011294e3fb9673a8bdd8e22"
|
||||
"checksum": "d09d83f406019b24b7cf9490cbe0200cba4bf09506d98d18d47fe43efa48aa3a"
|
||||
},
|
||||
"farcaster-cast-actions-simple": {
|
||||
"5_farcaster-cast-actions-simple": {
|
||||
"title": "Farcaster Cast Actions: Create a Simple Cast Action",
|
||||
"slug": "/farcaster-cast-actions-simple",
|
||||
"description": "A tutorial that teaches how to make a simple Farcaster cast action.",
|
||||
@@ -932,11 +1038,11 @@
|
||||
"difficulty": "beginner",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Apr 5, 2024",
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "9 min read",
|
||||
"checksum": "96225f883c79832d18459b3e47fd3a0bfd43c7ad6d1a1b1704bf902d33910025"
|
||||
"checksum": "f9ad99285bb78e91cde255b71c826554b6ff1f8cec69450afe814d033af945e6"
|
||||
},
|
||||
"shopify-storefront-commerce": {
|
||||
"5_shopify-storefront-commerce": {
|
||||
"title": "Deploy a Shopify Storefront with Coinbase Commerce",
|
||||
"slug": "/shopify-storefront-commerce",
|
||||
"description": "Learn how to launch a Shopify storefront that uses Coinbase Commerce as a crypto payment gateway.",
|
||||
@@ -962,8 +1068,8 @@
|
||||
"difficulty": "easy",
|
||||
"hide_table_of_contents": false,
|
||||
"displayed_sidebar": null,
|
||||
"last_updated": "Jun 13, 2024",
|
||||
"duration": "3 min read",
|
||||
"checksum": "810af1759f437cb4c77334a34513378fd1554e0bb8c13b408129df54050cb836"
|
||||
"last_updated": "Jul 11, 2024",
|
||||
"duration": "8 min read",
|
||||
"checksum": "1d485f4ded5aa492578d9887e36fefefea452c262769f5f30f6e9750c24c2913"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,674 @@
|
||||
---
|
||||
title: 'Gasless Transactions on Base using a Paymaster'
|
||||
slug: /gasless-transaction-on-base-using-a-paymaster
|
||||
description: Learn how to leverage the Base Paymaster for seamless, gasless transactions on the Coinbase Cloud Developer Platform.
|
||||
author: hughescoin
|
||||
keywords:
|
||||
[
|
||||
Gas,
|
||||
Gasless,
|
||||
Transactions,
|
||||
Paymaster,
|
||||
Sponsor,
|
||||
Sponsored Transactions,
|
||||
Onchain,
|
||||
Coinbase,
|
||||
Base,
|
||||
Crypto,
|
||||
Cloud Platform,
|
||||
]
|
||||
tags: ['Gasless', 'Crypto', 'Gas', 'Sponsor', 'Coinbase', 'Base']
|
||||
difficulty: easy
|
||||
hide_table_of_contents: false
|
||||
displayed_sidebar: null
|
||||
---
|
||||
|
||||
# Gasless Transactions on Base using Base Paymaster
|
||||
|
||||
Still trying to onboard users to your app? Want to break free from the worries of gas transactions and sponsor them for your users on Base? Look no further!
|
||||
|
||||
Base transaction fees are less than a penny, but the concept of gas can be confusing for new users. Abstract this away and improve your UX by using the Base Paymaster. The Base Paymaster allows you to batch multi-step transactions and create custom gasless experiences. Sponsor up to $10k monthly on mainnet (unlimited on testnet). To request an increase in limit, reach out in [Discord].
|
||||
|
||||
## Objectives
|
||||
|
||||
- Configure security measures to ensure safe and reliable transactions
|
||||
- Manage and allocate resources for sponsored transactions
|
||||
- Subsidize transaction fees for users, enhancing the user experience by making transactions free for them
|
||||
- Set up and manage sponsored transactions on various schedules, including weekly, monthly, and daily cadences
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This tutorial assumes you have a Coinbase Cloud Developer Platform account. If not, sign up on the [CDP site].
|
||||
|
||||
### Coinbase CDP account
|
||||
|
||||
This is your access point to the Coinbase Cloud Developer Platform, where you can manage projects and utilize tools like the Paymaster.
|
||||
|
||||
### Familiarity with Smart Accounts and ERC 4337
|
||||
|
||||
Understanding Smart Accounts and the ERC 4337 standard is crucial as they are the backbone of executing advanced transaction patterns and account abstractions on the Ethereum network.
|
||||
|
||||
### Foundry
|
||||
|
||||
Foundry is a development environment, testing framework, and smart contract toolkit for Ethereum. It's essential for deploying and testing smart contracts.
|
||||
|
||||
## Set Up a Base Paymaster & Bundler
|
||||
|
||||
In this section, you will configure a Paymaster to sponsor payments on behalf of a specific smart contract for a specified amount. First, navigate to the Coinbase Developer Platform, create or select your project, and click on the Paymaster tool from the left navigation. Then, go to the Configuration tab and save the RPC URL to your clipboard, which will be needed for later steps in your index.js file.
|
||||
|
||||
Navigate to the [Coinbase Developer Platform]:
|
||||
|
||||
Create or select your project of choice from the upper left corner of your screen.
|
||||
|
||||

|
||||
|
||||
Click on the `Paymaster` tool on the left navigation:\*\* [Paymaster Tool]
|
||||
|
||||

|
||||
|
||||
Click on `Configuration` at the top of the screen
|
||||
|
||||

|
||||
|
||||
Save the RPC URL to your paymaster to your clipboard. You will need it in your `index.js` file in a later step.
|
||||
|
||||
### Allowlist a Sponsorable Contract
|
||||
|
||||
Sponsoring transactions are beneficial to easing the onboarding and UX of a decentralized application. As a developer, you want to ensure this is done in the most secure and cost-effective manner. Start by allowlisting a contract of your choice.
|
||||
|
||||
Select **Base Mainnet**
|
||||
|
||||
From the configuration page, select `Base Mainnet` from the dropdown menu. Then, enable your paymaster by clicking on the toggle button to the right of the screen.
|
||||
|
||||

|
||||
|
||||
Allowlist the NFT contract and the mintTo functions:
|
||||
|
||||
Click the `Add` button to add a contract.
|
||||
|
||||
Add the following contract: [`0x83bd615eb93eE1336acA53e185b03B54fF4A17e8`](https://basescan.org/token/0x83bd615eb93ee1336aca53e185b03b54ff4a17e8)
|
||||
|
||||
Put `mintTo(address)` as the function to allowlist then click `Save` at the bottom of the page.
|
||||
|
||||

|
||||
|
||||
:::note Use your own contract
|
||||
You will be using this [simple NFT contract]deployed on Base mainnet for our example. Feel free to use a contract of your choice.
|
||||
:::
|
||||
|
||||
### Global & Per User Limits:
|
||||
|
||||
Scroll down to the “Per User Limit” section
|
||||
|
||||
You can set a Paymaster policy that specifies either a specific dollar amount or a number of UserOperations. You can enable a 'limit cycle' that allows this policy to refresh every week. This feature enables applications to sponsor smart wallets on a weekly, daily, or monthly basis, either by amount or by number of UserOperations.
|
||||
|
||||
Set the max USD to $0.05 and the max UserOperation to 1 to create a policy with a maximum sponsorship limit of $0.05 and a maximum of 1 UserOperation per user.
|
||||
|
||||
:::note Limit Cycles
|
||||
|
||||
Limit Cycles enables applications to sponsor smart wallets on a weekly, daily, or monthly basis, either by amount or by number of UserOperations. These limits reset based on the cadence selected in the dropdown menu.
|
||||
|
||||
:::
|
||||
|
||||
Set the Global limit:
|
||||
|
||||
This setting allows you to define the maximum amount of gas or USD that can be sponsored globally across all users. It helps control the total expenditure on gas sponsorship, ensuring that the allocated budget is not exceeded. For example, setting a global limit of $1 means that the Paymaster will sponsor transactions until the total gas cost reaches $1.
|
||||
|
||||
Set your global policy to be `$.07` by entering the amount in the text field and clicking the `Save` button.
|
||||
|
||||

|
||||
|
||||
## Test Your Paymaster policy
|
||||
|
||||
You will now test the policy that was created.
|
||||
|
||||
:::info The Use of Foundry
|
||||
|
||||
In this tutorial, You use foundry to create two key pairs that will allow us to create Smart Accounts that you will sponsor transactions on behalf of. You will **not** need to send any funds (ETH) to these wallets. You may use private keys of wallets you own as an alternative.
|
||||
|
||||
Foundry depends on Rust to work. If you do not have rust install:
|
||||
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
For more information, see the [Foundry Book installation guide](https://book.getfoundry.sh/getting-started/installation).
|
||||
|
||||
:::
|
||||
|
||||
Open a terminal and install Foundry by running:
|
||||
|
||||
```bash
|
||||
curl -L https://foundry.paradigm.xyz | bash
|
||||
```
|
||||
|
||||
Install the Foundry toolchain installer:
|
||||
|
||||
```bash
|
||||
foundryup
|
||||
```
|
||||
|
||||
Create a directory named `sponsored_transactions`:
|
||||
|
||||
```bash
|
||||
mkdir sponsored_transactions
|
||||
```
|
||||
|
||||
Change into the new directory, initialize a node project, install two dependencies `viem` and `permissionless` and create a file titled `index.js`:
|
||||
|
||||
```bash
|
||||
cd sponsored_transactions
|
||||
npm init es6
|
||||
npm install permissionless
|
||||
npm install viem
|
||||
touch index.js
|
||||
```
|
||||
|
||||
In the `index.js` file, import the dependencies from viem and permissionless.
|
||||
These dependencies will allow us to connect to Base, create our smart accounts, initialize a paymaster, and send our encoded data to the network.
|
||||
|
||||
```javascript
|
||||
import { http, createPublicClient, encodeFunctionData } from 'viem';
|
||||
import { base } from 'viem/chains';
|
||||
import { createSmartAccountClient } from 'permissionless';
|
||||
import { privateKeyToSimpleSmartAccount } from 'permissionless/accounts';
|
||||
import { createPimlicoPaymasterClient } from 'permissionless/clients/pimlico';
|
||||
```
|
||||
|
||||
### Set Constants for Your Paymaster & Bundler endpoint
|
||||
|
||||
:::note Find your Paymaster & Bundler endpoint
|
||||
|
||||
The Paymaster & Bundler endpoint is the URL for your Coinbase Developer Platform (CDP) Paymaster.
|
||||
|
||||
This was saved in the previous section and follows this format: `https://api.developer.coinbase.com/rpc/v1/base/<SPECIAL-KEY>`
|
||||
|
||||
Navigate to the [Paymaster Tool] and select the `Configuration` tab at the top of the screen to obtain your RPC URL.
|
||||
|
||||
:::
|
||||
|
||||
:::danger Secure your endpoints
|
||||
|
||||
You will create a constant for our Paymaster & Bundler endpoint obtained from cdp.portal.coinbase.com. The most secure way to do this is by using a proxy. For the purposes of this demo, hardcode it into our `index.js` file. For product, we highly recommend using a [proxy service].
|
||||
|
||||
:::
|
||||
|
||||
You will also need the address of the entrypoint contract for Base. A full list of entrypoint contracts and their addresses can be found [here](https://docs.alchemy.com/reference/factory-addresses).
|
||||
|
||||
Add the following to lines of code after your import statements:
|
||||
|
||||
```javascript
|
||||
const rpcUrl = https://api.developer.coinbase.com/rpc/v1/base/<SPECIAL-KEY> //Paymaster & Bundler endpoint
|
||||
```
|
||||
|
||||
```javascript
|
||||
const baseEntryPoint = '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789';
|
||||
```
|
||||
|
||||
```javascript
|
||||
const baseFactoryAddress = '0x15Ba39375ee2Ab563E8873C8390be6f2E2F50232';
|
||||
```
|
||||
|
||||
Now, create a public client. Public clients enable our application to interact with the Ethereum blockchain. They use JSON-RPC API methods to perform actions such as retrieving block numbers, transactions, and reading from smart contracts. Learn more about this on viem.
|
||||
|
||||
Initialize a public client named `publicClient` and set the chain to `base` and the transport to our `rpcURL` variable:
|
||||
|
||||
```javascript
|
||||
const publicClient = createPublicClient({
|
||||
chain: base,
|
||||
transport: http(rpcUrl),
|
||||
});
|
||||
```
|
||||
|
||||
### Create Two Smart Accounts
|
||||
|
||||
Now, you are going to create two `SimpleAccounts` for this demonstration. Collectively, these two accounts will help us test the security policies you set at both the global level and the per-account user operation level.
|
||||
|
||||
To do so, you'll first need two private keys that will be used to create the Smart Accounts using Foundry. Open a terminal and run the following command **twice**:
|
||||
|
||||
```bash
|
||||
cast wallet new
|
||||
```
|
||||
|
||||
You will see something like this:
|
||||
|
||||
```bash
|
||||
> cast wallet new
|
||||
Successfully created new keypair.
|
||||
Address: 0xD440D74620542...D6F005cfD9
|
||||
Private key: 0x01c9720c1dfa3c9...634793138897
|
||||
|
||||
|
||||
> cast wallet new
|
||||
Successfully created new keypair.
|
||||
Address: 0x5f8e5bC8620542...D6F005cfD9
|
||||
Private key: 0xbcd6fbc1dfa3c9...634793138897
|
||||
```
|
||||
|
||||
Use the newly generated private keys as the parameter to the `privateKeyToSimpleSmartAccount` function and set the factory address to
|
||||
|
||||
:::danger Secure your private keys
|
||||
|
||||
Be sure to store your private keys somewhere safe. Committing these to a public code repository will give anyone access to your smart accounts.
|
||||
|
||||
Saving them to a `.env` file is safest.
|
||||
|
||||
:::
|
||||
|
||||
```javascript
|
||||
const simpleAccount = await privateKeyToSimpleSmartAccount(publicClient, {
|
||||
privateKey: '<Your first key>',
|
||||
factoryAddress: baseFactoryAddress, //Base v0.6
|
||||
entryPoint: baseEntryPoint,
|
||||
});
|
||||
|
||||
const simpleAccount2 = await privateKeyToSimpleSmartAccount(publicClient, {
|
||||
privateKey: '<Your second key>',
|
||||
factoryAddress: baseFactoryAddress, //Base v0.6
|
||||
entryPoint: baseEntryPoint,
|
||||
});
|
||||
```
|
||||
|
||||
Let’s break down what’s happening here:
|
||||
Private key : the private key to the wallet that your created either using Foundry or a wallet that you own. Either is fine.
|
||||
|
||||
Factory address is the address to the smart account factory deployed on base. Account factories are smart contracts that facilitate the creation of new wallet contracts. You make view a more comprehensive [list of factory addresses] on Alchemy.
|
||||
|
||||
Entrypoint is the entrypoint contract for Base.
|
||||
|
||||
:::tip Find the correct entrypoint address
|
||||
|
||||
Make an JSON RPC request to a node using the `` method to get the correct entrpoint contract. Here's an example for Base using a Coinbase Base Node:
|
||||
|
||||
```
|
||||
curl --request POST \
|
||||
--url https://api.developer.coinbase.com/rpc/v1/base/<Your-Key> \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"id": 1, "jsonrpc": "2.0", "method": "eth_supportedEntryPoints", "params": []}'
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Initialize Paymaster and Create Accounts
|
||||
|
||||
Initialize the paymaster and smart account client for both smart accounts:
|
||||
|
||||
```javascript
|
||||
const cloudPaymaster = createPimlicoPaymasterClient({
|
||||
chain: base,
|
||||
transport: http(rpcUrl),
|
||||
entryPoint: baseEntryPoint,
|
||||
});
|
||||
|
||||
const smartAccountClient = createSmartAccountClient({
|
||||
account: simpleAccount,
|
||||
chain: base,
|
||||
bundlerTransport: http(rpcUrl),
|
||||
middleware: {
|
||||
sponsorUserOperation: cloudPaymaster.sponsorUserOperation,
|
||||
},
|
||||
});
|
||||
|
||||
const smartAccountClient2 = createSmartAccountClient({
|
||||
account: simpleAccount2,
|
||||
chain: base,
|
||||
bundlerTransport: http(rpcUrl),
|
||||
middleware: {
|
||||
sponsorUserOperation: cloudPaymaster.sponsorUserOperation,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
To sponsor transactions you will need the ABI and the address of the contracts you want to support.
|
||||
|
||||
:::note Sponsor any contract
|
||||
Feel free to use your own contract to interact with the Paymaster. For learning purposes, you will interact with an ERC-721 contract deployed on the Base mainnet. You will call the mintTo function, which takes an address as its only parameter.
|
||||
:::
|
||||
|
||||
You will be interacting with the NFT + ABI from a simple NFT contract deployed at: `0x83bd615eb93eE1336acA53e185b03B54fF4A17e8`
|
||||
|
||||
Copy and paste the NFT's abi into `index.js`
|
||||
|
||||
```javascript
|
||||
const abi = [
|
||||
{
|
||||
type: 'constructor',
|
||||
inputs: [
|
||||
{ name: '_name', type: 'string', internalType: 'string' },
|
||||
{ name: '_symbol', type: 'string', internalType: 'string' },
|
||||
],
|
||||
stateMutability: 'nonpayable',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'approve',
|
||||
inputs: [
|
||||
{ name: 'spender', type: 'address', internalType: 'address' },
|
||||
{ name: 'id', type: 'uint256', internalType: 'uint256' },
|
||||
],
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'balanceOf',
|
||||
inputs: [{ name: 'owner', type: 'address', internalType: 'address' }],
|
||||
outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'currentTokenId',
|
||||
inputs: [],
|
||||
outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'getApproved',
|
||||
inputs: [{ name: '', type: 'uint256', internalType: 'uint256' }],
|
||||
outputs: [{ name: '', type: 'address', internalType: 'address' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'isApprovedForAll',
|
||||
inputs: [
|
||||
{ name: '', type: 'address', internalType: 'address' },
|
||||
{ name: '', type: 'address', internalType: 'address' },
|
||||
],
|
||||
outputs: [{ name: '', type: 'bool', internalType: 'bool' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'mintTo',
|
||||
inputs: [{ name: 'recipient', type: 'address', internalType: 'address' }],
|
||||
outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }],
|
||||
stateMutability: 'payable',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'name',
|
||||
inputs: [],
|
||||
outputs: [{ name: '', type: 'string', internalType: 'string' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'ownerOf',
|
||||
inputs: [{ name: 'id', type: 'uint256', internalType: 'uint256' }],
|
||||
outputs: [{ name: 'owner', type: 'address', internalType: 'address' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'safeTransferFrom',
|
||||
inputs: [
|
||||
{ name: 'from', type: 'address', internalType: 'address' },
|
||||
{ name: 'to', type: 'address', internalType: 'address' },
|
||||
{ name: 'id', type: 'uint256', internalType: 'uint256' },
|
||||
],
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'safeTransferFrom',
|
||||
inputs: [
|
||||
{ name: 'from', type: 'address', internalType: 'address' },
|
||||
{ name: 'to', type: 'address', internalType: 'address' },
|
||||
{ name: 'id', type: 'uint256', internalType: 'uint256' },
|
||||
{ name: 'data', type: 'bytes', internalType: 'bytes' },
|
||||
],
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'setApprovalForAll',
|
||||
inputs: [
|
||||
{ name: 'operator', type: 'address', internalType: 'address' },
|
||||
{ name: 'approved', type: 'bool', internalType: 'bool' },
|
||||
],
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'supportsInterface',
|
||||
inputs: [{ name: 'interfaceId', type: 'bytes4', internalType: 'bytes4' }],
|
||||
outputs: [{ name: '', type: 'bool', internalType: 'bool' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'symbol',
|
||||
inputs: [],
|
||||
outputs: [{ name: '', type: 'string', internalType: 'string' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'tokenURI',
|
||||
inputs: [{ name: 'id', type: 'uint256', internalType: 'uint256' }],
|
||||
outputs: [{ name: '', type: 'string', internalType: 'string' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
name: 'transferFrom',
|
||||
inputs: [
|
||||
{ name: 'from', type: 'address', internalType: 'address' },
|
||||
{ name: 'to', type: 'address', internalType: 'address' },
|
||||
{ name: 'id', type: 'uint256', internalType: 'uint256' },
|
||||
],
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
},
|
||||
{
|
||||
type: 'event',
|
||||
name: 'Approval',
|
||||
inputs: [
|
||||
{
|
||||
name: 'owner',
|
||||
type: 'address',
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
},
|
||||
{
|
||||
name: 'spender',
|
||||
type: 'address',
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
},
|
||||
{ name: 'id', type: 'uint256', indexed: true, internalType: 'uint256' },
|
||||
],
|
||||
anonymous: false,
|
||||
},
|
||||
{
|
||||
type: 'event',
|
||||
name: 'ApprovalForAll',
|
||||
inputs: [
|
||||
{
|
||||
name: 'owner',
|
||||
type: 'address',
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
},
|
||||
{
|
||||
name: 'operator',
|
||||
type: 'address',
|
||||
indexed: true,
|
||||
internalType: 'address',
|
||||
},
|
||||
{ name: 'approved', type: 'bool', indexed: false, internalType: 'bool' },
|
||||
],
|
||||
anonymous: false,
|
||||
},
|
||||
{
|
||||
type: 'event',
|
||||
name: 'Transfer',
|
||||
inputs: [
|
||||
{ name: 'from', type: 'address', indexed: true, internalType: 'address' },
|
||||
{ name: 'to', type: 'address', indexed: true, internalType: 'address' },
|
||||
{ name: 'id', type: 'uint256', indexed: true, internalType: 'uint256' },
|
||||
],
|
||||
anonymous: false,
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
### Encode the Function Call
|
||||
|
||||
Encode the `mintTo` function call with the parameter being the address of the first smart wallet
|
||||
|
||||
```javascript
|
||||
const callData = encodeFunctionData({
|
||||
abi: abi,
|
||||
functionName: 'mintTo',
|
||||
args: [smartAccountClient.account.address],
|
||||
});
|
||||
```
|
||||
|
||||
Create a function called `sendTransactionFromAccount1` in order to send the transaction request and store the response in a variable named `txHash`:
|
||||
|
||||
```javascript
|
||||
async function sendTransactionFromAccount1() {
|
||||
try {
|
||||
const txHash = await smartAccountClient.sendTransaction({
|
||||
account: smartAccountClient.account,
|
||||
to: '0x83bd615eb93eE1336acA53e185b03B54fF4A17e8',
|
||||
data: callData,
|
||||
value: 0n,
|
||||
});
|
||||
|
||||
console.log('✅ Transaction successfully sponsored for account 1!');
|
||||
console.log(`🔍 View on Etherscan: https://basescan.org/tx/${txHash}`);
|
||||
} catch (error) {
|
||||
console.log('Transaction failed from account 1: ', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Call the function at the bottom of the `index.js` file:
|
||||
|
||||
```javascript
|
||||
async function sendTransactionFromAccount1() {
|
||||
// previous lines of code ...
|
||||
}
|
||||
|
||||
sendTransactionFromAccount1();
|
||||
```
|
||||
|
||||
Run the following line of code from your terminal, ensuring you are in the root folder:
|
||||
|
||||
```bash
|
||||
node index.js
|
||||
```
|
||||
|
||||
The first attempt should be successful with the following output:
|
||||
|
||||
```bash
|
||||
✅ Transaction successfully sponsored!
|
||||
🔍 View on Etherscan: https://basescan.org/tx/0x1439431b0bf333f8106bc1815a532fdec18d62c3ee1c07a10d07711c6e5a2e56
|
||||
```
|
||||
|
||||
Run the script again but this time you should get an error that contains a `code` and `message` similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": -32001,
|
||||
"message": "request denied - rejected due to maximum per address transaction count reached"
|
||||
}
|
||||
```
|
||||
|
||||
Awesome! Errors, in this case, are good. This means our Paymaster policy works and prevents unwanted sponsorship.
|
||||
|
||||
Navigate back to your CDP portal and update the number of UserOperations to `2`.
|
||||
|
||||
Give it about 10-15 minutes for the changes to take affect then run `node index.js` again to send a new transaction.
|
||||
|
||||
It should return a successful prompt:
|
||||
|
||||
```bash
|
||||
✅ Transaction successfully sponsored for account 1!
|
||||
🔍 View on Etherscan: https://basescan.org/tx/0x1439431b0bf333f8106bc1815a532fdec18d62c3ee1c07a10d07711c6e5a2e56
|
||||
```
|
||||
|
||||
Looking back at our Paymaster configuration, you'll see that you've used about $0.03 of our $0.05 global limit, and for account 1, you've hit our UserOperation limit.
|
||||
|
||||
Now, let's send another transaction, this time using the second account.
|
||||
|
||||
Before you do that, let's update the per-wallet UserOperation limit to 10. Since gas is cheap on Base, you may need to do a few transactions before you hit our global limit of $0.07.
|
||||
|
||||
:::note Increase the global limit
|
||||
|
||||
You may get a warning to increase the global limit and that is okay for now...
|
||||
|
||||
[Image-of-policy]
|
||||
|
||||
:::
|
||||
|
||||
Navigate back to the [UI] and update the policy to 10 and hit the `Save` button.
|
||||
|
||||
[image of updated policy]
|
||||
|
||||
### Testing Global Limits
|
||||
|
||||
Back in your code editor, open the `index.js` file and create a function called `sendTransactionFromAccount2` and run place it at the bottom on the `index.js` file:
|
||||
|
||||
```javascript
|
||||
// previous lines of code ...
|
||||
|
||||
async function sendTransactionFromAccount2() {
|
||||
try {
|
||||
const txHash2 = await smartAccountClient2.sendTransaction({
|
||||
account: smartAccountClient2.account,
|
||||
to: '0x83bd615eb93eE1336acA53e185b03B54fF4A17e8',
|
||||
data: callData,
|
||||
value: 0n,
|
||||
});
|
||||
|
||||
console.log('✅ Transaction successfully sponsored for account 2!');
|
||||
console.log(`🔍 View on Etherscan: https://basescan.org/tx/${txHash2}`);
|
||||
} catch (error) {
|
||||
console.log('Transaction failed from account 2: ', error);
|
||||
}
|
||||
}
|
||||
|
||||
// sendTransactionFromAccount1();
|
||||
sendTransactionFromAccount2();
|
||||
```
|
||||
|
||||
Run the `sendTransactionFromAccount2()` function a few times to help reach your global limit.
|
||||
|
||||
You should eventually encounter an error similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": -32001,
|
||||
"message": "request denied - rejected due to max global usd spend limit reached"
|
||||
}
|
||||
```
|
||||
|
||||
Congrats! Your global limit has been hit.
|
||||
|
||||
Navigate back to the cloud portal and increase the global policy to your choosing. Run `node index.js` again and you should be able to successfully sponsor a transaction.
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this tutorial, you learned to set up and configure the Base Paymaster on the Coinbase Developer Platform, allowing you to sponsor gasless transactions for your users. This enhances the user experience by abstracting away gas fees and making transactions more accessible and cost-effective.
|
||||
|
||||
---
|
||||
|
||||
[list of factory addresses]: https://docs.alchemy.com/reference/factory-addresses
|
||||
[Discord]: https://discord.gg/AaAcm4UW
|
||||
[CDP site]: https://portal.cdp.coinbase.com/
|
||||
[Coinbase Developer Platform]: https://portal.cdp.coinbase.com/
|
||||
[UI]: https://portal.cdp.coinbase.com/products/bundler-and-paymaster
|
||||
[proxy service]: https://www.smartwallet.dev/guides/paymasters
|
||||
[Paymaster Tool]: https://portal.cdp.coinbase.com/products/bundler-and-paymaster
|
||||
[Foundry Book installation guide]: https://book.getfoundry.sh/getting-started/installation
|
||||
[simple NFT contract]: https://basescan.org/token/0x83bd615eb93ee1336aca53e185b03b54ff4a17e8
|
||||