# 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.
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.**
# 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
### 2. Choose either Testnet or Mainnet
### 3. Choose a chain you want to send request
### 4. Choose API version
V1 supports EntryPoint 0.6.0
V2 supports EntryPoint 0.7.0
### 5. Click on Select API Key
### 6. Choose your API key
### 7. Click on Send Request
### 8. Choose from the range of supported RPC methods
### 9. Generate cURL command
### 10. The code will be generated ready to be used
# 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.

### [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.

### [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.

### [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.

## 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.

### [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.

### [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\://)

## 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**

### [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.

# null
Source: https://etherspot.fyi/introduction
Everything you need to get started with Etherspot’s Account & Chain Abstraction infrastructure
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
# 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.
)
}
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.
)
}
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.
)
}
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.
)
}
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.
)
}
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 ? (
) : (
)}
);
}
export default App;
```
# Viem
Source: https://etherspot.fyi/prime-sdk/third-party/viem
## Viem
Viem is a viable alternative to ethers.js, addressing the quadrilemma
of developer experience, stability, bundle size, and performance. With
its modular and intuitive APIs, comprehensive documentation, and focus
on type safety, viem provides developers with the tools they need to build
robust Ethereum applications and libraries.
```bash theme={null}
npm i viem
```
## Demo dapps
In the same way we generated a random EOA wallet using
Ethers.js, we will show this done with viem as well.
This is done with TransactionKit like this:
```javascript theme={null}
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const privateKey = generatePrivateKey();
const providerWallet = privateKeyToAccount(privateKey);
root.render(
);
```
You can find more information on working with Viem [here](https://viem.sh/docs/getting-started.html).
# Web3Auth
Source: https://etherspot.fyi/prime-sdk/third-party/web3auth
Web3Auth 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 Web3Auth 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
```
Below is a code example of how get up and running with social logins.
```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: "hex chain id",
},
clientId: "client id from web3auth dashboard",
});
// 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;
```
# Changing Wallet Factory
Source: https://etherspot.fyi/prime-sdk/wallet-factories
With the Etherspot Prime SDK we have the option to use some different
smart contract wallet implementations with the Etherspot infrastructure.
When instantiating the SDK, there is an optional parameter called **factoryWallet**.
If this is not included then it will default to Factory.ETHERSPOT and use our wallet factory.
Currently we have added support for ZeroDev and simpleAccount.
## ZeroDev instantiation
Factory contract address: 0x5de4839a76cf55d0c90e2061ef4386d962E15ae3
```javascript theme={null}
import { Factory, PrimeSdk } from '../src';
import * as dotenv from 'dotenv';
dotenv.config();
async function main() {
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) })
}
```
## simpleAccount instantiation
Factory contract address: 0x9406Cc6185a346906296840746125a0E44976454
```javascript theme={null}
import { Factory, PrimeSdk } from '../src';
import * as dotenv from 'dotenv';
dotenv.config();
async function main() {
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) })
}
```
# Quick Start
Source: https://etherspot.fyi/quick-start
A simple introduction to the Etherspot Prime SDK.
On this page we'll run through the easiest way to get up and running with the SDK from scratch.
The first step with using the SDK is instantiating it with a key based wallet so act as the owner.
Let's clone an example dapp we have built.
```
git clone https://github.com/etherspot/etherspot-prime-getting-started.git
```
Next let's install the packages.
```
cd etherspot-prime-getting-started
npm i
```
Then let's run the dapp.
```
npm start
```
And that's it! We have a dapp up and running which shows the intialisation flow.
An Etherspot smart account is created and now you're ready to tailor it however you want to your use case.
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 Sepola testnet and get the wallet address.
```javascript theme={null}
const primeSdk = new PrimeSdk({ privateKey: eoaPrivateKey}, { chainId: 11155111, bundlerProvider: new EtherspotBundler(11155111, bundlerApiKey) })
const address: string = await primeSdk.getCounterFactualAddress();
```
For next steps you can look at [functions](/prime-sdk/functions) or [examples](/examples/intro).
Keep in mind this is a fresh wallet so you will have to get
testnet funds via a faucet to send any transactions from it.
# Chains Supported
Source: https://etherspot.fyi/remote-signer-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/).
Etherspot RemoteSigner 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.
## Testnets
**MATIC** 80002
```
https://testnet-rpc.etherspot.io/v2/80002
```
**ETH** 11155111
```
https://testnet-rpc.etherspot.io/v2/11155111
```
# Contract Deployments
Source: https://etherspot.fyi/remote-signer-sdk/contracts/deployments
Remote Signer is available for Polygon-Amoy and Sepolia networks
Please refer the deployed contract addresses for `ERC20SessionKeyValidator` in the network constants below
[Deployed Contracts Addresses](https://github.com/etherspot/remote-signer-sdk/blob/master/src/sdk/network/constants.ts#L86-L489)
# sign UserOp via RemoteSigner ViemAccount
Source: https://etherspot.fyi/remote-signer-sdk/examples/using-extended-local-account
```javascript theme={null}
import * as dotenv from 'dotenv';
import { encodeFunctionData, Hex, parseAbi, parseUnits } from 'viem';
import { EtherspotBundler, RemoteSignerSdk, UserOperation, toRemoteSigner, BigNumber, createLocalAccount, ExtendedLocalAccount, printOp } from '@etherspot/remote-signer';
import { privateKeyToAccount } from 'viem/accounts';
dotenv.config();
const tokenAddress = process.env.TOKEN_ADDRESS as string; // token address
const bundlerApiKey = process.env.API_KEY as string; // bundlerApiKey
const sessionKey = process.env.SESSION_KEY as string; // user's sessionKey
const erc20SessionKeyValidator = process.env.ERC20_SESSION_KEY_VALIDATOR as string;
const apiKey = process.env.API_KEY as string;
const etherspotWalletAddress = process.env.ETHERSPOT_WALLET_ADDRESS as string;
const chainId = Number(process.env.CHAIN_ID);
const recipient = '0xdE79F0eF8A1268DAd0Df02a8e527819A3Cd99d40'; // recipient wallet address
const value = '1'; // transfer value
let remoteSigner: ExtendedLocalAccount;
async function main() {
remoteSigner = await toRemoteSigner({
account: createLocalAccount(etherspotWalletAddress),
chainId: chainId,
apiKey: apiKey,
sessionKey: sessionKey
});
const op = await generateSignedUserOp();
const signedUserOp: UserOperation = await remoteSigner.signUserOpWithRemoteSigner(op);
const signedUserOp_Printable = await printOp(signedUserOp);
console.log(`Signed UserOp: ${signedUserOp_Printable} generated via remote-signer`);
}
async function generateSignedUserOp() {
const externalViemAccount = privateKeyToAccount(process.env.WALLET_PRIVATE_KEY as string as Hex);
const bundlerProvider = new EtherspotBundler(chainId, bundlerApiKey);
const remoteSignerSdk = await RemoteSignerSdk.create(externalViemAccount, {
etherspotWalletAddress: etherspotWalletAddress,
chainId: chainId,
apiKey: apiKey,
sessionKey: sessionKey,
bundlerProvider: bundlerProvider
});
const transactionData = await getTransferERC20Data(remoteSignerSdk.getPublicClient());
// clear the transaction batch
await remoteSignerSdk.clearUserOpsFromBatch();
// add transactions to the batch
const userOpsBatch = await remoteSignerSdk.addUserOpsToBatch({ to: tokenAddress, data: transactionData });
if (userOpsBatch.data.length === 0) {
throw new Error('No user operations added to the batch');
}
let nonceKey = BigNumber.from(erc20SessionKeyValidator);
// estimate transactions added to the batch and get the fee data for the UserOp
const op = await remoteSignerSdk.estimate({ nonceKey: nonceKey });
const signedUserOp = await remoteSdkSigner.signUserOp(op);
return signedUserOp;
}
const erc20Abi = [
'function approve(address spender, uint256 value) returns (bool)',
'function decimals() view returns (uint8)',
'function name() view returns (string)',
'function symbol() view returns (string)',
'function totalSupply() returns (uint256)',
'function balanceOf(address account) returns (uint256)',
'function allowance(address owner, address spender) returns (uint256)',
'function transfer(address to, uint256 value) returns (bool)',
'function transferFrom(address from, address to, uint256 value) returns (bool)'
];
async function getTransferERC20Data(publicClient) {
const decimals = await publicClient.readContract({
address: tokenAddress as Hex,
abi: parseAbi(erc20Abi),
functionName: 'decimals',
args: []
});
// get transferFrom encoded data
const transactionData = encodeFunctionData({
functionName: 'transfer',
abi: parseAbi(erc20Abi),
args: [recipient, parseUnits(value, decimals as number)]
});
return transactionData;
}
```
# sign UserOp via RemoteSignerSDK Instance
Source: https://etherspot.fyi/remote-signer-sdk/examples/using-remote-signer-sdk
```javascript theme={null}
import { EtherspotBundler, RemoteSignerSdk, UserOperation, toRemoteSigner, BigNumber, erc20Abi, sleep, printOp } from '@etherspot/remote-signer';
import * as dotenv from 'dotenv';
import { privateKeyToAccount } from 'viem/accounts';
import { encodeFunctionData, Hex, http, parseAbi, parseUnits } from 'viem';
dotenv.config();
// add/change these values
const recipient = '0xdE79F0eF8A1268DAd0Df02a8e527819A3Cd99d40'; // recipient wallet address
const value = '0.00000001'; // transfer value
const tokenAddress = ''; // token address
const bundlerApiKey = process.env.API_KEY as string;
const sessionKey = '';
const erc20SessionKeyValidator = process.env.ERC20_SESSION_KEY_VALIDATOR as string;
const apiKey = process.env.API_KEY as string;
const etherspotWalletAddress = process.env.ETHERSPOT_WALLET_ADDRESS as string;
const chainId = Number(process.env.CHAIN_ID);
const privateKey = process.env.WALLET_PRIVATE_KEY as string;
async function main() {
const externalViemAccount = privateKeyToAccount(privateKey as Hex);
const bundlerProvider = new EtherspotBundler(chainId, bundlerApiKey);
const remoteSignerSdk = await RemoteSignerSdk.create(externalViemAccount, {
etherspotWalletAddress: etherspotWalletAddress,
chainId: chainId,
apiKey: apiKey,
sessionKey: sessionKey,
bundlerProvider: bundlerProvider
});
const transactionData = await getTransferERC20Data(remoteSignerSdk.getPublicClient());
// clear the transaction batch
await remoteSignerSdk.clearUserOpsFromBatch();
// add transactions to the batch
const userOpsBatch = await remoteSignerSdk.addUserOpsToBatch({ to: tokenAddress, data: transactionData });
console.log(`sessionkey erc20SessionKeyValidator ${erc20SessionKeyValidator} as BigNumber is: ${BigNumber.from(erc20SessionKeyValidator)}`);
let nonceKey = BigNumber.from(erc20SessionKeyValidator);
// estimate transactions added to the batch and get the fee data for the UserOp
const op = await remoteSignerSdk.estimate({nonceKey: nonceKey});
const signedUserOp = await remoteSignerSdk.signUserOp(op);
console.log(`Signed UserOp: ${await printOp(signedUserOp)}`);
console.log(`UserOpNonce is: ${BigNumber.from(signedUserOp.nonce)}`);
const userOpHashFromSignedUserOp = await remoteSignerSdk.getUserOpHash(signedUserOp);
console.log(`UserOpHash from Signed UserOp: ${userOpHashFromSignedUserOp}`);
// sending to the bundler with isUserOpAlreadySigned true...
const uoHash = await remoteSignerSdk.send(signedUserOp);
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 remoteSignerSdk.getUserOpReceipt(uoHash);
}
console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt);
}
main()
.catch(console.error)
.finally(() => process.exit());
async function getTransferERC20Data(publicClient) {
const decimals = await publicClient.readContract({
address: tokenAddress as Hex,
abi: parseAbi(erc20Abi),
functionName: 'decimals',
args: []
});
// get transferFrom encoded data
const transactionData = encodeFunctionData({
functionName: 'transfer',
abi: parseAbi(erc20Abi),
args: [recipient, parseUnits(value, decimals as number)]
});
return transactionData;
}
```
# Functions
Source: https://etherspot.fyi/remote-signer-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/remote-signer-sdk/blob/master/src/sdk/sdk.ts).
| Function Name | Description |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| validateSessionKey() returns boolean | get sessionKey details from remote permissioned backend service followed by a check onchain if the sessionKey is live |
| isSessionKeyLiveOnChain() returns boolean | runs a static simulation for onchain call - `isSessionKeyAlive` and returns true if session is live |
| getSessionKeyOnChainData() returns SessionKeyOnChainData | query ERC20SessionKeyValidator contract onchain via simulateCall on `sessionData` |
| signUserOp(userOp) returns SignedUserOp | accepts Userop Struct and sends it to permissioned backend service which signs the Userop usingthe privateKey of SessionKey from KMS |
| destroy() | Destroys the SDK object. |
| estimate(gasDetails?: TransactionGasInfoForUserOp) | Returns the estimated amount of gas a transaction 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. |
# Installation
Source: https://etherspot.fyi/remote-signer-sdk/installation
Learn how to install Etherspot RemoteSigner SDK
**Prerequisite** You should have installed Node.js (version 18.10.0 or
higher).
Step 1. Install Etherspot RemoteSigner SDK with this command
```bash npm theme={null}
npm i @etherspot/remote-signer --save
```
```bash yarn theme={null}
yarn add @etherspot/remote-signer
```
And that's it! You are now ready to dive into ReomteSigning using SessionKeys on Modular Wallets
Now let's learn how to instaniate the SDK.
# Instantiation
Source: https://etherspot.fyi/remote-signer-sdk/instantiation
Before doing anything with the SDK, we must instantiate it.
This will set the sessionKey, chainId and all properties needed to authenticate and authorise the remote-signing
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 RemoteSigner SDK.
```javascript theme={null}
import { RemoteSignerSdk } from '@etherspot/remote-signer';
```
Step 2. Instantiate the SDK with below initialisation properties using this block of code.
* privateKey
* ChainID
* etherspotWalletAddress
* sessionKey
* bundlerApiKey
```javascript theme={null}
import { privateKeyToAccount } from 'viem/accounts';
import { Account, Hex } from "viem";
import * as dotenv from 'dotenv';
import { createLocalAccount, EtherspotBundler, ExtendedLocalAccount, getViemAccount, RemoteSignerSdk, toRemoteSigner } from '@etherspot/remote-signer';
const externalViemAccount = privateKeyToAccount(process.env.WALLET_PRIVATE_KEY as string as Hex);
const bundlerProvider = new EtherspotBundler(chainId, bundlerApiKey);
const bundlerApiKey = process.env.API_KEY as string;
const sessionKey = ''; // sessionKey generated by modularSDK
const apiKey = process.env.API_KEY as string;
const etherspotWalletAddress = ''; // your etherspotModularWallet address
const chainId = Number(process.env.CHAIN_ID);
const remoteSignerSdk = await RemoteSignerSdk.create(externalViemAccount, {
etherspotWalletAddress: etherspotWalletAddress,
chainId: chainId,
apiKey: apiKey,
sessionKey: sessionKey,
bundlerProvider: bundlerProvider
});
```
And that's it! You're now ready to call any of the RemoteSigner SDK [functions.](/remote-signer-sdk/functions)
In the next page we'll take a look at the various functions the SDK offers.
# Introduction
Source: https://etherspot.fyi/remote-signer-sdk/intro
## Intro
A TypeScript library to sign UserOperations with a remote sessionKey
* SDK allows you to sign the UserOp using the sessionKey stored in the remote KeyManagementService
* The PrivateKey associated with the SessionKey is stored in a secured Cloud based KeyManagementService
* All interactions with the KMS is authenticated and only authorized users (via APIKey) can use the remoteSigner SDK
This section contains information on how to install the RemoteSigner SDK, all of the features you can implement
using it and which functions you can use to implement them.
## RemoteSigner SDK Features
The RemoteSigner SDK unlocks a number of easy to implement Sign and Send a UserOp using SessionKey and it enables features for your dapp or network
such as:
* Sign UserOp
* [SignUserOp using SDK function](/remote-signer-sdk/examples/using-remote-signer-sdk)
* [Construct a Overloaded Viem Account called as `ExtendedLocalAccount`](/remote-signer-sdk/examples/using-extended-local-account)
* RemoteSigner SDK is a fully open source SDK which let's dapp developers easily get building with SessionKey Signature enabled Modular Smart Accounts.
* The SDK makes it incredibly easy to get up and running with using Sessionkey based Signature from remote signer and send the UserOp to the bundler.
* Session-Key-Signer is hosted in AWS cloud environment which stores the sessionKeys and backend infra service to sign using the privateKey associated with the sessionKey. Signed UserOp is sent to the Etherspot Bundler
## Modules and functions in Remote-Signer
### Usage
* remote signer need to provide the necessary parameters
1. account
2. chainId
3. apiKey
4. sessionKey
The `signUserOperation` function will handle the signing process and return the signed user operation.
# SDK and Contract Interactions - DeeDive
Source: https://etherspot.fyi/remote-signer-sdk/remote-signer-deepdive
### Remote Signer Module
This module provides functionality to sign user operations using a remote signer. It includes the following key components:
### Types
RemoteSignerParams: Defines the parameters required to create a remote signer.
```js theme={null}
export type RemoteSignerParams = {
account: LocalAccount,
chainId: number,
apiKey: string,
sessionKey: string,
permissionsBackendUrl?: string
}
```
### Functions
signUserOp: Signs a user operation using the session key.
```ts theme={null}
async function signUserOp(
account: Account,
chainId: number,
apiKey: string,
sessionKey: string,
userOp: UserOperation,
permissionsBackendUrl: string = PERMISSIONS_URL
): Promise {
return signUserOpWithSessionKey(account.address, chainId, apiKey, sessionKey, userOp, permissionsBackendUrl);
}
```
#### toRemoteSigner: Converts a local account to an extended local account with remote signing capabilities.
```ts theme={null}
export async function toRemoteSigner({
account,
chainId,
apiKey,
sessionKey
}: RemoteSignerParams): Promise {
// Get sessionKey from apiKey and account
const sessionKeyResponse = await getSessionKey(account.address, chainId, apiKey, sessionKey);
// Create the extendedLocalAccount object and add the method
const extendedLocalAccount: ExtendedLocalAccount = {
...account, // Spread the properties of the original LocalAccount
async signUserOpWithRemoteSigner(userOp: UserOperation) {
const signedUserOp = await signUserOp(account, chainId, apiKey, sessionKeyResponse.sessionKey, userOp);
return signedUserOp;
},
async signMessage({ message }) {
throw new Error('signMessage with sessionKey not implemented');
},
async signTransaction(_, __) {
throw new Error('signTransaction with sessionKey not implemented');
},
async signTypedData<
const TTypedData extends TypedData | Record,
TPrimaryType extends keyof TTypedData | 'EIP712Domain' = keyof TTypedData
>(typedData: TypedDataDefinition) {
throw new Error('signTypedData not implemented');
},
};
return extendedLocalAccount;
}
```
#### signUserOperation: Signs a user operation using the remote signer.
```ts theme={null}
export const signUserOperation = async (
etherspotWalletAccount: LocalAccount,
chainId: number,
apiKey: string,
sessionKey: string,
userOp: UserOperation
) => {
const remoteSigner: ExtendedLocalAccount = await toRemoteSigner({
account: etherspotWalletAccount,
chainId: chainId,
apiKey: apiKey,
sessionKey: sessionKey,
permissionsBackendUrl: PERMISSIONS_URL
});
const signedUserOp = await remoteSigner.signUserOpWithRemoteSigner(userOp);
if (!signedUserOp || !signedUserOp.signature || signedUserOp.signature === '0x') {
throw new Error('Failed to sign user operation');
}
return signedUserOp;
}
```
## Pre-requisites for using a sessionKey in remote-signing
1. EtherspotWallet account should have SessionKeyValidator module installed.
2. SessionKeyValidator varies with the kind of operation performed, i.e there can be multiple kinds of SessionKeyValidator and the nonce to be used as part of userOp is generated from the address of `SessionKeyValidator`
Example:
ERC20SessionKeyValidator is used to perform the erc20 based operations from etherspotWalletAddress
All validations on the UserOp (ERC20 operations) are done by `ERC20SessionKeyValidator` module
Nonce used during the UserOp Estimation is to be from:
```js theme={null}
const erc20SessionKeyValidator = '';
BigNumber.from(erc20SessionKeyValidator)
```
This nonce is later used to identify the validatorModule used during the validationPhase in EntryPoint contract.
```js theme={null}
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
)
external
payable
virtual
override
onlyEntryPoint
payPrefund(missingAccountFunds)
returns (uint256 validSignature)
{
address validator;
// @notice validator encoding in nonce is just an example!
// @notice this is not part of the standard!
// Account Vendors may choose any other way to implement validator selection
uint256 nonce = userOp.nonce;
assembly {
validator := shr(96, nonce)
}
```
# eth_estimateUserOperationGas
Source: https://etherspot.fyi/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/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/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/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/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/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/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/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.
# Config
Source: https://etherspot.fyi/skandha/config
These are values which are included within **config.json**
# Config detail
| Value | Example | Description | Optional |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| relayers | \["0xPrivateKey1", "0xPrivateKey2"] | Array of relayer private keys or mnemonics, so multiple executors can be set. | No |
| beneficiary | "0xAddress" | Fee collector, avaiable via env var (SKANDHA\_BENEFICIARY etc) | Yes |
| rpcEndpoint | "[http://localhost:8545](http://localhost:8545)" | RPC provider, also available via env variable (SKANDHA\_RPC etc) | No |
| minInclusionDenominator | 10 | (See EIP-4337 spec)\[[https://eips.ethereum.org/EIPS/eip-4337](https://eips.ethereum.org/EIPS/eip-4337)] | Yes |
| throttlingSlack | 10 | (See EIP-4337 spec)\[[https://eips.ethereum.org/EIPS/eip-4337](https://eips.ethereum.org/EIPS/eip-4337)] | Yes |
| banSlack | 10 | (See EIP-4337 spec)\[[https://eips.ethereum.org/EIPS/eip-4337](https://eips.ethereum.org/EIPS/eip-4337)] | Yes |
| minSignerBalance | 1 | Default is 0.1 ETH. If the relayer's balance drops lower than this, it will be selected as a fee collector | Yes |
| multicall | "0xAddress" | Multicall3 contract (see here)\[[https://github.com/mds1/multicall#multicall3-contract-addresses](https://github.com/mds1/multicall#multicall3-contract-addresses)] | Yes |
| estimationStaticBuffer | 21000 | Adds certain amount of gas to callGasLimit on estimation | Yes |
| validationGasLimit | 10e6 | Gas limit during simulateHandleOps and simulateValidation calls | Yes |
| receiptLookupRange | 1024 | Limits the block range of getUserOperationByHash and getUserOperationReceipt | Yes |
| etherscanApiKey | "A1C15S3AXQHQ7PVVDX63VVK2IBAECS448Z" | Etherscan api is used to fetch gas prices | Yes |
| conditionalTransactions | false | Enabling this flag will make the bundler submit all transactions via eth\_sendRawTransactionConditional instead of eth\_sendRawTransaction | Yes |
| rpcEndpointSubmit | "[https://polygon-\{network}.blockpi.network/v1/rpc/public](https://polygon-\{network}.blockpi.network/v1/rpc/public)" | RPC endpoint that is used only during submission of a bundle | Yes |
| gasPriceMarkup | 0 | Adds % markup on reported gas price via skandha\_getGasPrice, 10000 = 100.00%, 500 = 5% | Yes |
| enforceGasPrice | false | Do not bundle userops with low gas prices | No |
| enforceGasPriceThreshold | 1000 | Gas price threshold in bps. If set to 500, userops' gas price is allowed to be 5% lower than the network's gas price | Yes |
| relayingMode | "classic" | Two options here, "classic" or ["flashbots" which enables flashbot protection](/skanda/mev-protection). | Yes |
| eip2930 | "false" | optional, enables eip-2930 | No |
| useropsTTL | 300 | optional, Userops time to live (in seconds) | No |
| whitelistedEntities | "whitelistedEntities": "factory": \[], "paymaster": \[], "account": \[] | optional, Entities that bypass stake and opcode validation (array of addresses) | No |
| bundleGasLimitMarkup | 25000 | optional, adds some amount of additional gas to a bundle tx | Yes |
| bundleInterval | 10000 | bundle creation interval | Yes |
| bundleSize | 4 | optional, max size of a bundle, 4 userops by default | Yes |
| pvgMarkup | 0 | optional, adds some gas on top of estimated PVG | Yes |
| cglMarkup | 35000 | optional, markup on estimated call gas limit | Yes |
| vglMarkup | 0 | optional, markup on estimated verification gas limit | Yes |
| skipBundleValidation | false | optional, skips bundle validation | Yes |
| userOpGasLimit | 25000000 | optional, gas limit of a userop | Yes |
| bundleGasLimit | 25000000 | optional, gas limit of a bundle | Yes |
| archiveDuration | 5184000 | optional, keeps submitted, reverted and cancelled userops in the mempool for this many seconds | Yes |
## Simplest config
```json theme={null}
{
"entryPoints": [
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
],
"relayers": ["0x{RELAYER-PRIVATE-KEY}"],
"beneficiary": "0x{BENEFICIARY-ADDRESS}",
"rpcEndpoint": "https://polygon-mumbai.blockpi.network/v1/rpc/public"
}
```
config.json with a default value of each config parameter
```json theme={null}
{
"entryPoints": [
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
],
"relayers": [
"0x0101010101010101010101010101010101010101010101010101010101010101",
"test test test test test test test test test test test junk"
],
"beneficiary": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"rpcEndpoint": "http://localhost:8545",
"minInclusionDenominator": 10,
"throttlingSlack": 10,
"banSlack": 50,
"minStake": 10000000000,
"minUnstakeDelay": 0,
"minSignerBalance": 1,
"multicall": "0xcA11bde05977b3631167028862bE2a173976CA11",
"estimationGasLimit": 0,
"receiptLookupRange": 1024,
"etherscanApiKey": "",
"conditionalTransactions": false,
"rpcEndpointSubmit": "",
"gasPriceMarkup": 0,
"enforceGasPrice": false,
"enforceGasPriceThreshold": 1000,
"eip2930": false,
"useropsTTL": 300,
"whitelistedEntities": {
"factory": [],
"paymaster": [],
"account": []
},
"bundleGasLimitMarkup": 25000,
"relayingMode": "classic",
"bundleInterval": 10000,
"bundleSize": 4,
"pvgMarkup": 0,
"cglMarkup": 35000,
"vglMarkup": 0,
"skipBundleValidation": false,
"userOpGasLimit": 25000000,
"bundleGasLimit": 25000000,
"archiveDuration": 5184000
}
```
# Installation
Source: https://etherspot.fyi/skandha/installation
Checkout Skandha bundler code on GitHub
## Run from Source Code
Run with one-liner:
```bash theme={null}
curl -fsSL https://skandha.run | bash
```
Or follow steps below:
```bash theme={null}
git clone https://github.com/etherspot/skandha && cd skandha
yarn build && yarn bootstrap
cp config.json.default config.json
edit config.json
(optional) run local geth-node from test/geth-dev
./skandha standalone
```
Skandha will run for chain specified in config.json
The bundler will be available at [http://localhost:14337/rpc](http://localhost:14337/rpc)
## How to run (a Docker image)
```bash theme={null}
cp config.json.default config.json
edit config.json
docker build -t etherspot/skandha .
docker run --mount type=bind,source="$(pwd)"/config.json,target=/usr/app/config.json,readonly -dp 14337:14337 etherspot/skandha standalone
```
## Additional features
**Unsafe mode** - bypass opcode & stake validation
**Redirect RPC** - Redirect ETH rpc calls to the underlying execution client. This is needed if you use UserOp.js
***CLI Options***
**--unsafeMode** - enables unsafeMode
**--redirectRpc** - enables redirecting eth rpc calls
**--executor.bundlingMode manual|auto** - sets bundling mode to manual or auto on start. Default value is auto
**--api.ws true|false** - enables / disables websocket server. Default is true
**--api.wsPort number** - sets websocket service port. Default is the same as api.port
# Introduction to Skandha
Source: https://etherspot.fyi/skandha/intro
**A modular typescript implementation of ERC4337 (Account Abstraction) bundler client.**
Supports EntryPoint 0.6.0
Supports EntryPoint 0.7.0
Bundlers are the backbone of 4337, their purpose is to read from a mempool of userOperations and bundle these together, to ensure multiple transactions are included on chain at a lower cost.
Skandha is a fully ERC:4337 compliant, production grade bundler built with Typescript.
Skandha specifically focuses on optimizing the gas costs associated with executing multiple transactions on the Ethereum network. It achieves this by intelligently grouping individual transactions into bundles, which are then processed as a single unit. By bundling transactions together, Skandha reduces the overall gas fees compared to executing each transaction individually.
One of the key features of Skandha is its ability to handle multiple transaction types, such as simple transfers, smart contract interactions, and even batched transactions. This flexibility allows developers to efficiently manage complex transaction scenarios and optimize their gas usage.
Additionally, Skandha provides advanced features like transaction ordering and priority management. It allows developers to specify the order in which transactions should be executed and assign priority levels to ensure critical transactions are processed promptly.
Etherspot's Skandha bundler aims to simplify and enhance the transaction management process for Ethereum developers, optimizing gas costs and improving overall efficiency when interacting with the Ethereum network.
## Bundler Modes
Skandha has two different modes it can be run in, public and private.
The public version of Skandha works like any other, showing all transactions that are validated publically.
The private version has the option to hide whitelisted networks of transactions and wallets from public view.
# MEV Protection
Source: https://etherspot.fyi/skandha/mev-protection
For instances of Skandha that you run yourself, Etherspot have
added options to protect your UserOps against frontrunners and
many other types of MEV.
We've done this by integrating the [Flashbots Auction API](https://docs.flashbots.net/flashbots-auction/overview)
which is a permissionless, transparent, and fair ecosystem
for efficient MEV extraction and frontrunning protection which
preserves the ideals of Ethereum. Flashbots Auction provides
a private communication channel between Ethereum users and
validators for efficiently communicating preferred transaction
order within a block.
This can be enabled by following [the Skandha installation](/skandha/installation)
page and changing **rpcEndpointSubmit** and **relayingMode** in config.json like so:
```json theme={null}
{
"networks": {
"sepolia": {
"entryPoints": [
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
],
"relayer": "0x{PRIVATE-KEY}",
"beneficiary": "0x{BENEFICIARY-ADDRESS}",
"rpcEndpoint": "wss://ethereum-sepolia-rpc.publicnode.com ",
"rpcEndpointSubmit": "https://relay-sepolia.flashbots.net",
"relayingMode": "flashbots"
}
}
}
```
Relayer URLs:
| Network | URL |
| ------- | ---------------------------------------------------------------------------- |
| Mainnet | "[https://relay.flashbots.net](https://relay.flashbots.net)" |
| Sepolia | "[https://relay-sepolia.flashbots.net](https://relay-sepolia.flashbots.net)" |
You can find more information about relayers here: [https://docs.flashbots.net/flashbots-auction/quick-start](https://docs.flashbots.net/flashbots-auction/quick-start)
Or more mainnet relayers here: [https://github.com/eth-educators/ethstaker-guides/blob/main/MEV-relay-list.md](https://github.com/eth-educators/ethstaker-guides/blob/main/MEV-relay-list.md)
# Batch RPC calls
Source: https://etherspot.fyi/skandha/multi-rpc-call
With Skandha we have the ability to batch rpc calls together.
An example of this in a node script:
```typescript theme={null}
const returnedValue = await fetch('https://rpc.etherspot.io/v1/137', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
// Batch together multiple calls like so
body: JSON.stringify([
{ "method": "skandha_config" },
{ "method": "eth_chainId" },
{ "method": "eth_supportedEntryPoints" },
{ "method": "skandha_feeHistory",
"params": [
"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789",
"15",
"latest"
]}])
})
.then((res) => {
return res.json()
}).catch((err) => {
console.log(err);
// throw new Error(JSON.stringify(err.response))
});
console.log('Value returned: ', returnedValue);
```
Here we're batching skandha\_config, eth\_chainId, eth\_supportedEntryPoints and skandha\_feeHistory together.
You can do this with any of amount of requests but a limit will be enabled in the future.
The response will look like this:
```bash theme={null}
Value returned: [
{
result: {
chainId: 137,
flags: [Object],
entryPoints: [Array],
beneficiary: '0xdCdD0DDEaA0407C26DFcD481De9A34e1C55F8d54',
relayer: '0xdCdD0DDEaA0407C26DFcD481De9A34e1C55F8d54',
minInclusionDenominator: 10,
throttlingSlack: 10,
banSlack: 10,
minSignerBalance: '0.1 eth',
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
estimationStaticBuffer: 35000,
validationGasLimit: 10000000,
receiptLookupRange: 1024,
etherscanApiKey: false,
conditionalTransactions: false,
rpcEndpointSubmit: false,
gasPriceMarkup: 2000,
enforceGasPrice: false,
enforceGasPriceThreshold: 1000,
eip2930: false
}
},
{ result: '0x89' },
{ result: [ '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789' ] },
{
result: {
actualGasPrice: [Array],
maxFeePerGas: [Array],
maxPriorityFeePerGas: [Array]
}
}
]
```
# P2P
Source: https://etherspot.fyi/skandha/p2p
In order for the P2P to work should set proper `canonicalMempoolId` and `canonicalEntryPoint` from the list below in config.json
```json theme={null}
"canonicalMempoolId": "QmRJ1EPhmRDb8SKrPLRXcUBi2weUN8VJ8X9zUtXByC7eJg",
"canonicalEntryPoint": "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789"
```
### Run the bootnode (optional)
```
./skandha node --redirectRpc
```
### Run a regular node
```
./skandha node --redirectRpc --p2p.bootEnrs [ENR here]
```
## The list of published canonical mempools are:
| Chain Name | Chain ID | Mempool ID | Description (Link to Mempool file) |
| ---------------------- | -------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Goerli` | 5 | `QmTmj4cizhWpEFCCqk5dP67yws7R2PPgCtb2bd2RgVPCbF` | [https://ipfs.io/ipfs/QmTmj4cizhWpEFCCqk5dP67yws7R2PPgCtb2bd2RgVPCbF?filename=goerli\_canonical\_mempool.yaml](https://ipfs.io/ipfs/QmTmj4cizhWpEFCCqk5dP67yws7R2PPgCtb2bd2RgVPCbF?filename=goerli_canonical_mempool.yaml) |
| `Sepolia` | 11155111 | `QmdDwVFoEEcgv5qnaTB8ncnXGMnqrhnA5nYpRr4ouWe4AT` | [https://ipfs.io/ipfs/QmdDwVFoEEcgv5qnaTB8ncnXGMnqrhnA5nYpRr4ouWe4AT?filename=sepolia\_canonical\_mempool.yaml](https://ipfs.io/ipfs/QmdDwVFoEEcgv5qnaTB8ncnXGMnqrhnA5nYpRr4ouWe4AT?filename=sepolia_canonical_mempool.yaml) |
| `Mumbai` | 80001 | `QmQfRyE9iVTBqZ17hPSP4tuMzaez83Y5wD874ymyRtj9VE` | [https://ipfs.io/ipfs/QmQfRyE9iVTBqZ17hPSP4tuMzaez83Y5wD874ymyRtj9VE?filename=mumbai\_canonical\_mempool.yaml](https://ipfs.io/ipfs/QmQfRyE9iVTBqZ17hPSP4tuMzaez83Y5wD874ymyRtj9VE?filename=mumbai_canonical_mempool.yaml) |
| `Arbitrum Sepolia` | 421614 | `QmVwhF77aVNzRUkMJNLDkeF9BtQMHLnfDY5ePpZ81uKLzA` | [https://ipfs.io/ipfs/QmVwhF77aVNzRUkMJNLDkeF9BtQMHLnfDY5ePpZ81uKLzA](https://ipfs.io/ipfs/QmVwhF77aVNzRUkMJNLDkeF9BtQMHLnfDY5ePpZ81uKLzA) |
| `Polygon Mainnet 500` | 137 | `QmRJ1EPhmRDb8SKrPLRXcUBi2weUN8VJ8X9zUtXByC7eJg` | [https://ipfs.io/ipfs/QmRJ1EPhmRDb8SKrPLRXcUBi2weUN8VJ8X9zUtXByC7eJg](https://ipfs.io/ipfs/QmRJ1EPhmRDb8SKrPLRXcUBi2weUN8VJ8X9zUtXByC7eJg) |
| `Polygon Mainnet 1000` | 137 | `QmRJ1EPhmRDb8SKrPLRXcUBi2weUN8VJ8X9zUtXByC7eJg` | [https://ipfs.io/ipfs/QmaHG3xiRYhxTth7vSTyZCyodBDrtj5hmEMz5DuzaJVKHH](https://ipfs.io/ipfs/QmaHG3xiRYhxTth7vSTyZCyodBDrtj5hmEMz5DuzaJVKHH) |
# skandha_subscribe
Source: https://etherspot.fyi/skandha/skandha-subscribe
Creates a new subscription for desired events. Sends data as soon as it occurs.
Using websockets you can listen to below event types.
The url will be `wss://rpc.etherspot.io/{version_here}/{chain_id}`
### Event Types
* `pendingUserOps` - user ops validated and put in the mempool
* `submittedUserOps` - user ops that are submitted on chain, reverted or deleted from mempool
* `onChainUserOps` - user ops successfully submitted on chain
### Examples:
### Pending UserOps
```json theme={null}
{
"method": "skandha_subscribe",
"params": [
"pendingUserOps"
],
"id": 1,
"jsonrpc": "2.0"
}
```
#### Response
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x106eb9867751ff1bf61bad4a80b8b486"
}
```
#### Event
```json theme={null}
{
"jsonrpc": "2.0",
"method": "skandha_subscription",
"params": {
"subscription": "0x106eb9867751ff1bf61bad4a80b8b486",
"result": {
"userOp": {
"sender": "0xb582979C2136189475326c648732F76677B16B98",
"nonce": "0x5",
"initCode": "0x",
"callData": "0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009fd4f6088f2025427ab1e89257a44747081ed590000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000009184e72a000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit": "0xb957",
"verificationGasLimit": "0x9b32",
"maxFeePerGas": "0x171ab3b64",
"maxPriorityFeePerGas": "0x59682f00",
"paymasterAndData": "0x",
"preVerificationGas": "0xae70",
"signature": "0x260dfe374ec4d662fae1ac99384abc50b0490d9a087877580f585e739be368e424576440db1d2fa8950b32207d023126a48749f86c35192d872b04eed22c4f2d1b"
},
"userOpHash": "0xf8a549671473d0ee532ca235b4629b239823b426b9a898d20c58ca5212a64c9e",
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"prefund": "0x2e7a15ccb8c44",
"submittedTime": "0x18f2990121c",
"status": "pending"
}
}
}
```
***
### Submitted, Reverted, Cancelled User Ops
```json theme={null}
{
"method": "skandha_subscribe",
"params": [
"submittedUserOps"
],
"id": 1,
"jsonrpc": "2.0"
}
```
#### Event
```json theme={null}
{
"jsonrpc": "2.0",
"method": "skandha_subscription",
"params": {
"subscription": "0x80e0632d2300aa2e1bcdb1e84329963f",
"result": {
"userOp": {
"sender": "0xb582979C2136189475326c648732F76677B16B98",
"nonce": "0x5",
"initCode": "0x",
"callData": "0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009fd4f6088f2025427ab1e89257a44747081ed590000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000009184e72a000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit": "0xb957",
"verificationGasLimit": "0x9b32",
"maxFeePerGas": "0x171ab3b64",
"maxPriorityFeePerGas": "0x59682f00",
"paymasterAndData": "0x",
"preVerificationGas": "0xae70",
"signature": "0x260dfe374ec4d662fae1ac99384abc50b0490d9a087877580f585e739be368e424576440db1d2fa8950b32207d023126a48749f86c35192d872b04eed22c4f2d1b"
},
"userOpHash": "0xf8a549671473d0ee532ca235b4629b239823b426b9a898d20c58ca5212a64c9e",
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"transaction": "0x3612daa69ec6d4804065e107e9055c9ec25c9c801d199886524e884e98179656",
"status": "Submitted"
}
}
}
```
### Onchain UserOps
#### Request
```json theme={null}
{
"method": "skandha_subscribe",
"params": [
"onChainUserOps"
],
"id": 1,
"jsonrpc": "2.0"
}
```
#### Event
```json theme={null}
{
"jsonrpc": "2.0",
"method": "skandha_subscription",
"params": {
"subscription": "0x2e8cf00cbe014abca180c1b6eae51173",
"result": {
"userOp": {
"sender": "0xb582979C2136189475326c648732F76677B16B98",
"nonce": "0x6",
"initCode": "0x",
"callData": "0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009fd4f6088f2025427ab1e89257a44747081ed590000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000009184e72a000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit": "0xb957",
"verificationGasLimit": "0x9b32",
"maxFeePerGas": "0x1420c636e",
"maxPriorityFeePerGas": "0x59682f00",
"paymasterAndData": "0x",
"preVerificationGas": "0xae70",
"signature": "0xbe055319adb23a465cf7439b7d4c2ab6e86383a100459c9c34942bd9a7fd016273a159b9239fca414633b6163353faa648dc3a41857075cde2cdd1813eb92fbc1c"
},
"userOpHash": "0xefafb37d346ccfaf183f0474015aacefe178707e78d56d95e19de8950c033393",
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"transaction": "0x8adba5c0463bd2cce16585871190972f49f00ead733b7005f43bf62c93296233",
"status": "onChain"
}
}
}
```
### Unsubscribe
#### Request
```json theme={null}
{
"method": "skandha_unsubscribe",
"params": [
"0xcf47424b5f492abfaa97ca5d4aed1f1d"
],
"id": 1,
"jsonrpc": "2.0"
}
```
#### Response
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": "ok"
}
```
#
Source: https://etherspot.fyi/transaction-kit/components/EtherspotApprovalTransaction
## Intro
The **EtherspotApprovalTransaction** component authorizes the spending of an asset, owned by yourself, by another Smart Contract. This Smart Contract can serve any purpose, but is usually associated with decentralised finance app (also known as DeFi) such as Uniswap or Gamma.
In other words, it's like giving your friend permission to spend some of your money, up to a certain limit. In this scenario, the friend is the Smart Contract mentioned above.## Component Properties
## Component Properties
| Property | Description |
| --------------- | ----------------------------------------------------------------------------------------------------------------------- |
| tokenAddress | The token's Smart Contract address that will be permitted to be moved to the recieverAddress. |
| receiverAddress | The destination blockchain address (on the same chain) permitted for the tokens located at tokenAddress to be moved to. |
| value | The maximum value that the token address is allowed to move to the receiverAddress. |
## How to use
```javascript theme={null}
import {
EtherspotContractTransaction,
EtherspotApprovalTransaction,
EtherspotBatches,
EtherspotBatch,
} from "@etherspot/transaction-kit";
import { utils } from "ethers";
// Later in your render function...
{/*
The following block is the first transaction in this batch
of transactions, and instructs Etherspot to set a spending
limit for the Smart Contract located at `receiverAddress`
to be allowed to spend the token located at `tokenAddress`
up to the amount specified in `value`.
So in summary, using the example below:
Smart Contract: 0x0493b9a21dE42546B2E3687Da683D0B7B6ec2180
is allowed to spend up to and including 10 of the token
located at: 0x2A9bb3fB4FBF8e536b9a6cBEbA33C4CD18369EaF.
*/}
{/*
The following block is the second transaction in this batch
of transactions, and instructs Etherspot to call and execute
the "stake" function on the Smart Contract located at the
`contractAddress`. The "stake" function in the Smart Contract
address located at the `contractAddress` takes 1 parameter
which is the numbber of tokens to stake.
In the component above, we
gave permission for the Smart Contract below to spend 10 tokens
of the token located at `tokenAddress` above from the Etherspot
Smart Wallet account.
*/}
```
## A note on setting the spend limit
Sometimes it may seem convenient to set the spending limit to a very high amount, more than what is actually needed. This will result in you not having to call the approval tag again for that Smart Contract.
Whilst no-one is going to stop you doing this, please consider that the Smart Contract you're giving permission to spend your funds may be compromised in the future and could possibly drain all the funds in your account that it has permission to.
#
Source: https://etherspot.fyi/transaction-kit/components/EtherspotBatch
## Intro
The **EtherspotBatch** component allows you to define one or more **EtherspotTransaction** components to be sent as part of the **EtherspotBatch**
## Component Properties
| Property | Description |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | Optional: An ID (which can be a string, number etc) that allows you to define the ID of this batch group. We will use this ID if you provide it internally, but also allows you to use it to keep track elsewhere within your app. |
| chainId | Optional: The blockchain ID that you would like to execute this batch on. Check out our [supported blockchains](get-started/chains-supported) to check what we support. The default is "1" - Ethereum Mainnet. |
| gasTokenAddress | Optional: You can choose to pay for for batch of transactions with something else other than the native token for the blockchain you defined in chainId (or Ethereum if none specified). |
```javascript theme={null}
// In your functional component or elsehwere
const onEstimateReceiver = (estimationData) => {
console.log(
'This is the cost estimate for all the batches',
estimationData,
);
}
// In your render or as a component...
{/*
Within the component,
you can add 1 or more
tags to be performed together and at the same
time (i.e. within the same "batch").
*/}
```
#
Source: https://etherspot.fyi/transaction-kit/components/EtherspotBatches
## Intro
The **EtherspotBatches** component is, at its simplest, a way to indicate to TransactionKit that you're about to start defining one or more **EtherspotBatch** components which may contain one or more **EtherspotTransaction** or **EtherspotContractTransaction** components.
This tag helps TransactionKit keep itself organised, takes various component props to customise the transactions inside it and returns events when we need to .
The EtherspotBatches component takes any child components like text, buttons etc.
## Component Properties
| Property | Description |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| via | Must be set to "etherspot-prime" to ensure the batches are sent via prime. |
| skip | Optional: Takes a boolean value of true or false. Set skip= when you would like to skip these batches from being estimated and sent. |
| id | Optional: An ID (which can be a string, number etc) that allows you to define the ID of this batches group. We will use this ID if you provide it internally, but also allows you to use it to keep track elsewhere within your app. |
| onEstimated | Optional: Takes a function which accepts a parameter which is an estimation object. This is fired when an transaction cost estimation has been completed of all the contained **EtherspotBatch** components. See the code example below to see how this is used. |
| onSent | |
```javascript theme={null}
// In your functional component or elsehwere
const onEstimateReceiver = (estimationData) => {
console.log(
'This is the cost estimate for all the batches',
estimationData,
);
}
// In your render or as a component...
```
#
Source: https://etherspot.fyi/transaction-kit/components/EtherspotContractTransaction
## Intro
This component allows you to tell TransactionKit that there will be a blockchain transaction performed, and it will be against a Smart Contract. This component is specifically tailored to Smart Contracts. If you are looking to send a simple transaction, then **EtherspotTransaction** is what you may be looking for.
You can have 1 or many **EtherspotContractTransaction** components inside an **EtherspotBatch** component to be sent at the same time (i.e. as part of the same "batch").
## Component Properties
| Property | Description |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | Optional: An ID (which can be a string, number etc) that allows you to define the ID of this batch group. We will use this ID if you provide it internally, but also allows you to use it to keep track elsewhere within your app. |
| contractAddress | The destination Smart Contract address on the blockchain. Every Smart Contract has a unique address, including tokens. |
| abi | The "Application Binary Interface" of the Smart Contract... in other words, a dictionary of all the things we can do with this Smart Contract, and what data it needs. |
| method | The name of the function we want to call on the Smart Contract |
| params | The parameter(s), if any, we want to provide to the "method" above. |
| value | Optional: The amount of native token we want to send along. This can either be a string represented in Ether or as a BigNumber (see example). |
## Sending a token
Sending a token is a very common practice within the blockchain ecosystem. When you send a token, you are interacting with the Smart Contract for that token. For example - you might want to send 10 USDC to pay for something, or, you might want to send 200 SHIB to a friend. Here's how to do that.\`\`\`javascript
```javascript theme={null}
// In your functional component or elsehwere
const onEstimateReceiver = (estimationData) => {
console.log(
'This is the cost estimate for all the batches',
estimationData,
);
}
// In your render or as a component...
{/*
The following will send
10 USDC to 0x0763d68dd586AB1DD8Be2e00c514B2ac8757453b by
instrucing the USDC contract (0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)
via the "transfer" method, which takes two parameters; the
address (who to transfer to) and the amount (how much USDC to send).
Note the "value" is set to 0 here. We do not want to send any of
our own native asset along with this transaction.
*/}
{/*
You can add 1 or more
components here, and they will all be executed
together and at the same time (i.e. as part of
this batch).
*/}
```
#
Source: https://etherspot.fyi/transaction-kit/components/EtherspotTokenTransferTransaction
## Intro
The **EtherspotTokenTransferTransaction** React Component helps you facilitate the transfer of an asset (such as PLR. USDC or SHIB) to another account.
You just need to provide the token address, the destination address and the amount of tokens you want to transfer to the destination address, and we'll take it from there.
## About token addresses
Remember to research the correct token address on the correct blockchain you are sending to. Do not send tokens to another blockchain as it will be lost.
Keep transfers on the same blockchain.
## Component Properties
| Property | Description |
| --------------- | ----------------------------------------------------------------------------------------------------------------------- |
| tokenAddress | The token's Smart Contract address that will be permitted to be moved to the recieverAddress. |
| receiverAddress | The destination blockchain address (on the same chain) permitted for the tokens located at tokenAddress to be moved to. |
| value | The maximum value that the token address is allowed to move to the receiverAddress. |
## How to use
```javascript theme={null}
// In your functional component or elsehwere
const onEstimateReceiver = (estimationData) => {
console.log(
'This is the cost estimate for all the batches',
estimationData,
);
}
// In your render or as a component...
{/*
The following
component will transfer 5 USDC from the built-in
Etherspot Smart Wallet account to the receiverAddress.
In the example below:
- The tokenAddress is the USDC contract address
on Ethereum
- The receiverAddress is the destination of the
token amount being transferred
- The value determines how much of the USDC is
being transferred to the receiverAddress
*/}
{/*
You can add more
components here, and they will all be executed
together and at the same time (i.e. as part of
this batch).
*/}
```
#
Source: https://etherspot.fyi/transaction-kit/components/EtherspotTransaction
## Intro
This component allows you to tell TransactionKit that there will be a blockchain transaction performed. This TransactionKit component is likely to be the one you use the most to send a basic transaction, such as sending ETH (or the native token on any other chain we support) to another blockchain address on the same chain.
You can have 1 or many **EtherspotTransaction** components inside an **EtherspotBatch** component to be sent at the same time (i.e. as part of the same "batch").
## Component Properties
| Property | Description |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | Optional: An ID (which can be a string, number etc) that allows you to define the ID of this batch group. We will use this ID if you provide it internally, but also allows you to use it to keep track elsewhere within your app. |
| to | The destination blockchain address, on the same chain. For example, if you are sending from the Polygon blockchain, make sure you sent it to another address on the Polygon blockchain. |
| value | This can either be a string represented in Ether or as a BigNumber (see example). |
| data | Optional: An optional data object which can be read by the recipient (the to address), if it is a Smart Contract, to perform additional functions as part of the transaction (see example), or, to just store an arbitrary piece of data along with the transaction. |
## Sending some ETH
Sending some ETH (or any other native token for another blockchain) is one of the most common transactions to performed - for example, sending some ETH to a friend. Here is how you can do that.
```javascript theme={null}
// In your functional component or elsehwere
const onEstimateReceiver = (estimationData) => {
console.log(
'This is the cost estimate for all the batches',
estimationData,
);
}
// In your render or as a component...
{/*
The following will send
0.01 ETH to 0x0763d68dd586AB1DD8Be2e00c514B2ac8757453b.
*/}
{/*
You can add 1 or more
components here, and they will all be executed
together and at the same time (i.e. as part of
this batch).
*/}
```
## Sending a transaction with a data object
Another type of transaction is sending a transaction with some data. Other dapps and services may read this data, or if you're sending some ETH (or other native token) to a Smart Contract, the Smart Contract may use this data to perform additional functions. Here how you can send some arbitrary data along with your transaction.
```javascript theme={null}
// In your functional component or elsehwere
const onEstimateReceiver = (estimationData) => {
console.log(
'This is the cost estimate for all the batches',
estimationData,
);
}
// In your render or as a component...
{/*
The following will send
0.01 ETH to 0x0763d68dd586AB1DD8Be2e00c514B2ac8757453b
and will include a data object containing the the
message 'i am a teapot'.
*/}
{/*
You can add 1 or more
components here, and they will all be executed
together and at the same time (i.e. as part of
this batch).
*/}
```
#
Source: https://etherspot.fyi/transaction-kit/components/EtherspotTransactionKit
## Intro
In order for us to provide all the power of Etherspot to your app, we need to wrap your app in an **EtherspotTransactionKit** tag. This will allow the whole app to access libraries and services provided by TransactionKit.
## How to use
The **EtherspotTransactionKit** component will wrap your top level **App** tag which is usually found in the React app's index.js file.
Here is how you would use the **EtherspotTransactionKit** tag.
```javascript theme={null}
// Import the following libraries
import { EtherspotTransactionKit } from '@etherspot/transaction-kit';
// We're importing Ethers here to create a random wallet
import { ethers } from 'ethers';
/**
* Later in your app's function code...
*/
// Let's create the random wallet for demonstration purposes.
const randomWallet = ethers.Wallet.createRandom();
// Pass the private key into a new ethers.Wallet to return a
// provider. This is the account we pass into EtherspotUi.
const providerWallet = new ethers.Wallet(randomWallet.privateKey);
/**
* In your app's render function, "wrap" the tag...
*/
root.render(
// <-- open here
// <-- close here
);
```
# getAccountTransaction()
Source: https://etherspot.fyi/transaction-kit/hooks/getAccountTransaction
## Intro
You can fetch a single historical transaction for any blockchain address that belongs to the Etherspot ecosystem. All you need is the hash you want to look up the history item for.
## How to use
To fetch a historical transaction for a blockchain address on the Etherspot platform, simply call getAccountTransaction(hash) from useEtherspotHistory(), passing a blockchain hash where hash is above.
```javascript theme={null}
import {
useEtherspotHistory,
} from '@etherspot/transaction-kit';
// Later in the main component function...
const { getAccountTransaction } = useEtherspotHistory();
// And when you're ready to fetch your transaction history item...
const accountTransactionHistoryItem = await getAccountTransactions(
'0xdd2f99257393a054588fbfaf7702c293b05aea2ffa034920c0d02f475d6e97d0',
);
// accountTransactionHistoryItem will now return an object containing the history item.
```
# getAccountTransactions()
Source: https://etherspot.fyi/transaction-kit/hooks/getAccountTransactions
## Intro
You can fetch the historical transactions for any blockchain address that belongs to the Etherspot ecosystem.
## How to use
```javascript theme={null}
import {
useEtherspotHistory,
} from '@etherspot/transaction-kit';
// Later in the main component function...
const { getAccountTransactions } = useEtherspotHistory();
// And when you're ready to fetch your transaction history...
const accountTransactionHistory = await getAccountTransactions(); // This is also a Promise
// accountTransactionHistory will now contain an array of history objects.
```
# useEtherspotAssets()
Source: https://etherspot.fyi/transaction-kit/hooks/useEtherspotAssets
## Intro
As part of any cryptocurrency related app, it's essential to be able to access a list of other cryptocurrencies and their asset data (such as asset logo) to use within your app, otherwise you'll likely need to try and find this yourself.
The useEtherspotAssets hook makes this easy for you by allowing you to access our prebuilt list of tokens for every chain.
## How to use
```javascript theme={null}
import {
useEtherspotAssets
} from "@etherspot/transaction-kit";
// Later in your component function...
const { getAssets } = useEtherspotAssets();
// When you're ready to fetch the assets...
const tokens = await getAssets();
// `tokens` will look similar to the following...
// [
// {
// "address": "0xe3818504c1B32bF1557b16C238B2E01Fd3149C17",
// "chainId": 1,
// "decimals": 18,
// "logoURI": "https://images.prismic.io/pillar-app/83dcf8ff-6459-41d4-8d43-7ec143814b2d_pillar-logo-5.png?auto=compress,format",
// "name": "Pillar",
// "symbol": "PLR"
// },
// {
// "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
// "chainId": 1,
// "decimals": 6,
// "logoURI": "https://raw.githubusercontent.com/bnb-chain/tokens-info-v2/master/tokens/usdt/usdt.png",
// "name": "USDT",
// "symbol": "USDT"
// },
// ...
// ]
```
# useEtherspotNfts()
Source: https://etherspot.fyi/transaction-kit/hooks/useEtherspotNfts
## Intro
Depending on what type of app you are building, NFTs can sometimes form part of your app, or - can even be the main focus of your app entirely.
The useEtherspotNfts hook will allow you to fetch NFTs per blockchain and by account address.
## Parameters
```javascript theme={null}
/**
* useEtherspotNfts(chainId?: number)
*/
// useEtherspotNfts takes a chain ID as
// its only parameter. For example, the following
// will fetch your NFTs for your Smart Wallet
// address on Polygon:
const { getAccountNfts } = useEtherspotNfts(137);
// ^
// Note the chain ID
const accountNfts = await getAccountNfts();
// By default, this fetches the NFTs of your own
// Smart Wallet account.
/**
* getAccountNfts(accountAddress?: string)
*/
// The getAccountNfts function takes an account
// address as its only parameter. The function
// will fetch the NFTs for the provided address
// instead of your Smart Wallet address.
const otherAccountNfts = await getAccountNfts(
'0x0763d68dd586AB1DD8Be2e00c514B2ac8757453b'
);
```
## How to use
```javascript theme={null}
import {
useEtherspotNfts
} from "@etherspot/transaction-kit";
// Later in your component function...
const { getAccountNfts } = useEtherspotNfts();
// When you're ready to fetch NFTs...
const nfts = await getAccountNfts();
// `nfts` will look similar to the following...
// [
// {
// "contractName": null,
// "contractSymbol": null,
// "contractAddress": "0xa07e45a987f19e25176c877d98388878622623fa",
// "tokenType": "Erc1155",
// "nftVersion": null,
// "nftDescription": null,
// "balance": 1,
// "items": [
// {
// "tokenId": "123",
// "name": "null #123",
// "amount": 2,
// "image": "Dummy ERC1155",
// "ipfsGateway": null
// }
// ]
// }
// ]
```
# useEtherspotTransactions()
Source: https://etherspot.fyi/transaction-kit/hooks/useEtherspotTransactions
## estimate()
Any transaction that is intended to be sent to the blockchain must be estimated first. The estimation function performs several checks including cost estimation and transaction validity. This method must always be called before we send, otherwise the send method will return an error.
Whenever you add, edit or remove an **EtherspotBatches**, **EtherspotBatch** or **EtherspotTransaction** component - call the estimate hook.
You simply need to import the hook and call the function as shown below.
```javascript theme={null}
import {
useEtherspotTransactions,
} from '@etherspot/transaction-kit';
// Later in the main component function...
const { estimate } = useEtherspotTransactions();
// And when you're ready to perform the estimate...
estimate(); // This is also a Promise
// Now your transactions are ready to send!
```
## send()
The send function simply sends all the **EtherspotBatches**, which contain all **EtherspotBatch** and **EtherspotTransaction** components to the blockchain via the Etherspot platform.
**You must always estimate before sending.**
Estimating first performs important transaction cost calculations that are required before sending.
Whenever you are ready to send your transaction to the blockchain, you simply need to call send() as shown below.
```javascript theme={null}
import {
useEtherspotTransactions,
} from '@etherspot/transaction-kit';
// Later in the main component function...
const { send } = useEtherspotTransactions();
// And when you're ready to end your transaction(s)...
send(); // This is also a Promise
// Your transactions are now being sent to the blockchain
```
# Installation
Source: https://etherspot.fyi/transaction-kit/installation
## Install transaction kit
Install TransactionKit and Ethers:
```bash npm theme={null}
npm i @etherspot/transaction-kit ethers@5.4.0
// or
yarn add @etherspot/transaction-kit ethers@5.4.0
```
Now you're ready to take a look at a basic example, sending some of the networks native token.
# Introduction to Transaction Kit
Source: https://etherspot.fyi/transaction-kit/intro
## What is Transaction Kit?
Writing code to perform transactions on the blockchain is still difficult. The learning curve is steep and some blockchain knowledge and Web3 development experience is needed.
We have leveraged the power of the Etherspot platform to simplify this into a few React Components and Hooks that can be controlled via your React UI.
Transaction Kit is available on v1 and on Prime. If we don't yet support the chain yet you need on Prime, consider using [v1](https://docs.etherspot.io/transaction-kit/quick-start)
until we support the chain you want to work on.
All you need to know is some basic React and you're ready to develop Web3 applications which use Account Abstraction!
To get going with Transaction Kit instantly you can simply run:
```bash theme={null}
npx start-web3 my-app
cd my-app
npm start
```
### Features
# Paymaster usage
Source: https://etherspot.fyi/transaction-kit/paymaster
Sponsored Transactions are an important part of Account Abstraction.
When creating a batch of transacions to send via a paymaster and have them sponsored,
you first must get an api key to do so, details of this can be found [here](/arka/intro).
Then when using the [Etherspot Batches tag](/transaction-kit/components/EtherspotBatches)
we can add paymaster values like so:
```javascript theme={null}
```
If the address is whitelisted with the paymaster for this api\_key, then the
transaction will be sponsored. You can learn more about Arka API calls [here](/arka/api-calls/whitelist-an-address).
# Send Native Token
Source: https://etherspot.fyi/transaction-kit/send-native-token
In this example we're going to learn how to send a native token using transaction kit.
This is the most basic Transaction Kit tutorial and a great place to start.
We have a code sandbox for this example which you can fork [here](https://codesandbox.io/s/etherspot-prime-send-native-token-demo-j2h44g).
## Bootstrap a react app
Let's keep it simple and use create-react-app here. Run the following command in a directory of your choice:
```bash npm theme={null}
npx create-react-app txkit-quickstart
```
The above command will install and bootstrap a basic React App into a directory called txkit-quickstart. Once the installation has finished, change directory into your newly bootstrapped React app by typing:
```bash npm theme={null}
cd txkit-quickstart
```
Make sure you have the Transaction Kit and Ethers (version 5.4.0) packages installed, then we'll edit two files.
App.js and index.js.
## index.js
```javascript theme={null}
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { EtherspotTransactionKit } from "@etherspot/transaction-kit";
import { ethers } from "ethers";
import App from "./App";
const randomWallet = ethers.Wallet.createRandom();
const providerWallet = new ethers.Wallet(randomWallet.privateKey);
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
);
```
## App.js
```javascript theme={null}
import "./styles.css";
import {
EtherspotBatches,
EtherspotBatch,
EtherspotTransaction,
useEtherspotTransactions,
useWalletAddress
} from "@etherspot/transaction-kit";
import React from "react";
export default function App() {
const [address, setAddress] = React.useState(
"0x271Ae6E03257264F0F7cb03506b12A027Ec53B31"
);
const [amount, setAmount] = React.useState("0.001");
const { estimate, send } = useEtherspotTransactions();
const etherspotAddresses = useWalletAddress("etherspot-prime", 11155111);
React.useEffect(() => {
console.log(etherspotAddresses);
}, [etherspotAddresses]);
return (
);
}
```
# Staking
Source: https://etherspot.fyi/transaction-kit/staking
In this example we're going to mint some tokens from one contract and stake them in another using transaction kit.
This example will show how to do custom contract calls, show how to approve ERC20 tokens, and show how to batch transactions.
You can check how to do these things within the components we will create, and then we'll call these components in **App.js**
We have a code sandbox for this example which you can fork [here](https://codesandbox.io/s/etherspot-prime-staking-tutorial-q2m4dw).
Start off by creating a react app, you can see how to do this [here](transaction-kit/send-native-token)
Make sure you have the Transaction Kit and Ethers (version 5.4.0) packages installed, then we'll edit two files and create two more as components.
App.js and index.js.
## index.js
```javascript theme={null}
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { EtherspotTransactionKit } from "@etherspot/transaction-kit";
import * as ethers from "ethers";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
const randomWallet = ethers.Wallet.createRandom();
const providerWallet = new ethers.Wallet(randomWallet.privateKey);
console.log(providerWallet);
root.render(
);
```
## App.js
```javascript theme={null}
import { Typography, Container, Box, Paper } from "@mui/material";
import { useWalletAddress } from "@etherspot/transaction-kit";
import { useEffect, useState } from "react";
import MintTktTokens from "./components/MintTktTokens/MintTktTokens";
import StakeTktTokens from "./components/StakeTktTokens/StakeTktTokens";
export default function App() {
const [hasSentMintTx, setHasSentMintTx] = useState(false);
const [hasSentStakingTx, setHasSentStakingTx] = useState(false);
const mumbaiAddress = useWalletAddress("etherspot-prime", 80001);
const [mumbaiSmartWalletAddress, setMumbaiSmartWalletAddress] = useState(
false
);
useEffect(() => {
console.log("etherspotAddresses", mumbaiAddress);
const fetchMumbaiSmartWallet = () => {
setMumbaiSmartWalletAddress(mumbaiAddress);
};
fetchMumbaiSmartWallet();
}, [mumbaiAddress, mumbaiSmartWalletAddress]);
const onSentStakingTransactionReceiver = (e) => {
console.log("Sent Staking Transaction:", e);
setHasSentStakingTx(true);
};
const onSentMintTransactionReceiver = (e) => {
console.log("Sent Mint Transaction:", e);
setHasSentMintTx(true);
};
return (
Transaction KitStaking Example
{mumbaiSmartWalletAddress ? (
) : null}
{hasSentMintTx ? (
) : null}
Etherspot Smart Wallet address: {mumbaiSmartWalletAddress}
TKT Token Contract: {"0x2A9bb3fB4FBF8e536b9a6cBEbA33C4CD18369EaF"}
TKT Staking Contract: {"0x0493b9a21dE42546B2E3687Da683D0B7B6ec2180"}
);
}
```
Now we want to create a directory called **components** in the same directory as index.js and App.js
Within **components**, create a directory called **MintTktTokens** and within that create a file called **MintTktTokens.js**
## MintTktTokens.js
```javascript theme={null}
import { Typography, Paper, Button, Grid, Alert } from "@mui/material";
import {
EtherspotContractTransaction,
EtherspotBatches,
EtherspotBatch,
useEtherspotTransactions
} from "@etherspot/transaction-kit";
import { utils } from "ethers";
export default function MintTktTokens(props) {
const { estimate, send } = useEtherspotTransactions();
const estimateAndMint = async () => {
const estimateData = await estimate();
console.log("Estimate Data:", estimateData);
if (JSON.stringify(estimateData).includes("reverted")) {
alert("Tx reverted! No gas token in account");
return;
}
const sendData = await send();
console.log("Send Data:", sendData);
};
return (
<>
{props.hasSentMintTx ? (
The transaction to mint TKT tokens was sent to Etherspot
) : (
First, let's mint some TKT tokens into your Etherspot Smart
Wallet on Mumbai:
{props.mumbaiSmartWalletAddress}
)}
>
);
}
```
Also within that create a folder called **StakeTktTokens** and within that create a file called **StakeTktTokens.js**
## StakeTktTokens.js
```javascript theme={null}
import { Typography, Paper, Button, Grid, Alert } from "@mui/material";
import {
EtherspotContractTransaction,
EtherspotApprovalTransaction,
EtherspotBatches,
EtherspotBatch,
useEtherspotTransactions
} from "@etherspot/transaction-kit";
import { utils } from "ethers";
export default function StakeTktTokens(props) {
const { estimate, send } = useEtherspotTransactions();
const estimateAndStake = async () => {
const estimateData = await estimate();
console.log("Stake Estimate Data:", estimateData);
if (JSON.stringify(estimateData).includes("reverted")) {
alert("Tx reverted!");
return;
}
const sendData = await send();
console.log("Stake Send Data:", sendData);
};
return (
<>
{props.hasSentStakingTx ? (
You have sent the staking transaction to Etherspot!
) : (
Next, lets stake these tokens into an example staking contract
we have created.
)}
>
);
}
```