# Account Abstraction Source: https://etherspot.fyi/account-abstraction/accountabstraction An overview of Account Abstraction on Ethereum ## Overview Account abstraction is a concept in the Ethereum blockchain that allows for greater flexibility and functionality when it comes to executing transactions and smart contracts. It is an upgrade to the Ethereum Virtual Machine (EVM) that introduces new features and capabilities. Traditionally, in Ethereum, user accounts are externally owned accounts (EOAs) and smart contracts are contract accounts. EOAs are controlled by private keys and can send and receive ether, while contract accounts hold the code and state of smart contracts and can be interacted with by sending transactions to them. With account abstraction, the boundary between EOAs and contract accounts becomes blurred. It enables the creation of new types of accounts called "contract-type accounts" or simply "accounts." These accounts can hold both code and ether, and they can execute transactions and smart contract functions. This means that contracts can directly control and manipulate funds, eliminating the need for a separate EOA to initiate transactions. Account abstraction brings several benefits to the Ethereum ecosystem: 1. Enhanced efficiency: By allowing contracts to directly control funds, account abstraction reduces the number of transactions and storage operations required. This leads to improved efficiency and reduces gas costs. 2. Improved privacy: Account abstraction enables the creation of more sophisticated smart contracts that can handle transactions privately within the contract itself. It eliminates the need for external transactions, enhancing privacy for users. 3. Flexible fee payment models: With account abstraction, contracts can pay transaction fees on behalf of users. This allows for more flexible fee payment models, such as subscriptions or microtransactions, where users don't need to have ether to execute transactions. 4. Customized transaction semantics: Account abstraction opens up possibilities for customizing transaction semantics. Contracts can define their own rules and conditions for executing transactions, enabling more complex and dynamic interactions. Etherspot prime let's developers make use of these features within their dapps. # Entrypoints Source: https://etherspot.fyi/account-abstraction/entrypoints ```bash eth_supportedEntryPoints request theme={null} curl --request POST 'https://{network}-bundler.etherspot.io' \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc":"2.0","id":1, "method":"eth_supportedEntryPoints", "params":[] }' ``` ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": [ "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" ] } ``` The entrypoint is the same across all EVM networks, the one at the top is the most recent. The ones listed here are compatible with Skandha. | Address | Commit | | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | 0x0000000071727De22E5E9d8BAf0edAc6f37da032 | [https://github.com/eth-infinitism/account-abstraction/tree/releases/v0.7](https://github.com/eth-infinitism/account-abstraction/tree/releases/v0.7) | | 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 | [https://github.com/eth-infinitism/account-abstraction/tree/releases/v0.6](https://github.com/eth-infinitism/account-abstraction/tree/releases/v0.6) | | ~~0x0576a174D229E3cFA37253523E645A78A0C91B57~~ deprecated | [https://github.com/eth-infinitism/account-abstraction/tree/releases/v0.5](https://github.com/eth-infinitism/account-abstraction/tree/releases/v0.5) | # Smart Contract Wallets Source: https://etherspot.fyi/account-abstraction/eoa-vs-scw Explaining accounts on Ethereum If you're new to Account Abstraction it's important to know the differences between accounts. Ethereum has two account types: **Externally-owned account (EOA)**: Controlled by anyone with the private keys, the type of account Metamask/TrustWallet/Web3Auth uses. **Smart Contract Wallet(SCW)**: a smart contract deployed to the network, controlled by code. The type of account an Etherspot wallet is.

drawing

Both account types have the ability to: * Receive, hold and send ETH and tokens * Interact with deployed smart contracts With Etherspot, we take an EOA as a parameter when [instantiating the SDK](/prime-sdk/instantiation). This EOA will be the owner of the new Etherspot Smart Contract Wallet that is created. There will be two addresses, the original EOA address and the new Etherspot SCW address created by the SDK. **We only want to fund and use the Etherspot SCW address as this let's us make use of Etherspot's great Account Abstraction features.**

drawing

# ERC4337 Source: https://etherspot.fyi/account-abstraction/erc4337 ## Overview This page provides a simplified overview of ERC-4337, aiming to help developers grasp the basic concepts of its different components and their integration for application development. ERC-4337 consists of four main components: UserOperations, Bundlers, EntryPoint, and Contract Accounts. Paymasters and Aggregators can also complement these components. Components of ERC-4337: **UserOperations**: These are pseudo-transaction objects generated by your application, facilitating the execution of transactions with contract accounts. **Bundlers**: These are actors responsible for gathering UserOperations from a mempool and transmitting them to the EntryPoint contract on the blockchain. **EntryPoint**: This is a singleton smart contract that handles the verification and execution logic for transactions. **Contract Accounts**: These are smart contract accounts owned by users. **Paymasters**: These optional smart contract accounts can sponsor transactions for Contract Accounts. **Aggregators**: These optional smart contracts can validate signatures for multiple Contract Accounts. # UserOperations Source: https://etherspot.fyi/account-abstraction/userops The mempool operation requested by the user focuses on the following concept: The decentralized mempool for EIP-4337 consists of an unrestricted peer-to-peer network of independent bundlers. It operates without assuming any contracts as permissible or impermissible, treating all contracts equally when validating them. However, there may be instances where certain contracts have undergone audits and been proven to be secure, despite violating some rules set by the decentralized mempool. In such cases, a group of bundlers can create alternative mempools specifically for handling these exceptions. An example scenario that might require this is when using a Deposit Paymaster that can abstract gas fees with any ERC-20 token. Furthermore, in addition to its main connection to the decentralized mempool, a bundler also maintains connections to any other alternative mempools it chooses to participate in. | Field | Type | Description | | -------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sender | address | The address of the smart contract account. | | nonce | uint256 | Anti-replay protection. | | initCode | bytes | Code used to deploy the account if not yet on-chain. | | callData | bytes | Data that's passed to the sender for execution. | | callGasLimit | uint256 | Gas limit for the execution phase. | | verificationGasLimit | uint256 | Gas limit for the verification phase. | | preVerificationGas | uint256 | Gas to compensate the bundler for the overhead to submit a UserOperation. | | maxFeePerGas | uint256 | Similar to EIP-1559 max fee. | | maxPriorityFeePerGas | uint256 | Similar to EIP-1559 priority fee. | | paymasterAndData | bytes | Paymaster contract address and any extra data the paymaster contract needs for verification and execution. When set to 0x or the zero address, no paymaster is used. | | signature | bytes | Used to validate a UserOperation during verification. | # API Credits Source: https://etherspot.fyi/api-credits/pricing Each API endpoint requires a certain number of credits to execute. Below is a comprehensive list of endpoints and their credit costs: ## Skandha Endpoints ### Standard Operations | Endpoint | Credits | | ---------------------------------------- | ------- | | eth\_chainId | 5 | | eth\_supportedEntryPoints | 5 | | web3\_sha3 | 5 | | net\_version | 5 | | net\_listening | 5 | | net\_peerCount | 5 | | eth\_protocolVersion | 5 | | eth\_gasPrice | 10 | | eth\_blockNumber | 5 | | eth\_getBalance | 10 | | eth\_getStorageAt | 10 | | eth\_getTransactionCount | 10 | | eth\_getBlockTransactionCountByHash | 5 | | eth\_getBlockTransactionCountByNumber | 5 | | eth\_getUncleCountByBlockHash | 10 | | eth\_getUncleCountByBlockNumber | 10 | | eth\_sign | 5 | | eth\_getBlockByHash | 10 | | eth\_getBlockByNumber | 10 | | eth\_getTransactionByBlockHashAndIndex | 10 | | eth\_getTransactionByBlockNumberAndIndex | 10 | | eth\_getUncleByBlockHashAndIndex | 10 | | eth\_getUncleByBlockNumberAndIndex | 10 | | eth\_newFilter | 20 | | eth\_newBlockFilter | 20 | | eth\_maxPriorityFeePerGas | 10 | | web3\_clientVersion | 10 | ### Medium Complexity Operations | Endpoint | Credits | | -------------------------------- | ------- | | skandha\_config | 10 | | eth\_getUserOperationByHash | 15 | | eth\_getCode | 5 | | eth\_call | 10 | | eth\_estimateGas | 10 | | eth\_newPendingTransactionFilter | 30 | | eth\_uninstallFilter | 30 | | eth\_getFilterChanges | 30 | | eth\_getFilterLogs | 50 | | eth\_getLogs | 50 | | eth\_feeHistory | 20 | | eth\_getUserOperationReceipt | 25 | ### Complex Operations | Endpoint | Credits | | ----------------------------- | ------- | | eth\_getTransactionByHash | 10 | | eth\_getTransactionReceipt | 10 | | eth\_sendRawTransaction | 50 | | eth\_sendUserOperation | 600 | | eth\_estimateUserOperationGas | 300 | ## Arka Endpoints ### Standard Operations | Endpoint | Credits | | ------------------- | ------- | | /checkWhitelist/v2 | 5 | | /checkWhitelist | 15 | | /whitelist/v2 | 5 | | /whitelist | 10 | | /removeWhitelist/v2 | 5 | | /removeWhitelist | 10 | | /deposit | 10 | | /deposit/v2 | 5 | | /getAllWhitelist/v2 | 5 | | /metadata | 2 | ### Complex Operations | Endpoint | Credits | | ------------------------ | ------- | | pm\_getERC20TokenQuotes | 200 | | pm\_getPaymasterData | 450 | | pm\_getPaymasterStubData | 300 | | pm\_sponsorUserOperation | 700 | # addStake Source: https://etherspot.fyi/api-endpoints/arka/api-calls/add-stake arka post /addStake Add Stake Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": ["EPV_07", "0.01"] } ``` Example response: ```json theme={null} { "message": "Successfully staked with transaction Hash 0x..." } ``` # checkWhitelist Source: https://etherspot.fyi/api-endpoints/arka/api-calls/check-an-address-is-whitelisted arka post /checkWhitelist Check an address is whitelisted Example values you can use to demo the API: ```json theme={null} { "params": [ "0x725404c8Eead111d9E6DFE118c535F43402a9511" ]} ``` Example response: ```json theme={null} { "message": "Already added" } ``` # checkWhitelist/v2 Source: https://etherspot.fyi/api-endpoints/arka/api-calls/check-an-address-is-whitelisted-v2 arka post /checkWhitelist/v2 Check an address is whitelisted v2 Example values you can use to demo the API: ```json theme={null} { "params": [ "0x725404c8Eead111d9E6DFE118c535F43402a9511", ]} ``` Example response: ```json theme={null} { "message": "Already added" } ``` # getAllCommonERC20PaymasterAddress Source: https://etherspot.fyi/api-endpoints/arka/api-calls/check-erc20-paymaster-address arka post /getAllCommonERC20PaymasterAddress Check ERC20 Paymaster address Example values you can use to demo the API: ```json theme={null} { "params": ["0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"] } ``` Example response: ```json theme={null} { "message": "[{"paymasterAddress":"0x33fA8047AaeE6b0571002F076a1bce3f00DA7301","gasToken":"0xC168E40227E4ebD8C1caE80F7a55a4F0e6D66C97","chainId":137,"decimals":18},...]" } ``` # addPolicy Source: https://etherspot.fyi/api-endpoints/arka/api-calls/create-sponsorship-policy arka post /addPolicy Creates a new policy with the provided details. Example values you can use to demo the API: ```json theme={null} { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": ["EPV_06", "EPV_07"], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": 5000, "globalMaximumNative": 1000, "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": 100, "perUserMaximumNative": 200, "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": 10, "perOpMaximumNative": 20 } ``` Example response: ```json theme={null} { "createdAt": "2024-06-30T17:39:26.938Z", "updatedAt": "2024-06-30T17:39:26.939Z", "id": 5, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null } ``` # deletePolicy/{id} Source: https://etherspot.fyi/api-endpoints/arka/api-calls/delete-sponsorship-policy arka delete /deletePolicy/{id} Deletes a policy by its ID. Example values you can use to demo the API: ```json theme={null} { "headers": { "apikey": "your_api_key_here" }, "pathVariables": { "id": "policy_id_here" } } ``` Example response: ```json theme={null} { "message": "Policy deleted successfully." } ``` # deployVerifyingPaymaster Source: https://etherspot.fyi/api-endpoints/arka/api-calls/deploy-verifying-paymaster arka post /deployVerifyingPaymaster Deploy Verifying Paymaster Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": ["EPV_07"] } ``` Example response: ```json theme={null} { "verifyingPaymaster": "0xfe08B548Bd73F2559B1c759A6F41914a09456461", "txHash": "0x28ed5b8fce76b310d255953ba5ac3032730825da2389e7bfb11c7fb047d72e61" } ``` # deposit Source: https://etherspot.fyi/api-endpoints/arka/api-calls/deposit-to-paymaster arka post /deposit Deposit to paymaster Example values you can use to demo the API: ```json theme={null} { "params": ["0.000001"] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully deposited with transaction Hash 0x79137319a6c9c67827b67dbbc16c0729465305516da4788bce578d5fdf59a52e" } ``` # deposit/v2 Source: https://etherspot.fyi/api-endpoints/arka/api-calls/deposit-to-paymaster-v2 arka post /deposit/v2 Deposit to paymaster v2 Example values you can use to demo the API: ```json theme={null} { "params": ["0.000001"] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully deposited with transaction Hash 0x79137319a6c9c67827b67dbbc16c0729465305516da4788bce578d5fdf59a52e" } ``` # disablePolicy/{id} Source: https://etherspot.fyi/api-endpoints/arka/api-calls/disable-sponsorship-policy arka put /disablePolicy/{id} Disables an existing sponsorship policy by its ID. Example values you can use to demo the API: ```json theme={null} { "headers": { "apikey": "your_api_key_here" }, "pathVariables": { "id": "policy_id_here" } } ``` Example response: ```json theme={null} { "message": "Policy disabled successfully." } ``` # enablePolicy/{id} Source: https://etherspot.fyi/api-endpoints/arka/api-calls/enable-sponsorship-policy arka put /enablePolicy/{id} Enables an existing sponsorship policy by its ID. Example values you can use to demo the API: ```json theme={null} { "headers": { "apikey": "your_api_key_here" }, "pathVariables": { "id": "policy_id_here" } } ``` Example response: ```json theme={null} { "message": "Policy enabled successfully." } ``` # getAllWhitelist/v2 Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-all-whitelist-v2 arka post /getAllWhitelist/v2 Get all Whitelist addresses v2 Example values you can use to demo the API: ```json theme={null} { "params": [ "1", ] } ``` Example response: ```json theme={null} Value returned: { "addresses": [ "0x725404c8Eead111d9E6DFE118c535F43402a9511" ] } ``` # pm_getERC20TokenQuotes Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-erc20-token-quotes arkageterc20tokenquotes post / get token exchange quotes Example values you use to demo the API: ```json theme={null} { "jsonrpc": "2.0", "method": "pm_getERC20TokenQuotes", "params": [ { "sender": "0xB3aF6CFDDc444B948132753AD8214a20605692eF", "nonce": "0x6", "initCode": "0x", "callData": "0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000080a1874e1046b1cc5defdf4d3153838b72ff94ac0000000000000000000000000fd9e8d3af1aaee056eb9e802c3a762a667b1904000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000005af3107a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000386ed13ba07e1c409693b299bbb9839d05af20c3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000", "callGasLimit": "0x88b8", "verificationGasLimit": "0x186a0", "maxFeePerGas": "0x8dcac078f", "maxPriorityFeePerGas": "0x8dcac078f", "paymasterAndData": "0x0101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000001010101010100000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101", "signature": "0x", "preVerificationGas": "0xcb78" }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", [{ token: "0x0Fd9e8d3aF1aaee056EB9e802c3A762a667b1904" }] ], "id": 1 } ``` Example response: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { etherUSDExchangeRate: "0x1e" paymasterAddress: "0x386eD13Ba07E1C409693b299BbB9839d05aF20c3" gasEstimates: { "preVerificationGas": "0xc01c", "verificationGasLimit": "0xf4f1", "callGasLimit": "0x15971" } feeEstimates: { "maxFeePerGas": "0x8dcac078f", "maxPriorityFeePerGas": "0x8dcac078f" } quotes: [ { "token": "0x0Fd9e8d3aF1aaee056EB9e802c3A762a667b1904", "symbol": "LINK", "decimals": 18, "etherTokenExchangeRate": "0x01a055690d9db80000", "serviceFeePercent": 15 } ] unsupportedTokens: [] } } ``` # policy/walletAddress/{walletAddress}/latest Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-latest-sponsorship-policy-by-walletaddress arka get /policy/walletAddress/{walletAddress}/latest Retrieves the latest sponsorship policy by wallet address. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "walletAddress" } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # policy/walletAddress/{walletAddress}/chainId/{chainId}/latest Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-latest-sponsorship-policy-by-walletaddress-chainid arka get /policy/walletAddress/{walletAddress}/chainId/{chainId}/latest Retrieves the latest sponsorship policy by wallet address and chain ID. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "chainId": 80002 } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # policy/walletAddress/{walletAddress}/epVersion/{epVersion}/chainId/{chainId}/latest Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-latest-sponsorship-policy-by-walletaddress-epv-chainid arka get /policy/walletAddress/{walletAddress}/epVersion/{epVersion}/chainId/{chainId}/latest Retrieves the latest sponsorship policy for a given wallet address, EP version, and chain ID. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "epVersion": "EPV_06", "chainId": 80002 } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # policy/walletAddress/{walletAddress}/epVersion/{epVersion}/latest Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-latest-sponsorship-policy-by-walletaddress-epversion arka get /policy/walletAddress/{walletAddress}/epVersion/{epVersion}/latest Retrieves the latest sponsorship policy by wallet address and EP version. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "epVersion": "EPV_06" } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # metadata Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-metadata arka get /metadata Return paymaster Metadata for EP6 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", } ``` Example response: ```json theme={null} { "sponsorAddress": "0xaeAF09795d8C0e6fA4bB5f89dc9c15EC02021567", "sponsorWalletBalance": { "type": "BigNumber", "hex": "0x1ed81fac4b400e4d" }, "chainsSupported": [ 5, 114, 420, 84532, 84531, 421613, 534351, 11155111, ], "tokenPaymasters": { "1": { "USDC": "0x0000000000fABFA8079AB313D1D14Dcf4D15582a" }, "10": { "USDC": "0x0000000000fce6614d3c6f679e48c9cdd09aa634" }, "56": { "USDC": "0x0000000000db7995889f54d72dac9d36a9f7f467" }, } } ``` # metadata/v2 Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-metadata-v2 arka get /metadata/v2 Return paymaster Metadata for EP7 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", } ``` Example response: ```json theme={null} { "sponsorAddress": "0xaeAF09795d8C0e6fA4bB5f89dc9c15EC02021567", "sponsorWalletBalance": { "type": "BigNumber", "hex": "0x1ed81fac4b400e4d" }, "chainsSupported": [ 5, 114, 420, 84532, 84531, 421613, 534351, 11155111, ], "tokenPaymasters": { "1": { "USDC": "0x0000000000fABFA8079AB313D1D14Dcf4D15582a" }, "10": { "USDC": "0x0000000000fce6614d3c6f679e48c9cdd09aa634" }, "56": { "USDC": "0x0000000000db7995889f54d72dac9d36a9f7f467" }, } } ``` # policy Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-sponsorship-policies arka get /policy Retrieves all existing sponsorship policies, sorted by creation time in descending order. Example response: ```json theme={null} [ { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" }, { "id": 1, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "6000.0000", "globalMaximumNative": "2000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:49:08.252Z", "updatedAt": "2024-06-27T19:38:39.674Z" } ] ``` # policy/walletAddress/{walletAddress} Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-sponsorship-policies-by-walletaddress arka get /policy/walletAddress/{walletAddress} Retrieves all sponsorship policies associated with a wallet address. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "walletAddress" } } ``` Example response: ```json theme={null} [ { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" }, { "id": 1, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "6000.0000", "globalMaximumNative": "2000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:49:08.252Z", "updatedAt": "2024-06-27T19:38:39.674Z" } ] ``` # policy/walletAddress/{walletAddress}/epVersion/{epVersion} Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-sponsorship-policies-by-walletaddress-epversion arka get /policy/walletAddress/{walletAddress}/epVersion/{epVersion} Retrieves all sponsorship policies by wallet address and EP version. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "epVersion": "EPV_07" } } ``` Example response: ```json theme={null} [ { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" }, { "id": 1, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "6000.0000", "globalMaximumNative": "2000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:49:08.252Z", "updatedAt": "2024-06-27T19:38:39.674Z" } ] ``` # policy/walletAddress/{walletAddress}/epVersion/{epVersion}/chainId/{chainId} Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-sponsorship-policies-by-walletaddress-epversion-chainid arka get /policy/walletAddress/{walletAddress}/epVersion/{epVersion}/chainId/{chainId} Retrieves all sponsorship policies for a given wallet address, EP version, and chain ID. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "epVersion": "EPV_07", "chainId": 80002 } } ``` Example response: ```json theme={null} [ { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" }, { "id": 1, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "6000.0000", "globalMaximumNative": "2000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:49:08.252Z", "updatedAt": "2024-06-27T19:38:39.674Z" } ] ``` # policy/{id} Source: https://etherspot.fyi/api-endpoints/arka/api-calls/get-sponsorship-policy-by-id arka get /policy/{id} Retrieves a sponsorship policy by its unique ID. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "id": "policy_id_here" } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # removeWhitelist Source: https://etherspot.fyi/api-endpoints/arka/api-calls/remove-address-from-whitelist arka post /removeWhitelist Remove address from whitelist Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": ["0x725404c8Eead111d9E6DFE118c535F43402a9511"] } ``` Example response: ```json theme={null} { "message": "Successfully removed whitelisted addresses with transaction Hash 0x0f87967a28230efd3624ddbd9b33887d1324887364576b90d9d6c0fe78fc0a67" } ``` # removeWhitelist/v2 Source: https://etherspot.fyi/api-endpoints/arka/api-calls/remove-address-from-whitelist-v2 arka post /removeWhitelist/v2 Remove address from whitelist v2 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": [ ["0x725404c8Eead111d9E6DFE118c535F43402a9511"] ] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully removed whitelisted addresses" } ``` # pm_sponsorUserOperation Source: https://etherspot.fyi/api-endpoints/arka/api-calls/sponsor-user-operation arkasponsoruseroperation post / Sponsor a userOps Example values you use to demo the API: ```json theme={null} { "jsonrpc": "2.0", "method": "pm_sponsorUserOperation", "params": [ { "sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45", "nonce":"0x0", "initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000", "callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "callGasLimit":"0x0", "verificationGasLimit":"0x0", "preVerificationGas":"0x0", "maxPriorityFeePerGas":"0x3b9aca00", "maxFeePerGas":"0x7a5cf70d5", "paymasterAndData":"0x", "signature":"0x00000000fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", { "mode": 'sponsor' } ], "id": 1 } ``` Example response: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "paymasterAndData": "0x26fec24b0d467c9de105217b483931e8f944ff500000000000000000000000000000000000000000000000000000000066ebe8b80000000000000000000000000000000000000000000000000000000066ebceb4b5b1df6daf753a0017ac66af78cf10aad42803f86efceff5db656890034e588e10c33ca89eab9ecaef9f0188303b3a3674299fc5208684f9d4de7f34202df1521c", "verificationGasLimit": "0xe967", "preVerificationGas": "0x18810", "callGasLimit": "0x28ede", } } ``` # updatePolicy Source: https://etherspot.fyi/api-endpoints/arka/api-calls/update-sponsorship-policy arka put /updatePolicy Updates an existing sponsorship policy based on the provided information. Example values you can use to demo the API: ```json theme={null} { "id": "1", "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": ["EPV_06", "EPV_07"], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": 6000, "globalMaximumNative": 2000, "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": 100, "perUserMaximumNative": 200, "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": 10, "perOpMaximumNative": 20 } ``` Example response: ```json theme={null} { "createdAt": "2024-06-30T17:39:26.938Z", "updatedAt": "2024-06-30T17:39:26.939Z", "id": 5, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null } ``` # whitelist Source: https://etherspot.fyi/api-endpoints/arka/api-calls/whitelist-an-address arka post /whitelist Whitelist an address Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": ["0x725404c8Eead111d9E6DFE118c535F43402a9511"] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully whitelisted with transaction Hash 0x91575dc46283aee04bab45763b5ea77250ac84232d6a38095f8ed08d1feef5d3" } ``` # whitelist/v2 Source: https://etherspot.fyi/api-endpoints/arka/api-calls/whitelist-an-address-v2 arka post /whitelist/v2 Whitelist an address v2 Example values you can use to demo the API: ```json theme={null} { "params": [ ["0x725404c8Eead111d9E6DFE118c535F43402a9511"] ] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully whitelisted" } ``` # eth_estimateUserOperationGas Source: https://etherspot.fyi/api-endpoints/skandha/api-reference/estimate-userop skandhaestimateuserop post / Estimate User Operation Example values you use to demo the API: ```json theme={null} { "jsonrpc": "2.0", "method": "eth_estimateUserOperationGas", "params": [ { "sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45", "nonce":"0x0", "initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000", "callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "callGasLimit":"0x0", "verificationGasLimit":"0x0", "preVerificationGas":"0x0", "maxPriorityFeePerGas":"0x3b9aca00", "maxFeePerGas":"0x7a5cf70d5", "paymasterAndData":"0x", "signature":"0x00000000fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" ], "id": 1 } ``` Example response: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "preVerificationGas": "0xdf55", "verificationGas": "0x52503", "verificationGasLimit": "0x52503", "callGasLimit": "0x13880", "maxFeePerGas": "0x59682f00", "maxPriorityFeePerGas": "0x59682f00" } } ``` # skandha_feeHistory Source: https://etherspot.fyi/api-endpoints/skandha/api-reference/get-bundler-fee-history skandhagetfeehistory post / Get bundler fee history Example values you use to demo the API: ```json theme={null} { "id": 3, "method": "skandha_feeHistory", "params": [ "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", "10", "latest" ] } ``` Example response: ```json theme={null} { "id": 3, "result": { "actualGasPrice": [ "0xfbdf3621f", "0xfbdf3621f", "0xfbdf3621f" ], "maxFeePerGas": [ "0x118b2ce6d2", "0x115bdb995e", "0x118b2ce6d2" ], "maxPriorityFeePerGas": [ "0x861c46800", "0x861c46800", "0x861c46800" ] } } ``` # skandha_getGasPrice Source: https://etherspot.fyi/api-endpoints/skandha/api-reference/get-gas-price skandhagetgasprice post / Get gas price Example values you use to demo the API: ```json theme={null} { "id": 3, "method": "skandha_getGasPrice" } ``` Example response: ```json theme={null} { "id": 3, "result": { "maxPriorityFeePerGas": "0xaac11ce38", "maxFeePerGas": "0x1663d57dc4" } } ``` # skandha_config Source: https://etherspot.fyi/api-endpoints/skandha/api-reference/get-skandha-config skandhagetconfig post / Get skandha config Example values you use to demo the API: ```json theme={null} { "id": 3, "method": "skandha_config" } ``` Example response: ```json theme={null} { "id": 3, "result": { "chainId": 137, "flags": { "testingMode": false, "redirectRpc": true }, "entryPoints": [ "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789" ], "beneficiary": "0xdCdD0DDEaA0407C26DFcD481De9A34e1C55F8d54", "relayers": [ "0xdCdD0DDEaA0407C26DFcD481De9A34e1C55F8d54" ], "minInclusionDenominator": 10, "throttlingSlack": 10, "banSlack": 50, "minStake": { "type": "BigNumber", "hex": "0x00" }, "minUnstakeDelay": 0, "minSignerBalance": "0.1 eth", "multicall": "0xcA11bde05977b3631167028862bE2a173976CA11", "estimationStaticBuffer": 35000, "validationGasLimit": 10000000, "receiptLookupRange": 1024, "etherscanApiKey": false, "conditionalTransactions": false, "rpcEndpointSubmit": true, "gasPriceMarkup": 2000, "enforceGasPrice": false, "enforceGasPriceThreshold": 1000, "eip2930": false, "useropsTTL": 300, "whitelistedEntities": { "paymaster": [ "0xa683b47e447de6c8a007d9e294e87b6db333eb18", "0x474ea64bedde53aad1084210bd60eef2989bf80f", "0xe93eca6595fe94091dc1af46aac2a8b5d7990770", "0x3870419ba2bbf0127060bcb37f69a1b1c090992b", "0xfb8a7d1786e01f31fc6466a48243ca9ff0820ccb" ], "account": [], "factory": [ "0x7f6d8f107fe8551160bd5351d5f1514a6ad5d40e" ] }, "bundleGasLimitMarkup": 25000, "relayingMode": "kolibri", "bundleInterval": 10000, "bundleSize": 4, "pvgMarkup": 50000, "skipBundleValidation": false } } ``` # eth_getUserOperationByHash Source: https://etherspot.fyi/api-endpoints/skandha/api-reference/get-userop-by-hash skandhagetuseropbyhash post / Get UserOp by hash Example values you use to demo the API: ```json theme={null} { "id": 3, "method": "eth_getUserOperationByHash", "params": [ "0xd5925a6e45370570e10d134b904817b4cf0b82346bdf066ae4f13aecbbc36789" ] } ``` Example response: ```json theme={null} { "id": 3, "result": { "userOperation": { "sender": "0x86df74bC5afE17743A9d54E7ebd1171A7F7C958c", "nonce": "0x1e", "initCode": "0x", "callData": "0x9e5d4c49000000000000000000000000163f88becdf706499023d4364fd9c4fe51a032830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e4f62ded810000000000000000000000000dfca6af8663914e6923d4eba5fca7ffcdd5b24300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000418161ccb46373a31b5a6d3f4434996bf41e0117d80b7c129424485982b0e59e4647d2ba17bfc8d1f8be04f75e8e57829d9f5045f1734da0ac4855ef9c4f4f893b1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "callGasLimit": "0xc1bf", "verificationGasLimit": "0x12580", "preVerificationGas": "0x1108f", "maxFeePerGas": "0xfa29e3f70", "maxPriorityFeePerGas": "0x861c46800", "paymasterAndData": "0x000031dd6d9d3a133e663660b959162870d755d40000000000000000000000003b03c8e69522d5d3caea3d321047dc213470053900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041f4c091431612d7ab9462b7fb7b08a0825c9235dae5fb0de07f338b54f3c055ed231bcc16983e992d85ef624239546712172a7cd0c4f3cda20fedc507cc2107f11c00000000000000000000000000000000000000000000000000000000000000", "signature": "0xcafda1635ebc64b157140d14c1bf81f138988dd12003f3b6daa56b760481c9a271819b1a82e95a2d8784469a0a96b16616665f033451b362ffee7161e5efb7091b" }, "entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead", "blockNumber": "0x345eccc" } } ``` # eth_getUserOperationReceipt Source: https://etherspot.fyi/api-endpoints/skandha/api-reference/get-userop-receipt skandhagetuseropreceipt post / Get UserOp receipt Example values you use to demo the API: ```json theme={null} { "id": 3, "method": "eth_getUserOperationReceipt", "params": [ "0xd5925a6e45370570e10d134b904817b4cf0b82346bdf066ae4f13aecbbc36789" ] } ``` Example response: ```json theme={null} { "id": 3, "result": { "userOpHash": "0xd5925a6e45370570e10d134b904817b4cf0b82346bdf066ae4f13aecbbc36789", "sender": "0x86df74bC5afE17743A9d54E7ebd1171A7F7C958c", "nonce": "0x1e", "actualGasCost": "0x2890e066ae0993", "actualGasUsed": "0x2ad7d", "success": true, "logs": [ { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "topics": [ "0xbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972" ], "data": "0x", "logIndex": "0xe4", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x473989BF6409D21f8A7Fdd7133a40F9251cC1839", "topics": [ "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb", "0x000000000000000000000000163f88becdf706499023d4364fd9c4fe51a03283", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000dfca6af8663914e6923d4eba5fca7ffcdd5b243" ], "data": "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", "logIndex": "0xe5", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x163F88bEcDF706499023D4364Fd9C4FE51a03283", "topics": [ "0x4b9abc22f4c4dc79e3e5e71305668c0c487e00f351014a5f373472ba7e697bef" ], "data": "0x0000000000000000000000000dfca6af8663914e6923d4eba5fca7ffcdd5b24300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", "logIndex": "0xe6", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x000031DD6D9D3A133E663660b959162870D755D4", "topics": [ "0x5dc1c754041954fe976773fa441397a7928c7127a1c83904214a7d2563399007", "0x0000000000000000000000003b03c8e69522d5d3caea3d321047dc2134700539", "0x00000000000000000000000000000000000000000000000000289430c76a1adb" ], "data": "0x", "logIndex": "0xe7", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" } ], "receipt": { "to": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "from": "0x683aD32Fe9aE436654b621fbd5fCd3747a86D9c1", "contractAddress": null, "transactionIndex": "0x3e", "gasUsed": "0x2657a", "logsBloom": "0x000010000000000000000010000000000000400000000000000000000004014000080000000000000002201100000000011080000000000000200200020000001000000000000000000000000000008000000000004000000001000800000400000004000a0000024000040000000800000000801800104080880000000000000000000100000000010000000000000000000000000000000000000000000002280000000400000000400000400001000000080000000000000002000000004001000000000008000001000000400000004408000000800800108001000020000000000000000100000000000100000400000000000000000000000002100000", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "logs": [ { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "topics": [ "0xbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972" ], "data": "0x", "logIndex": "0xe4", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x473989BF6409D21f8A7Fdd7133a40F9251cC1839", "topics": [ "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb", "0x000000000000000000000000163f88becdf706499023d4364fd9c4fe51a03283", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000dfca6af8663914e6923d4eba5fca7ffcdd5b243" ], "data": "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", "logIndex": "0xe5", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x163F88bEcDF706499023D4364Fd9C4FE51a03283", "topics": [ "0x4b9abc22f4c4dc79e3e5e71305668c0c487e00f351014a5f373472ba7e697bef" ], "data": "0x0000000000000000000000000dfca6af8663914e6923d4eba5fca7ffcdd5b24300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", "logIndex": "0xe6", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x000031DD6D9D3A133E663660b959162870D755D4", "topics": [ "0x5dc1c754041954fe976773fa441397a7928c7127a1c83904214a7d2563399007", "0x0000000000000000000000003b03c8e69522d5d3caea3d321047dc2134700539", "0x00000000000000000000000000000000000000000000000000289430c76a1adb" ], "data": "0x", "logIndex": "0xe7", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "topics": [ "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f", "0xd5925a6e45370570e10d134b904817b4cf0b82346bdf066ae4f13aecbbc36789", "0x00000000000000000000000086df74bc5afe17743a9d54e7ebd1171a7f7c958c", "0x000000000000000000000000000031dd6d9d3a133e663660b959162870d755d4" ], "data": "0x000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000002890e066ae0993000000000000000000000000000000000000000000000000000000000002ad7d", "logIndex": "0xe8", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x0000000000000000000000000000000000001010", "topics": [ "0xe6497e3ee548a3372136af2fcb0696db31fc6cf20260707645068bd3fe97f3c4", "0x0000000000000000000000000000000000000000000000000000000000001010", "0x0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789", "0x000000000000000000000000683ad32fe9ae436654b621fbd5fcd3747a86d9c1" ], "data": "0x000000000000000000000000000000000000000000000000002890e066ae0993000000000000000000000000000000000000000000000d73fb7352b8ad3a4d0d0000000000000000000000000000000000000000000000020a51910b51bf50a1000000000000000000000000000000000000000000000d73fb4ac1d8468c437a0000000000000000000000000000000000000000000000020a7a21ebb86d5a34", "logIndex": "0xe9", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" }, { "transactionIndex": "0x3e", "blockNumber": "0x345eccc", "transactionHash": "0x337202cf2860b796728ed374035980ca39a151840d597b896e18e5e937bf645c", "address": "0x0000000000000000000000000000000000001010", "topics": [ "0x4dfe1bbbcf077ddc3e01291eea2d5c70c2b422b415d95645b9adcfd678cb1d63", "0x0000000000000000000000000000000000000000000000000000000000001010", "0x000000000000000000000000683ad32fe9ae436654b621fbd5fcd3747a86d9c1", "0x0000000000000000000000007c7379531b2aee82e4ca06d4175d13b9cbeafd49" ], "data": "0x00000000000000000000000000000000000000000000000000141619e4a190000000000000000000000000000000000000000000000000020af9798ff95de62b00000000000000000000000000000000000000000002da3d6b687df2b13d89b60000000000000000000000000000000000000000000000020ae5637614bc562b00000000000000000000000000000000000000000002da3d6b7c940c95df19b6", "logIndex": "0xea", "blockHash": "0x9ed5d4aa90f5b174e99deb7ea2bcc341f877ca78fb599ac36097568d9c35fead" } ], "blockNumber": "0x345eccc", "confirmations": "0x428", "cumulativeGasUsed": "0x88ab12", "effectiveGasPrice": "0xf264c804f", "status": "0x1", "type": "0x2", "byzantium": true } } } ``` # eth_sendUserOperation Source: https://etherspot.fyi/api-endpoints/skandha/api-reference/send-userop skandhasenduserop post / Submit User Operation to be included on-chain Example values you use to demo the API: ```json theme={null} { "jsonrpc": "2.0", "method": "eth_sendUserOperation", "params": [ { "sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45", "nonce":"0x0", "initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000", "callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "callGasLimit":"0x0", "verificationGasLimit":"0x0", "preVerificationGas":"0x0", "maxPriorityFeePerGas":"0x3b9aca00", "maxFeePerGas":"0x7a5cf70d5", "paymasterAndData":"0x", "signature":"0x00000000fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" ], "id": 1 } ``` Example response: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": "0x4c31ae84205a9c862dd8d0822f427fb516448451850ee6f65351951f6a2b2154" } ``` # Manually Submit UserOp Source: https://etherspot.fyi/api-endpoints/skandha/api-reference/submit-user-op Along with the other api calls we can make to Skandha, users can manually submit a UserOperation by making a POST request to one of our bundlers. With a valid UserOp created, we pass this into the params field along with the entrypoint contract address. Example curl request: ``` curl --request POST \ --url https://testnet-rpc.etherspot.io/v1/114 \ --header "Content-Type: application/json" \ --data '{ "params": [ { "sender": "0x6cAe98A0039D336EF56404917418e4De4BA300F2", "nonce": { "type": "BigNumber", "hex": "0x00" }, "initCode": "0x7f6d8f107fe8551160bd5351d5f1514a6ad5d40e5fbfb9cf0000000000000000000000007a4d44f341e4fcbafdaa81ba993d8d0e5db21ade0000000000000000000000000000000000000000000000000000000000000000", "callData": "0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000725404c8eead111d9e6dfe118c535f43402a951100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", "callGasLimit": { "type": "BigNumber", "hex": "0xb957" }, "verificationGasLimit": { "type": "BigNumber", "hex": "0x04d60d" }, "maxFeePerGas": "0xbfda3a300", "maxPriorityFeePerGas": "0x59682f00", "paymasterAndData": "0x", "preVerificationGas": { "type": "BigNumber", "hex": "0xb23c" }, "signature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" ], "method": "eth_sendUserOperation" }' ``` On successful submission to the bundler we will receive a UserOp hash. ```json theme={null} {"result":"0x7b1d33806fcf983834da03abb71a75ac242076aacfa8b376dbc50565ca77f72e"} ``` The transaction will then be processed 30-40 seconds after this. # addStake Source: https://etherspot.fyi/arka/api-calls/add-stake arka post /addStake Add Stake Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "84532", "params": ["EPV_07", "0.01"] } ``` Example response: ```json theme={null} { "message": "Successfully staked with transaction Hash 0x..." } ``` # checkWhitelist Source: https://etherspot.fyi/arka/api-calls/check-an-address-is-whitelisted arka post /checkWhitelist Check an address is whitelisted Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": [ "0x725404c8Eead111d9E6DFE118c535F43402a9511" ]} ``` Example response: ```json theme={null} { "message": "Already added" } ``` # checkWhitelist/v2 Source: https://etherspot.fyi/arka/api-calls/check-an-address-is-whitelisted-v2 arka post /checkWhitelist/v2 Check an address is whitelisted v2 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": [ "0x725404c8Eead111d9E6DFE118c535F43402a9511", ]} ``` Example response: ```json theme={null} { "message": "Already added" } ``` # getAllCommonERC20PaymasterAddress Source: https://etherspot.fyi/arka/api-calls/check-erc20-paymaster-address arka post /getAllCommonERC20PaymasterAddress Check ERC20 Paymaster address Example values you can use to demo the API: ```json theme={null} { "params": ["0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"] } ``` Example response: ```json theme={null} { "message": "[{"paymasterAddress":"0x33fA8047AaeE6b0571002F076a1bce3f00DA7301","gasToken":"0xC168E40227E4ebD8C1caE80F7a55a4F0e6D66C97","chainId":137,"decimals":18},...]" } ``` # addPolicy Source: https://etherspot.fyi/arka/api-calls/create-sponsorship-policy arka post /addPolicy Creates a new policy with the provided details. Example values you can use to demo the API: ```json theme={null} { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": ["EPV_06", "EPV_07"], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": 5000, "globalMaximumNative": 1000, "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": 100, "perUserMaximumNative": 200, "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": 10, "perOpMaximumNative": 20 } ``` Example response: ```json theme={null} { "createdAt": "2024-06-30T17:39:26.938Z", "updatedAt": "2024-06-30T17:39:26.939Z", "id": 5, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null } ``` # deletePolicy/{id} Source: https://etherspot.fyi/arka/api-calls/delete-sponsorship-policy arka delete /deletePolicy/{id} Deletes a policy by its ID. Example values you can use to demo the API: ```json theme={null} { "headers": { "apikey": "your_api_key_here" }, "pathVariables": { "id": "policy_id_here" } } ``` Example response: ```json theme={null} { "message": "Policy deleted successfully." } ``` # deployVerifyingPaymaster Source: https://etherspot.fyi/arka/api-calls/deploy-verifying-paymaster arka post /deployVerifyingPaymaster Deploy Verifying Paymaster Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "84532", "params": ["EPV_07"] } ``` Example response: ```json theme={null} { "verifyingPaymaster": "0xfe08B548Bd73F2559B1c759A6F41914a09456461", "txHash": "0x28ed5b8fce76b310d255953ba5ac3032730825da2389e7bfb11c7fb047d72e61" } ``` # deposit Source: https://etherspot.fyi/arka/api-calls/deposit-to-paymaster arka post /deposit Deposit to paymaster Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": ["0.000001"] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully deposited with transaction Hash 0x79137319a6c9c67827b67dbbc16c0729465305516da4788bce578d5fdf59a52e" } ``` # deposit/v2 Source: https://etherspot.fyi/arka/api-calls/deposit-to-paymaster-v2 arka post /deposit/v2 Deposit to paymaster v2 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": ["0.000001"] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully deposited with transaction Hash 0x79137319a6c9c67827b67dbbc16c0729465305516da4788bce578d5fdf59a52e" } ``` # disablePolicy/{id} Source: https://etherspot.fyi/arka/api-calls/disable-sponsorship-policy arka put /disablePolicy/{id} Disables an existing sponsorship policy by its ID. Example values you can use to demo the API: ```json theme={null} { "headers": { "apikey": "your_api_key_here" }, "pathVariables": { "id": "policy_id_here" } } ``` Example response: ```json theme={null} { "message": "Policy disabled successfully." } ``` # enablePolicy/{id} Source: https://etherspot.fyi/arka/api-calls/enable-sponsorship-policy arka put /enablePolicy/{id} Enables an existing sponsorship policy by its ID. Example values you can use to demo the API: ```json theme={null} { "headers": { "apikey": "your_api_key_here" }, "pathVariables": { "id": "policy_id_here" } } ``` Example response: ```json theme={null} { "message": "Policy enabled successfully." } ``` # getAllWhitelist/v2 Source: https://etherspot.fyi/arka/api-calls/get-all-whitelist-v2 arka post /getAllWhitelist/v2 Get all Whitelist addresses v2 Example values you can use to demo the API: ```json theme={null} { "chainId": "80002", "apiKey": "etherspot_public_key", "params": [ "1", ] } ``` Example response: ```json theme={null} Value returned: { "addresses": [ "0x725404c8Eead111d9E6DFE118c535F43402a9511" ] } ``` # pm_getERC20TokenQuotes Source: https://etherspot.fyi/arka/api-calls/get-erc20-token-quotes arkageterc20tokenquotes post / get token exchange quotes Example values you use to demo the API: ```json theme={null} { "jsonrpc": "2.0", "method": "pm_getERC20TokenQuotes", "params": [ { "sender": "0xB3aF6CFDDc444B948132753AD8214a20605692eF", "nonce": "0x6", "initCode": "0x", "callData": "0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000080a1874e1046b1cc5defdf4d3153838b72ff94ac0000000000000000000000000fd9e8d3af1aaee056eb9e802c3a762a667b1904000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000005af3107a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000386ed13ba07e1c409693b299bbb9839d05af20c3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000", "callGasLimit": "0x88b8", "verificationGasLimit": "0x186a0", "maxFeePerGas": "0x8dcac078f", "maxPriorityFeePerGas": "0x8dcac078f", "paymasterAndData": "0x0101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000001010101010100000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101", "signature": "0x", "preVerificationGas": "0xcb78" }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", [{ token: "0x0Fd9e8d3aF1aaee056EB9e802c3A762a667b1904" }] ], "id": 1 } ``` Example response: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { etherUSDExchangeRate: "0x1e" paymasterAddress: "0x386eD13Ba07E1C409693b299BbB9839d05aF20c3" gasEstimates: { "preVerificationGas": "0xc01c", "verificationGasLimit": "0xf4f1", "callGasLimit": "0x15971" } feeEstimates: { "maxFeePerGas": "0x8dcac078f", "maxPriorityFeePerGas": "0x8dcac078f" } quotes: [ { "token": "0x0Fd9e8d3aF1aaee056EB9e802c3A762a667b1904", "symbol": "LINK", "decimals": 18, "etherTokenExchangeRate": "0x01a055690d9db80000", "serviceFeePercent": 15 } ] unsupportedTokens: [] } } ``` # policy/walletAddress/{walletAddress}/latest Source: https://etherspot.fyi/arka/api-calls/get-latest-sponsorship-policy-by-walletaddress arka get /policy/walletAddress/{walletAddress}/latest Retrieves the latest sponsorship policy by wallet address. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "walletAddress" } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # policy/walletAddress/{walletAddress}/chainId/{chainId}/latest Source: https://etherspot.fyi/arka/api-calls/get-latest-sponsorship-policy-by-walletaddress-chainid arka get /policy/walletAddress/{walletAddress}/chainId/{chainId}/latest Retrieves the latest sponsorship policy by wallet address and chain ID. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "chainId": 80002 } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # policy/walletAddress/{walletAddress}/epVersion/{epVersion}/chainId/{chainId}/latest Source: https://etherspot.fyi/arka/api-calls/get-latest-sponsorship-policy-by-walletaddress-epv-chainid arka get /policy/walletAddress/{walletAddress}/epVersion/{epVersion}/chainId/{chainId}/latest Retrieves the latest sponsorship policy for a given wallet address, EP version, and chain ID. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "epVersion": "EPV_06", "chainId": 80002 } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # policy/walletAddress/{walletAddress}/epVersion/{epVersion}/latest Source: https://etherspot.fyi/arka/api-calls/get-latest-sponsorship-policy-by-walletaddress-epversion arka get /policy/walletAddress/{walletAddress}/epVersion/{epVersion}/latest Retrieves the latest sponsorship policy by wallet address and EP version. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "epVersion": "EPV_06" } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # metadata Source: https://etherspot.fyi/arka/api-calls/get-metadata arka get /metadata Return paymaster Metadata for EP6 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", } ``` Example response: ```json theme={null} { "sponsorAddress": "0xaeAF09795d8C0e6fA4bB5f89dc9c15EC02021567", "sponsorWalletBalance": { "type": "BigNumber", "hex": "0x1ed81fac4b400e4d" }, "chainsSupported": [ 5, 114, 420, 84532, 84531, 421613, 534351, 11155111, ], "tokenPaymasters": { "1": { "USDC": "0x0000000000fABFA8079AB313D1D14Dcf4D15582a" }, "10": { "USDC": "0x0000000000fce6614d3c6f679e48c9cdd09aa634" }, "56": { "USDC": "0x0000000000db7995889f54d72dac9d36a9f7f467" }, } } ``` # metadata/v2 Source: https://etherspot.fyi/arka/api-calls/get-metadata-v2 arka get /metadata/v2 Return paymaster Metadata for EP7 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", } ``` Example response: ```json theme={null} { "sponsorAddress": "0xaeAF09795d8C0e6fA4bB5f89dc9c15EC02021567", "sponsorWalletBalance": { "type": "BigNumber", "hex": "0x1ed81fac4b400e4d" }, "chainsSupported": [ 5, 114, 420, 84532, 84531, 421613, 534351, 11155111, ], "tokenPaymasters": { "1": { "USDC": "0x0000000000fABFA8079AB313D1D14Dcf4D15582a" }, "10": { "USDC": "0x0000000000fce6614d3c6f679e48c9cdd09aa634" }, "56": { "USDC": "0x0000000000db7995889f54d72dac9d36a9f7f467" }, } } ``` # policy Source: https://etherspot.fyi/arka/api-calls/get-sponsorship-policies arka get /policy Retrieves all existing sponsorship policies, sorted by creation time in descending order. Example response: ```json theme={null} [ { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" }, { "id": 1, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "6000.0000", "globalMaximumNative": "2000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:49:08.252Z", "updatedAt": "2024-06-27T19:38:39.674Z" } ] ``` # policy/walletAddress/{walletAddress} Source: https://etherspot.fyi/arka/api-calls/get-sponsorship-policies-by-walletaddress arka get /policy/walletAddress/{walletAddress} Retrieves all sponsorship policies associated with a wallet address. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "walletAddress" } } ``` Example response: ```json theme={null} [ { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" }, { "id": 1, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "6000.0000", "globalMaximumNative": "2000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:49:08.252Z", "updatedAt": "2024-06-27T19:38:39.674Z" } ] ``` # policy/walletAddress/{walletAddress}/epVersion/{epVersion} Source: https://etherspot.fyi/arka/api-calls/get-sponsorship-policies-by-walletaddress-epversion arka get /policy/walletAddress/{walletAddress}/epVersion/{epVersion} Retrieves all sponsorship policies by wallet address and EP version. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "epVersion": "EPV_07" } } ``` Example response: ```json theme={null} [ { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" }, { "id": 1, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "6000.0000", "globalMaximumNative": "2000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:49:08.252Z", "updatedAt": "2024-06-27T19:38:39.674Z" } ] ``` # policy/walletAddress/{walletAddress}/epVersion/{epVersion}/chainId/{chainId} Source: https://etherspot.fyi/arka/api-calls/get-sponsorship-policies-by-walletaddress-epversion-chainid arka get /policy/walletAddress/{walletAddress}/epVersion/{epVersion}/chainId/{chainId} Retrieves all sponsorship policies for a given wallet address, EP version, and chain ID. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "epVersion": "EPV_07", "chainId": 80002 } } ``` Example response: ```json theme={null} [ { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" }, { "id": 1, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "6000.0000", "globalMaximumNative": "2000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:49:08.252Z", "updatedAt": "2024-06-27T19:38:39.674Z" } ] ``` # policy/{id} Source: https://etherspot.fyi/arka/api-calls/get-sponsorship-policy-by-id arka get /policy/{id} Retrieves a sponsorship policy by its unique ID. Example values you can use to demo the API: ```json theme={null} { "pathVariables": { "id": "policy_id_here" } } ``` Example response: ```json theme={null} { "id": 2, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-24T00:40:00.000Z", "endTime": "2024-06-25T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null, "createdAt": "2024-06-26T19:50:09.475Z", "updatedAt": "2024-06-26T19:50:09.475Z" } ``` # removeWhitelist Source: https://etherspot.fyi/arka/api-calls/remove-address-from-whitelist arka post /removeWhitelist Remove address from whitelist Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": ["0x725404c8Eead111d9E6DFE118c535F43402a9511"] } ``` Example response: ```json theme={null} { "message": "Successfully removed whitelisted addresses with transaction Hash 0x0f87967a28230efd3624ddbd9b33887d1324887364576b90d9d6c0fe78fc0a67" } ``` # removeWhitelist/v2 Source: https://etherspot.fyi/arka/api-calls/remove-address-from-whitelist-v2 arka post /removeWhitelist/v2 Remove address from whitelist v2 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": [ ["0x725404c8Eead111d9E6DFE118c535F43402a9511"] ] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully removed whitelisted addresses" } ``` # pm_sponsorUserOperation Source: https://etherspot.fyi/arka/api-calls/sponsor-user-operation arkasponsoruseroperation post / Sponsor a userOps Example values you use to demo the API: ```json theme={null} { "jsonrpc": "2.0", "method": "pm_sponsorUserOperation", "params": [ { "sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45", "nonce":"0x0", "initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000", "callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "callGasLimit":"0x0", "verificationGasLimit":"0x0", "preVerificationGas":"0x0", "maxPriorityFeePerGas":"0x3b9aca00", "maxFeePerGas":"0x7a5cf70d5", "paymasterAndData":"0x", "signature":"0x00000000fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" }, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", { "mode": 'sponsor' } ], "id": 1 } ``` Example response: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "paymasterAndData": "0x26fec24b0d467c9de105217b483931e8f944ff500000000000000000000000000000000000000000000000000000000066ebe8b80000000000000000000000000000000000000000000000000000000066ebceb4b5b1df6daf753a0017ac66af78cf10aad42803f86efceff5db656890034e588e10c33ca89eab9ecaef9f0188303b3a3674299fc5208684f9d4de7f34202df1521c", "verificationGasLimit": "0xe967", "preVerificationGas": "0x18810", "callGasLimit": "0x28ede", } } ``` # updatePolicy Source: https://etherspot.fyi/arka/api-calls/update-sponsorship-policy arka put /updatePolicy Updates an existing sponsorship policy based on the provided information. Example values you can use to demo the API: ```json theme={null} { "id": "1", "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy modified", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": ["EPV_06", "EPV_07"], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": 6000, "globalMaximumNative": 2000, "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": 100, "perUserMaximumNative": 200, "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": 10, "perOpMaximumNative": 20 } ``` Example response: ```json theme={null} { "createdAt": "2024-06-30T17:39:26.938Z", "updatedAt": "2024-06-30T17:39:26.939Z", "id": 5, "walletAddress": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "name": "Sample Policy", "description": "This is a sample policy", "isPublic": true, "isEnabled": true, "isApplicableToAllNetworks": false, "enabledChains": [ 80002 ], "supportedEPVersions": [ "EPV_06", "EPV_07" ], "isPerpetual": false, "startTime": "2024-06-26T00:40:00.000Z", "endTime": "2024-06-27T23:59:59.999Z", "globalMaximumApplicable": true, "globalMaximumUsd": "5000.0000", "globalMaximumNative": "1000.000000000000000000", "globalMaximumOpCount": 1000, "perUserMaximumApplicable": true, "perUserMaximumUsd": "100.0000", "perUserMaximumNative": "200.000000000000000000", "perUserMaximumOpCount": 50, "perOpMaximumApplicable": true, "perOpMaximumUsd": "10.0000", "perOpMaximumNative": "20.000000000000000000", "addressAllowList": null, "addressBlockList": null } ``` # whitelist Source: https://etherspot.fyi/arka/api-calls/whitelist-an-address arka post /whitelist Whitelist an address Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": ["0x725404c8Eead111d9E6DFE118c535F43402a9511"] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully whitelisted with transaction Hash 0x91575dc46283aee04bab45763b5ea77250ac84232d6a38095f8ed08d1feef5d3" } ``` # whitelist/v2 Source: https://etherspot.fyi/arka/api-calls/whitelist-an-address-v2 arka post /whitelist/v2 Whitelist an address v2 Example values you can use to demo the API: ```json theme={null} { "apiKey": "etherspot_public_key", "chainId": "11155111", "params": [ ["0x725404c8Eead111d9E6DFE118c535F43402a9511"] ] } ``` Example response: ```json theme={null} Value returned: { "message": "Successfully whitelisted" } ``` # Deposit to Arka Source: https://etherspot.fyi/arka/deposit ## Deposit In short the deposit flow is as follows: 1. Create ticket on Discord to get api key and public address to top up to. 2. Send funds to public address provided. 3. Call [the deposit api](/arka/api-calls/deposit-to-paymaster) 4. Call [whitelist api](/arka/api-calls/whitelist-an-address) to whitelist an address. 5. Add api key to estimate function like so: ```javascript theme={null} const CHAIN_ID = 11155111; const ARKA_API_KEY = "etherspot_public_key"; const op = await primeSdk.estimate({ url: `https://rpc.etherspot.io/paymaster?apiKey=${ARKA_API_KEY}&chainId=${CHAIN_ID}`, context: { mode: 'sponsor' } }); ``` Now any transactions created like this will be sponsored for this address. # Introduction to Arka Source: https://etherspot.fyi/arka/intro ## Intro Arka is Etherspot's implementation of a [paymaster](https://github.com/eth-infinitism/account-abstraction/blob/ver0.6.0/contracts/interfaces/IPaymaster.sol). This means we can let the user pay with ERC-20 tokens, or pay for specific user transactions ourselves. ## Using Arka When estimating a transaction with the SDK we can pass in paymaster values and choose one of three modes: 1. sponsor 2. erc20 3. multitoken For either mode we need to pass the parameter 'url' inside estimate fn of the primeSdk in order for the sdk to know that the userOp needs to be sponsored. In this url we include the **url of the Arka instance**, the [**apiKey**](/prime-sdk/api-key-portal), and the [**chainId**](prime-sdk/chains-supported). > **Note:** The 'useVp' parameter is passed so that the system uses the deployed Verifying Paymaster that you deployed using developer dashboard if not it will error out as 'Paymaster not deployed' message. > And by default, the value is false and will use EtherspotPaymaster contract which is only for EntryPoint v6 We can create the arguments like so: ```javascript theme={null} const ARKA_API_KEY = 'etherspot_public_key'; // replace with your arka api key const ARKA_URL = 'https://rpc.etherspot.io/paymaster/'; const CHAIN_ID = 137; const queryString = `?apiKey=${ARKA_API_KEY}&chainId=${CHAIN_ID}&useVp=true`; ``` ## mode: 'sponsor' This works by having Arka pay for the transaction fee. ```javascript theme={null} await primeSdk.estimate({ url: `${ARKA_URL}${queryString}`, context: { mode: 'sponsor' } }); ``` ### validUntil / validAfter **validUntil and validAfter are relevant only with mode: sponsor transactions and not for mode: erc20.** validUntil and validAfter are optional defaults to 10 mins of expiry from send call and should be passed in terms of milliseconds. For example purpose, the valid is fixed as expiring in 100 mins once the paymaster data is generated. ```javascript theme={null} await primeSdk.estimate({ url: `${ARKA_URL}${queryString}`, context: { mode: 'sponsor', validAfter: new Date().valueOf(), validUntil: new Date().valueOf() + 6000000 } }); ``` ## mode: 'erc20' This works by having the gas fee paid with whatever token is specified. ```javascript theme={null} await primeSdk.estimate({ url: `${ARKA_URL}${queryString}`, context: { token: "USDC", mode: 'erc20' } }); ``` ## mode: 'multitoken' This works by having the gas fees paid with whatever token is specified but unlike erc20, each chain would have the same paymaster address across different tokens supported on that contract. ```javascript theme={null} await primeSdk.estimate({ url: `${ARKA_URL}${queryString}`, context: { token: "0x453478E2E0c846c069e544405d5877086960BEf2", mode: 'multitoken' } }); ``` Please note that the above token is from Ancient8 testnet(28122024) which is available on `etherspot_public_key` apiKey. Request `0x453478E2E0c846c069e544405d5877086960BEf2` tokens if you unable to fetch it on [discord](https://discord.etherspot.io/) Please [get in touch](https://discord.etherspot.io/) if you wish to get an api key for development. # Pay for gas with ERC20 tokens using MultiTokenPaymaster Source: https://etherspot.fyi/arka/pay-with-erc20-multiToken Using token paymasters we have the ability to pay for gas with whatever ERC20 tokens the paymaster supports. We can do this in two steps: 1. Fetch the paymaster address and see if the MultiTokenPaymaster supports the particular token. 2. Approve the tokens we wish to pay for gas with if exists and when estimating the transaction include the Arka and ERC20 parameters and send it to the bundler as a single batch transaction. We can do this like so: ```typescript theme={null} const ARKA_API_KEY = ''; // insert your api key here const TOKEN_ADDRESS = "0xC168E40227E4ebD8C1caE80F7a55a4F0e6D66C97"; // token on polygon const CHAIN_ID = 137; const ARKA_URL = 'https://rpc.etherspot.io/paymaster'; const returnedValue = await fetch(`${ARKA_URL}/getAllCommonERC20PaymasterAddress?apiKey=${ARKA_API_KEY}&chainId=${CHAIN_ID}`, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ "params": ["0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"] }) }) .then((res) => { return res.json() }).catch((err) => { console.log(err); }); const returnData: { chainId: number; paymasterAddress: string; gasToken: string; decimals: number; }[] = JSON.parse(returnedValue.message); // List of all paymasters available const paymasterDetails = returnData.filter((item: any) => item.chainId === chainId && item.gasToken === tokenAddress); if (paymasterDetails.length < 0) throw new Error('Token not supported'); const paymasterAddress = paymasterDetails[0].paymasterAddress; ``` From the above list of multiTokenPaymasters, check if your token is supported. If so, continue to the next step after taking the paymaster address associated with your token address. Note: ERC20\_ABI is the ERC20 ABI for the token you want to pay for gas with. You can copy from [here](https://gist.github.com/veox/8800debbf56e24718f9f483e1e40c35c#file-erc20-abi-json) ```typescript theme={null} const erc20Contract = new ethers.Contract(TOKEN_ADDRESS, ERC20_ABI) const encodedData = erc20Contract.interface.encodeFunctionData('approve', [paymasterAddress, ethers.utils.parseEther('1')]) // Approval for 1 ETH await primeSdk.addUserOpsToBatch({ to: TOKEN_ADDRESS, data: encodedData }); ``` Once the tokens are approved we can create whatever transactions we want, then estimate them before sending like this: ```typescript theme={null} const op = await primeSdk.estimate( { url: "https://rpc.etherspot.io/paymaster?apiKey=${ARKA_API_KEY}&chainId=${CHAIN_ID}", context: { token: TOKEN_ADDRESS, mode: 'commonerc20' } }); ``` And send the estimated transactions in a batch once you are okay with the estimated gas fee ```typescript theme={null} await primeSdk.send(op); ``` If you still face any issues, please reach out to us on [Discord](https://discord.gg/jxu75XeY). # Sponsor A Transaction Source: https://etherspot.fyi/arka/sponsor-a-transaction The below example shows full working code of a transaction being sponsored. > **Note:** The 'useVp' parameter is passed so that the system uses the deployed Verifying Paymaster that you deployed using developer dashboard if not it will error out as 'Paymaster not deployed' message. > And by default, the value is false and will use EtherspotPaymaster contract which is only for EntryPoint v6 You must change the values in **primeSdk.estimate()** with your own API key. ```javascript theme={null} import { ethers } from 'ethers'; import { PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); const ARKA_API_KEY = '' // replace with your arka api key const CHAIN_ID = 137; // replace with your desired chain id const RECIPIENT = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address const VALUE = '0.01'; // transfer value async function main() { // initializing sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(CHAIN_ID), }) console.log('address: ', primeSdk.state.walletAddress) // get address of EtherspotWallet... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await primeSdk.addUserOpsToBatch({ to: RECIPIENT, value: ethers.utils.parseEther(VALUE) }); console.log('transactions: ', transactionBatch); // get balance of the account address const balance = await primeSdk.getNativeBalance(); console.log('balances: ', balance); // estimate transactions added to the batch and get the fee data for the UserOp const op = await primeSdk.estimate({ paymasterDetails: { url: "https://rpc.etherspot.io/paymaster?apiKey=${ARKA_API_KEY}&chainId=${CHAIN_ID}&useVp=true", context: { mode: "sponsor" }, }, }); console.log(`Estimate UserOp: ${await printOp(op)}`); // 'printOp' is a helper function to print the UserOp which is imported from etherspot package // sign the UserOp and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); // sleep 2 seconds userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Arka testnet Usage Source: https://etherspot.fyi/arka/testnets ## Using Arka When estimating a transaction with the SDK on testnets we support we can pass in the testnet api key value as **'etherspot\_public\_key'** and set the mode of the transaction to be sponsored like so: ```javascript theme={null} try { const CHAIN_ID = 11155111; const API_KEY = 'etherspot_public_key'; // replace this with your api key const op = await primeSdk.estimate({ url: `https://rpc.etherspot.io/paymaster?apiKey=${API_KEY}&chainId=${CHAIN_ID}&useVp=true`, context: { mode: 'sponsor' } }); } catch(err) { console.log(err); } ``` ### Supported testnets * Sepolia * Base Sepolia * Scroll Sepolia * Fuse Sparknet * Ancient8 Testnet * Flare Coston2 * Rootstock testnet This will use testnet funds and a paymaster we have specifically setup for devs to test out before trying sponsored transactions on mainnet. # Audits Source: https://etherspot.fyi/audits/audits Smart contract audits are of paramount importance in ensuring the security and reliability of blockchain-based systems. By conducting thorough code reviews and identifying potential vulnerabilities, audits help mitigate security risks, protect user funds, and maintain the integrity of the system. We have completed our first audit with a ERC4337 specialist and a second with [Nethermind](https://nethermind.io/smart-contract-audits/). You can read both audits [here on our Github.](https://github.com/etherspot/etherspot-prime-contracts/tree/master/audits) # RPC Playground Source: https://etherspot.fyi/developer-dashboard/rpc-playground This feature is available on all plans. ## Overview The [RPC Playground](https://developer.etherspot.io/dashboard/rpc-playground) provides a seamless interface for developers to interact with blockchain networks using JSON-RPC. It simplifies testing, debugging, and integrating blockchain functionalities directly from your Dashboard. ✅ Faster Development & Testing – Quickly execute and validate RPC calls without writing code. ✅ Simplified API Integration – Copy JSON-RPC requests and responses for easy implementation. ✅ Multi-Chain & Version Support – Test different blockchain networks on different API versions effortlessly. ✅ Real-Time Gas & Fee Insights – Retrieve gas prices, fee history, and user operations for transaction optimization. ✅ One-Click cURL Commands – Generate cURL requests to streamline API testing and integration. ✅ Interactive & Developer-Friendly – Modify parameters, analyze responses, and debug efficiently. What’s Included: * Network Selection: Choose from available testnets and mainnets. * API Key Management: Authenticate requests securely. * RPC Method Explorer: Browse and execute supported methods dynamically. * Live JSON Request & Response: Instantly view structured API interactions. * cURL code generation: Generate cURL commands for direct integration. * Docs & SDK Integration: Direct links for further reference and implementation. This tool significantly enhances developer productivity by offering an intuitive, interactive environment for blockchain API testing and validation. 🚀 ## Step-by-Step Guide ### 1. Click on RPC Playground RPC Playground Dashboard ### 2. Choose either Testnet or Mainnet Network Selection ### 3. Choose a chain you want to send request Chain Selection ### 4. Choose API version V1 supports EntryPoint 0.6.0 V2 supports EntryPoint 0.7.0 API Version Selection ### 5. Click on Select API Key API Key Selection ### 6. Choose your API key Choose API Key ### 7. Click on Send Request Send Request ### 8. Choose from the range of supported RPC methods RPC Methods ### 9. Generate cURL command Generate cURL ### 10. The code will be generated ready to be used Generated Code # API Key Security Settings Source: https://etherspot.fyi/developer-dashboard/security-settings This feature is exclusively available on paid plans (Developer, Scale, and Startup). Upgrade your plan to access these security settings. ## Overview API key security settings provide essential protection mechanisms for your API access. By configuring these security measures, you can: * **Control Access**: Restrict API usage to specific IP addresses, preventing unauthorized access from unknown locations * **Manage CORS**: Whitelist specific domains to enable secure cross-origin requests from your applications * **Secure UserOperations**: Limit which addresses can perform sensitive operations like sending transactions and estimating gas Implementing these security settings helps prevent unauthorized usage, protect against potential attacks, and ensure your API keys are only used as intended. ## Whitelist IP Addresses IP whitelisting is a crucial security measure that restricts API access to specific IP addresses. This ensures that only requests from trusted locations can use your API key. ### [1. Go to Developer Portal and click Settings](https://developer.etherspot.io/dashboard) Navigate to the settings section where you can manage your API key's security configurations. ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/a5bf827c-0d2d-4ec6-8582-3476b5f03f42/1.5/0/0.57517188566482?1) ### [2. Choose your API key you wan't to apply ](https://developer.etherspot.io/dashboard/api-keys/settings) Select the specific API key you want to configure security settings for. Each API key can have its own unique security configuration. ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/3b3c93a7-556c-4366-a772-3f75a8bb6a63/1/0/0?1) ### [3. Click Add button to whitelist IP Addresses](https://developer.etherspot.io/dashboard/api-keys/settings) Initiate the process of adding a new IP address to your whitelist. This opens the form where you can specify the trusted IP. ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/675daa83-945c-496e-8fec-92cdb1e3a919/2.5/96.825396825397/20.576671035387?1) ### [4. Enter IP address and click Create](https://developer.etherspot.io/dashboard/api-keys/settings) Specify the IP address you want to whitelist. This can be your server's IP, office IP, or any other trusted location. After adding, only requests from these IPs will be allowed. ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/bc2d4566-e787-4ea6-9203-650a00443497/2.5/50/50.655307994758?1) ## Whitelist Domains (CORS) Domain whitelisting enables cross-origin resource sharing (CORS) for specific domains, allowing your web applications to interact with the API securely. ### [1. Click "Allowed Origins"](https://developer.etherspot.io/dashboard/api-keys/settings) Access the domain whitelisting section to manage which websites can make requests using your API key. ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/3392ecf5-bb65-4ee1-9c0d-9b894382a7fa/2.5/57.936507936508/12.581913499345?1) ### [2. Click Add button](https://developer.etherspot.io/dashboard/api-keys/settings) Start the process of adding a new domain to your whitelist. This ensures your web applications can communicate with the API. ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/cec2ac9c-e27e-42e9-b2db-baa3712d64aa/2.5/96.825396825397/20.576671035387?1) ### [3. Enter domain and click Create](https://developer.etherspot.io/dashboard/api-keys/settings) Add the domain of your web application (e.g., [https://etherspot.io](https://etherspot.io)). Only requests from whitelisted domains will be allowed to interact with the API. Note: It has to be exaclty the same (with the https\://) ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/1bddddff-8ee0-4fd6-9b4e-b1ce575297d0/2.5/50/59.829619921363?1) ## Whitelist Sender Addresses Address whitelisting adds an extra layer of security by controlling which blockchain addresses can perform sensitive operations. ### [1. Click on Sender Addresses](https://developer.etherspot.io/dashboard/api-keys/settings) Access the address whitelisting section to manage which blockchain addresses can perform specific operations. This whitelisting applies to the following RPC methods: * **eth\_sendUserOperation** * **eth\_estimateUserOperationGas** * **pm\_getPaymasterData** * **pm\_sponsorUserOperation** * **pm\_getERC20TokenQuotes** ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/607b7097-58c6-45b8-8155-76ad7a97a5d5/2.5/96.825396825397/12.581913499345?1) ### [2. Add address and click Create](https://developer.etherspot.io/dashboard/api-keys/settings) Specify the blockchain address you want to whitelist. Only transactions and operations from these addresses will be processed, providing granular control over who can use specific RPC methods. ![](https://d3q7ie80jbiqey.cloudfront.net/media/image/zoom/09347627-7993-400b-9449-c6f1e24fa054/2.5/50/75.163826998689?1) # null Source: https://etherspot.fyi/introduction Everything you need to get started with Etherspot’s Account & Chain Abstraction infrastructure

Etherspot

Etherspot is a top-notch [Account & Chain Abstraction](/account-abstraction/accountabstraction) infrastructure designed to help developers create an unparalleled cross-chain user experience for their blockchain protocols on Ethereum and EVM-compatible chains. With Etherspot’s comprehensive tools, building a beautiful user experience in Web3 has never been easier!
Choose a tool: *Supports EntryPoint 0.7.0* To customise your smart accounts with ERC-7579 modules *Supports EntryPoint 0.6.0* The tools you need to build with AA *Coming soon* Build a seamless, cross-chain Web3 user experience with Etherspot Our ERC4337 compliant Bundler (Bundled Transactions) Our ERC4337 compliant Paymaster (Gasless & Sponsored Transactions) A React Library for seamless integration with our AA infra # Batching Transactions Source: https://etherspot.fyi/modular-sdk/batching-transactions Batching transactions is the ability to include a number of transactions within one block to save the user both gas, time, and clicks. Here we'll show a code example of how easy it is to batch transactions using the Modular SDK. We'll be using the **addUserOpsToBatch** function for this. ```javascript theme={null} import { ethers } from 'ethers'; import { ModularSdk } from '@etherspot/modular-sdk'; import { printOp } from '../src/sdk/common/OperationUtils'; const recipient1: string = '0x10a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient1 wallet address const recipient2: string = '0x20a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient2 wallet address const recipient3: string = '0x30a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient3 wallet address const value: string = '0.01'; // transfer value async function main() { // initializating sdk... const modularSdk = new ModularSdk( { privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey, customBundlerUrl) } ); // add transaction 1 to the batch let transactionBatch = await modularSdk.addUserOpsToBatch({to: recipient1, value: ethers.utils.parseEther(value)}); // add transaction 2 to the batch transactionBatch = await modularSdk.addUserOpsToBatch({to: recipient2, value: ethers.utils.parseEther(value)}); // add transaction 3 to the batch transactionBatch = await modularSdk.addUserOpsToBatch({to: recipient3, value: ethers.utils.parseEther(value)}); // estimate transactions added to the batch and get the fee data for the UserOp const op = await modularSdk.estimate(); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await modularSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Chains Supported Source: https://etherspot.fyi/modular-sdk/chains-supported We're always on the lookout for more EVM based networks who want to get setup with relevant Account Abstraction infrastructure. If you're a network who's interested in this, please [get in touch](https://discord.etherspot.io/). ## Mainnet Etherspot Moudlar SDK is currently usable on the following chains. The bundler URL can be copied here for each. These networks are now deprecated: Goerli, Mumbai, Base Goerli, Arbitrum Goerli, Optimism Goerli, Mantle Goerli. **ETH** 1 ``` https://rpc.etherspot.io/v2/1?api-key=APIKEY-HERE ``` **MATIC** 137 ``` https://rpc.etherspot.io/v2/137?api-key=APIKEY-HERE ``` **ETH** 10 ``` https://rpc.etherspot.io/v2/10?api-key=APIKEY-HERE ``` **ETH** 42161 ``` https://rpc.etherspot.io/v2/42161?api-key=APIKEY-HERE ``` **FUSE** 122 ``` https://rpc.etherspot.io/v2/122?api-key=APIKEY-HERE ``` **MNT** 5000 ``` https://rpc.etherspot.io/v2/5000?api-key=APIKEY-HERE ``` **XDAI** 100 ``` https://rpc.etherspot.io/v2/100?api-key=APIKEY-HERE ``` **ETH** 8453 ``` https://rpc.etherspot.io/v2/8453?api-key=APIKEY-HERE ``` **AVAX** 43114 ``` https://rpc.etherspot.io/v2/43114?api-key=APIKEY-HERE ``` **BNB** 56 ``` https://rpc.etherspot.io/v2/56?api-key=APIKEY-HERE ``` **ETH** 59144 ``` https://rpc.etherspot.io/v2/59144?api-key=APIKEY-HERE ``` **FLR** 14 ``` https://rpc.etherspot.io/v2/14?api-key=APIKEY-HERE ``` **ETH** 534352 ``` https://rpc.etherspot.io/v2/534352?api-key=APIKEY-HERE ``` **RBTC** 30 ``` https://rpc.etherspot.io/v2/30?api-key=APIKEY-HERE ``` **ETH** 888888888 ``` https://rpc.etherspot.io/v2/888888888?api-key=APIKEY-HERE ``` **XDC** 50 ``` https://rpc.etherspot.io/v2/50?api-key=APIKEY-HERE ``` **CELO** 42220 ``` https://rpc.etherspot.io/v2/42220?api-key=APIKEY-HERE ``` ## Testnets **MATIC** 80002 ``` https://testnet-rpc.etherspot.io/v2/80002?api-key=APIKEY-HERE ``` **ETH** 11155111 ``` https://testnet-rpc.etherspot.io/v2/11155111?api-key=APIKEY-HERE ``` **ETH** 84532 ``` https://testnet-rpc.etherspot.io/v2/84532?api-key=APIKEY-HERE ``` **ETH** 421614 ``` https://testnet-rpc.etherspot.io/v2/421614?api-key=APIKEY-HERE ``` **ETH** 11155420 ``` https://testnet-rpc.etherspot.io/v2/11155420?api-key=APIKEY-HERE ``` **ETH** 534351 ``` https://testnet-rpc.etherspot.io/v2/534351?api-key=APIKEY-HERE ``` **MNT** 5003 ``` https://testnet-rpc.etherspot.io/v2/5003?api-key=APIKEY-HERE ``` **C2FLR** 114 ``` https://testnet-rpc.etherspot.io/v2/114?api-key=APIKEY-HERE ``` **SPARK** 123 ``` https://testnet-rpc.etherspot.io/v2/123?api-key=APIKEY-HERE ``` **ETH** 28122024 ``` https://testnet-rpc.etherspot.io/v2/28122024?api-key=APIKEY-HERE ``` **tRBTC** 31 ``` https://testnet-rpc.etherspot.io/v2/31?api-key=APIKEY-HERE ``` **tXDC** 51 ``` https://testnet-rpc.etherspot.io/v2/51?api-key=APIKEY-HERE ``` **CELO** 44787 ``` https://testnet-rpc.etherspot.io/v2/44787?api-key=APIKEY-HERE ``` # Contract Deployments Source: https://etherspot.fyi/modular-sdk/contracts/deployments [Deployed Contracts Addresses](https://github.com/etherspot/etherspot-modular-sdk/blob/master/src/sdk/network/constants.ts#L82-L448) # Generate Module deinitData Source: https://etherspot.fyi/modular-sdk/examples/generate-module-deinitdata ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { const bundlerApiKey = process.env.API_KEY; // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) } ); console.log('address: ', modularSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); //this should be previous node address of the module to be uninstalled and the deinit data //deinit data is the data that is passed to the module to be uninstalled // here we need to call the function which can find out the address of previous node of the module to be uninstalled // and the deinit data can be 0x00 as default value const deInitData = '0x00'; // set moduleAddress on which deinitData is to be generated const moduleAddress = ''; const deinitData = await modularSdk.generateModuleDeInitData(MODULE_TYPE.VALIDATOR, moduleAddress, deInitData); console.log(`deinitData: ${deinitData}`); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get previous Module Source: https://etherspot.fyi/modular-sdk/examples/get-previous-module-address ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { const bundlerApiKey = process.env.API_KEY; // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }); console.log('address: ', modularSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const moduleAddress = ''; const previousAddress = await modularSdk.getPreviousAddress(MODULE_TYPE.VALIDATOR, moduleAddress); console.log(`previousAddress: ${previousAddress}`); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get Address Source: https://etherspot.fyi/modular-sdk/examples/getaddress ```javascript theme={null} import { ethers } from 'ethers'; import { EtherspotBundler, ModularSdk } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { const bundlerApiKey = ''; const customBundlerUrl = ''; // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey, customBundlerUrl) }) // Testnets dont need apiKey on bundlerProvider // get EtherspotWallet address... const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); } main() .catch(console.error) .finally(() => process.exit()); ``` # Install Module Source: https://etherspot.fyi/modular-sdk/examples/install-module ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; async function main() { const bundlerApiKey = process.env.API_KEY; // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // set your module address const moduleAddress = ''; const uoHash = await modularSdk.installModule(MODULE_TYPE.VALIDATOR, moduleAddress); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } ``` # Run Modular SDK examples Source: https://etherspot.fyi/modular-sdk/examples/intro To run these examples, you can clone the [Etherspot Modular SDK repo](https://github.com/etherspot/etherspot-modular-sdk/). Then cd into the directory and run: ``` npm i && npm run init ``` Then create a .env file in the etherspot-modular-sdk directory. Within it we want to put something like this: ``` WALLET_PRIVATE_KEY='' WALLET_ADDRESS='' API_KEY='' ``` Please ensure to prefix your private key with "0x" so the SDK is instantiated correctly. Then, you can try running the get address example like this: ``` npm run 01-get-address ``` And it should output something like this to show the Etherspot SDK has been instantiated using the private key you used. ``` > ./node_modules/.bin/ts-node ./examples/01-get-address EtherspotWallet address: 0xb9798dF748E45F1fB724F50B7542802E5462a58a ``` # List all Modules Source: https://etherspot.fyi/modular-sdk/examples/list-all-modules ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; async function main() { const bundlerApiKey = process.env.API_KEY; // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const moduleInfo = await modularSdk.getAllModules(); console.log(`moduleInfo: ${JSON.stringify(moduleInfo)}`); } main() .catch(console.error) .finally(() => process.exit()); ``` # Paymaster Sponsor Transaction Source: https://etherspot.fyi/modular-sdk/examples/paymaster-sponsored-transaction ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); const recipient = ''; // recipient wallet address const value = '0.01'; // transfer value const apiKey = 'etherspot_public_key'; // Only testnets are available, if you need further assistance in setting up a paymaster service for your dapp, please reach out to us on discord or https://etherspot.fyi/arka/intro const bundlerApiKey = process.env.API_KEY; async function main() { // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // clear the transaction batch await modularSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await modularSdk.addUserOpsToBatch({ to: recipient, value: ethers.utils.parseEther(value) }); console.log('transactions: ', transactionBatch); // get balance of the account address const balance = await modularSdk.getNativeBalance(); console.log('balances: ', balance); // estimate transactions added to the batch and get the fee data for the UserOp const op = await modularSdk.estimate({ paymasterDetails: { url: `https://arka.etherspot.io?apiKey=${apiKey}&chainId=${Number(process.env.CHAIN_ID)}`, context: { mode: 'sponsor' } } }); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await modularSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Transfer erc20 Source: https://etherspot.fyi/modular-sdk/examples/transfer-erc20 ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, printOp, sleep, ERC20_ABI } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; import { ethers } from 'ethers'; dotenv.config(); // add/change these values const recipient = ''; // recipient wallet address const value = '0.0001'; // transfer value const tokenAddress = ''; // token address const bundlerApiKey = process.env.API_KEY; async function main() { // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const provider = new ethers.providers.JsonRpcProvider('https://polygon-amoy.drpc.org') // get erc20 Contract Interface const erc20Instance = new ethers.Contract(tokenAddress, ERC20_ABI, provider); // get decimals from erc20 contract const decimals = await erc20Instance.functions.decimals(); // get transferFrom encoded data const transactionData = erc20Instance.interface.encodeFunctionData('transfer', [recipient, ethers.utils.parseUnits(value, decimals)]) // clear the transaction batch await modularSdk.clearUserOpsFromBatch(); // add transactions to the batch const userOpsBatch = await modularSdk.addUserOpsToBatch({to: tokenAddress, data: transactionData}); console.log('transactions: ', userOpsBatch); // estimate transactions added to the batch and get the fee data for the UserOp const op = await modularSdk.estimate(); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await modularSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Transfer native funds Source: https://etherspot.fyi/modular-sdk/examples/transfer-native ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, printOp, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; async function main() { const recipient = ''; // recipient wallet address const value = '0.0000001'; // transfer value const bundlerApiKey = process.env.API_KEY; // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // clear the transaction batch await modularSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await modularSdk.addUserOpsToBatch({ to: recipient, value: ethers.utils.parseEther(value) }); console.log('transactions: ', transactionBatch); // get balance of the account address const balance = await modularSdk.getNativeBalance(); console.log('balances: ', balance); // estimate transactions added to the batch and get the fee data for the UserOp const op = await modularSdk.estimate(); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await modularSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Transfer NFT Source: https://etherspot.fyi/modular-sdk/examples/transfer-nft ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, printOp, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; import { ethers } from 'ethers'; dotenv.config(); dotenv.config(); // add/change these values const recipient = ''; // recipient wallet address const tokenAddress = '' // nft token address; const tokenId = 4; const bundlerApiKey = process.env.API_KEY; // npx ts-node examples/04-transfer-nft.ts async function main() { // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const erc721Interface = new ethers.utils.Interface([ 'function safeTransferFrom(address _from, address _to, uint256 _tokenId)' ]) const erc721Data = erc721Interface.encodeFunctionData('safeTransferFrom', [address, recipient, tokenId]); // clear the transaction batch await modularSdk.clearUserOpsFromBatch(); // add transactions to the batch const userOpsBatch = await modularSdk.addUserOpsToBatch({to: tokenAddress, data: erc721Data}); console.log('transactions: ', userOpsBatch); // sign transactions added to the batch const op = await modularSdk.estimate(); console.log(`Estimated UserOp: ${await printOp(op)}`); // sign the userOps and sending to the bundler... const uoHash = await modularSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # UnInstall Module Source: https://etherspot.fyi/modular-sdk/examples/uninstall-module ```javascript theme={null} import { EtherspotBundler, ModularSdk, MODULE_TYPE, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; async function main() { const bundlerApiKey = process.env.API_KEY; // initializating sdk... const modularSdk = new ModularSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); //this should be previous node address of the module to be uninstalled and the deinit data //deinit data is the data that is passed to the module to be uninstalled // here we need to call the function which can find out the address of previous node of the module to be uninstalled // and the deinit data can be 0x00 as default value const deInitDataDefault = '0x00'; // set the module address const moduleAddress = ''; //generate deinit data... const deInitData = await modularSdk.generateModuleDeInitData(MODULE_TYPE.VALIDATOR, moduleAddress, deInitDataDefault); console.log(`deinitData: ${deInitData}`); const uoHash = await modularSdk.uninstallModule(MODULE_TYPE.VALIDATOR, moduleAddress, deInitData); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } ``` # Functions Source: https://etherspot.fyi/modular-sdk/functions This page will contain an exhaustive list of all functions we can call using the SDK. If you want to take a look at the SDK code in more detail then you can check these functions out [here on Github](https://github.com/etherspot/etherspot-modular-sdk/blob/master/src/sdk/sdk.ts). | Function Name | Description | | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [installModule(moduleTypeId: MODULE\_TYPE, module: string, initData?: string)](/modular-sdk/module-details) | installs a module identified by its moduleType and moduleAddress. moduleType must be from the list of valid moduleTypes in module-details page. initData is optional | | getPreviousModuleAddress(moduleTypeId: MODULE\_TYPE, module: string) | get previous module for a specific module installed on etherspot modular wallet | | generateModuleDeInitData(moduleTypeId: MODULE\_TYPE, module: string, moduleDeInitData: string) | generate the deInitData to uninstall the module. deInitData includes the prevModuleAddress along with deinitData specific to the module being uninstalled. | | [uninstallModule(moduleTypeId: MODULE\_TYPE, module: string, deinitData: string)](/modular-sdk/install-uninstall-module) | uninstalls a module identified by its moduleType and moduleAddress. moduleType must be from the list of valid moduleTypes in module-details page. deinitData is mandatory which is used to identify previous module to unlink current module | | isModuleInstalled(moduleTypeId: MODULE\_TYPE, module: string) | check if the module is installed on the etherspot modular wallet. returns true or false | | getAllModules(pageSize: number = DEFAULT\_QUERY\_PAGE\_SIZE) | get List of all modules installed on etherspot modular wallet. Query is made in paginated manner with an option to set page-size, In absence of page-size, defaultPageSize will override the pageSize argument | | destroy() | Destroys the SDK object. | | signMessage() | Signs a message using the Etherspot modular wallet. | | supportedNetworks() | get list of supported networks and chainIds | | getCounterFactualAddress() | Gets the address of the Etherspot wallet created. | | estimate(gasDetails?: TransactionGasInfoForUserOp) | Returns the estimated amount of gas a transaction will cost. | | totalGasEstimated(userOp: UserOperationStruct) | Returns the estimated amount of gas a batch of transactions will cost. | | getGasFee() | Returns the current gas fee for the network. | | send(userOp: UserOperationStruct) | Sends a struct of signed UserOps to the bundler. | | getNativeBalance() | Returns the native token amount of the Etherspot wallet. | | getUserOpReceipt(userOpHash: string) | Returns the receipt of a UserOp processed by the bundler. | | getUserOpHash(userOp: UserOperationStruct) | Returns the hash of a UserOp processed by the bundler. | | [addUserOpsToBatch(tx: UserOpsRequest)](/modular-sdk/batching-transactions) | Adds a UserOp to the batch before sending. | | clearUserOpsFromBatch() | Clears the batch. | | getAccountContract() | Returns the Etherspot smart contract for the Etherspot smart wallet. | # Uninstall Module Details Source: https://etherspot.fyi/modular-sdk/install-uninstall-module This page will contain detailed information on how to uninstall a module from the Modular Wallet and how to prepare deinitData for module installation ## Install Module Module can be installed on a Etherspot Modular wallet with some pre-requisite checks before transaction is sent to bundler 1. Module is not installed yet on the wallet 2. Module is a valid contract address ## Uninstall Module 1. Module can be uninstalled from wallet via `uninstall` SDK function 2. Module uninstallation is associated with deinitData which contains the data needed to complete module removal from wallet storage * previous module address * optional module specific deinit data which is used during uninstall process ### Get deinitData * Uninstallation needs deinitData which has 2 essential components: 1. previous module address 2. module specific deinitData (this is an optional argument and replaced by default deinitData if there is no need of module specific data) * DeinitData can be generated using the SDK helperfunction: [generateModuleDeInitData](https://github.com/etherspot/etherspot-modular-sdk/blob/master/src/sdk/sdk.ts#L286-L288) * moduleType * module (moduleAddress) * moduleDeInitData (module specific deInitData or defaulted deinitData - `0x00`) * Example to generate DeinitData and uninstall is at: [module-uninstall-example](https://github.com/etherspot/etherspot-modular-sdk/blob/master/examples/12-uninstall-module.ts#L29-L34) If you want to take a look at the SDK code in more detail then you can check these functions out [here on Github](https://github.com/etherspot/etherspot-modular-sdk/blob/master/src/sdk/sdk.ts). # Installation Source: https://etherspot.fyi/modular-sdk/installation Learn how to install Etherspot Modular SDK **Prerequisite** You should have installed Node.js (version 18.10.0 or higher). Step 1. Install Etherspot Modular SDK with this command ```bash npm theme={null} npm i @etherspot/modular-sdk --save ``` ```bash yarn theme={null} yarn add @etherspot/modular-sdk ``` And that's it! You are now ready to dive into Modular Accounts Now let's learn how to instaniate the SDK. # Instantiation Source: https://etherspot.fyi/modular-sdk/instantiation Before doing anything with the SDK, we must instantiate it. This will create the Etherspot smart account based off of the values we pass in. Step 1. Import the Etherspot Modular SDK. ```javascript theme={null} import { ModularSdk } from '@etherspot/modular-sdk'; ``` Step 2. Instantiate the SDK with below initialisation properties using this block of code. * privateKey * ChainID * bundlerProvider * bundlerApiKey * customBundlerUrl (can be left empty) ```javascript theme={null} const modularSdk = new ModularSdk( { privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey, customBundlerUrl) } ); ``` And that's it! You're now ready to call any of the Modular SDK [functions.](/modular-sdk/functions) You can also pass in different parameters when instantiating the SDK. * chainId : The chain ID of the blockchain. * customBundlerUrl : The bundler you wish to use. ### Sending funds to another address ```typescript theme={null} const recipient = ''; // recipient wallet address const value = '0.0000001'; // transfer value // get address of EtherspotWallet... const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // clear the transaction batch await modularSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await modularSdk.addUserOpsToBatch({ to: recipient, value: ethers.utils.parseEther(value) }); console.log('transactions: ', transactionBatch); // get balance of the account address const balance = await modularSdk.getNativeBalance(); console.log('balances: ', balance); // estimate transactions added to the batch and get the fee data for the UserOp const op = await modularSdk.estimate(); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await modularSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } ``` ### Next steps For next steps you can look at [functions](/modular-sdk/functions) or [examples](/modular-sdk/examples/intro) to tailor the dapp to what you're trying to achieve. In the next page we'll take a look at the various functions the SDK offers. # Introduction Source: https://etherspot.fyi/modular-sdk/intro ## Intro A TypeScript library for using smart account modules in applications SDK allows you to install and uninstall modules for Etherspot ERC-7579 account, interact with and use modules. This section contains information on how to install the Etherspot Modular SDK, all of the features you can implement using it and which functions you can use to implement them. ## Etherspot Modular SDK Features The Etherspot Modular SDK unlocks a number of easy to implement Etherspot Modular Account features for your dapp or network such as: * Install a module * Validator * Executor * Hook * UnInstall a module * Validator * Executor * Hook * Generate Uninstall Module - deinitData * check if the module is installed * List all modules * Get Previous Module # Module Details Source: https://etherspot.fyi/modular-sdk/module-details This page will contain detailed information on how a module is installed, uninstalled and the logic behing the deinitialisation data during uninstallation and session-key operations ## Module Types 1. Validator 2. Executor 3. Fallback 4. Hook * Validator, Executor and Fallback modules on the wallet are stored as a circular linked list * Any installation or uninstallation of module is to link or unlink the module from module store * Only 1 hook is permitted to be installed for the wallet. More Details on the Modular contracts can be refered from: [Module-Contracts](https://github.com/etherspot/etherspot-prime-contracts/tree/master/src/modular-etherspot-wallet/modules) While using module SDK functions, moduleType is mandatory and they are set using the helper constant [MODULE\_TYPE](https://github.com/etherspot/etherspot-modular-sdk/blob/master/src/sdk/common/constants.ts#L26-L31) If you want to take a look at the SDK code in more detail then you can check these functions out [here on Github](https://github.com/etherspot/etherspot-modular-sdk/blob/master/src/sdk/sdk.ts). # Chains Supported Source: https://etherspot.fyi/modular-sdk/sessionkey/chains-supported We're always on the lookout for more EVM based networks who want to get setup with relevant Account Abstraction infrastructure. If you're a network who's interested in this, please [get in touch](https://discord.etherspot.io/). ## Mainnet SessionKeyValidator SDK is currently usable on the following chains. The bundler URL can be copied here for each. Public bundlers (\*-bundler.etherspot.io) are deprecated. Register on [https://developer.etherspot.io](https://developer.etherspot.io) to [get a key](/prime-sdk/api-key-portal) These networks are now deprecated: Goerli, Mumbai, Base Goerli, Arbitrum Goerli, Optimism Goerli, Mantle Goerli. **ETH** 1 ``` https://rpc.etherspot.io/v2/1?api-key=APIKEY-HERE ``` **MATIC** 137 ``` https://rpc.etherspot.io/v2/137?api-key=APIKEY-HERE ``` **ETH** 10 ``` https://rpc.etherspot.io/v2/10?api-key=APIKEY-HERE ``` **ETH** 42161 ``` https://rpc.etherspot.io/v2/42161?api-key=APIKEY-HERE ``` **FUSE** 122 ``` https://rpc.etherspot.io/v2/122?api-key=APIKEY-HERE ``` **MNT** 5000 ``` https://rpc.etherspot.io/v2/5000?api-key=APIKEY-HERE ``` **XDAI** 100 ``` https://rpc.etherspot.io/v2/100?api-key=APIKEY-HERE ``` **ETH** 8453 ``` https://rpc.etherspot.io/v2/8453?api-key=APIKEY-HERE ``` **AVAX** 43114 ``` https://rpc.etherspot.io/v2/43114?api-key=APIKEY-HERE ``` **BNB** 56 ``` https://rpc.etherspot.io/v2/56?api-key=APIKEY-HERE ``` **ETH** 59144 ``` https://rpc.etherspot.io/v2/59144?api-key=APIKEY-HERE ``` **FLR** 14 ``` https://rpc.etherspot.io/v2/14?api-key=APIKEY-HERE ``` **ETH** 534352 ``` https://rpc.etherspot.io/v2/534352?api-key=APIKEY-HERE ``` **RBTC** 30 ``` https://rpc.etherspot.io/v2/30?api-key=APIKEY-HERE ``` **ETH** 888888888 ``` https://rpc.etherspot.io/v2/888888888?api-key=APIKEY-HERE ``` **XDC** 50 ``` https://rpc.etherspot.io/v2/50?api-key=APIKEY-HERE ``` ## Testnets **MATIC** 80002 ``` https://testnet-rpc.etherspot.io/v2/80002?api-key=APIKEY-HERE ``` **ETH** 11155111 ``` https://testnet-rpc.etherspot.io/v2/11155111?api-key=APIKEY-HERE ``` **ETH** 84532 ``` https://testnet-rpc.etherspot.io/v2/84532?api-key=APIKEY-HERE ``` **ETH** 421614 ``` https://testnet-rpc.etherspot.io/v2/421614?api-key=APIKEY-HERE ``` **ETH** 11155420 ``` https://testnet-rpc.etherspot.io/v2/11155420?api-key=APIKEY-HERE ``` **ETH** 534351 ``` https://testnet-rpc.etherspot.io/v2/534351?api-key=APIKEY-HERE ``` **MNT** 5003 ``` https://testnet-rpc.etherspot.io/v2/5003?api-key=APIKEY-HERE ``` **C2FLR** 114 ``` https://testnet-rpc.etherspot.io/v2/114?api-key=APIKEY-HERE ``` **SPARK** 123 ``` https://testnet-rpc.etherspot.io/v2/123?api-key=APIKEY-HERE ``` **ETH** 28122024 ``` https://testnet-rpc.etherspot.io/v2/28122024?api-key=APIKEY-HERE ``` **tRBTC** 31 ``` https://testnet-rpc.etherspot.io/v2/31?api-key=APIKEY-HERE ``` **tXDC** 51 ``` https://testnet-rpc.etherspot.io/v2/51?api-key=APIKEY-HERE ``` # Disable SessionKey Source: https://etherspot.fyi/modular-sdk/sessionkey/examples/disable-sessionkey ```javascript theme={null} import { EtherspotBundler, ModularSdk, SessionKeyValidator, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { const bundlerApiKey = process.env.API_KEY as string; const walletPrivateKey = process.env.WALLET_PRIVATE_KEY as string; const chainId = Number(process.env.CHAIN_ID); // initializating sdk... const modularSdk = new ModularSdk({ privateKey: privateKey }, { chainId: chainId, bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // get instance of SessionKeyValidator const sessionKeyModule = await SessionKeyValidator.create( modularSdk, new EtherspotBundler(chainId, bundlerApiKey) ) const sessionKey = '0xb1D8541544f240C80d5c4489990bfADAa238b0b3'; // session key which you want to disable const response = await sessionKeyModule.disableSessionKey(sessionKey); console.log('\x1b[33m%s\x1b[0m', `UserOpHash: `, response.userOpHash); console.log('\x1b[33m%s\x1b[0m', `SessionKey: `, response.sessionKey); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(response.userOpHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); const sessionKeys = await sessionKeyModule.getAssociatedSessionKeys(); console.log('\x1b[33m%s\x1b[0m', `AssociatedSessionKeys: `, sessionKeys); } ``` # Enable SessionKey Source: https://etherspot.fyi/modular-sdk/sessionkey/examples/enable-sessionkey ```javascript theme={null} import { EtherspotBundler, ModularSdk, SessionKeyValidator, KeyStore } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); const secondsInAMonth = 30 * 24 * 60 * 60; // 2592000 seconds async function main() { const bundlerApiKey = process.env.API_KEY as string; const walletPrivateKey = process.env.WALLET_PRIVATE_KEY as string; const chainId = Number(process.env.CHAIN_ID); // initializating sdk... const modularSdk = new ModularSdk({ privateKey: walletPrivateKey }, { chainId: chainId, bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const token = process.env.TOKEN_ADDRESS as string; const functionSelector = process.env.FUNCTION_SELECTOR as string; const spendingLimit = '1000000000000000000000'; const validAfter = getEpochTimeInSeconds() + 31; // 10 seconds from now const validUntil = getEpochTimeInSeconds() + secondsInAMonth; console.log(`validAfter: ${validAfter} validUntil: ${validUntil}`); // get instance of SessionKeyValidator const sessionKeyModule = await SessionKeyValidator.create( modularSdk, new EtherspotBundler(chainId, bundlerApiKey) ) const response = await sessionKeyModule.enableSessionKey( token, functionSelector, spendingLimit, validAfter, validUntil, KeyStore.AWS ); console.log('\x1b[33m%s\x1b[0m', `UserOpHash: `, response.userOpHash); console.log('\x1b[33m%s\x1b[0m', `SessionKey: `, response.sessionKey); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(response.userOpHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); const sessionKeys = await sessionKeyModule.getAssociatedSessionKeys(); console.log('\x1b[33m%s\x1b[0m', `AssociatedSessionKeys: `, sessionKeys); const sessionData = await sessionKeyModule.sessionData(response.sessionKey); console.log('\x1b[33m%s\x1b[0m', `SessionData: `, sessionData); } const getEpochTimeInSeconds = () => Math.floor(new Date().getTime() / 1000); ``` # ERC20-Transfer with SessionKey Signature Source: https://etherspot.fyi/modular-sdk/sessionkey/examples/erc20-transfer-with-sessionkey ```javascript theme={null} import { BigNumber, ethers } from 'ethers'; import { EtherspotBundler, ModularSdk, SessionKeyValidator, sleep, ERC20_ABI, printOp } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { // add/change these values const recipient = '0xdE79F0eF8A1268DAd0Df02a8e527819A3Cd99d40'; // recipient wallet address const value = '1'; // transfer value const tokenAddress = ''; // token address const decimals = 18; const erc20SessionKeyValidator = '0x22A55192a663591586241D42E603221eac49ed09'; const bundlerApiKey = process.env.API_KEY as string; const walletPrivateKey = process.env.WALLET_PRIVATE_KEY as string; const chainId = Number(process.env.CHAIN_ID); // initializating sdk... const modularSdk = new ModularSdk({ privateKey: privateKey }, { chainId: chainId, bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey) }) const sessionKeyModule = await SessionKeyValidator.create( modularSdk, new EtherspotBundler(chainId, bundlerApiKey) ); console.log(`sessionKey SDK initialized`); // get address of EtherspotWallet... const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const provider = new ethers.providers.JsonRpcProvider(process.env.BUNDLER_URL) // get erc20 Contract Interface const erc20Instance = new ethers.Contract(tokenAddress, ERC20_ABI, provider); // get transferFrom encoded data const transactionData = erc20Instance.interface.encodeFunctionData('transfer', [recipient, ethers.utils.parseUnits(value, decimals)]) // clear the transaction batch await modularSdk.clearUserOpsFromBatch(); // add transactions to the batch const userOpsBatch = await modularSdk.addUserOpsToBatch({ to: tokenAddress, data: transactionData }); console.log('transactions: ', userOpsBatch); console.log(`erc20SessionKeyValidator ${erc20SessionKeyValidator} as BigNumber is: ${BigNumber.from(erc20SessionKeyValidator)}`); // estimate transactions added to the batch and get the fee data for the UserOp const op = await modularSdk.estimate({ key: BigNumber.from(erc20SessionKeyValidator) }); const nonceBig = BigNumber.from(op.nonce); console.log(`Nonce: ${nonceBig}`); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp using sessionKey const sessionKey = ''; const signedUserOp = await sessionKeyModule.signUserOpWithSessionKey(sessionKey, op); console.log(`etherspot-modular-sdk -> Signed UserOp: ${signedUserOp.signature}`); console.log(`Signed UserOp: ${await printOp(signedUserOp)}`); console.log(`UserOpNonce is: ${BigNumber.from(signedUserOp.nonce)}`); const userOpHashFromSignedUserOp = await modularSdk.getUserOpHash(signedUserOp); console.log(`UserOpHash from Signed UserOp: ${userOpHashFromSignedUserOp}`); // sending to the bundler with isUserOpAlreadySigned true... const uoHash = await modularSdk.send(signedUserOp, true); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } ``` # Run SessionKeyValidator SDK examples Source: https://etherspot.fyi/modular-sdk/sessionkey/examples/intro To run these examples, you can clone the [Etherspot Modular SDK repo](https://github.com/etherspot/etherspot-modular-sdk/). Then cd into the directory and run: ``` npm i && npm run init ``` Then create a .env file in the etherspot-modular-sdk directory. Within it we want to put something like this: ``` WALLET_PRIVATE_KEY='' WALLET_ADDRESS='' API_KEY='' ``` Please ensure to prefix your private key with "0x" so the SDK is instantiated correctly. Then, you can try running the get address example like this: ``` npx ts-node 13-list-modules.ts ``` And it should output something like this to show the Etherspot SDK has been instantiated using the private key you used. ``` > ./node_modules/.bin/ts-node ./examples/13-list-modules.ts ``` # Rotate SessionKey Source: https://etherspot.fyi/modular-sdk/sessionkey/examples/rotate-sessionkey ```javascript theme={null} import { EtherspotBundler, ModularSdk, SessionKeyValidator, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { const bundlerApiKey = process.env.API_KEY as string; const walletPrivateKey = process.env.WALLET_PRIVATE_KEY as string; const chainId = Number(process.env.CHAIN_ID); // initializating sdk... const modularSdk = new ModularSdk({ privateKey: privateKey }, { chainId: chainId, bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey) }) console.log('address: ', modularSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await modularSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // get instance of SessionKeyValidator const sessionKeyModule = await SessionKeyValidator.create( modularSdk, new EtherspotBundler(chainId, bundlerApiKey) ) const token = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238'; // replace with your token address const functionSelector = '0xa9059cbb'; // replace with your function selector const spendingLimit = '100000'; // replace with your intended spending-limit for sessionKey const validAfter = new Date().getTime(); // replace with your validAfter time for the sessionKey const validUntil = new Date().getTime() + 24 * 60 * 60 * 1000; // replace with your validUtil time for the sessionKey const oldSessionKey = ''; // session key which you want to rotate const response = await sessionKeyModule.rotateSessionKey( token, functionSelector, spendingLimit, validAfter, validUntil, oldSessionKey, KeyStore.AWS ); console.log('\x1b[33m%s\x1b[0m', `UserOpHash: `, response.userOpHash); console.log('\x1b[33m%s\x1b[0m', `SessionKey: `, response.sessionKey); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await modularSdk.getUserOpReceipt(response.userOpHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); const sessionKeys = await sessionKeyModule.getAssociatedSessionKeys(); console.log('\x1b[33m%s\x1b[0m', `AssociatedSessionKeys: `, sessionKeys); } ``` # Functions Source: https://etherspot.fyi/modular-sdk/sessionkey/functions This page will contain an exhaustive list of all functions we can call using the SessionKeyValidator SDK. If you want to take a look at the SDK code in more detail then you can check these functions out [here on Github](https://github.com/etherspot/etherspot-modular-sdk/blob/master/src/sdk/SessionKeyValidator/SessionKeyValidator.ts). | Function Name | Description | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | enableSessionKey(token: string,functionSelector: string,spendingLimit: string,validAfter: number,validUntil: number,keyStore?: KeyStore) returns SessionKeyResponse | generates a new sessionKey in KMS in secured cloud service and enables the sessionKey onchain in the ERC20SessionKeyValidator | | rotateSessionKey(token: string,functionSelector: string,spendingLimit: string,validAfter: number,validUntil: number,oldSessionKey: string,keyStore?: KeyStore) returns SessionKeyResponse | generates a new sessionKey in KMS in secured cloud service and enables the sessionKey onchain in the ERC20SessionKeyValidator followed by deletion of oldSessionkey in KMS | | disableSessionKey(sessionKey: string) returns SessionKeyResponse | verify if the sessionKey exists and active followed by disabling sessionKey onchain on ERC20SessionKeyValidator and ending with deletion of sessionkey from KMS in secured cloud service | | getAssociatedSessionKeys(sessionKey: string) returns string\[] | query all sessionKeys of the etherspotModularWallet address from onchain - ERC20SessionKeyValidator | | sessionData(sessionKey: string) returns GetSessionKeyResponse | get onchain sessionData from ERC20SessionKeyValidator | | getSignUserOp(account: string,chainId: number,apiKey: string,sessionKey: string,userOp: UserOperation) returns SignedUserOp | extracts the privateKey of the sessionKey from KMS and signs the UserOp with the privateKey and returns the signedUserOp. this function is to be tried from remote-signer SDK | # Introduction Source: https://etherspot.fyi/modular-sdk/sessionkey/sessionkey-intro This page will contain detailed information about SessionKey and how SessionKeys are created, rotated and disabled and used to sign UserOps ## ERC20-SessionKeyValidator Background ERC20-SessionKeyValidator is a wrapper on Etherspot-Modular-SDK which handles * enable SessionKey * disable SessionKey * rotate SessionKey * get SessionKeyInfo ## Pre-Requisites for SessionKeyValidator SDK functions on Modular-Wallet 1. SessionKey SDK functions can be executed on an existing Etherspot Modular Wallets 2. If ModularWallet doesnot exist, then please refer to the ModularSDK user guide to create a ModularWallet 3. ERC20SessionKeyValidator module must be installed on the ModularWallet ## SessionKey creation 1. SessionKey is created offchain and stored in the KMS system and managed by a secured backend service 2. Users Operating on SessionKey must use APIKey which is used to authorize if they are permitted to use sessionKey for signing ## ERC20SessionKeyValidator SmartContracts ERC20SessionKeyValidator is a smart-contract validator module Smartcontract has functions to handle enable, rotate, disable, validateSignatures made by SessionKey Reference: [ERC20SessionKeyValidator.sol](https://github.com/etherspot/etherspot-prime-contracts/blob/master/src/modular-etherspot-wallet/modules/validators/ERC20SessionKeyValidator.sol) If you want to take a look at the SDK code in more detail then you can check these functions out [here on Github](https://github.com/etherspot/etherspot-modular-sdk/blob/master/src/sdk/SessionKeyValidator/SessionKeyValidator.ts). # Instantiation Source: https://etherspot.fyi/modular-sdk/sessionkey/sessionkey-validator-instantiation Before doing anything with the SessionKeyValidatorSDK, you must ensure that the walletPrivateKey being used must have a etherspot-modular-account. SessionKeys can be created on an existing etherspotModularWallet with ERC20SessionKeyValidator installed to it For Steps to install ERC20SessionKeyValidator, please follow instructions in: [install-module](/modular-sdk/examples/install-module) Step 1. set .env variables via export or in your .env If you chose to use them directly skip to Step-2 ```sh theme={null} export WALLET_PRIVATE_KEY='' export CHAIN_ID= export API_KEY='' ``` Step 2. Import the Etherspot Modular SDK. ```javascript theme={null} import { ModularSdk, SessionKeyValidator } from '@etherspot/modular-sdk'; ``` Step 3. Instantiate the SDK with below initialisation properties using this block of code. * privateKey * ChainID * bundlerProvider * bundlerApiKey * customBundlerUrl (can be left empty) ```javascript theme={null} import { EtherspotBundler, ModularSdk, SessionKeyValidator, KeyStore, sleep } from '@etherspot/modular-sdk'; import * as dotenv from 'dotenv'; dotenv.config(); const bundlerApiKey = process.env.API_KEY as string; const chainId = Number(process.env.CHAIN_ID); const privateKey = process.env.WALLET_PRIVATE_KEY as string; const customBundlerUrl = ''; const modularSdk = new ModularSdk( { privateKey: privateKey }, { chainId: chainId, bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey, customBundlerUrl) } ); // get instance of SessionKeyValidator const sessionKeyModule = await SessionKeyValidator.create( modularSdk, new EtherspotBundler(chainId, bundlerApiKey) ) ``` And that's it! You're now ready to call any of the SessionKeyValidator SDK [functions.](/modular-sdk/sessionkey/functions) You can also pass in different parameters when instantiating the SDK. * chainId : The chain ID of the blockchain. * customBundlerUrl : The bundler you wish to use. In the next page we'll take a look at the various functions the SessionKeyValidator SDK offers. # Get an API key Source: https://etherspot.fyi/prime-sdk/api-key-portal Bundler API keys are required to use the Prime SDK. These keys: * Track API calls made to Skandha across [supported chains](/skandha/chains) * Are configured during SDK [instantiation](/prime-sdk/instantiation) * Come with different subscription plans based on your API usage needs

Getting Started with portal.etherspot.io

1. Sign in to the Developer Portal

Visit portal.etherspot.io and sign in / register

Sign in to Developer Portal

2. Navigate to API Keys

From the dashboard, locate and click on the API Keys section

Navigate to API Keys

3. Create New API Key

Click the "Create API Key" button to start generating your key

Create New API Key

4. Name Your Project

Enter a descriptive name for your project to help identify this API key

Name Your Project

5. Generate the Key

Click "Create" to generate your new API key

Generate API Key

6. Access RPC URLs

Click on "RPC URLs" to configure your network settings

Access RPC URLs

7. Choose Network

Click "Select here" to choose your network configuration

Choose Network

8. Choose Network Type

Select between Testnet or Mainnet based on your development needs

Choose Network Type

9. Select Specific Network

Click "Select network" to view available networks

Select Network

10. Pick Your Network

Choose the specific network you want to work with

Pick Network

11. Select EntryPoint Version

Choose between EntryPoint 6 (v1) or EntryPoint 7 (v2)

Select EntryPoint Version

12. Configure for EP6

Select v1 if you need EntryPoint 6 compatibility

Configure EP6

13. Configure for EP7

Select v2 if you need EntryPoint 7 compatibility

Configure EP7
### [14. Copy bundler URL](https://developer.etherspot.io/dashboard/api-keys) ![](https://dubble-prod-01.s3.amazonaws.com/assets/240b4eb7-dc67-4024-94b7-e7fa3ac34d43.png?0)
# Arka Paymaster Source: https://etherspot.fyi/prime-sdk/arka ## Intro Paymasters are used to sponsor transactions and enable paying for gas with ERC20 Tokens. This section will cover how to use Arka within the SDK, for a more detailed look at Arka and it's API calls and how to send requests manually, you can [check out this part of the docs.](/arka) ## How-to guide 1. Import the ArkaPaymaster object. ```typescript theme={null} import { ArkaPaymaster } from "@etherspot/prime-sdk"; ``` 2. Initialise the object. This takes the chainId, Arka API key, and Arka Paymaster URL as parameters. ```typescript theme={null} const arka_api_key = 'etherspot_public_key'; const arka_url = 'https://arka.etherspot.io'; const arkaPaymaster = new ArkaPaymaster(11155111, arka_api_key, arka_url); ``` 3. Use arkaPaymaster to call functions. ```typescript theme={null} console.log(await arkaPaymaster.metadata()); console.log(await arkaPaymaster.getTokenPaymasterAddress("USDC")) console.log(await arkaPaymaster.addWhitelist(["0xB3aF6CFDDc444B948132753AD8214a20605692eF"])); console.log(await arkaPaymaster.removeWhitelist(["0xB3aF6CFDDc444B948132753AD8214a20605692eF"])); console.log(await arkaPaymaster.checkWhitelist("0xB3aF6CFDDc444B948132753AD8214a20605692eF")); console.log(await arkaPaymaster.deposit(0.000000001)); ``` ## Function list Here is a list of the functions we can call and what they do. ### metadata() Returns information about the paymaster. Example output: ```json theme={null} { "sponsorAddress": "0xaeAF09795d8C0e6fA4bB5f89dc9c15EC02021567", "sponsorWalletBalance": { "type": "BigNumber", "hex": "0x1ed81fac4b400e4d" }, "chainsSupported": [ 5, 114, 420, 11155111, 84531, 421613, 534351, 11155111, 84532 ], "tokenPaymasters": { "1": { "USDC": "0x0000000000fABFA8079AB313D1D14Dcf4D15582a" }, "10": { "USDC": "0x0000000000fce6614d3c6f679e48c9cdd09aa634" }, "56": { "USDC": "0x0000000000db7995889f54d72dac9d36a9f7f467" }, } } ``` ### getTokenPaymasterAddress("USDC") Accepts a string of a token ticker as input and outputs the address of the token paymaster if it is supported. ### addWhitelist(\["0xB3aF6CFDDc444B948132753AD8214a20605692eF"]) Accepts an array of strings (valid addresses) and whitelists them on the paymaster. ### removeWhitelist(\["0xB3aF6CFDDc444B948132753AD8214a20605692eF"]) Accepts an array of strings (valid addresses) and removes them from the whitelist. ### checkWhitelist("0xB3aF6CFDDc444B948132753AD8214a20605692eF") Accepts a valid address as a string and returns whether or not an address is whitelisted. ### deposit(0.000000001) Accepts a number and deposits this to the paymaster. # Batching Transactions Source: https://etherspot.fyi/prime-sdk/batching-transactions Batching transactions is the ability to include a number of transactions within one block to save the user both gas, time, and clicks. Here we'll show a code example of how easy it is to batch transactions using the Prime SDK. We'll be using the **addUserOpsToBatch** function for this. ```javascript theme={null} import { ethers } from 'ethers'; import { PrimeSdk } from '@etherspot/prime-sdk'; import { printOp } from '../src/sdk/common/OperationUtils'; const recipient1: string = '0x10a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient1 wallet address const recipient2: string = '0x20a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient2 wallet address const recipient3: string = '0x30a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient3 wallet address const value: string = '0.01'; // transfer value async function main() { // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID) }) // add transaction 1 to the batch let transactionBatch = await primeSdk.addUserOpsToBatch({to: recipient1, value: ethers.utils.parseEther(value)}); // add transaction 2 to the batch transactionBatch = await primeSdk.addUserOpsToBatch({to: recipient2, value: ethers.utils.parseEther(value)}); // add transaction 3 to the batch transactionBatch = await primeSdk.addUserOpsToBatch({to: recipient3, value: ethers.utils.parseEther(value)}); // estimate transactions added to the batch and get the fee data for the UserOp const op = await primeSdk.estimate(); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # User Specific callGasLimit Source: https://etherspot.fyi/prime-sdk/callGasLimit The callGasLimit (CGL) sets the upper boundary for the gas available during the execution phase of a UserOperation. In compliance with the ERC-4337 standard, all account actions are represented within the callData and receive allocated gas within the limit defined by the callGasLimit. When estimating a transaction, we have the ability to set this callGasLimit like so: ```javascript theme={null} // estimate transactions added to the batch and get the fee data for the UserOp // passing callGasLimit as 40000 to manually set it const op = await primeSdk.estimate({ callGasLimit: 4000 }); ``` # Chains Supported Source: https://etherspot.fyi/prime-sdk/chains-supported We're always on the lookout for more EVM based networks who want to get setup with relevant Account Abstraction infrastructure. If you're a network who's interested in this, please [get in touch](https://discord.etherspot.io/). ## Mainnet Etherspot Prime is currently usable on the following chains. The bundler URL can be copied here for each. Public bundlers (\*-bundler.etherspot.io) are deprecated. Register on [https://developer.etherspot.io](https://developer.etherspot.io) to [get a key](/prime-sdk/api-key-portal) These networks are now deprecated: Goerli, Mumbai, Base Goerli, Arbitrum Goerli, Optimism Goerli, Mantle Goerli. **ETH** 1 ``` https://rpc.etherspot.io/v1/1?api-key=APIKEY-HERE ``` **MATIC** 137 ``` https://rpc.etherspot.io/v1/137?api-key=APIKEY-HERE ``` **ETH** 10 ``` https://rpc.etherspot.io/v1/10?api-key=APIKEY-HERE ``` **ETH** 42161 ``` https://rpc.etherspot.io/v1/42161?api-key=APIKEY-HERE ``` **FUSE** 122 ``` https://rpc.etherspot.io/v1/122?api-key=APIKEY-HERE ``` **MNT** 5000 ``` https://rpc.etherspot.io/v1/5000?api-key=APIKEY-HERE ``` **XDAI** 100 ``` https://rpc.etherspot.io/v1/100?api-key=APIKEY-HERE ``` **ETH** 8453 ``` https://rpc.etherspot.io/v1/8453?api-key=APIKEY-HERE ``` **AVAX** 43114 ``` https://rpc.etherspot.io/v1/43114?api-key=APIKEY-HERE ``` **BNB** 56 ``` https://rpc.etherspot.io/v1/56?api-key=APIKEY-HERE ``` **ETH** 59144 ``` https://rpc.etherspot.io/v1/59144?api-key=APIKEY-HERE ``` **FLR** 14 ``` https://rpc.etherspot.io/v1/14?api-key=APIKEY-HERE ``` **ETH** 534352 ``` https://rpc.etherspot.io/v1/534352?api-key=APIKEY-HERE ``` **RBTC** 30 ``` https://rpc.etherspot.io/v1/30?api-key=APIKEY-HERE ``` **ETH** 888888888 ``` https://rpc.etherspot.io/v1/888888888?api-key=APIKEY-HERE ``` **CELO** 42220 ``` https://rpc.etherspot.io/v1/42220?api-key=APIKEY-HERE ``` ## Testnets **MATIC** 80002 ``` https://testnet-rpc.etherspot.io/v1/80002?api-key=APIKEY-HERE ``` **ETH** 11155111 ``` https://testnet-rpc.etherspot.io/v1/11155111?api-key=APIKEY-HERE ``` **ETH** 84532 ``` https://testnet-rpc.etherspot.io/v1/84532?api-key=APIKEY-HERE ``` **ETH** 421614 ``` https://testnet-rpc.etherspot.io/v1/421614?api-key=APIKEY-HERE ``` **ETH** 11155420 ``` https://testnet-rpc.etherspot.io/v1/11155420?api-key=APIKEY-HERE ``` **ETH** 534351 ``` https://testnet-rpc.etherspot.io/v1/534351?api-key=APIKEY-HERE ``` **MNT** 5003 ``` https://testnet-rpc.etherspot.io/v1/5003?api-key=APIKEY-HERE ``` **C2FLR** 114 ``` https://testnet-rpc.etherspot.io/v1/114?api-key=APIKEY-HERE ``` **SPARK** 123 ``` https://testnet-rpc.etherspot.io/v1/123?api-key=APIKEY-HERE ``` **ETH** 28122024 ``` https://testnet-rpc.etherspot.io/v1/28122024?api-key=APIKEY-HERE ``` **tRBTC** 31 ``` https://testnet-rpc.etherspot.io/v1/31?api-key=APIKEY-HERE ``` **CELO** 44787 ``` https://testnet-rpc.etherspot.io/v1/44787?api-key=APIKEY-HERE ``` # AccessController Source: https://etherspot.fyi/prime-sdk/contracts/accesscontroller # AccessController.sol ## Overview The `AccessController` abstract contract is a simple implementation that allows for wallet ownership/guardianship. It provides the functionality to check if an address is a owner or guardian, add a new owner an guardian, or remove an existing owner and guardian. It contains modifiers that check for ownership, guardianship and calls from `EntryPoint`. In it's current iteration it is designed to be used with `EtherspotWallet` to allow for wallets to have multiple owners and guardians. ## Version Solidity pragma version `^0.8.12`. ## State Variables * `MULTIPLY_FACTOR`: immutable value of `1000` for calculation of percentages. * `SIXTY_PERCENT`: immutable value of `600` for calculation of percentages. * `ownerCount`: public value, tracks count of how many owners a wallet has. * `guardianCount`: public value, tracks count of how many guardians a wallet has. * `proposalId`: public value, tracks proposal ids for guardians adding new owners. ## Structs * `NewOwnerProposal`: stores the following data for guardians proposing new owners: * `newOwnerProposed`: address of the new owner that a guardian is proposing to add. * `approvalCount`: how many guardians have approved this proposal (quorum required 60% of total guardians). * `guardiansApproved`: array of the guardian addresses that have approved this proposal. * `resolved`: boolean to indicate whether the proposal has been actioned or discarded. ## Modifiers * `onlyOwner()`: check caller is an owner of the `EtherspotWallet` contract or `EtherspotWallet` contract itself. * `onlyGuardian()`: check caller is a guardian of the `EtherspotWallet` contract. * `onlyOwnerOrGuardian()`: check caller is an owner of the `EtherspotWallet` contract, a guardian of the `EtherspotWallet` contract or `EtherspotWallet` contract itself. * `onlyOwnerOrEntryPoint()`: check caller is an owner of the `EtherspotWallet` contract, the `EntryPoint` contract or `EtherspotWallet` contract itself. ## Mappings * `mapping(address => bool) private owners`: A mapping of addresses to boolean values that indicate whether the address is an owner or not. * `mapping(address => bool) private guardians`: A mapping of addresses to boolean values that indicate whether the address is a guardian or not. * `mapping(uint256 => NewOwnerProposal) private proposals`: A mapping of proposal ids to NewOwnerProposals (see Structs). ## Events * `event OwnerAdded(address newOwner)`: Triggered when a new guardian is added. * `event OwnerRemoved(address removedOwner)`: Triggered when a guardian is removed. * `event GuardianAdded(address newGuardian)`: Triggered when a new guardian is added. * `event GuardianRemoved(address removedGuardian)`: Triggered when a guardian is removed. * `event ProposalSubmitted(uint256 proposalId, address newOwnerProposed, address proposer)`: Triggered when a guardian proposes a new owner to be added to `EtherspotWallet`. * `event QuorumNotReached(uint256 proposalId, address newOwnerProposed, uint256 guardiansApproved)`: Triggered when a guardian cosigns a proposal to add a new owner to `EtherspotWallet` but the required quorum has not been reached (60% of total guardians). * `event ProposalDiscarded(uint256 proposalId)`: Triggered when a proposal will not be actioned and is discarded. ## Public/External Functions * `function isOwner(address _address) public view returns (bool)`: Checks if an address is a owner or not. * `function isGuardian(address _address) public view returns (bool)`: Checks if an address is a guardian or not. * `function getProposal(uint256 _proposalId) public view returns (address ownerProposed_, uint256 approvalCount_, address[] memory guardiansApproved_)`: Returns stored information of a NewOwnerProposal for the specified proposal id. * Error `ACL:: invalid proposal id`: Has to be a valid proposal. * `function guardianPropose(address _newOwner) external onlyGuardian`: Allows a guardian to propose adding a new `EtherspotWallet` owner. Only one proposal is allowed at any time and needs to either be actioned or discarded for another proposal to be submitted. * Error `ACL:: not enough guardians to propose new owner (minimum 3)`: Requires minimum amount of 3 guardians to add a new owner. * Emits `ProposalSubmitted(proposalId, _newOwner, msg.sender)`. * `function guardianCosign(uint256 _proposalId) external onlyGuardian`: Allows other guardians than the one that proposed adding a new owner to cosign the proposal. If quorum (60% of total guardians) is not reached then `QuorumNotReached` event will be emitted. If quorum is reached, it will add a new owner. * Error `ACL:: invalid proposal id`: Has to be a valid proposal. * Error `ACL:: guardian already signed proposal`: Guardian cannot sign proposal more than once. * Emits `QuorumNotReached(_proposalId, newOwner, proposals[_proposalId].approvalCount)`. * `function discardCurrentProposal() external onlyOwnerOrGuardian`: Allows for a proposal to be discarded if it is decided that it will not be required/actioned. ## Internal Functions * `function _addOwner(address _newOwner) internal`: Adds a new owner. * Error `ACL:: zero address`: Cannot add zero address as owner. * Error `ACL:: already owner`: Address cannot already be an owner. * Error `ACL:: guardian cannot be owner`: Guardians cannot add themselves as an owner. * Emits `OwnerAdded(_newOwner)`. * `function _removeOwner(address _owner) internal`: Removes an existing owner. * Error `ACL:: removing self`: An owner cannot remove themselves. * Error `ACL:: non-existant owner`: Must be a valid owner to be removed. * Emits `OwnerRemoved(_owner)`. * `function _addGuardian(address _newGuardian) internal`: Adds a new guardian. * Error `ACL:: zero address`: Cannot add zero address as guardian. * Error `ACL:: already guardian`: Existing guardian cannot be re-added as a guardian. * Error `ACL:: guardian cannot be owner`: Guardians cannot be owners. * Emits `GuardianAdded(_newGuardian)`. * `function _removeGuardian(address _guardian) internal`: Removes an existing guardian. * Error `ACL:: non-existant guardian`: Must be a valid guardian to be removed. * Emits `GuardianRemoved(_guardian)`. * `function _checkIfSigned(uint256 _proposalId) internal view returns (bool)`: Checks if a guardian has cosigned a NewOwnerProposal. * `function _checkQuorumReached(uint256 _proposalId) internal view returns (bool)`: Checks if a NewOwnerProposal has reached the required quorum to be processed or not. ## Contract Source Code ```Solidity theme={null} // SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import "../interfaces/IAccessController.sol"; abstract contract AccessController is IAccessController { uint128 immutable MULTIPLY_FACTOR = 1000; uint16 immutable SIXTY_PERCENT = 600; uint24 immutable INITIAL_PROPOSAL_TIMELOCK = 24 hours; uint256 public ownerCount; uint256 public guardianCount; uint256 public proposalId; uint256 public proposalTimelock; mapping(address => bool) private owners; mapping(address => bool) private guardians; mapping(uint256 => NewOwnerProposal) private proposals; struct NewOwnerProposal { address newOwnerProposed; bool resolved; uint256 approvalCount; address[] guardiansApproved; uint256 proposedAt; } modifier onlyOwner() { require( isOwner(msg.sender) || msg.sender == address(this), "ACL:: only owner" ); _; } modifier onlyGuardian() { require(isGuardian(msg.sender), "ACL:: only guardian"); _; } modifier onlyOwnerOrGuardian() { require( isOwner(msg.sender) || isGuardian(msg.sender), "ACL:: only owner or guardian" ); _; } modifier onlyOwnerOrEntryPoint(address _entryPoint) { require( msg.sender == _entryPoint || isOwner(msg.sender), "ACL:: not owner or entryPoint" ); _; } function isOwner(address _address) public view returns (bool) { return owners[_address]; } function isGuardian(address _address) public view returns (bool) { return guardians[_address]; } function addOwner(address _newOwner) external onlyOwner { _addOwner(_newOwner); } function removeOwner(address _owner) external onlyOwner { _removeOwner(_owner); } function addGuardian(address _newGuardian) external onlyOwner { _addGuardian(_newGuardian); } function removeGuardian(address _guardian) external onlyOwner { _removeGuardian(_guardian); } function changeProposalTimelock(uint256 _newTimelock) external onlyOwner { proposalTimelock = _newTimelock; emit ProposalTimelockChanged(_newTimelock); } function getProposal( uint256 _proposalId ) public view returns ( address ownerProposed_, uint256 approvalCount_, address[] memory guardiansApproved_, bool resolved_, uint256 proposedAt_ ) { require( _proposalId != 0 && _proposalId <= proposalId, "ACL:: invalid proposal id" ); NewOwnerProposal memory proposal = proposals[_proposalId]; return ( proposal.newOwnerProposed, proposal.approvalCount, proposal.guardiansApproved, proposal.resolved, proposal.proposedAt ); } function discardCurrentProposal() external onlyOwnerOrGuardian { require( !proposals[proposalId].resolved, "ACL:: proposal already resolved" ); if (isGuardian(msg.sender) && proposalTimelock > 0) require( (proposals[proposalId].proposedAt + proposalTimelock) < block.timestamp, "ACL:: guardian cannot discard proposal until timelock relased" ); if (isGuardian(msg.sender) && proposalTimelock == 0) require( (proposals[proposalId].proposedAt + INITIAL_PROPOSAL_TIMELOCK) < block.timestamp, "ACL:: guardian cannot discard proposal until timelock relased" ); proposals[proposalId].resolved = true; emit ProposalDiscarded(proposalId, msg.sender); } function guardianPropose(address _newOwner) external onlyGuardian { require( guardianCount >= 3, "ACL:: not enough guardians to propose new owner (minimum 3)" ); if ( proposals[proposalId].guardiansApproved.length != 0 && proposals[proposalId].resolved == false ) revert("ACL:: latest proposal not yet resolved"); proposalId = proposalId + 1; proposals[proposalId].newOwnerProposed = _newOwner; proposals[proposalId].guardiansApproved.push(msg.sender); proposals[proposalId].approvalCount += 1; proposals[proposalId].resolved = false; proposals[proposalId].proposedAt = block.timestamp; emit ProposalSubmitted(proposalId, _newOwner, msg.sender); } function guardianCosign() external onlyGuardian { require(proposalId != 0, "ACL:: invalid proposal id"); require( !_checkIfSigned(proposalId), "ACL:: guardian already signed proposal" ); require( !proposals[proposalId].resolved, "ACL:: proposal already resolved" ); proposals[proposalId].guardiansApproved.push(msg.sender); proposals[proposalId].approvalCount += 1; address newOwner = proposals[proposalId].newOwnerProposed; if (_checkQuorumReached(proposalId)) { proposals[proposalId].resolved = true; _addOwner(newOwner); } else { emit QuorumNotReached( proposalId, newOwner, proposals[proposalId].approvalCount ); } } // INTERNAL function _addOwner(address _newOwner) internal { // no check for address(0) as used when creating wallet via BLS. require(_newOwner != address(0), "ACL:: zero address"); require(!owners[_newOwner], "ACL:: already owner"); if (isGuardian(_newOwner)) revert("ACL:: guardian cannot be owner"); emit OwnerAdded(_newOwner); owners[_newOwner] = true; ownerCount = ownerCount + 1; } function _addGuardian(address _newGuardian) internal { require(_newGuardian != address(0), "ACL:: zero address"); require(!guardians[_newGuardian], "ACL:: already guardian"); require(!isOwner(_newGuardian), "ACL:: guardian cannot be owner"); emit GuardianAdded(_newGuardian); guardians[_newGuardian] = true; guardianCount = guardianCount + 1; } function _removeOwner(address _owner) internal { require(owners[_owner], "ACL:: non-existant owner"); require(ownerCount > 1, "ACL:: wallet cannot be ownerless"); emit OwnerRemoved(_owner); owners[_owner] = false; ownerCount = ownerCount - 1; } function _removeGuardian(address _guardian) internal { require(guardians[_guardian], "ACL:: non-existant guardian"); emit GuardianRemoved(_guardian); guardians[_guardian] = false; guardianCount = guardianCount - 1; } function _checkIfSigned(uint256 _proposalId) internal view returns (bool) { for (uint i; i < proposals[_proposalId].guardiansApproved.length; i++) { if (proposals[_proposalId].guardiansApproved[i] == msg.sender) { return true; } } return false; } function _checkQuorumReached( uint256 _proposalId ) internal view returns (bool) { return ((proposals[_proposalId].approvalCount * MULTIPLY_FACTOR) / guardianCount >= SIXTY_PERCENT); } } ``` ## License This contract is licensed under the MIT license. # Contract Deployments Source: https://etherspot.fyi/prime-sdk/contracts/deployments ## Mainnets | Contract Name | Network | Contract Address | | ------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `EtherspotPaymaster` | `mainnet` | [0x450D2374dd63F62929Ff8C64B443c17A139B669A](https://etherscan.io/address/0x450D2374dd63F62929Ff8C64B443c17A139B669A) | | `EtherspotWalletFactory` | `mainnet` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://etherscan.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `arbitrum` | [0x68BA597bf6B9097b1D89b8E0D34646D30997f773](https://arbiscan.io/address/0x68BA597bf6B9097b1D89b8E0D34646D30997f773) | | `EtherspotWalletFactory` | `arbitrum` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://arbiscan.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `optimism` | [0x9e1D8FD5563C3723e98539b0DD0972f6e1Bd5a4d](https://optimistic.etherscan.io/address/0x9e1D8FD5563C3723e98539b0DD0972f6e1Bd5a4d) | | `EtherspotWalletFactory` | `optimism` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://optimistic.etherscan.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `polygon` | [0x80463F318075C893e343EeC461BbeB445fDe7951](https://polygonscan.com/address/0x80463F318075C893e343EeC461BbeB445fDe7951) | | `EtherspotWalletFactory` | `polygon` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://polygonscan.com/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `fuse` | [0x805650ce74561C85baA44a8Bd13E19633Fd0F79d](https://explorer.fuse.io/address/0x805650ce74561C85baA44a8Bd13E19633Fd0F79d) | | `EtherspotWalletFactory` | `fuse` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://explorer.fuse.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `gnosis` | [0xFbfb916cC102ca3530151B8A552696159c921025](https://gnosisscan.io/address/0xFbfb916cC102ca3530151B8A552696159c921025) | | `EtherspotWalletFactory` | `gnosis` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://gnosisscan.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `mantle` | [0x8A41594e5c6Fe492e437414c24eA6f401186b8d2 ](https://explorer.mantle.xyz/address/0x8A41594e5c6Fe492e437414c24eA6f401186b8d2) | | `EtherspotWalletFactory` | `mantle` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E ](https://explorer.mantle.xyz/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `klaytn` | [0x4ebd86AAF89151b5303DB072e0205C668e31E5E7](https://scope.klaytn.com/account/0x4ebd86AAF89151b5303DB072e0205C668e31E5E7?tabId=internalTx) | | `EtherspotWalletFactory` | `klaytn` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://scope.klaytn.com/account/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E?tabId=txList) | | `EtherspotPaymaster` | `avalanche` | [0x527569794781671319f20374A050BDbef4181aB3](https://snowtrace.io/address/0x527569794781671319f20374A050BDbef4181aB3) | | `EtherspotWalletFactory` | `avalanche` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://snowtrace.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `bsc` | [0xEA5ecE95D3A28f9faB161779d20128b449F9EC9C](https://bscscan.com/address/0xEA5ecE95D3A28f9faB161779d20128b449F9EC9C) | | `EtherspotWalletFactory` | `bsc` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://bscscan.com/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `base` | [0x810FA4C915015b703db0878CF2B9344bEB254a40](https://basescan.org/address/0x810FA4C915015b703db0878CF2B9344bEB254a40) | | `EtherspotWalletFactory` | `base` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://basescan.org/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `linea` | [0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C](https://lineascan.build/address/0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C) | | `EtherspotWalletFactory` | `linea` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://lineascan.build/address/0x7f6d8f107fe8551160bd5351d5f1514a6ad5d40e) | | `EtherspotPaymaster` | `bifrost` | [0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C](https://explorer.mainnet.thebifrost.io/address/0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C) | | `EtherspotWalletFactory` | `bifrost` | [0x527bAb8bDC50A809d7c35D0129173BBed55C5EAE](https://explorer.mainnet.thebifrost.io/address/0x527bAb8bDC50A809d7c35D0129173BBed55C5EAE) | | `EtherspotPaymaster` | `flare` | [0x8A41594e5c6Fe492e437414c24eA6f401186b8d2](https://flare-explorer.flare.network/address/0x8A41594e5c6Fe492e437414c24eA6f401186b8d2) | | `EtherspotWalletFactory` | `flare` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://flare-explorer.flare.network/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `scroll` | [0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C](https://scrollscan.com/address/0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C) | | `EtherspotWalletFactory` | `scroll` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://scrollscan.com/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | ## Testnets | Contract Name | Network | Contract Address | | ------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `EtherspotPaymaster` | `goerli` | [0x590Cf408033f6516F5CBA15189033bF7452fDa0c](https://goerli.etherscan.io/address/0x590Cf408033f6516F5CBA15189033bF7452fDa0c) | | `EtherspotWalletFactory` | `goerli` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://goerli.etherscan.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `sepolia` | [0x18D9405BfdD22eA84C0B481e0AAA4638e4F71Af4](https://sepolia.etherscan.io/address/0x18D9405BfdD22eA84C0B481e0AAA4638e4F71Af4) | | `EtherspotWalletFactory` | `sepolia` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://sepolia.etherscan.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `arbitrumGoerli` | [0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C](https://goerli.arbiscan.io/address/0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C) | | `EtherspotWalletFactory` | `arbitrumGoerli` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://goerli.arbiscan.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `optimismGoerli` | [0x590Cf408033f6516F5CBA15189033bF7452fDa0c](https://goerli-optimism.etherscan.io/address/0x590Cf408033f6516F5CBA15189033bF7452fDa0c) | | `EtherspotWalletFactory` | `optimismGoerli` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://goerli-optimism.etherscan.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `mumbai` | [0xe893A26DD53b325BffAacDfA224692EfF4C448c4](https://mumbai.polygonscan.com/address/0xe893A26DD53b325BffAacDfA224692EfF4C448c4) | | `EtherspotWalletFactory` | `mumbai` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://mumbai.polygonscan.com/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `fuseSparknet` | [0x46dC4A1804de656551Ff30A53dd39ED373B30520](https://explorer.fusespark.io/address/0x46dC4A1804de656551Ff30A53dd39ED373B30520) | | `EtherspotWalletFactory` | `fuseSparknet` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://explorer.fusespark.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `baseGoerli` | [0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C](https://base-goerli.blockscout.com/address/0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C) | | `EtherspotWalletFactory` | `baseGoerli` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://base-goerli.blockscout.com/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `chiado` | [0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C](https://blockscout.chiadochain.net/address/0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C) | | `EtherspotWalletFactory` | `chiado` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://blockscout.chiadochain.net/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `rskt` | [0x898c530A5fA37720DcF1843AeCC34b6B0cBaEB8a](https://explorer.testnet.rsk.co/address/0x898c530A5fA37720DcF1843AeCC34b6B0cBaEB8a) | | `EtherspotWalletFactory` | `rskt` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://explorer.testnet.rsk.co/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `kromaSepolia` | [0x810FA4C915015b703db0878CF2B9344bEB254a40](https://blockscout.sepolia.kroma.network/address/0x810FA4C915015b703db0878CF2B9344bEB254a40) | | `EtherspotWalletFactory` | `kromaSepolia` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://blockscout.sepolia.kroma.network/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `taikot` | [0xcaDBADcFeD5530A49762DFc9d1d712CcD6b09b25](https://explorer.test.taiko.xyz/address/0xcaDBADcFeD5530A49762DFc9d1d712CcD6b09b25) | | `EtherspotWalletFactory` | `taikot` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://explorer.test.taiko.xyz/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `verse` | [0x898c530A5fA37720DcF1843AeCC34b6B0cBaEB8a](https://scan.sandverse.oasys.games/address/0x898c530A5fA37720DcF1843AeCC34b6B0cBaEB8a) | | `EtherspotWalletFactory` | `verse` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://scan.sandverse.oasys.games/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `klaytnTest` | [0x810FA4C915015b703db0878CF2B9344bEB254a40](https://baobab.klaytnscope.com/account/0x810FA4C915015b703db0878CF2B9344bEB254a40?tabId=internalTx) | | `EtherspotWalletFactory` | `klaytnTest` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://baobab.klaytnscope.com/account/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E?tabId=txList) | | `EtherspotPaymaster` | `fuji` | [0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C](https://testnet.snowtrace.io/address/0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C) | | `EtherspotWalletFactory` | `fuji` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://testnet.snowtrace.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `bscTestnet` | [0x153e26707DF3787183945B88121E4Eb188FDCAAA](https://testnet.bscscan.com/address/0x153e26707DF3787183945B88121E4Eb188FDCAAA) | | `EtherspotWalletFactory` | `bscTestnet` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://testnet.bscscan.com/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `lineaTestnet` | [0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C](https://goerli.lineascan.build/address/0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C) | | `EtherspotWalletFactory` | `lineaTestnet` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://goerli.lineascan.build/address/0x7f6d8f107fe8551160bd5351d5f1514a6ad5d40e) | | `EtherspotPaymaster` | `bifrostTest` | [0x4602818693b3D0d9D8D5CaeA4e7803031ee8DBd3](https://explorer.testnet.thebifrost.io/address/0x4602818693b3D0d9D8D5CaeA4e7803031ee8DBd3) | | `EtherspotWalletFactory` | `bifrostTest` | [0x155321a3F8706159A43FDAd68bdD4AE41B0664f4](https://explorer.testnet.thebifrost.io/address/0x155321a3F8706159A43FDAd68bdD4AE41B0664f4) | | `EtherspotPaymaster` | `scrollSepolia` | [0xe893A26DD53b325BffAacDfA224692EfF4C448c4](https://sepolia-blockscout.scroll.io/address/0xe893A26DD53b325BffAacDfA224692EfF4C448c4) | | `EtherspotWalletFactory` | `scrollSepolia` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://sepolia-blockscout.scroll.io/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `mantleTestnet` | [0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C](https://explorer.testnet.mantle.xyz/address/0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C) | | `EtherspotWalletFactory` | `mantleTestnet` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://explorer.testnet.mantle.xyz/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | | `EtherspotPaymaster` | `coston2` | [0x2a18C360b525824B3e5656B5a705554f2a5036Be](https://coston2-explorer.flare.network/address/0x2a18C360b525824B3e5656B5a705554f2a5036Be) | | `EtherspotWalletFactory` | `coston2` | [0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E](https://coston2-explorer.flare.network/address/0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E) | # Entrypoint Contract Error Codes Source: https://etherspot.fyi/prime-sdk/contracts/error-codes A detailed look into what each of the EntryPoint error codes mean. The entrypoint contract and address is the same across all EVM networks, this page gives a brief overview of each error code to help with debug. ## AA1x error codes relate to creating an account ### AA10 Sender already constructed The sender has already been established, therefore there is no need to execute the initCode. This issue might arise if you try to create an account multiple times. ### AA13 initCode failed or OOG The initCode failed to create the account or ran out of gas. "OOG" is an abbreviation for Out-Of-Gas. Check the amount of gas consumed, and then verify the initCode or the factory contract is correct. ### AA14 initCode must return sender The initCode fails to provide the sender address. Verify either the initCode or the factory contract for potential issues. ### AA15 initCode must create sender The initCode within the user operation fails to generate an account. Please inspect the initCode or the factory contract for potential issues. ## AA2x error codes relate to the sender of the user operation ### AA20 Account not deployed The user operation's sender has not been deployed, and no initCode is specified. If this is the initial transaction for this account, ensure that an initCode is included. Otherwise, confirm that the specified sender address is correct and corresponds to an ERC-4337 account. ### AA21 Didn't pay prefund The sender lacked sufficient funds to prefund the EntryPoint for the user operation. If a paymaster is utilized, it's probable that the paymasterAndData field is not properly set. In the absence of a paymaster, the sender's address might not have an adequate gas token balance. Following the execution of the user operation, any remaining prefund is reimbursed to the sender. ### AA22 Expired or not due The signature is invalid as it falls outside the designated time range. ### AA23 reverted (or OOG) The validation of the sender's signature was declined or encountered a gas exhaustion issue, denoted by "OOG" for Out-Of-Gas. This could be due to a low verificationGasLimit. In case you encounter an "AA23 reverted OOG" error, it indicates insufficient native tokens with the sender to cover the gas expenses for the User Operation. If you intend to utilize a Paymaster for sponsorship, verify that the paymasterAndData field in the user operation is accurately configured to facilitate the correct handling of gas fees. ### AA24 Signature Error Verify the format of the signature field in the user operation, as it may be in an incompatible format. ### AA25 Invalid account nonce The nonce is not valid. The user operation might be reusing an outdated nonce or incorrectly formatting the nonce. ## AA3x error codes relate to paymasters ### AA30 Paymaster not deployed The specified paymaster address in paymasterAndData does not contain any code. Verify that the initial characters in the paymasterAndData field correspond to the intended paymaster address. ### AA31 Paymaster deposit too low The paymaster has insufficient funds. Additional gas tokens must be deposited into the EntryPoint for the paymaster, typically achieved by invoking the deposit function within the paymaster contract. If you are utilizing a paymaster service, promptly get in touch with them. ### AA32 Paymaster expired or not due The paymaster's signature is invalid as it falls outside the designated time range. ### AA33 reverted (or OOG) The paymaster validation was declined or encountered a gas exhaustion issue, denoted by "OOG" for Out-Of-Gas. Initially, verify the paymaster's signature in paymasterAndData. If the signature is accurate, consider that the verificationGasLimit may be set too low. ### AA34 Signature Error The paymaster's signature is not valid. Examine the format of the signature in paymasterAndData. ## AA4x error codes relate to verification generally ### AA40 Over verification gas limit The verification gas limit has been surpassed. Review the verificationGasLimit specified in your user operation. ### AA41 Too little verification gas Verifying the user operation took too much gas and did not complete. You may need to increase verificationGasLimit. ## AA5x errors relate to actions after the user operation was executed ### AA50 PostOp reverted Following the completion of the user operation, the execution of additional logic by the EntryPoint resulted in a revert. ### AA51 prefund below actualGasCost The actual cost of the user operation is higher than the total amount of gas approved. The prefund is the amount that the EntryPoint is allowed to execute the user operation. After the user operation is executed, the remainder of the prefund is credited back to the sender. # Paymaster Source: https://etherspot.fyi/prime-sdk/contracts/etherspotpaymaster ## Overview EtherspotPaymaster is a smart contract that allows an external signer to sign a UserOperation and pay for the gas costs of executing that UserOperation. The paymaster signs to agree to pay for gas, and the wallet signs to prove identity and account ownership. ## Version Solidity pragma version `^0.8.12`. ## Global Variables * `VALID_TIMESTAMP_OFFSET`: A constant of type uint256 that represents a 20 second time offset used to validate timestamps. * `SIGNATURE_OFFSET`: A constant of type uint256 that represents an 84-byte signature offset. * `COST_OF_POST`: The pre-calculated cost of calling `_postOp` (required for gas calculation). ## Imports * `ECDSA`: A contract from the OpenZeppelin library used for signature verification. * `IERC20`: A contract from the OpenZeppelin library used for interacting with ERC20 tokens. * `SafeERC20`: A contract from the OpenZeppelin library used for safe ERC20 token transfers. * `Whitelist`: Whitelist.sol smart contract used for whitelisting addresses. ## Mappings * `sponsorFunds`: A mapping of type `mapping(address => uint256)` used to store the amount of sponsor funds transferred to the paymaster contract. * `senderNonce`: A mapping of type `mapping(address => uint256)` used to store the nonce of the sender. ## Events * `SponsorSuccessful`: An event emitted when a sponsor successfully sponsors a user operation. * `SponsorUnsuccessful`: An event emitted when a sponsor is unsuccessful in sponsoring a user operation. ## Constructor * `constructor(IEntryPoint _entryPoint)`: A constructor that accepts an `IEntryPoint` parameter `_entryPoint`. ## Public/External Functions * `depositFunds() external payable`: A function used to deposit funds to the paymaster. * Error `EtherspotPaymaster:: Not enough balance`: Checks that the sponsor has enough funds to deposit into paymaster contract. * `withdrawFunds() address payable _sponsor, uint256 _amount) external`: A function used to withdraw sponsor funds from paymaster. * Error `EtherspotPaymaster:: can only withdraw own funds`: Checks `msg.sender` matches the sponsor address provided. * Error `EtherspotPaymaster:: not enough deposited funds`: Checks amount is >= deposited funds for the given sponsor. * `checkSponsorFunds(address _sponsor) public view returns (uint256)`: A function used to check the amount of sponsor funds transferred to the paymaster contract for a given sponsor. * `function getHash(UserOperation calldata userOp, uint48 validUntil, uint48 validAfter) public view returns (bytes32)`: A function to return the hash to be sign off-chain (and validate on-chain) by a sponsor. * `function parsePaymasterAndData(bytes calldata paymasterAndData) public pure returns (uint48 validUntil, uint48 validAfter, bytes calldata signature)`: Extracts `validUntil`, `validAfter` and `signature` from `paymasterAndData` passed in as input. ## Internal Functions * `_debitSponsor(address _sponsor, uint256 _amount) internal`: A function used to debit a sponsor's fund amount for gas costs once a transaction has been processed. * `_creditSponsor`: A function used to credit a sponsor's deposited amount. * `_pack(UserOperation calldata userOp)`: A function used to pack the user operation. * `_validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 requiredPreFund)`: A function used to verify the external signer (sponsor) that signed the request. Debits the sponsor's deposited balance by full requiredPreFund amount (credits back in `_postOp`). * Error `EtherspotPaymaster:: invalid signature length in paymasterAndData`: Triggered on incorrect signature length. * Error `EtherspotPaymaster:: Sponsor paymaster funds too low`: Checks sponsor has enough funds to pay the gas costs for a sponsored UserOperation. * `_postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal override`: A function that overrides the `_postOp` function from `BasePaymaster.sol` that checks for a validated UserOperation and credits back any remaining funds after the gas cost for the UserOperation execution plus `_postOp` call. * Emits `SponsorSuccessful(paymaster, sender, userOpHash)` on successfully sponsored UserOperation. * Emits `SponsorUnsuccessful(paymaster, sender, userOpHash)` on unsuccessfully sponsored UserOperation. ## Contract Source Code ```Solidity theme={null} // SPDX-License-Identifier: MIT pragma solidity ^0.8.12; /* solhint-disable reason-string */ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../../account-abstraction/contracts/core/UserOperationLib.sol"; import "./BasePaymaster.sol"; import "./Whitelist.sol"; /** * A sample paymaster that uses external service to decide whether to pay for the UserOp. * The paymaster trusts an external signer to sign the transaction. * The calling user must pass the UserOp to that external signer first, which performs * whatever off-chain verification before signing the UserOp. * Note that this signature is NOT a replacement for wallet signature: * - the paymaster signs to agree to PAY for GAS. * - the wallet signs to prove identity and account ownership. */ contract EtherspotPaymaster is BasePaymaster, Whitelist, ReentrancyGuard { using ECDSA for bytes32; using UserOperationLib for UserOperation; uint256 private constant VALID_TIMESTAMP_OFFSET = 20; uint256 private constant SIGNATURE_OFFSET = 84; // calculated cost of the postOp uint256 private constant COST_OF_POST = 40000; mapping(address => uint256) private _sponsorBalances; event SponsorSuccessful(address paymaster, address sender); constructor(IEntryPoint _entryPoint) BasePaymaster(_entryPoint) {} function depositFunds() external payable nonReentrant { _creditSponsor(msg.sender, msg.value); entryPoint.depositTo{value: msg.value}(address(this)); } function withdrawFunds(uint256 _amount) external nonReentrant { require( getSponsorBalance(msg.sender) >= _amount, "EtherspotPaymaster:: not enough deposited funds" ); _debitSponsor(msg.sender, _amount); entryPoint.withdrawTo(payable(msg.sender), _amount); } function getSponsorBalance(address _sponsor) public view returns (uint256) { return _sponsorBalances[_sponsor]; } function _debitSponsor(address _sponsor, uint256 _amount) internal { _sponsorBalances[_sponsor] -= _amount; } function _creditSponsor(address _sponsor, uint256 _amount) internal { _sponsorBalances[_sponsor] += _amount; } function _pack( UserOperation calldata userOp ) internal pure returns (bytes32) { return keccak256( abi.encode( userOp.getSender(), userOp.nonce, keccak256(userOp.initCode), keccak256(userOp.callData), userOp.callGasLimit, userOp.verificationGasLimit, userOp.preVerificationGas, userOp.maxFeePerGas, userOp.maxPriorityFeePerGas ) ); } /** * return the hash we're going to sign off-chain (and validate on-chain) * this method is called by the off-chain service, to sign the request. * it is called on-chain from the validatePaymasterUserOp, to validate the signature. * note that this signature covers all fields of the UserOperation, except the "paymasterAndData", * which will carry the signature itself. */ function getHash( UserOperation calldata userOp, uint48 validUntil, uint48 validAfter ) public view returns (bytes32) { //can't use userOp.hash(), since it contains also the paymasterAndData itself. return keccak256( abi.encode( _pack(userOp), block.chainid, address(this), validUntil, validAfter ) ); } /** * verify our external signer signed this request. * the "paymasterAndData" is expected to be the paymaster and a signature over the entire request params * paymasterAndData[:20] : address(this) * paymasterAndData[20:84] : abi.encode(validUntil, validAfter) * paymasterAndData[84:] : signature */ function _validatePaymasterUserOp( UserOperation calldata userOp, bytes32 /*userOpHash*/, uint256 requiredPreFund ) internal override returns (bytes memory context, uint256 validationData) { (requiredPreFund); ( uint48 validUntil, uint48 validAfter, bytes calldata signature ) = parsePaymasterAndData(userOp.paymasterAndData); // ECDSA library supports both 64 and 65-byte long signatures. // we only "require" it here so that the revert reason on invalid signature will be of "EtherspotPaymaster", and not "ECDSA" require( signature.length == 64 || signature.length == 65, "EtherspotPaymaster:: invalid signature length in paymasterAndData" ); bytes32 hash = ECDSA.toEthSignedMessageHash( getHash(userOp, validUntil, validAfter) ); address sig = userOp.getSender(); // check for valid paymaster address sponsorSig = ECDSA.recover(hash, signature); // don't revert on signature failure: return SIG_VALIDATION_FAILED if (!_check(sponsorSig, sig)) { return ("", _packValidationData(true, validUntil, validAfter)); } uint256 costOfPost = userOp.maxFeePerGas * COST_OF_POST; uint256 totalPreFund = requiredPreFund + costOfPost; // check sponsor has enough funds deposited to pay for gas require( getSponsorBalance(sponsorSig) >= totalPreFund, "EtherspotPaymaster:: Sponsor paymaster funds too low" ); // debit requiredPreFund amount _debitSponsor(sponsorSig, totalPreFund); // no need for other on-chain validation: entire UserOp should have been checked // by the external service prior to signing it. return ( abi.encode(sponsorSig, sig, totalPreFund, costOfPost), _packValidationData(false, validUntil, validAfter) ); } function parsePaymasterAndData( bytes calldata paymasterAndData ) public pure returns (uint48 validUntil, uint48 validAfter, bytes calldata signature) { (validUntil, validAfter) = abi.decode( paymasterAndData[VALID_TIMESTAMP_OFFSET:SIGNATURE_OFFSET], (uint48, uint48) ); signature = paymasterAndData[SIGNATURE_OFFSET:]; } function _postOp( PostOpMode, bytes calldata context, uint256 actualGasCost ) internal override { ( address paymaster, address sender, uint256 totalPrefund, uint256 costOfPost ) = abi.decode(context, (address, address, uint256, uint256)); _creditSponsor(paymaster, totalPrefund - (actualGasCost + costOfPost)); emit SponsorSuccessful(paymaster, sender); } } ``` ## License This contract is licensed under the MIT license. # EtherspotWallet Source: https://etherspot.fyi/prime-sdk/contracts/etherspotwallet # EtherspotWallet.sol ## Overview EtherspotWallet is a EIP4337 compliant smart contract that acts as a multi-ownership wallet. It allows multiple owners to control a single account and execute transactions via an entry point contract. It also allows for guardian account recovery. ## Version Solidity pragma version `^0.8.12`. ## Imports * `BaseAccount`: A contract that defines the interface for accounts and provides implementations for required methods for following the EIP4337 standards. * `UUPSUpgradeable`: A contract that enables the contract to be upgraded. * `Initializable`: A contract that provides support for initializer functions. * `TokenCallbackHandler`: A contract that defines the interface for token callbacks. * `IERC721Wallet`: A contract that provides support for ERC721 signature validation. * `AccessController`: A contract that provides support for owner and guardian management. ## Variables * `_entryPoint`: An IEntryPoint variable that holds the address of the entry point contract. * `_filler`: A bytes28 variable that serves as a filler. * `_nonce`: A uint96 variable that holds the current nonce. ## Events * `EtherspotWalletInitialized`: Emitted when the contract is initialized. * `EtherspotWalletReceived`: Emitted when the contract receives ether. * `EntryPointChanged`: Emitted when the entry point contract address is changed. ## Modifiers * `onlyOwner`: Allows only the owners of the contract to call the function. * `onlyOwnerOrGuardian`: Allows only the owners or guardians of the contract to call the function. * `onlyOwnerOrEntryPoint`: Allows only the owners or the entry point contract to call the function. ## Public Functions * `nonce() public view virtual override returns (uint256)`: Returns the current nonce. * `entryPoint() public view virtual override returns (IEntryPoint)`: Returns the entry point contract address. * `initialize(IEntryPoint anEntryPoint, address anOwner) public virtual initializer`: Initializes the contract. Calls `_initialize`. * `getDeposit() public view`: Returns the balance of the wallet deposited to the `EntryPoint` contract. * `addDeposit() public payable`: This function deposits tokens to the `EntryPoint` contract from wallet's address. * `withdrawDepositTo(address payable withdrawAddress, uint256 amount) public onlyOwner`: Withdraws deposited tokens in the `EntryPoint` contract for the wallet to an external address. Only callable by wallet owner. ## External Functions * `receive() external payable`: A fallback function that is triggered when the contract receives ether. * Emits `EtherspotWalletReceived(address indexed from, uint256 indexed amount)`. * `execute(address dest, uint256 value, bytes calldata func) external onlyOwnerOrEntryPoint`: Executes a transaction. It can only be called by the owners or the entry point contract. It calls the \_call() function. * `executeBatch(address[] calldata dest, bytes[] calldata func) external onlyOwnerOrEntryPoint`: Executes a sequence of transactions. It can only be called by the owners or the entry point contract. It calls the \_call() function. * `updateEntryPoint(address _newEntryPoint) external`: Updates the `EntryPoint` contract stored in the wallet. Only callable by wallet owner. * Emits `EntryPointChanged(address(_entryPoint), _newEntryPoint)`. * `addOwner(address _newOwner) external onlyOwnerOrGuardian`: Adds a new owner to the `EtherspotWallet`. Interacts with the `Owned.sol`. Only callable by a wallet owner or an approved guardian. See OWNED.md for more information regarding this. * `removeOwner(address _owner) external onlyOwnerOrGuardian`: Removes an owner from the `EtherspotWallet`. Interacts with the `Owned.sol`. Only callable by a wallet owner or an approved guardian. See OWNED.md for more information regarding this. * `addGuardian(address _newGuardian) external onlyOwner`: Adds a new guardian to the `EtherspotWallet`. Interacts with the `Guarded.sol`. Only callable by a wallet owner. See GUARDED.md for more information regarding this. * `removeGuardian(address _guardian) external onlyOwner`: Removes a guardian from the `EtherspotWallet`. Interacts with the `Guarded.sol`. Only callable by a wallet owner. See GUARDED.md for more information regarding this. ## Internal Functions * `_initialize(IEntryPoint anEntryPoint, address anOwner) internal virtual`: Initializes the contract. It sets the entry point contract address and adds the initial owner. * Emits `event EtherspotWalletInitialized(IEntryPoint indexed entryPoint, address indexed owner)`. * `_validateAndUpdateNonce(UserOperation calldata userOp) internal override`: This function validates the user operation nonce and updates the wallet's nonce. * `_validateSignature(UserOperation calldata userOp, bytes32 userOpHash) internal virtual override`: This function validates the UserOperation signature. * `_call(address target, uint256 value, bytes memory data) internal`: Makes a contract call to another smart contract. * `_authorizeUpgrade(address newImplementation) internal view override`: Upgrades `EtherspotWallet`. Only callable by wallet owner. ## Contract source code ```Solidity theme={null} // SPDX-License-Identifier: MIT pragma solidity ^0.8.12; /* solhint-disable avoid-low-level-calls */ /* solhint-disable no-inline-assembly */ /* solhint-disable reason-string */ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import "../../account-abstraction/contracts/core/BaseAccount.sol"; import "../../account-abstraction/contracts/samples/callback/TokenCallbackHandler.sol"; import "../interfaces/IEtherspotWallet.sol"; import "../interfaces/IEtherspotWalletFactory.sol"; import "../access/AccessController.sol"; contract EtherspotWallet is BaseAccount, UUPSUpgradeable, Initializable, TokenCallbackHandler, AccessController, IEtherspotWallet { using ECDSA for bytes32; /// STORAGE IEntryPoint private immutable _entryPoint; IEtherspotWalletFactory private immutable _walletFactory; bytes4 private constant ERC1271_SUCCESS = 0x1626ba7e; /// EXTERNAL METHODS constructor( IEntryPoint anEntryPoint, IEtherspotWalletFactory anWalletFactory ) { require( address(anEntryPoint) != address(0) && address(anWalletFactory) != address(0), "EtherspotWallet:: invalid constructor parameter" ); _entryPoint = anEntryPoint; _walletFactory = anWalletFactory; _disableInitializers(); // solhint-disable-previous-line no-empty-blocks } function execute( address dest, uint256 value, bytes calldata func ) external onlyOwnerOrEntryPoint(address(entryPoint())) { _call(dest, value, func); } function executeBatch( address[] calldata dest, uint256[] calldata value, bytes[] calldata func ) external onlyOwnerOrEntryPoint(address(entryPoint())) { require( dest.length > 0 && dest.length == value.length && value.length == func.length, "EtherspotWallet:: executeBatch: wrong array lengths" ); for (uint256 i; i < dest.length; ) { _call(dest[i], value[i], func[i]); unchecked { ++i; } } } /** * Implementation of ISignatureValidator * @dev doesn't allow the owner to be a smart contract, SCW should use {isValidSig} * @param hash 32 bytes hash of the data signed on the behalf of address(msg.sender) * @param signature Signature byte array associated with _dataHash * @return ERC1271 magic value. */ function isValidSignature( bytes32 hash, bytes calldata signature ) external view returns (bytes4) { address owner = ECDSA.recover(hash, signature); if (isOwner(owner)) { return ERC1271_SUCCESS; } return bytes4(0xffffffff); } receive() external payable { emit EtherspotWalletReceived(msg.sender, msg.value); } /// PUBLIC /// @inheritdoc BaseAccount function entryPoint() public view virtual override(BaseAccount, IEtherspotWallet) returns (IEntryPoint) { return _entryPoint; } /** * check current account deposit in the entryPoint */ function getDeposit() public view returns (uint256) { return entryPoint().balanceOf(address(this)); } function initialize(address anOwner) public virtual initializer { _initialize(anOwner); } /** * deposit more funds for this account in the entryPoint */ function addDeposit() external payable { entryPoint().depositTo{value: msg.value}(address(this)); } /** * withdraw value from the account's deposit * @param withdrawAddress target to send to * @param amount to withdraw */ function withdrawDepositTo( address payable withdrawAddress, uint256 amount ) external onlyOwner { entryPoint().withdrawTo(withdrawAddress, amount); } /// INTERNAL function _initialize(address anOwner) internal virtual { _addOwner(anOwner); emit EtherspotWalletInitialized(_entryPoint, anOwner); } function _call(address target, uint256 value, bytes memory data) internal { (bool success, bytes memory result) = target.call{value: value}(data); if (!success) { assembly { revert(add(result, 32), mload(result)) } } } function _validateSignature( UserOperation calldata userOp, bytes32 userOpHash ) internal virtual override returns (uint256) { bytes32 hash = userOpHash.toEthSignedMessageHash(); if (!isOwner(hash.recover(userOp.signature))) return SIG_VALIDATION_FAILED; return 0; } function _authorizeUpgrade( address newImplementation ) internal view override onlyOwner { require( _walletFactory.checkImplementation(newImplementation), "EtherspotWallet:: upgrade implementation invalid" ); } } ``` ## License This contract is licensed under the MIT license. # Whitelist Source: https://etherspot.fyi/prime-sdk/contracts/whitelist # Whitelist.sol ## Overview This smart contract is a simple whitelist implementation that allows an owner to add and remove addresses to/from a whitelist. The contract is designed in its current iteration to interact with `EtherspotPaymaster` contract to allow for a single Paymaster contract that can handle payments from sponsors to fund transaction gas for approved wallets. ## Version Solidity pragma version `^0.8.12`. ## Mappings `mapping(address => mapping(address => bool)) public whitelist`: A mapping of sponsor addresses to another mapping of account addresses to a boolean value that indicates whether the account address is whitelisted for the sponsor address. ## Events * `event WhitelistInitialized(address owner)`: Triggered when the contract is initialized. * `event AddedToWhitelist(address indexed paymaster, address indexed account)`: Triggered when an account is added to the whitelist for a specific paymaster. * `event AddedBatchToWhitelist(address indexed paymaster, address[] indexed accounts)`: Triggered when multiple accounts are added to the whitelist for a specific paymaster. * `event RemovedFromWhitelist(address indexed paymaster, address indexed account)`: Triggered when an account is removed from the whitelist for a specific paymaster. * `event RemovedBatchFromWhitelist(address indexed paymaster, address[] indexed accounts)`: Triggered when multiple accounts are removed from the whitelist for a specific paymaster. ## External Functions * `function check(address _sponsor, address _account) external view returns (bool)`: Checks if an account is whitelisted for a specific sponsor. * `function add(address _account) external`: Adds an account to the whitelist for the caller. * Emits `AddedToWhitelist(msg.sender, _account)`. * `function addBatch(address[] calldata _accounts) external`: Adds multiple accounts to the whitelist for the caller. * Emits`AddedBatchToWhitelist(msg.sender, _accounts)`. * `function remove(address _account) external`: Removes an account from the whitelist for the caller. * Emits `RemovedFromWhitelist(msg.sender, _account)`. * `function removeBatch(address[] calldata _accounts) external`: Removes multiple accounts from the whitelist for the caller. * Emits `RemovedBatchFromWhitelist(msg.sender, _accounts)`. ## Internal Functions * `function _check(address _sponsor, address _account) internal view returns (bool)`: Checks if an account is whitelisted for a specific sponsor. * `function _add(address _account) internal`: Adds an account to the whitelist for the caller. * Error `Whitelist:: Zero address`: Zero address cannot be added to whitelist. * Error `Whitelist:: Account is already whitelisted`: Existing whitelisted address cannot be re-added to the whitelist. * `function _addBatch(address[] calldata _accounts) internal`: Adds multiple accounts to the whitelist for the caller. * `function _remove(address _account) internal`: Removes an account from the whitelist for the caller. * Error `Whitelist:: Zero address`: Cannot try to remove zero address from whitelist. * Error `Whitelist:: Account is not whitelisted`: Must be a valid whitelisted account to be removed from whitelist. * `function _removeBatch(address[] calldata _accounts) internal`: Removes multiple accounts from the whitelist for the caller. ## Contact Source Code ```Solidity theme={null} // SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import "../interfaces/IWhitelist.sol"; contract Whitelist is IWhitelist { // Mappings mapping(address => mapping(address => bool)) private whitelist; // External function check( address _sponsor, address _account ) external view returns (bool) { return _check(_sponsor, _account); } function addToWhitelist(address _account) external { _add(_account); emit AddedToWhitelist(msg.sender, _account); } function addBatchToWhitelist(address[] calldata _accounts) external { _addBatch(_accounts); emit AddedBatchToWhitelist(msg.sender, _accounts); } function removeFromWhitelist(address _account) external { _remove(_account); emit RemovedFromWhitelist(msg.sender, _account); } function removeBatchFromWhitelist(address[] calldata _accounts) external { _removeBatch(_accounts); emit RemovedBatchFromWhitelist(msg.sender, _accounts); } // Internal function _check( address _sponsor, address _account ) internal view returns (bool) { return whitelist[_sponsor][_account]; } function _add(address _account) internal { require(_account != address(0), "Whitelist:: Zero address"); require( !_check(msg.sender, _account), "Whitelist:: Account is already whitelisted" ); whitelist[msg.sender][_account] = true; } function _addBatch(address[] calldata _accounts) internal { for (uint256 ii; ii < _accounts.length; ++ii) { _add(_accounts[ii]); } } function _remove(address _account) internal { require(_account != address(0), "Whitelist:: Zero address"); require( _check(msg.sender, _account), "Whitelist:: Account is not whitelisted" ); whitelist[msg.sender][_account] = false; } function _removeBatch(address[] calldata _accounts) internal { for (uint256 ii; ii < _accounts.length; ++ii) { _remove(_accounts[ii]); } } } ``` ## License This contract is licensed under the MIT license. # Data Service Source: https://etherspot.fyi/prime-sdk/data-service Along with using the Prime SDK, developers can make use of the data service to retreive information such as Account Balances. For making these api-calls, you'll need to get a data service api key. Without passing one in, there is a default key which is very heavily rate limited. ## Code example Start by importing DataUtils from the Prime SDK like so: ```javascript theme={null} import { DataUtils } from '@etherspot/prime-sdk'; ``` Then initialise the Data Service with your api key: ```javascript theme={null} // initializating Data service... const dataService = new DataUtils(data-api-key); ``` Finally, call one of the functions listed below: ```javascript theme={null} const hash = '0x7f8633f21d0c0c71d248333a0a2b976495015109a270a6f8a51befe3baf6fb6e'; const transaction = await dataService.getTransaction({ hash, chainId: 11155111 }); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet transaction:`, transaction); ``` ## Function List | Function Name | Description | | ---------------------------- | ----------------------------------------------------- | | getAccountBalances() | Returns the Account Balance. | | getTransaction() | Returns details about a transaction. | | getNftList() | Returns a list of NFTs that the account owns. | | getExchangeSupportedAssets() | Returns exchange supported tokens on the account. | | getExchangeOffers() | Returns a list of exchange offers between two assets. | | getAdvanceRoutesLiFi() | Returns bridging routes between two assets via LiFi. | | getStepTransaction() | | | getCrossChainQuotes() | Returns bridging routes between two assets | | getTokenLists() | Returns a token list. | | getTokenListTokens() | Returns a specific token from the list. | | fetchExchangeRates() | Returns exchange rates between two tokens. | # Add Guardians Source: https://etherspot.fyi/prime-sdk/examples/add-guardians ```javascript theme={null} import { ethers } from 'ethers'; import { EtherspotBundler, PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); async function main() { const bundlerApiKey = 'etherspot_public_key'; // initializating sdk... const primeSdk = new PrimeSdk( { privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }, ); console.log('address: ', primeSdk.state.EOAAddress); // get address of EtherspotWallet const address: string = await primeSdk.getCounterFactualAddress(); // update the addresses in this array with the guardian addresses you want to set const guardianAddresses: string[] = [ '0xa8430797A27A652C03C46D5939a8e7698491BEd6', '0xaf2D76acc5B0e496f924B08491444076219F2f35', '0xBF1c0A9F3239f5e7D35cE562Af06c92FB7fdF0DF', ]; console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const addGuardianInterface = new ethers.utils.Interface(['function addGuardian(address _newGuardian)']); const addGuardianData1 = addGuardianInterface.encodeFunctionData('addGuardian', [guardianAddresses[0]]); const addGuardianData2 = addGuardianInterface.encodeFunctionData('addGuardian', [guardianAddresses[1]]); const addGuardianData3 = addGuardianInterface.encodeFunctionData('addGuardian', [guardianAddresses[2]]); // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch let userOpsBatch = await primeSdk.addUserOpsToBatch({ to: address, data: addGuardianData1 }); userOpsBatch = await primeSdk.addUserOpsToBatch({ to: address, data: addGuardianData2 }); userOpsBatch = await primeSdk.addUserOpsToBatch({ to: address, data: addGuardianData3 }); console.log('transactions: ', userOpsBatch); // sign transactions added to the batch const op = await primeSdk.estimate(); console.log(`Estimated UserOp: ${await printOp(op)}`); // sign the userOps and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while (userOpsReceipt == null && Date.now() < timeout) { await sleep(2); userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # User Specific callGasLimit Source: https://etherspot.fyi/prime-sdk/examples/call-gas-limit ```javascript theme={null} import { ethers } from 'ethers'; import { EtherspotBundler, PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); const recipient = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address const value = '0.0001'; // transfer value const bundlerApiKey = 'etherspot_public_key'; async function main() { // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', primeSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await primeSdk.addUserOpsToBatch({to: recipient, value: ethers.utils.parseEther(value)}); console.log('transactions: ', transactionBatch); // get balance of the account address const balance = await primeSdk.getNativeBalance(); console.log('balances: ', balance); // estimate transactions added to the batch and get the fee data for the UserOp // passing callGasLimit as 40000 to manually set it const op = await primeSdk.estimate({ callGasLimit: 4000 }); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Concurrent UserOps Source: https://etherspot.fyi/prime-sdk/examples/concurrent-userops ```javascript theme={null} import { ethers, providers } from 'ethers'; import { EtherspotBundler, PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); const recipient = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address const value = '0.000001'; // transfer value const bundlerApiKey = 'etherspot_public_key'; async function main() { const provider = new providers.JsonRpcProvider(process.env.RPC_PROVIDER_URL); // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', primeSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); if ((await provider.getCode(address)).length <= 2) { console.log("Account must be created first"); return; } // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await primeSdk.addUserOpsToBatch({to: recipient, value: ethers.utils.parseEther(value)}); console.log('transactions: ', transactionBatch); // get balance of the account address const balance = await primeSdk.getNativeBalance(); console.log('balances: ', balance); // Note that usually Bundlers do not allow sending more than 10 concurrent userops from an unstaked entites (wallets, factories, paymaster) // Staked entities can send as many userops as they want let concurrentUseropsCount = 5; const userops = []; const uoHashes = []; while (--concurrentUseropsCount >= 0) { const op = await primeSdk.estimate({ key: concurrentUseropsCount }); console.log(`Estimate UserOp: ${await printOp(op)}`); userops.push(op); } console.log("Sending userops..."); for (const op of userops) { const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); uoHashes.push(uoHash); } console.log('Waiting for transactions...'); const userOpsReceipts = new Array(uoHashes.length).fill(null); const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipts.some(receipt => receipt == null)) && (Date.now() < timeout)) { await sleep(2); for (let i = 0; i < uoHashes.length; ++i) { if (userOpsReceipts[i]) continue; const uoHash = uoHashes[i]; userOpsReceipts[i] = await primeSdk.getUserOpReceipt(uoHash); } } if (userOpsReceipts.some(receipt => receipt != null)) { console.log('\x1b[33m%s\x1b[0m', `Transaction hashes: `); for (const uoReceipt of userOpsReceipts) { if (!uoReceipt) continue; console.log(uoReceipt.receipt.transactionHash); } } else { console.log("Could not submit any user op"); } } main() .catch(console.error) .finally(() => process.exit()); ``` # Show gas fee in fiat Source: https://etherspot.fyi/prime-sdk/examples/gas-fee-in-usd With account abstraction we want to give users who are new to blockchain as simple a user experience as possible. Showing the gas fee in USD rather than the native token will make it a lot easier to understand for newer users, and we can do this in a couple of simple steps. First we start by estimating the current batch of [UserOps](/account-abstraction/userops). This will be the units of gas used. ```typescript theme={null} const estimation = await primeSdk.estimate(); ``` Then we want to get the gas in wei for this batch. ```typescript theme={null} const totalGas = await primeSdk.totalGasEstimated(estimation); ``` And multiply this by the current gas fees on the network. [gas = units of gas used \* (base fee + priority fee)](https://ethereum.org/en/developers/docs/gas/#:~:text=A%20standard%20ETH%20transfer%20requires,get%20back%20the%20remaining%2029%2C000.) ```typescript theme={null} const gas = totalGas.mul(estimation.maxFeePerGas as BigNumberish); ``` Next fix the formatting using the formatEther from ethers. ```typescript theme={null} const gasInMatic = ethers.utils.formatEther(gas); ``` Then get the current price of the native token to the network. ```typescript theme={null} const rateData = await primeSdk.fetchExchangeRates({tokens: [ethers.constants.AddressZero], chainId: 137}); ``` Finally, multiply the current token price by the gas in the token to get the gas price in USD for this transaction. ```typescript theme={null} const priceInUsd = usdRate.usd * (+gasInMatic); ``` # Get Account Balances Source: https://etherspot.fyi/prime-sdk/examples/get-account-balances ```javascript theme={null} import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { // initializating Data service... const dataService = new DataUtils(); const balances = await dataService.getAccountBalances({ account: '', // address chainId: 1, }); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet balances:`, balances); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get bridging quotes (LiFi) Source: https://etherspot.fyi/prime-sdk/examples/get-bridging-quotes-lifi ```javascript theme={null} import { ethers, utils } from 'ethers'; import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main(): Promise { // initializating Data service... const dataService = new DataUtils(); const fromChainId = 56; const toChainId = 137; const fromAmount = utils.parseUnits('1', 18); const quoteRequestPayload = { fromAddress: '', fromChainId: fromChainId, toChainId: toChainId, fromTokenAddress: ethers.constants.AddressZero, toTokenAddress: ethers.constants.AddressZero, fromAmount: fromAmount, }; const quotes = await dataService.getAdvanceRoutesLiFi(quoteRequestPayload); console.log('\x1b[33m%s\x1b[0m', `Quotes:`, quotes.items); if (quotes.items.length > 0) { const quote = quotes.items[0]; // Selected the first route const transactions = await dataService.getStepTransaction({ route: quote, account: '' }); console.log('\x1b[33m%s\x1b[0m', `transactions:`, transactions); } } main() .catch(console.error) .finally(() => process.exit()); ``` # Get bridging quotes (other) Source: https://etherspot.fyi/prime-sdk/examples/get-bridging-quotes-other ```javascript theme={null} import { utils } from 'ethers'; import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; import { BridgingQuotes, CrossChainServiceProvider } from '../src/sdk/data'; dotenv.config(); async function main(): Promise { // initializating Data service... const dataService = new DataUtils('data-api-key'); const XdaiUSDC = '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83'; // Xdai - USDC const MaticUSDC = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174'; // Matic - USDC const fromChainId = 137; const toChainId = 100; const fromTokenAddress: string = MaticUSDC; const toTokenAddress: string = XdaiUSDC; // MATIC USDC has 6 decimals const fromAmount = utils.parseUnits('1', 6); // 10 USDC const quoteRequestPayload = { fromChainId: fromChainId, toChainId: toChainId, fromTokenAddress: fromTokenAddress, toTokenAddress: toTokenAddress, fromAddress: '', // from address fromAmount: fromAmount, serviceProvider: CrossChainServiceProvider.LiFi, // Optional parameter }; const quotes: BridgingQuotes = await dataService.getCrossChainQuotes(quoteRequestPayload); console.log('\x1b[33m%s\x1b[0m', `Quotes:`, quotes); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get Exchange Rates Source: https://etherspot.fyi/prime-sdk/examples/get-exchange-rates ```javascript theme={null} import { RateData } from '../src/sdk/data'; import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main(): Promise { // initializating Data service... const dataService = new DataUtils(); const ETH_AAVE_ADDR = '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9'; const ETH_MATIC_ADDR = '0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0'; const ETH_USDC_ADDR = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; const TOKEN_LIST = [ETH_AAVE_ADDR, ETH_MATIC_ADDR, ETH_USDC_ADDR]; const ETH_CHAIN_ID = 1; const requestPayload = { tokens: TOKEN_LIST, chainId: ETH_CHAIN_ID, }; const rates: RateData = await dataService.fetchExchangeRates(requestPayload); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet Rates:`, rates); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get Exchange Supported assets Source: https://etherspot.fyi/prime-sdk/examples/get-exchange-supported-assets ```javascript theme={null} import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main(): Promise { // initializating Data service... const dataService = new DataUtils(); const exchangeSupportedAssets = await dataService.getExchangeSupportedAssets({ page: 1, limit: 100, account: '', chainId: Number(process.env.CHAIN_ID) }); console.log('\x1b[33m%s\x1b[0m', `Found exchange supported assets:`, exchangeSupportedAssets.items.length); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get NFT List Source: https://etherspot.fyi/prime-sdk/examples/get-nft-list ```javascript theme={null} import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main(): Promise { // initializating Data service... const dataService = new DataUtils(); const chainId = 137; const account = ''; // account address const nfts = await dataService.getNftList({ chainId, account }); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet nfts:`, nfts); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get SimpleAccount Address Source: https://etherspot.fyi/prime-sdk/examples/get-simpleaccount-address ```javascript theme={null} import { EtherspotBundler, Factory, PrimeSdk } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { const bundlerApiKey = 'etherspot_public_key'; // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), factoryWallet: Factory.SIMPLE_ACCOUNT, bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) // get SimpleAccount address... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `SimpleAccount address: ${address}`); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get Token List Source: https://etherspot.fyi/prime-sdk/examples/get-token-list ```javascript theme={null} import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main(): Promise { // initializating Data service... const dataService = new DataUtils(); const tokenLists = await dataService.getTokenLists({ chainId: 1 }); console.log('\x1b[33m%s\x1b[0m', `TokenLists:`, tokenLists); const { name } = tokenLists[0]; let tokenListTokens = await dataService.getTokenListTokens({ chainId: 1 }); console.log('\x1b[33m%s\x1b[0m', `Default token list tokens length:`, tokenListTokens.length); tokenListTokens = await dataService.getTokenListTokens({ chainId: 1, name, }); console.log('\x1b[33m%s\x1b[0m', `${name} token list tokens length:`, tokenListTokens.length); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get Transaction Source: https://etherspot.fyi/prime-sdk/examples/get-transaction ```javascript theme={null} import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main(): Promise { // initializating Data service... const dataService = new DataUtils(); const hash = '0x7f8633f21d0c0c71d248333a0a2b976495015109a270a6f8a51befe3baf6fb6e'; const transaction = await dataService.getTransaction({ hash, chainId: 11155111 }); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet transaction:`, transaction); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get Zerodev Address Source: https://etherspot.fyi/prime-sdk/examples/get-zerodev-address ```javascript theme={null} import { EtherspotBundler, Factory, PrimeSdk } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { const bundlerApiKey = 'etherspot_public_key'; // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), factoryWallet: Factory.ZERO_DEV, bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) // get ZeroDev address... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `ZeroDev address: ${address}`); } main() .catch(console.error) .finally(() => process.exit()); ``` # Get Address Source: https://etherspot.fyi/prime-sdk/examples/getaddress ```javascript theme={null} import { EtherspotBundler, PrimeSdk } from '../src'; import * as dotenv from 'dotenv'; dotenv.config(); async function main() { const bundlerApiKey = 'etherspot_public_key'; const customBundlerUrl = ''; // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey, customBundlerUrl) }) // Testnets dont need apiKey on bundlerProvider // get EtherspotWallet address... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); } main() .catch(console.error) .finally(() => process.exit()); ``` # Run Prime SDK examples Source: https://etherspot.fyi/prime-sdk/examples/intro This will not work with the social logins example, please follow the instructions on that page for that one specifically. To run these examples, you can clone the [Etherspot Prime SDK repo](https://github.com/etherspot/etherspot-prime-sdk/). Then cd into the directory and run: ``` npm i && npm run init ``` Then create a .env file in the etherspot-prime-sdk directory. Within it we want to put something like this: ``` WALLET_PRIVATE_KEY=YOUR_PRIVATE_KEY_HERE CHAIN_ID=1 ``` Please ensure to prefix your private key with "0x" so the SDK is instantiated correctly. Then, you can try running the get address example like this: ``` npm run 01-get-address ``` And it should output something like this to show the Etherspot SDK has been instantiated using the private key you used. ``` > ./node_modules/.bin/ts-node ./examples/01-get-address EtherspotWallet address: 0xb9798dF748E45F1fB724F50B7542802E5462a58a ``` # Paymaster valid until after specified time Source: https://etherspot.fyi/prime-sdk/examples/paymaster-valid-until-after-time ```javascript theme={null} import { ethers } from 'ethers'; import { EtherspotBundler, PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); const recipient = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address const value = '0.0001'; // transfer value const bundlerApiKey = 'etherspot_public_key'; const arka_api_key = 'etherspot_public_key'; // Only testnets are available, if you need further assistance in setting up a paymaster service for your dapp, please reach out to us on discord or https://etherspot.fyi/arka/intro const arka_url = 'https://arka.etherspot.io'; const queryString = `?apiKey=${arka_api_key}&chainId=${Number(process.env.CHAIN_ID)}`; async function main() { // initializing sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', primeSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // get balance of the account address let balance = await primeSdk.getNativeBalance(); console.log('balances: ', balance); // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await primeSdk.addUserOpsToBatch({ to: recipient, value: ethers.utils.parseEther(value) }); console.log('transactions: ', transactionBatch); // get balance of the account address balance = await primeSdk.getNativeBalance(); console.log('balances: ', balance); /* estimate transactions added to the batch and get the fee data for the UserOp validUntil and validAfter are optional defaults to 10 mins of expiry from send call and should be passed in terms of milliseconds For example purpose, the valid is fixed as expiring in 100 mins once the paymaster data is generated validUntil and validAfter is relevant only with sponsor transactions and not for token paymasters */ const op = await primeSdk.estimate({ paymasterDetails: { url: `${arka_url}${queryString}`, context: { mode: 'sponsor', validAfter: new Date().valueOf(), validUntil: new Date().valueOf() + 6000000 } } }); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Social Logins Source: https://etherspot.fyi/prime-sdk/examples/social-logins Learn how to add social logins to your dapp. Etherspot has partnered with Web3Auth, to bring users a frictionless Web3 experience by combining the power of Web3auth's social login onboarding and Etherspot's Smart Wallet infrastructure. Users can easily login with a number of different platforms such as Twitter or Google mail. An Etherspot smart contract wallet is then created for them and they are ready to interact with your dapp. You should have installed the appropriate [Etherspot Prime](installation) and Web3Auth packages before proceeding. ## Packages ```bash npm theme={null} npm i @web3auth/no-modal@5.2.0 --save npm i @web3auth/openlogin-adapter@5.2.0 --save npm i @web3auth/base@5.2.0 --save ``` ```bash yarn theme={null} yarn add @web3auth/no-modal yarn add @web3auth/openlogin-adapter yarn add @web3auth/base ``` Below is an example of a working Web3Auth social login implementation using the Etherspot Prime SDK. A full example which you can build and deploy can be found [here](https://github.com/etherspot/etherspot-prime-sdk-with-web3auth-social-logins-nextjs-example/tree/main). ## Variables to replace **WEB3AUTH\_CHAIN\_ID\_HEX** is the chain id in hex, for example 0xaa36a7 for Sepolia. For the Etherspot Prime SDK part of this we only look at: 1. If it's a [chain we support](../get-started/chains-supported). 2. Whether it's a mainnet or test net chain. Then we only generate wallets on either of these. **WEB3AUTH\_CLIENT\_ID** is the web3auth client id specific to your project. You can learn at how to get one [here](https://web3auth.io/docs/dashboard-setup/get-client-id). ## Code example ```typescript theme={null} import React from 'react'; import styled from 'styled-components'; import { Web3AuthNoModal } from '@web3auth/no-modal'; import { OpenloginAdapter } from '@web3auth/openlogin-adapter'; import { CHAIN_NAMESPACES, WALLET_ADAPTERS } from '@web3auth/base'; import { Oval } from 'react-loader-spinner'; import { LOGIN_PROVIDER } from '@toruslabs/base-controllers'; import { PrimeSdk, Web3WalletProvider } from '@etherspot/prime-sdk'; import { ethers } from 'ethers'; // Initialise web3auth with our custom values const web3auth = new Web3AuthNoModal({ chainConfig: { chainNamespace: "eip155", chainId: process.env.WEB3AUTH_CHAIN_ID_HEX, }, clientId: process.env.WEB3AUTH_CLIENT_ID as string, }); // Define the openLoginAdapter const openloginAdapter = new OpenloginAdapter(); web3auth.configureAdapter(openloginAdapter); const App = () => { const [isConnecting, setIsConnecting] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState(''); const [walletAddress, setWalletAddress] = React.useState(''); // Logout function to clear web3auth cache const logout = async () => { setWalletAddress(''); try { await web3auth.logout({ cleanup: true }); web3auth.clearCache(); } catch (e) { console.error(e); } } // Function to pass in specific platform to web3auth // E.g Twitter, gmail const loginWithProvider = async (loginProvider: string) => { if (isConnecting) return; setIsConnecting(true); setErrorMessage(''); setWalletAddress(''); let newErrorMessage; if (web3auth.status !== 'connected') { await web3auth.init(); try { // Auth via loginProvider await web3auth.connectTo(WALLET_ADAPTERS.OPENLOGIN, { loginProvider, mfaLevel: 'none', }); } catch (e) { // @ts-ignore newErrorMessage = e?.message; } } if (newErrorMessage) { setErrorMessage(newErrorMessage); setIsConnecting(false); } if (web3auth.status !== 'connected' || !web3auth.provider) { setErrorMessage('Something went wrong, please try again later.'); setIsConnecting(false); } // Initialising web3Auth Provider as Web3 Injectable const mappedProvider = new Web3WalletProvider(web3auth.provider); await mappedProvider.refresh(); // Instantiate Etherspot Prime SDK with wrapped web3auth provider const etherspotPrimeSdk = new PrimeSdk(mappedProvider, { chainId: ethers.BigNumber.from(process.env.WEB3AUTH_CHAIN_ID_HEX as string).toNumber() }); // Get smart account address try { const address = await etherspotPrimeSdk.getCounterFactualAddress(); } catch (e) { console.error(e); } if (!address) { setErrorMessage('Something went wrong, please try again later.'); setIsConnecting(false); } // Set smart wallet address generated by Etherspot Prime SDK setWalletAddress(address); setIsConnecting(false); } // GUI return ( {walletAddress && ( Your address on Ethereum blockchain: {walletAddress} Logout )} {isConnecting && ( )} {!isConnecting && !walletAddress && ( <> loginWithProvider(LOGIN_PROVIDER.GOOGLE)}> Login with Google loginWithProvider(LOGIN_PROVIDER.LINKEDIN)}> Login with LinkedIn loginWithProvider(LOGIN_PROVIDER.GITHUB)}> Login with GitHub )} {errorMessage && {errorMessage}} ) } export default App; ``` # Swap Source: https://etherspot.fyi/prime-sdk/examples/swap ```javascript theme={null} import { DataUtils } from '../src'; import * as dotenv from 'dotenv'; import { BigNumber, constants } from 'ethers'; dotenv.config(); async function main(): Promise { // initializating Data service... const dataService = new DataUtils(); const exchangeSupportedAssets = await dataService.getExchangeSupportedAssets({ page: 1, limit: 100, account: '', chainId: Number(process.env.CHAIN_ID) }); console.log('\x1b[33m%s\x1b[0m', `Found exchange supported assets:`, exchangeSupportedAssets.items.length); const fromTokenAddress = '0xe3818504c1b32bf1557b16c238b2e01fd3149c17'; const toTokenAddress = constants.AddressZero; const fromAmount = '1000000000000000000'; const fromChainId = 1; const offers = await dataService.getExchangeOffers({ fromAddress: '', fromChainId, fromTokenAddress, toTokenAddress, fromAmount: BigNumber.from(fromAmount), }); console.log('\x1b[33m%s\x1b[0m', `Exchange offers:`, offers); } main() .catch(console.error) .finally(() => process.exit()); ``` # Transfer NFT Source: https://etherspot.fyi/prime-sdk/examples/transferNFT ```javascript theme={null} import { ethers } from 'ethers'; import { EtherspotBundler, PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); // add/change these values const recipient = '0xD129dB5e418e389c3F7D3ae0B8771B3f76799A52'; // recipient wallet address const tokenAddress = '0xe55C5793a52AF819fBf3e87a23B36708E6FDd2Cc'; const tokenId = 4; const bundlerApiKey = 'etherspot_public_key'; async function main() { // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', primeSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const erc721Interface = new ethers.utils.Interface([ 'function safeTransferFrom(address _from, address _to, uint256 _tokenId)' ]) const erc721Data = erc721Interface.encodeFunctionData('safeTransferFrom', [address, recipient, tokenId]); // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const userOpsBatch = await primeSdk.addUserOpsToBatch({to: tokenAddress, data: erc721Data}); console.log('transactions: ', userOpsBatch); // sign transactions added to the batch const op = await primeSdk.estimate(); console.log(`Estimated UserOp: ${await printOp(op)}`); // sign the userOps and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Transfer ERC20 Source: https://etherspot.fyi/prime-sdk/examples/transfererc20 ```javascript theme={null} import { ethers } from 'ethers'; import { EtherspotBundler, PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import { ERC20_ABI } from '../src/sdk/helpers/abi/ERC20_ABI'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); // add/change these values const recipient = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address const value = '0.1'; // transfer value const tokenAddress = '0x326C977E6efc84E512bB9C30f76E30c160eD06FB'; const bundlerApiKey = 'etherspot_public_key'; async function main() { // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', primeSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); const provider = new ethers.providers.JsonRpcProvider(process.env.BUNDLER_URL) // get erc20 Contract Interface const erc20Instance = new ethers.Contract(tokenAddress, ERC20_ABI, provider); // get decimals from erc20 contract const decimals = await erc20Instance.functions.decimals(); // get transferFrom encoded data const transactionData = erc20Instance.interface.encodeFunctionData('transfer', [recipient, ethers.utils.parseUnits(value, decimals)]) // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const userOpsBatch = await primeSdk.addUserOpsToBatch({to: tokenAddress, data: transactionData}); console.log('transactions: ', userOpsBatch); // estimate transactions added to the batch and get the fee data for the UserOp const op = await primeSdk.estimate(); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Transfer Funds Source: https://etherspot.fyi/prime-sdk/examples/transferfunds ```javascript theme={null} import { ethers } from 'ethers'; import { EtherspotBundler, PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); const recipient = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address const value = '0.00001'; // transfer value const bundlerApiKey = 'etherspot_public_key'; async function main() { // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', primeSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await primeSdk.addUserOpsToBatch({to: recipient, value: ethers.utils.parseEther(value)}); console.log('transactions: ', transactionBatch); // get balance of the account address const balance = await primeSdk.getNativeBalance(); console.log('balances: ', balance); // estimate transactions added to the batch and get the fee data for the UserOp const op = await primeSdk.estimate(); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Use Paymaster Source: https://etherspot.fyi/prime-sdk/examples/use-paymaster ```javascript theme={null} import { ethers } from 'ethers'; import { EtherspotBundler, PrimeSdk } from '../src'; import { printOp } from '../src/sdk/common/OperationUtils'; import * as dotenv from 'dotenv'; import { sleep } from '../src/sdk/common'; dotenv.config(); const recipient = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address const value = '0.01'; // transfer value const api_key = 'etherspot_public_key'; // Only testnets are available, if you need further assistance in setting up a paymaster service for your dapp, please reach out to us on discord or https://etherspot.fyi/arka/intro const bundlerApiKey = 'etherspot_public_key'; async function main() { // initializating sdk... const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), bundlerProvider: new EtherspotBundler(Number(process.env.CHAIN_ID), bundlerApiKey) }) console.log('address: ', primeSdk.state.EOAAddress) // get address of EtherspotWallet... const address: string = await primeSdk.getCounterFactualAddress(); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await primeSdk.addUserOpsToBatch({ to: recipient, value: ethers.utils.parseEther(value) }); console.log('transactions: ', transactionBatch); // get balance of the account address const balance = await primeSdk.getNativeBalance(); console.log('balances: ', balance); // estimate transactions added to the batch and get the fee data for the UserOp const op = await primeSdk.estimate({ paymasterDetails: { url: `https://arka.etherspot.io?apiKey=${api_key}&chainId=${Number(process.env.CHAIN_ID)}`, context: { mode: 'sponsor' } } }); console.log(`Estimate UserOp: ${await printOp(op)}`); // sign the UserOp and sending to the bundler... const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); // get transaction hash... console.log('Waiting for transaction...'); let userOpsReceipt = null; const timeout = Date.now() + 60000; // 1 minute timeout while ((userOpsReceipt == null) && (Date.now() < timeout)) { await sleep(2); userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); } console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); } main() .catch(console.error) .finally(() => process.exit()); ``` # Fiat Onramp Source: https://etherspot.fyi/prime-sdk/fiat-onramp ## Onramper Etherspot have partnered with [Onramper](https://onramper.com/) to provide a seamless fiat onramp experience for both dapp developers and users. ## Onramper Docs | Docs | Link | | ------------------ | -------------------------------------------------------------------------------------------------- | | Crypto supported | [https://docs.onramper.com/docs/coverage-crypto](https://docs.onramper.com/docs/coverage-crypto) | | Networks supported | [https://docs.onramper.com/docs/coverage-network](https://docs.onramper.com/docs/coverage-network) | | Fiat supported | [https://docs.onramper.com/docs/coverage-fiat](https://docs.onramper.com/docs/coverage-fiat) | | Payment methods | [https://docs.onramper.com/docs/coverage-payment](https://docs.onramper.com/docs/coverage-payment) | Using the SDK, developers can easily call a function which will open the [Onramper widget](https://buy.onramper.com/?defaultAmount=10\&defaultFiat=USD) with all the values the user wishes. These include things such as the address of the smart contract account, the type of fiat they want to onramp from, the amount, and the type of crypto they wish to purchase. With the Etherspot SDK instantiated, you can simply call: ```javascript theme={null} primeSdk.getFiatOnRamp() ``` Without any values passed in this will open the [default Onramper widget](https://buy.onramper.com/). The Etherspot SDK offers a number of values that can be passed in to make sure the user is directed correctly. All of these are optional. ## Optional Parameters | Parameter | Type | Description | | --------------------- | ------- | -------------------------------------------------------- | | defaultAmount | number | Default fiat amount to display when the widget loads | | defaultFiat | string | Default fiat currency to display when the widget loads | | isAmountEditable | boolean | Default fiat currency to display when the widget loads | | onlyFiats | string | Select the specific fiat currencies to display | | excludeFiats | string | Select the specific fiat currencies to exclude | | defaultCrypto | string | Default crypto currency to display when the widget loads | | excludeCryptos | string | Parameter to exclude specific crypto currencies | | onlyCryptos | string | Select the specific crypto currencies to display | | excludeCryptoNetworks | string | Parameter to exclude specific crypto networks | | onlyCryptoNetworks | string | Select the specific crypto networks to display | | themeName | string | Select the theme the widget will use (dark or light) | So now we can tailor the widget specifically to what the dev wants their user to purchase. In this example we want **USD** to show as the fiat. We only want **ETH** to show as the crypto to buy. We don't want to make the amount configurable. We don't want the amount to be editable. We can put together the function like this: ```javascript theme={null} primeSdk.getFiatOnRamp(defaultFiat="USD", onlyCryptos="ETH", defaultAmount=10, isAmountEditable=false) ``` Which will generate a link like this. **[https://buy.onramper.com/?networkWallets=ETHEREUM:0x705F9070da822804Ed045E26Bac7C3d2F8a4804C\&defaultAmount=10\&defaultFiat=USD\&onlyCryptos=ETH\&isAmountEditable=false](https://buy.onramper.com/?networkWallets=ETHEREUM:0x705F9070da822804Ed045E26Bac7C3d2F8a4804C\&defaultAmount=10\&defaultFiat=USD\&onlyCryptos=ETH\&isAmountEditable=false)** The address will be set to the Etherspot smart account generated by the SDK. # Functions Source: https://etherspot.fyi/prime-sdk/functions This page will contain an exhaustive list of all functions we can call using the SDK. If you want to take a look at the SDK code in more detail then you can check these functions out [here on Github](https://github.com/etherspot/etherspot-prime-sdk/blob/master/src/sdk/sdk.ts). | Function Name | Description | | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | supportedNetworks() | Returns which networks this version of the SDK currently supports. | | destroy() | Destroys the SDK object. | | signMessage() | Signs a message using the Etherspot wallet. | | createSession(dto: CreateSessionDto = ) | Creates a session for the initialised SDK. | | getCounterFactualAddress() | Gets the address of the Etherspot wallet created. | | estimate(gasDetails?: TransactionGasInfoForUserOp) | Returns the estimated amount of gas a transaction will cost. | | totalGasEstimated(userOp: UserOperationStruct) | Returns the estimated amount of gas a batch of transactions will cost. | | getGasFee() | Returns the current gas fee for the network. | | send(userOp: UserOperationStruct) | Sends a struct of signed UserOps to the bundler. | | getNativeBalance() | Returns the native token amount of the Etherspot wallet. | | getUserOpReceipt(userOpHash: string) | Returns the receipt of a UserOp processed by the bundler. | | getUserOpHash(userOp: UserOperationStruct) | Returns the hash of a UserOp processed by the bundler. | | [addUserOpsToBatch(tx: UserOpsRequest)](/prime-sdk/batching-transactions) | Adds a UserOp to the batch before sending. | | clearUserOpsFromBatch()]\() | Clears the batch. | | getAccountContract()]\() | Returns the Etherspot smart contract for the Etherspot smart wallet. | | [getFiatOnRamp(params: OnRamperDto = )](/prime-sdk/fiat-onramp) | Links a user to the OnRamper widget based off of parameters passed in. | | signTypedData(DataFields: TypedDataField\[], message: any)() | EIP-712 signTypedData | # Guardians Source: https://etherspot.fyi/prime-sdk/guardians ## Guardians Guardians are used with Etherspot to add an extra layer of security to your smart contract wallet. ## Adding a new guardian as an owner To add a new guardian to a wallet, the wallet owner can call this function from the Etherspot smart contract wallet address: ```javascript theme={null} addGuardian(address _newGuardian) ``` Guardians can then add new owners to a wallet. The minimum number of guardians required to do this is 3 and it requires a 60% minimum quorum of guardians to pass the proposal to add a new owner (2/3, 3/4. 3/5 etc). An owner itself can just add a new owner by calling: ```javascript theme={null} addOwner(address _newOwner) ``` ## Adding a new owner as a guardian 1. A guardian calls guardianPropose(address \_newOwner). This creates a proposal to add a new owner to the wallet. 2. Other guardians then have to call guardianCosign(). The guardian that created the proposal cannot call this as their vote is already recorded in the proposal. This function then checks whether the minimum quorum threshold for the proposal has been met or not. * If yes, then the proposal is passed and the new owner is added to the wallet. * If no, then nothing happens. Only one proposal is active at any time. If there is an issue with the proposal or its simply just not going to pass, then after a timelock period (which is set as default to 24 hours but can be changed by the owner) the proposal can be discarded and then a new proposal can be submitted. # Installation Source: https://etherspot.fyi/prime-sdk/installation Learn how to install Etherspot Prime SDK **Prerequisite** You should have installed Node.js (version 18.10.0 or higher). Step 1. Install Etherspot Prime SDK with this command ```bash npm theme={null} npm i @etherspot/prime-sdk --save ``` ```bash yarn theme={null} yarn add @etherspot/prime-sdk ``` And that's it! You are now ready to dive into Account Abstraction Now let's learn how to instaniate the SDK. # Instantiation Source: https://etherspot.fyi/prime-sdk/instantiation Before doing anything with the SDK, we must instantiate it. This will create the Etherspot smart account based off of the values we pass in. Step 1. Import the Etherspot Prime SDK. ```javascript theme={null} import { PrimeSdk } from '@etherspot/prime-sdk'; ``` Step 2. Instantiate the SDK with a private key and Chain ID using this block of code. ```javascript theme={null} const primeSdk = new PrimeSdk( { privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), }, ); ``` And that's it! You're now ready to call any of the Prime SDK [functions.](/prime-sdk/functions) You can also pass in different parameters when instantiating the SDK. * chainId : The chain ID of the blockchain. * entryPointAddress : The 4337 entry point address you wish to use. * bundlerRpcUrl : The bundler you wish to use. * walletFactoryAddress : The wallet factory implementation you wish to use. An example of how to configure this is shown below: ```javascript theme={null} const primeSdk = new PrimeSdk( { privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: 123, entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', rpcProviderUrl: 'https://fusetestnet-bundler.etherspot.io/', walletFactoryAddress: '0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E', }, ); ``` You can check **primeSdk.state()** to check if the Etherspot network is up and running correctly. In the next page we'll take a look at the various functions the SDK offers. # Aarc Source: https://etherspot.fyi/prime-sdk/integrations/aarc ## Aarc Aarc is a set of tools built to simplify the use of smart accounts. Etherspot's infrastructure is included within this tooling and available to use within it. In this tutorial we'll learn how to use an Etherspot smart account with the Aarc SDK. We'll setup the Smart Account in two steps using Aarc SDK: 1. Fetch the Smart Account address for the EOA. 2. Deploy and transfer native funds to the Smart Account. ## Guide 1. Create a new directory, initialize npm in it and install Aarc Migrator Package and ethers ``` mkdir aarc_etherspotSW && cd aarc_etherspotSW && npm init -y && npm i @aarc-xyz/migrator ethers@5.7.2 ``` 2. Create a new file index.js in src folder. 3. Import the dependencies. ```javascript theme={null} const { ethers, BigNumber } = require("ethers"); const { Migrator, WALLET_TYPE}= require("@aarc-xyz/migrator"); ``` 4. Get the Aarc API Key (you can learn how to get it from [here](https://docs.aarc.xyz/developer-docs/getting-started/quick-start-guide/get-the-api-key)), your EOA private key, and the RPC URL. 5. Initialize the provider and Aarc SDK ```javascript theme={null} let provider = new ethers.providers.JsonRpcProvider("YOUR_RPC_URL"); let signer = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); let eoaAddress = signer.address; let aarcSDK = new Migrator({ rpcUrl: "YOUR_RPC_URL", chainId: 11155111, // Sepolia Testnet apiKey: "YOUR_AARC_API_KEY" }); ``` 6. Fetch the Smart Account addresses of the EOA. ```javascript theme={null} async function main() { try { const smartWalletAddresses = await aarcSDK.getSmartWalletAddresses( WALLET_TYPE.ETHERSPOT, // WALLET_TYPE imported from @aarc-xyz/migrator eoaAddress ); console.log(' smartWalletAddresses ', smartWalletAddresses); } catch (error) { console.error(' error ', error); } } ``` Here, we provide the WALLET\_TYPE.ETHERSPOT to fetch the Smart Account addresses associated with the EOA. In the response, we will see the addresses associated with the EOA for Etherspot's Smart Accounts. No Smart Account may be deployed, so it will only generate and return the Smart Account address, mentioning its deployment status and the walletIndex. 7. Deploy the Smart Account. To deploy the smart account, use aarcSDK.transferNativeAndDeploy this will take a few parameters, as shown below. * deploymentWalletIndex and receiver can be fetched from the above response. * amount should be in hex string, which can be easily done by usingBigNumbers from ethers. ```javascript theme={null} async function main() { // ... Previous Code ... try { const response = await aarcSDK.transferNativeAndDeploy({ walletType: WALLET_TYPE.ETHERSPOT, owner: eoaAddress, receiver: "0xa1A6dd310A45C4EDB53BF6512317093F820cbA65", signer: signer, deploymentWalletIndex: 0, amount: BigNumber.from("10000")._hex }); console.log("Transfer and Deploy Response: ", response); }catch(error){ console.error(' error ', error); } } ``` 8. Run the script. ```bash theme={null} node src/index.js ``` You will see the following response after deploying the Etherspot Smart Wallet. ```bash theme={null} Transfer and Deploy Response: [ { tokenAddress: '', amount: '0x00', message: 'Deployment tx sent', txHash: '0x6797639f26391e6d5335090df4984ce84e617908f0c1554756c4c60c0f678f28' }, { tokenAddress: '0x0000000000000000000000000000000000001010', amount: '0x2710', message: 'Token transfer tx sent', txHash: '0xd0f141776971f6a6211b514842b02c24c6fde53e9cb392a83e1489d65d9b53b7' } ] ``` # Introduction Source: https://etherspot.fyi/prime-sdk/intro ## Intro This section contains information on how to install the Prime SDK, all of the features you can implement using it and which functions you can use to implement them. ## Prime SDK Features The Prime SDK unlocks a number of easy to implement Account Abstraction features for your dapp or network such as: * Transaction batching * Sponsored Transactions * Pay for transactions with ERC20 Tokens * Social logins * Fiat on/off ramp * A seamless multichain experience # Etherspot Prime on Flare Source: https://etherspot.fyi/prime-sdk/other-chains/getting-started-on-flare An introduction to Account Abstraction on Flare using the Etherspot Prime SDK On this page we'll run through some introductory code on getting set up with using Etherspot Prime on Flare. ## Installing packages **Prerequisite** You should have installed Node.js (version 18.10.0 or higher). Install Etherspot Prime SDK with this command ```bash npm theme={null} npm i @etherspot/prime-sdk --save ``` ```bash yarn theme={null} yarn add @etherspot/prime-sdk ``` ## A look into the code ### Importing the SDK ```javascript theme={null} import { PrimeSdk } from '@etherspot/prime-sdk'; ``` ### Getting the smart account address If you're unsure about the difference between key based accounts and smart contract accounts, [please take a look at this page](/account-abstraction/eoa-vs-scw) In this example we simply create a key based wallet with ethers.js like so: ```javascript theme={null} const randomWallet = ethers.Wallet.createRandom(); setEoaPrivateKey(randomWallet.privateKey); ``` Then we use this key based wallet to instantiate the SDK on Coston2 and get the wallet address. ```javascript theme={null} const primeSdk = new PrimeSdk({ privateKey: privateKey}, { chainId: 114, bundlerProvider: new EtherspotBundler(114, bundlerApiKey) }); const address: string = await primeSdk.getCounterFactualAddress(); console.log(address); ``` ### Sending funds to another address Now that we have a smart contract account on Coston2, we can fund it using [Flare's offical faucet.](https://coston2-faucet.towolabs.com/) Once we have funds on our account ([you can check this here](https://coston2-explorer.flare.network/)) we can try a simple send operation. This can be done through creating a sendFunds function like so: We'll want to save **destinationAddress** and **amount** in some input field within the dapp. ```typescript theme={null} const sendFunds = async () => { const primeSdk = new PrimeSdk({ privateKey: eoaPrivateKey}, { chainId: 114, bundlerProvider: new EtherspotBundler(114, bundlerApiKey) }); // clear the transaction batch await primeSdk.clearUserOpsFromBatch(); // add transactions to the batch const transactionBatch = await primeSdk.addUserOpsToBatch({to: destinationAddress, value: ethers.utils.parseEther(destinationAddress)}); // estimate transactions added to the batch and get the fee data for the UserOp const op = await primeSdk.estimate(); // sign the UserOp and send to bundler const uoHash = await primeSdk.send(op); console.log(`UserOpHash: ${uoHash}`); } ``` ### Next steps For next steps you can look at [functions](/prime-sdk/functions) or [examples](/prime-sdk/examples/intro) to tailor the dapp to what you're trying to achieve. # Account Abstraction on Fuse Source: https://etherspot.fyi/prime-sdk/other-chains/getting-started-on-fuse An introduction to Account Abstraction on Fuse using the Etherspot Prime SDK ## Intro On this guide we'll learn about Account Abstraction and run through some introductory code to get setup with using it on Fuse using the Etherspot Prime SDK. ## Code Tutorial Here we'll get set up with the very basics of using the Prime SDK. We'll set up a React app, install the Etherspot Prime SDK, and create an Etherspot smart contract wallet. Start by creating a react app like so: ```bash theme={null} npx create-react-app etherspot-starter ``` Then cd into the directory and install the Etherspot Prime SDK and Ethers. ```bash theme={null} cd etherspot-starter/ npm i @etherspot/prime-sdk --save npm i ethers --save ``` Now open the code in your editor, and open up App.js. Paste in the following code: ```javascript theme={null} 'use client'; import React from 'react'; import { PrimeSdk } from '@etherspot/prime-sdk'; import { ethers } from 'ethers' import './App.css'; const App = () => { const [etherspotWalletAddress, setEtherspotWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaWalletAddress, setEoaWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaPrivateKey, setEoaPrivateKey] = React.useState(''); const generateRandomEOA = async () => { // Create random EOA wallet const randomWallet = ethers.Wallet.createRandom(); setEoaWalletAddress(randomWallet.address); setEoaPrivateKey(randomWallet.privateKey); } const generateEtherspotWallet = async () => { // Initialise Etherspot SDK const primeSdk = new PrimeSdk({ privateKey: eoaPrivateKey}, { chainId: 122, bundlerProvider: new EtherspotBundler(122, bundlerApiKey) }) const address = await primeSdk.getCounterFactualAddress(); setEtherspotWalletAddress(address); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); } return (

Getting started with Etherspot Prime

To initialise the SDK, it requires a Key Based Wallet(KBW) to be passed in.

KBW Address: {eoaWalletAddress}

Now we can intialise the SDK with this address as the owner, and create an Etherspot smart contract wallet.

Etherspot Smart Account Address: {etherspotWalletAddress}

Now you have a wallet created on Fuse you can explore what else we can do with the Prime SDK.

) } export default App; ``` And that's it! We've not created a random key based wallet on Fuse on page load, and then using this KBW we pass it into the Etherspot Prime SDK, creating an Etherspot Smart Contract Wallet. You can learn more about the differences between these two types of accounts [here.](https://etherspot.fyi/account-abstraction/eoa-vs-scw) ### Next steps Now you're ready to get developing with Account Abstraction! For next steps you can look at [functions](/prime-sdk/functions)) or [examples](/prime-sdkexamples/intro) to tailor the dapp to what you're trying to achieve. # Account Abstraction on Linea Source: https://etherspot.fyi/prime-sdk/other-chains/getting-started-on-linea An introduction to Account Abstraction on Linea using the Etherspot Prime SDK ## Intro On this guide we'll learn about Account Abstraction and run through some introductory code to get setup with using it on Linea using the Etherspot Prime SDK. ## Code Tutorial Here we'll get set up with the very basics of using the Prime SDK. We'll set up a React app, install the Etherspot Prime SDK, and create an Etherspot smart contract wallet. Start by creating a react app like so: ```bash theme={null} npx create-react-app etherspot-starter ``` Then cd into the directory and install the Etherspot Prime SDK and Ethers. ```bash theme={null} cd etherspot-starter/ npm i @etherspot/prime-sdk --save npm i ethers --save ``` Now open the code in your editor, and open up App.js. Paste in the following code: ```javascript theme={null} 'use client'; import React from 'react'; import { PrimeSdk } from '@etherspot/prime-sdk'; import { ethers } from 'ethers' import './App.css'; const App = () => { const [etherspotWalletAddress, setEtherspotWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaWalletAddress, setEoaWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaPrivateKey, setEoaPrivateKey] = React.useState(''); const generateRandomEOA = async () => { // Create random EOA wallet const randomWallet = ethers.Wallet.createRandom(); setEoaWalletAddress(randomWallet.address); setEoaPrivateKey(randomWallet.privateKey); } const generateEtherspotWallet = async () => { // Initialise Etherspot SDK const primeSdk = new PrimeSdk({ privateKey: eoaPrivateKey}, { chainId: 59144, bundlerProvider: new EtherspotBundler(59144, bundlerApiKey) }) const address = await primeSdk.getCounterFactualAddress(); setEtherspotWalletAddress(address); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); } return (

Getting started with Etherspot Prime

To initialise the SDK, it requires a Key Based Wallet(KBW) to be passed in.

KBW Address: {eoaWalletAddress}

Now we can intialise the SDK with this address as the owner, and create an Etherspot smart contract wallet.

Etherspot Smart Account Address: {etherspotWalletAddress}

Now you have a wallet created on Linea you can explore what else we can do with the Prime SDK.

) } export default App; ``` And that's it! We've not created a random key based wallet on Linea on page load, and then using this KBW we pass it into the Etherspot Prime SDK, creating an Etherspot Smart Contract Wallet. You can learn more about the differences between these two types of accounts [here.](/account-abstraction/eoa-vs-scw) ### Next steps Now you're ready to get developing with Account Abstraction! For next steps you can look at [functions](/prime-sdk/functions) or [examples](/prime-sdk/examples/intro) to tailor the dapp to what you're trying to achieve. # Account Abstraction on Mantle Source: https://etherspot.fyi/prime-sdk/other-chains/getting-started-on-mantle An introduction to Account Abstraction on Mantle using the Etherspot Prime SDK ## Intro On this guide we'll learn about Account Abstraction and run through some introductory code to get setup with using it on Mantle using the Etherspot Prime SDK. ## Code Tutorial Here we'll get set up with the very basics of using the Prime SDK. We'll set up a React app, install the Etherspot Prime SDK, and create an Etherspot smart contract wallet. Start by creating a react app like so: ```bash theme={null} npx create-react-app etherspot-starter ``` Then cd into the directory and install the Etherspot Prime SDK and Ethers. ```bash theme={null} cd etherspot-starter/ npm i @etherspot/prime-sdk --save npm i ethers --save ``` Now open the code in your editor, and open up App.js. Paste in the following code: ```javascript theme={null} 'use client'; import React from 'react'; import { PrimeSdk, EtherspotBundler } from '@etherspot/prime-sdk'; import { ethers } from 'ethers' import './App.css'; const App = () => { const [etherspotWalletAddress, setEtherspotWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaWalletAddress, setEoaWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaPrivateKey, setEoaPrivateKey] = React.useState(''); const bundlerApiKey = 'etherspot_public_key'; const generateRandomEOA = async () => { // Create random EOA wallet const randomWallet = ethers.Wallet.createRandom(); setEoaWalletAddress(randomWallet.address); setEoaPrivateKey(randomWallet.privateKey); } const generateEtherspotWallet = async () => { // Initialise Etherspot SDK const primeSdk = new PrimeSdk({ privateKey: eoaPrivateKey}, { chainId: 5000, bundlerProvider: new EtherspotBundler(5000, bundlerApiKey) }) const address = await primeSdk.getCounterFactualAddress(); setEtherspotWalletAddress(address); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); } return (

Getting started with Etherspot Prime

To initialise the SDK, it requires a Key Based Wallet(KBW) to be passed in.

KBW Address: {eoaWalletAddress}

Now we can intialise the SDK with this address as the owner, and create an Etherspot smart contract wallet.

Etherspot Smart Account Address: {etherspotWalletAddress}

Now you have a wallet created on Mantle you can explore what else we can do with the Prime SDK.

) } export default App; ``` And that's it! We've not created a random key based wallet on Mantle on page load, and then using this KBW we pass it into the Etherspot Prime SDK, creating an Etherspot Smart Contract Wallet. You can learn more about the differences between these two types of accounts [here.](/account-abstraction/eoa-vs-scw) ### Next steps Now you're ready to get developing with Account Abstraction! For next steps you can look at [functions](/prime-sdk/functions) or [examples](/prime-sdk/examples/intro) to tailor the dapp to what you're trying to achieve. # Account Abstraction on Rootstock Source: https://etherspot.fyi/prime-sdk/other-chains/getting-started-on-rootstock An introduction to Account Abstraction on Rootstock using the Etherspot Prime SDK ## Intro In this guide we'll learn about Account Abstraction and run through some introductory code to get setup with using it on Rootstock using the Etherspot Prime SDK. ## Code Tutorial Here we'll get set up with the very basics of using the Prime SDK. We'll set up a React app, install the Etherspot Prime SDK, and create an Etherspot smart contract wallet. Start by creating a react app like so: ```bash theme={null} npx create-react-app etherspot-starter ``` Then cd into the directory and install the Etherspot Prime SDK and Ethers. ```bash theme={null} cd etherspot-starter/ npm i @etherspot/prime-sdk --save npm i ethers --save ``` Now open the code in your editor, and open up App.js. Paste in the following code: ```javascript theme={null} 'use client'; import React from 'react'; import { PrimeSdk, EtherspotBundler } from '@etherspot/prime-sdk'; import { ethers } from 'ethers' import './App.css'; const App = () => { const [etherspotWalletAddress, setEtherspotWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaWalletAddress, setEoaWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaPrivateKey, setEoaPrivateKey] = React.useState(''); const generateRandomEOA = async () => { // Create random EOA wallet const randomWallet = ethers.Wallet.createRandom(); setEoaWalletAddress(randomWallet.address); setEoaPrivateKey(randomWallet.privateKey); } const generateEtherspotWallet = async () => { const bundlerApiKey = 'etherspot_public_key'; const customBundlerUrl = "https://rootstocktestnet-bundler.etherspot.io/" // Initialise Etherspot SDK const primeSdk = new PrimeSdk({ privateKey: eoaPrivateKey}, { chainId: 31, bundlerProvider: new EtherspotBundler(31, bundlerApiKey, customBundlerUrl) }) const address = await primeSdk.getCounterFactualAddress(); setEtherspotWalletAddress(address); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); } return (

Getting started with Etherspot Prime

To initialise the SDK, it requires a Key Based Wallet(KBW) to be passed in.

KBW Address: {eoaWalletAddress}

Now we can intialise the SDK with this address as the owner, and create an Etherspot smart contract wallet.

Etherspot Smart Account Address: {etherspotWalletAddress}

Now you have a wallet created on Rootstock you can explore what else we can do with the Prime SDK.

) } export default App; ``` And that's it! We've not created a random key based wallet on Rootstock on page load, and then using this KBW we pass it into the Etherspot Prime SDK, creating an Etherspot Smart Contract Wallet. You can learn more about the differences between these two types of accounts [here.](/account-abstraction/eoa-vs-scw) ### Next steps Now you're ready to get developing with Account Abstraction! For next steps you can look at [functions](/prime-sdk/functions) or [examples](/prime-sdk/examples/intro) to tailor the dapp to what you're trying to achieve. # Account Abstraction on Scroll Source: https://etherspot.fyi/prime-sdk/other-chains/getting-started-on-scroll An introduction to Account Abstraction on Scroll using the Etherspot Prime SDK ## Intro On this guide we'll learn about Account Abstraction and run through some introductory code to get setup with using it on Scroll using the Etherspot Prime SDK. ## Code Tutorial Here we'll get set up with the very basics of using the Prime SDK. We'll set up a React app, install the Etherspot Prime SDK, and create an Etherspot smart contract wallet. Start by creating a react app like so: ```bash theme={null} npx create-react-app etherspot-starter ``` Then cd into the directory and install the Etherspot Prime SDK and Ethers. ```bash theme={null} cd etherspot-starter/ npm i @etherspot/prime-sdk --save npm i ethers --save ``` Now open the code in your editor, and open up App.js. Paste in the following code: ```javascript theme={null} 'use client'; import React from 'react'; import { PrimeSdk } from '@etherspot/prime-sdk'; import { ethers } from 'ethers' import './App.css'; const App = () => { const [etherspotWalletAddress, setEtherspotWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaWalletAddress, setEoaWalletAddress] = React.useState('0x0000000000000000000000000000000000000000'); const [eoaPrivateKey, setEoaPrivateKey] = React.useState(''); const generateRandomEOA = async () => { // Create random EOA wallet const randomWallet = ethers.Wallet.createRandom(); setEoaWalletAddress(randomWallet.address); setEoaPrivateKey(randomWallet.privateKey); } const generateEtherspotWallet = async () => { // Initialise Etherspot SDK const primeSdk = new PrimeSdk({ privateKey: eoaPrivateKey}, { chainId: 534352, bundlerProvider: new EtherspotBundler(534352, bundlerApiKey) }) const address = await primeSdk.getCounterFactualAddress(); setEtherspotWalletAddress(address); console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); } return (

Getting started with Etherspot Prime

To initialise the SDK, it requires a Key Based Wallet(KBW) to be passed in.

KBW Address: {eoaWalletAddress}

Now we can intialise the SDK with this address as the owner, and create an Etherspot smart contract wallet.

Etherspot Smart Account Address: {etherspotWalletAddress}

Now you have a wallet created on Scroll you can explore what else we can do with the Prime SDK.

) } export default App; ``` And that's it! We've not created a random key based wallet on Scroll on page load, and then using this KBW we pass it into the Etherspot Prime SDK, creating an Etherspot Smart Contract Wallet. You can learn more about the differences between these two types of accounts [here.](/account-abstraction/eoa-vs-scw) ### Next steps Now you're ready to get developing with Account Abstraction! For next steps you can look at [functions](/prime-sdk/functions) or [examples](/prime-sdk/examples/intro) to tailor the dapp to what you're trying to achieve. # Intro Source: https://etherspot.fyi/prime-sdk/other-chains/intro In this section we'll run through various beginner guides on how to get up and running with Etherspot and Account Abstraction on specific chains. If you're an EVM based network and wish to get setup with our Account Abstraction infrastructure then please get in touch. # SDK Reference Source: https://etherspot.fyi/prime-sdk/sdk-reference To get a comprehensive look into the entire Prime SDK, you can take a look at [https://sdk.etherspot.io/](https://sdk.etherspot.io/) This contains references generated by TypeDoc to all methods, variables, globals, contructors, etc. # Sponsored Transactions Source: https://etherspot.fyi/prime-sdk/sponsored-transactions Sponsored transactions are the ability to pay for another user's transaction fees. Etherspot offers a simple way to do this using our SDK: 1. Get an API key from us to make use of [Arka](/arka/intro) 2. [Deposit](/arka/api-calls/deposit-to-paymaster) assets to pay for transactions. 3. Integrate the Etherspot SDK into their dapp to [enable sponsored transactions](/arka/sponsor-a-transaction) using the address/account created above. To create an account with us internally please join our [Discord](http://discord.etherspot.io/) and open a ticket. Etherspot members in the Discord will be able to assist in setting up your account and integrating the SDK in a customised way that suits your dapps needs. # Ethers.js Source: https://etherspot.fyi/prime-sdk/third-party/ethers ## Ethers.js Ethers. js is a library that helps developers create decentralized applications, while Web3. js is a library that helps developers connect to the Ethereum network. Internally within the SDK and when used with TransactionKit, it's important that we use ether.js version 5.4.0. This is the current version that we support for such use cases. For using it outside of TransactionKit it's still recommended to use version 5 as it's more stable. ```bash theme={null} npm i ethers@5 ``` ## Demo dapps Commonly we use ethers.js when creating demo or test dapps because it easily lets us create a new key based wallet on page load. This is done with TransactionKit like this: ```javascript theme={null} import { ethers } from "ethers"; const randomWallet = ethers.Wallet.createRandom(); const providerWallet = new ethers.Wallet(randomWallet.privateKey); root.render( ); ``` You can find more information on working with ethers [here](https://docs.ethers.org/v5/). # Intro Source: https://etherspot.fyi/prime-sdk/third-party/intro On this page we'll detail how to get started with the Etherspot Prime SDK using other third party providers such as Metamask. In general the Prime SDK will work in the same way to get instaniated, we need to pass in a provider ([Key based wallet](/account-abstraction/eoa-vs-scw/)) which will act as the owner of the Etherspot SCW that is created. Then we can call any of the SDK functions. # Metamask Source: https://etherspot.fyi/prime-sdk/third-party/metamask Metamask is a cryptocurrency wallet used to interact with the Ethereum blockchain. It allows users to access their Ethereum wallet through a browser extension or mobile app, which can then be used to interact with decentralized applications. ## Connecting via Metamask If you want the user to be able to sign in via Metamask, we can simply import the MetaMaskWalletProvider object like so: ```javascript theme={null} import { MetaMaskWalletProvider } from '@etherspot/prime-sdk'; ``` Then call connect on it which will prompt the user to connect their Metamask. Finally, we pass this provider into the Prime SDK to instantiate it. ```javascript theme={null} const metamaskProvider = await MetaMaskWalletProvider.connect(); const primeSdk = new PrimeSdk(metamaskProviderTemp, { chainId: 11155111, bundlerProvider: new EtherspotBundler(11155111, bundlerApiKey) }); ``` ## Transaction Kit For transaction kit we can follow the same process above, then pass the provider straight into the Transaction kit tags like this: ```javascript theme={null} const metamaskProvider = await MetaMaskWalletProvider.connect(); ``` # Privy Source: https://etherspot.fyi/prime-sdk/third-party/privy Privy isn't a wallet itself, but serves as an essential wallet infrastructure that can be integrated seamlessly into any application. Its primary function is to offer more tailored flows for decentralized applications (dApps) or even blockchain wallets alike, solving the user onboarding and key management problem. Users can easily login with a number of different platforms such as Twitter or Google mail. An Etherspot smart contract wallet is then created for them and they are ready to interact with your dapp. Start by installing the Privy packages: ```bash npm theme={null} npm install @privy-io/react-auth ``` We'll take a look at two files here, **index.js** and **App.js** To use Privy in production you will need to request an appId. For testing purposes you can use their test appId like we do in this tutorial. ### index.js Here we import the Privy Provider component and add some values. Wallets are not created by default for social logins so we must set createPrivyWalletOnLogin to true. ```javascript theme={null} import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import { PrivyProvider } from '@privy-io/react-auth'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( console.log(`User ${user.id} logged in!`)} createPrivyWalletOnLogin={true} > ); ``` ### App.js In this file we'll import both the Privy and Etherspot hooks we need, and return them below. Once we have logged in via Privy, we'll pass the provider which is obtained from the Privy wallet, and use this to generate our Etherspot smart contract account. ```javascript theme={null} import "./App.css"; import { usePrivy, useWallets } from "@privy-io/react-auth"; import { PrimeSdk, Web3WalletProvider, Web3eip1193WalletProvider } from "@etherspot/prime-sdk"; function App() { const { ready, authenticated, user, login, logout } = usePrivy(); const { wallets } = useWallets(); // Wait until the Privy client is ready before taking any actions if (!ready) { return null; } const etherspotLogin = async () => { const privyProvider = await wallets[0].getWeb3jsProvider(); const provider = privyProvider.walletProvider; let mappedProvider; if (!isWalletProvider(provider)) { try { mappedProvider = new Web3eip1193WalletProvider(provider); await mappedProvider.refresh(); } catch (e) { // no need to log, this is an attempt } if (!mappedProvider) { throw new Error('Invalid provider!'); } } const etherspotPrimeSdk = new PrimeSdk(mappedProvider ?? provider, { chainId: 1, }); console.log(etherspotPrimeSdk); }; return (
{ready && authenticated ? (