# Authentication Source: https://docs.p2p.org/docs/authentication Get your authentication token Staking Platform APIs use bearer JSON Web Token (JWT). Each client is assigned a unique authentication token for secure access to the service. Contact [support@p2p.org](mailto:support@p2p.org) with your request, and you'll receive your authentication token directly via email or direct message. Specify the received authentication token when interacting with our APIs. Pass the token in the Authorization header in the following format: ```text HTTP headers theme={null} Authorization: Bearer ``` # Cardano Transaction Signing Source: https://docs.p2p.org/docs/cardano-transaction-signing Sign and broadcast Cardano transactions built with the P2P.org Unified API. To sign and broadcast the transaction to the Cardano network, follow these steps: 1. Prepare unsigned transaction in Base64 encrypted format. 2. Sign the transaction using the following code: ```javascript JavaScript theme={null} import { Bip32PrivateKey, Transaction, TransactionHash, TransactionWitnessSet, Vkey, Vkeywitness, Vkeywitnesses, } from "@emurgo/cardano-serialization-lib-nodejs"; import { mnemonicToEntropy } from "bip39"; import { blake2b } from "blakejs"; const MNEMONIC = ""; const RAW_TX_HEX = ""; function deriveKey(mnemonic: string, role: number, index = 0): Bip32PrivateKey { const entropy = Buffer.from(mnemonicToEntropy(mnemonic), "hex"); const rootKey = Bip32PrivateKey.from_bip39_entropy(entropy, Buffer.from("")); return rootKey .derive(1852 | 0x80000000) // purpose .derive(1815 | 0x80000000) // coin type .derive(0 | 0x80000000) // account 0 .derive(role) // role: 0=payment, 2=stake .derive(index); // index } function signTransactionHex( unsignedHex: string, paymentKey: Bip32PrivateKey, stakeKey: Bip32PrivateKey ): string { const tx = Transaction.from_bytes(Buffer.from(unsignedHex, "hex")); const txHashBytes = blake2b(tx.body().to_bytes(), undefined, 32); const txHash = TransactionHash.from_bytes(txHashBytes); const witnesses = Vkeywitnesses.new(); // Payment key witness const rawPaymentKey = paymentKey.to_raw_key(); witnesses.add( Vkeywitness.new( Vkey.new(rawPaymentKey.to_public()), rawPaymentKey.sign(txHash.to_bytes()) ) ); // Stake key witness const rawStakeKey = stakeKey.to_raw_key(); witnesses.add( Vkeywitness.new( Vkey.new(rawStakeKey.to_public()), rawStakeKey.sign(txHash.to_bytes()) ) ); const witnessSet = TransactionWitnessSet.new(); witnessSet.set_vkeys(witnesses); const signedTx = Transaction.new(tx.body(), witnessSet, tx.auxiliary_data()); return Buffer.from(signedTx.to_bytes()).toString("hex"); } async function main() { if (!RAW_TX_HEX || RAW_TX_HEX.length < 10) { throw new Error("❌ RAW_TX_HEX is missing or invalid."); } const paymentKey = deriveKey(MNEMONIC, 0); // m/1852'/1815'/0'/0/0 const stakeKey = deriveKey(MNEMONIC, 2); // m/1852'/1815'/0'/2/0 const signedHex = signTransactionHex(RAW_TX_HEX, paymentKey, stakeKey); console.log("✅ Signed Cardano Tx (hex):"); console.log(signedHex); } main().catch(console.error); ``` Upon successful execution, the script prints the signed transaction in hexadecimal format, ready to be broadcasted: ```bash bash theme={null} ✅ Signed Cardano Tx (hex): ... ``` 3. Broadcast the transaction to the Cardano network by sending a POST request to [/api/v1/unified/transaction/broadcast](/reference/unified-transaction-send). # Contacts Source: https://docs.p2p.org/docs/contacts We're here to help! Experiencing issues or need help? Don't worry, our product team is here for you. We are committed to resolving any problems you might be facing as quickly and efficiently as possible. Email [support@p2p.org](mailto:support@p2p.org) and include your question to ensure that we can assist you effectively. # Delegator Source: https://docs.p2p.org/docs/data-delegator Staking Data API provides methods to retrieve essential information about delegators within the network. These endpoints offer insights into a delegator's current stake, rewards received, Annual Percentage Yield (APY), associated fees, and other information. ## Methods Unless stated otherwise, the `startAt`–`finishAt` range cannot exceed 31 days, and `finishNumber` cannot be lower than `startNumber`. The `addressType` values accepted by each network are listed in [Getting Started](/docs/getting-started-data-api#get-delegator-stake). ### Get Delegator Summary **Endpoint:** [GET /api/v1/\{network}/data/delegator/summary](/reference/data-delegator-summary) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, TON, Cardano, Celestia. **Description:** Utilize this endpoint to obtain the basic general information about a delegator for the period, such as a list of rewards earned by the delegator, the current stake, and the gross and net APY (`grossApy`, `netApy`) associated with the delegator. ### Get Delegator Stake **Endpoint:** [GET /api/v1/\{network}/data/delegator/stakes](/reference/data-delegator-stakes) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, TON, Cardano, Celestia. **Description:** Utilize this endpoint to monitor the current stake of a delegator. Regularly check the stake data to assess the delegator's commitment and participation in the network. `groupBy` selects the aggregation: `stakingPeriod` aggregates by epoch, `day` by calendar day, `all` over the whole requested period. `groupBy=day` cannot be combined with `startNumber` / `finishNumber`; use `startAt` / `finishAt` instead, otherwise the request returns `400`. `stake` is the total stake balance of the delegator. On Ethereum it includes ETH that is still in the deposit or activation queue. With `groupBy=day` on Ethereum, the response additionally contains the optional fields `activeStake` (already active on the Beacon Chain) and `inactiveStake` (still waiting to be activated), which together make up `stake`. Historical Ethereum data is available from 2024-01-01. ### Get Delegator Rewards **Endpoint:** [GET /api/v1/\{network}/data/delegator/rewards](/reference/data-delegator-rewards) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, TON, Cardano, Celestia. **Description:** Utilize this endpoint to track the rewards earned by the delegator. Monitor reward data to assess the delegator's performance and profitability. Use `rewardTypes` to filter by reward type and `skip=validator` to drop the per-validator breakdown. ### Get Delegator APY **Endpoint:** [GET /api/v1/\{network}/data/delegator/apy](/reference/data-delegator-apy) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, TON, Cardano, Celestia. **Description:** Utilize this endpoint to understand the APY associated with the delegator. The response contains `grossApy` (before the validator fee) and `netApy` (after the fee). Regularly check APY data for insights into the delegator's long-term performance. ### Get Total Delegator Rewards **Endpoint:** [GET /api/v1/\{network}/data/delegator/total-rewards](/reference/data-delegator-total-rewards) **Supported networks:** Ethereum. **Description:** Utilize this endpoint to track the total rewards earned by the delegator over its lifetime. ### Get Delegator APR **Endpoint:** [GET /api/v1/\{network}/data/delegator/apr](/reference/data-delegator-apr) **Supported networks:** Ethereum. **Description:** Utilize this endpoint to obtain the lifetime Annual Percentage Rate (APR) of the delegator, returned as `apr`. Only `address` is required. ### Get Delegator Rewards (SSV) **Endpoint:** [GET /api/v1/\{network}/data/delegator/ssv](/reference/data-delegator-ssv) **Supported networks:** Ethereum. **Description:** Utilize this endpoint to obtain the monthly SSV rewards for a delegator address. Each item covers one `month` and contains `distributedAmount`, `claimedAmount`, `predictedAmount`, the P2P `fee`, and `netAmount` (claimed minus fee). # Getting Started Source: https://docs.p2p.org/docs/defi-api-getting-started Simplify your work with decentralized assets with DeFi API from P2P.ORG. This article describes how the DeFi API process works in general. To see detailed guides for a specific protocol, refer to the [Protocols Supported](/docs/defi-api-protocols) section. The DeFi API supports the complete transaction lifecycle, from creation through execution. When a user initiates a deposit action, the API constructs the necessary transaction, awaits user signing, and submits it to the blockchain network. The API includes the major group of [transaction endpoints](/reference/get-transaction-schema), with the `transactions/steps/craft` method serving as the primary interface for transaction creation—handling approximately 95% of operations. The data part with endpoints for getting details on earnings, positions, and rewards is also provided. ## Action types Currently, each vault integration supports the following operations: * `approvePermit2`: Allow the P2P.ORG smart contract to manage tokens by obtaining approval. * `deposit`: Initiate a deposit transaction to delegate the assets. * `withdraw`: Withdraw the assets to the wallet. To start using the DeFi API, [get an authentication token](/docs/authentication). ## General flow 1. Get a schema with the step list. To retrieve a schema describing which transactions are required for a specified action, protocol, chain, and strategy, send a GET request to [/api/defi/v1/transactions/schema](/reference/get-transaction-schema). 2. Upon the schema retrieved, construct the required transactions. Depending on the action type and the request data, a schema may include up to 3 transactions. To initiate the constructing process, send a POST request to [/api/defi/v1/transactions/steps/craft](/reference/craft-transaction-step). Upon each request, a raw unsigned transaction is generated. 3. Sign each transaction using the [transaction-type-specific libraries](/docs/defi-api-signing-transaction). 4. Check the status of the constructed transaction by using the [/api/defi/v1/transactions/steps/result](/reference/get-step-result) endpoint. If the schema contains several transactions, for [intermediate transactions](/docs/defi-api-signing-transaction), you may use a [/api/defi/v1/transactions/steps/status](/reference/get-step-status) endpoint. 5. Broadcast the final transaction to the network by sending a POST request to [/api/defi/v1/transactions/broadcast](/reference/broadcast-transaction). # Overview Source: https://docs.p2p.org/docs/defi-api-overview The P2P.ORG DeFi API is a specialized solution designed for institutions, exchanges, and wallet providers seeking to integrate seamless staking functionality directly into their platforms. Building upon the proven success of the — a visual interface enabling users to deposit tokens across multiple protocols and vaults — this API offers the same robust capabilities in a flexible, native integration format. [Get an authentication token](/docs/authentication) to start using DeFi API. ## Key features * **Comprehensive Protocol Coverage**. Users can deposit various tokens across multiple supported protocols and vaults, all through a single integration point. * **Flexible Integration Options**. Direct integration for complete control over the transaction flow and P2P.ORG proxy contracts for simplified deployment. * **Enhanced Security**. The P2P.ORG DeFi API implements a three-module verification system. Every transaction undergoes dual verification—during creation and at signing—ensuring only authorized, P2P.ORG-generated transactions are processed, protecting users from malicious activities. ## Supported chains and networks See all [Protocols Supported](/docs/defi-api-protocols). Currently, integration with [Morpho vaults](https://app.morpho.org/) is supported. # Protocols Supported Source: https://docs.p2p.org/docs/defi-api-protocols The table below summarizes all protocols supported in the DeFi API and the `protocol` and `chain` parameters that are required for the crafting endpoint: | `protocol` | Networks | `chain` | | ---------------------- | -------- | ----------------- | | [Morpho](/docs/morpho) | Ethereum | `mainnet`, `base` | You can also get the full list of all supported protocols and networks through the GET request to [/api/defi/v1/earning/protocols](/reference/get-protocols). The list of available strategies may vary dynamically. To see the most up-to-date list for each protocol, make an API request to [/api/defi/v1/earning/strategies](/reference/get-strategies). ## Environments We provide one endpoint for production environment: [edge.p2p.org](https://edge.p2p.org) Note that DeFi API is only supported on mainnet, no testnet is provided. # Sign and Broadcast Transaction Source: https://docs.p2p.org/docs/defi-api-signing-transaction To complete any action using the DeFi API, sign the final unsigned transaction and broadcast it to the network. ## 1. Prepare unsigned transaction Obtain the unsigned transaction object either in the `encoded` or `json` format provided by the [Craft Transaction Step](/reference/craft-transaction-step) endpoint. ## 2. Sign transaction Since the DeFi API flow is determined by the individual schema depending on the action type, protocol, and strategy, construction of multiple transactions may be required. As a result, there are two types of unsigned transaction objects provided by the DeFi API — *intermediate* and *final*. * *Intermediate transactions* are typically required to grant permissions to smart contracts involved in the integration process. These objects need a signature, as they must be included in the payload of the final transaction, but there is no need to broadcast such transactions to the network. * *Final transactions*, on the other hand, require both signing and broadcasting. The steps at which these transactions are constructed have the word "final" in their name and are executed last according to the schema's order. Such objects must contain the data of all intermediate transactions in the payload. Check your transaction object type and sign it using the examples of code provided below. **Note that for intermediate and final types, different signing libraries are used.** It is possible to sign the final transaction with the code for the intermediate one, but this will lead to the validation error on attempting to broadcast such a transaction to the network. ### For intermediate transactions > NB: signing only, no broadcasting. Use any of the code examples for the `base` network below. ```javascript With private key theme={null} import { signTypedData, sign, serializeSignature, privateKeyToAccount, } from 'viem/accounts'; import { createWalletClient, custom } from 'viem'; import { base } from 'viem/chains'; // Example API response shape // response = { json: { domain, types, primaryType, message }, encoded: "0x..." } // ----------------------------------------------------------------------------- // 1. With private key — use response.json (typedData) // ----------------------------------------------------------------------------- async function signResponseWithPrivateKey(privateKey, response) { const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`; const account = privateKeyToAccount(privateKeyHex); const { json: typedData } = response; return signTypedData({ account, domain: typedData.domain, types: typedData.types, primaryType: typedData.primaryType, message: typedData.message, }); } // ----------------------------------------------------------------------------- // 2. With private key — use response.encoded (hash only) // ----------------------------------------------------------------------------- async function signResponseHashWithPrivateKey(privateKey, response) { const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`; const hashHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`; const sig = await sign({ privateKey: privateKeyHex, hash: hashHex, }); return serializeSignature(sig); } ``` ```javascript Via provider theme={null} import { signTypedData, sign, serializeSignature, privateKeyToAccount, } from 'viem/accounts'; import { createWalletClient, custom } from 'viem'; import { base } from 'viem/chains'; // Example API response shape // response = { json: { domain, types, primaryType, message }, encoded: "0x..." } // ----------------------------------------------------------------------------- // 3. Via provider (frontend) — use response.json (typedData), preferred // ----------------------------------------------------------------------------- async function signResponseViaProvider(provider, response) { const walletClient = createWalletClient({ chain: base, transport: custom(provider), }); const [address] = await walletClient.getAddresses(); if (!address) throw new Error('Wallet not connected'); const { json: typedData } = response; return walletClient.signTypedData({ account: address, domain: typedData.domain, types: typedData.types, primaryType: typedData.primaryType, message: typedData.message, }); } // ----------------------------------------------------------------------------- // 4. Via provider (frontend) — use response.encoded (hash only), eth_sign // ----------------------------------------------------------------------------- async function signResponseHashViaProvider(provider, response, address) { const hashHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`; if (hashHex.length !== 66) throw new Error('Hash must be 32 bytes (66 hex chars with 0x)'); return provider.request({ method: 'eth_sign', params: [address, hashHex], }); } ``` ### For final transactions > NB: contain the word "final" in the step name; require signing and broadcasting. Use any of the code examples for the `base` network below. ```javascript With private key theme={null} import { createWalletClient, custom, http, parseTransaction } from 'viem'; import { privateKeyToAccount, signTransaction } from 'viem/accounts'; import { base } from 'viem/chains'; // Example API response shape // response = { json: { domain, types, primaryType, message }, encoded: "0x..." } // ----------------------------------------------------------------------------- // 1. With private key — use response.encoded (raw hex), then sign // ----------------------------------------------------------------------------- async function signRawTransactionWithPrivateKey(privateKey, response, rpcUrl) { const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`; const account = privateKeyToAccount(privateKeyHex); const rawTxHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`; const transaction = parseTransaction(rawTxHex); const walletClient = createWalletClient({ chain: base, account, transport: http(rpcUrl), }); const request = await walletClient.prepareTransactionRequest({ ...transaction, gas: transaction.gas, }); return walletClient.signTransaction(request); // signed hex "0x02..." } // ----------------------------------------------------------------------------- // 2. With private key — use response.json (transaction object), then sign // ----------------------------------------------------------------------------- async function signTransactionObjectWithPrivateKey(privateKey, response, rpcUrl) { const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`; const account = privateKeyToAccount(privateKeyHex); const txRequest = response.json; // { to, data, value, gas, nonce, chainId, maxFeePerGas, maxPriorityFeePerGas, type } const walletClient = createWalletClient({ chain: base, account, transport: http(rpcUrl), }); const prepared = await walletClient.prepareTransactionRequest(txRequest); return walletClient.signTransaction(prepared); } // ----------------------------------------------------------------------------- // 3. With private key — sign only (no RPC), from encoded, using viem/accounts // ----------------------------------------------------------------------------- async function signRawTransactionWithPrivateKeyNoRpc(privateKey, response) { const privateKeyHex = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`; const rawTxHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`; const transaction = parseTransaction(rawTxHex); const signedHex = await signTransaction({ privateKey: privateKeyHex, transaction, }); return signedHex; } ``` ```javascript Via provider theme={null} import { createWalletClient, custom, http, parseTransaction } from 'viem'; import { privateKeyToAccount, signTransaction } from 'viem/accounts'; import { base } from 'viem/chains'; // Example API response shape // response = { json: { domain, types, primaryType, message }, encoded: "0x..." } // ----------------------------------------------------------------------------- // 4. Via provider (frontend) — use response.encoded (raw hex), wallet signs // ----------------------------------------------------------------------------- async function signRawTransactionViaProvider(provider, response) { const rawTxHex = response.encoded.startsWith('0x') ? response.encoded : `0x${response.encoded}`; const transaction = parseTransaction(rawTxHex); const walletClient = createWalletClient({ chain: base, transport: custom(provider), }); const [address] = await walletClient.getAddresses(); if (!address) throw new Error('Wallet not connected'); const request = await walletClient.prepareTransactionRequest({ ...transaction, gas: transaction.gas, account: address, }); return walletClient.signTransaction(request); } // ----------------------------------------------------------------------------- // 5. Via provider (frontend) — use response.json (transaction object) // ----------------------------------------------------------------------------- async function signTransactionObjectViaProvider(provider, response) { const walletClient = createWalletClient({ chain: base, transport: custom(provider), }); const [address] = await walletClient.getAddresses(); if (!address) throw new Error('Wallet not connected'); const request = await walletClient.prepareTransactionRequest({ ...response.json, account: address, }); return walletClient.signTransaction(request); } ``` ## 3. Broadcast signed final transaction > NB: required for final transaction type only. To submit the final transaction to the network, send a POST request to [/api/defi/v1/transactions/steps/broadcast](/reference/broadcast-transaction). Example request: ```bash bash theme={null} curl --request POST \ --url 'https://edge.p2p.org/api/defi/v1/transactions/steps/broadcast' \ --header 'accept: application/json' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "chain": "base", "protocol": "morpho", "strategy": "Steakhouse USDC", "signedPayload": "0x…" }' ``` * `signedTransaction` — signed final transaction in the hexadecimal format which needs to be broadcasted to the network. Example response: ```json JSON theme={null} { "error": null, "result": { "transactionHash": "0x1234567890abcdef..." }, "meta": { "time": 0, "requestId": "3fa85f64-5717-4562-b3fc-2c963f66afa6" } } ``` * `transactionHash` — hash of the final transaction. * `time` — request execution time in ms. * `requestId` — unique identifier of the request. # Direct staking v1-v3 Source: https://docs.p2p.org/docs/direct-staking ## P2P Direct Staking: Eth2 Depositor & Message Sender Flow **Action required for integrators with smart contract allowlists** The API now returns unsigned transactions targeting the [new contract](/docs/direct-staking#p2peth2depositor-v3). If your application validates the `to` address against a hardcoded list, you must add this address before your next transaction. Client‑side rejection of these transactions is the only breaking change; the previous P2pEth2Depositor remains usable on-chain, no migration required for existing integrations. ### Non-Custodial On-Chain Deposit (via P2pEth2Depositor) Diagram 1: Direct Deposit Flow for P2pEth2Depositor (v1) ```mermaid theme={null} sequenceDiagram autonumber participant Client (Validator Owner) participant P2pEth2Depositor participant BeaconDepositContract participant P2P Infra (Observer) Client->>P2pEth2Depositor: deposit(pubkey[], withdrawalCreds, signatures, roots)\nwith 32 ETH * N activate P2pEth2Depositor loop For each validator P2pEth2Depositor->>BeaconDepositContract: deposit(pubkey, withdrawalCreds, signature, root)\nwith 32 ETH end P2pEth2Depositor-->>Client: emit DepositEvent(msg.sender, count) deactivate P2pEth2Depositor P2P Infra (Observer)-->>P2P Infra (Observer): Listens for DepositEvent ``` ETH is atomically forwarded to the official Beacon chain deposit contract — no ETH is ever held or pooled. In P2P.org’s direct staking v1 flow, users stake ETH without ever giving custody of their funds to P2P. The key on-chain component is the **P2pEth2Depositor** smart contract, which atomically forwards the user’s ETH to Ethereum’s official deposit contract. The flow is as follows: 1. **User Initiates Deposit:** The user (validator owner) generates or obtains the required deposit data (validator BLS public key, withdrawal credential, signature, etc.) and sends a transaction calling `P2pEth2Depositor.deposit(...)` with this data. The call includes the exact ETH required (32 ETH per validator) for one or multiple validator deposits. The contract enforces that the caller sends the correct amount of ETH — it requires `msg.value` to equal 32 ETH \* number\_of\_validators and allows batching up to 100 validators in one transaction. (This batching is purely for gas efficiency; each validator still gets a distinct 32 ETH deposit.) **For v3 batcher deposits**: The API returns an unsigned transaction whose `to` address points to the new deposit contract (0x...). The user signs and broadcasts this transaction. The new contract atomically forwards ETH to the official `BeaconDepositContract` exactly like the legacy contract. The direct `P2pEth2Depositor` path remains functional for backward compatibility. 2. **Atomic Deposit to Beacon Chain:** Inside the `deposit(...)` function, P2pEth2Depositor immediately forwards the ETH to the official Eth2 DepositContract for each validator. It iterates over the provided list of validator pubkeys and calls `depositContract.deposit{value: 32 ETH}(pubkey, withdrawal_credential, signature, deposit_data_root)` for each one. This means the user’s ETH is transferred directly into the **Beacon Chain deposit contract** within the same transaction that the user sent, without any intermediate holding account. If any deposit in the batch fails (e.g., invalid data), the entire transaction reverts, ensuring no partial custody or leftover funds – it’s all-or-nothing and handled atomically on-chain. 3. **No P2P Custody or Pools:** The design explicitly prevents P2P or the proxy contract from holding the ETH. The P2pEth2Depositor contract does not accept free-floating ETH transfers at all – its fallback/receive function immediately reverts any direct ETH sent to it. This guarantees that users can only send ETH through the `deposit` function (which forwards it onward). In other words, P2P never aggregates user ETH in its own wallet or contract; the stake goes straight from the user’s address to the official deposit contract (under the user’s withdrawal credentials) in one step. 4. **Event Emission:** After successfully processing the deposits, P2pEth2Depositor emits a `DepositEvent` recording the sender’s address and the number of validators deposited. This on-chain event serves as a record that the deposit transaction for those validators was completed. (The event does not contain the validator pubkeys or other details, just a count, since P2P already knows which keys were included based on the transaction data.) **For v3 batcher deposits**, the new contract does not emit a custom `DepositEvent`; instead, P2P’s monitoring (MW) listens directly to the Beacon chain deposit contract for deposit logs. This means deposit detection is fully native. ### Validator Key Management & Withdrawal Credentials A core principle of this direct staking flow is that the user retains control of withdrawal rights, while validator operation is handled by P2P. In practice, this means **the user’s chosen withdrawal credential is used for the deposit, and P2P never controls the withdrawal of the staked ETH or rewards.** P2P’s system is non-custodial by ensuring the roles of the two key pairs are separated: * **Validator Keys (BLS Signing Keys):** These are the keys that actually run the validator (sign attestations, propose blocks, and sign the deposit and exit messages). In the v1 flow, the user delegates validator key management to P2P’s infrastructure. In many cases, P2P (and/or a Distributed Validator Technology like SSV) generates or holds the BLS validator private keys to operate the validators on behalf of the user. The user does not need to run the validator node; P2P handles all “live” validator duties. Importantly, these validator keys are also required to initiate a voluntary exit (they must sign the exit message when the user wants to stop validating). * **Withdrawal Credentials/Keys:** The withdrawal credential defines where validator withdrawals (refund of 32 ETH and earned rewards) will be paid out on the execution layer. In direct staking v1, the client (user) maintains custody of the withdrawal credential. Typically, the withdrawal credential is set to an **Ethereum address controlled by the user** (a 0x01 credential, hashed from the user’s ETH address). This means when withdrawals are enabled, the unlocked ETH will go directly to the user’s own wallet. P2P does **not** hold the withdrawal private key and cannot unilaterally withdraw or move the staked funds — this is what makes the setup *non-custodial*. (In some setups, the withdrawal credential could point to a smart contract that trustlessly splits fees, but it would still be a pre-set address that P2P cannot change or divert; P2P v1 generally had the user’s address as the withdrawal target to ensure P2P never controls the funds.) By structuring key ownership this way, the user’s capital is secure from P2P or any third-party misappropriation. P2P’s role is limited to validator operations, while the user’s role is to ultimately control the funds at withdrawal. This **“user holds withdrawal key, operator holds validator key”** model is explicitly highlighted by P2P: the client preserves custody of withdrawal credentials, delegating only the validator key management to P2P. The trade-off is that the user must trust P2P to manage the validator key honestly (e.g., not to double-sign or to properly sign an exit when asked), but P2P cannot steal or redirect the stake or rewards because it lacks the withdrawal key. ### On-Chain Signaling & Validator Exit via **P2pMessageSender** Diagram 2: Validator Exit via On-Chain Signal ```mermaid theme={null} sequenceDiagram autonumber participant Client (Validator Owner) participant P2pMessageSender participant P2P Backend participant Beacon Chain Client->>P2pMessageSender: send("exit validator X") activate P2pMessageSender P2pMessageSender-->>Client: emit Message(msg.sender, "exit validator X") deactivate P2pMessageSender P2P Backend->>P2P Backend: Listen for Message events P2P Backend->>P2P Backend: Verify msg.sender is authorized P2P Backend->>Beacon Chain: Broadcast VEM (Voluntary Exit Message) ``` Only messages from the real validator owner (msg.sender) are accepted. P2P backend handles validator exit using the BLS key. Direct staking v1 also provides a mechanism for the user to signal certain actions (especially to request a validator exit/withdrawal) in a secure, trust-minimized way. This is handled by the **P2pMessageSender** contract, which works in tandem with off-chain services: * **P2pMessageSender Contract:** It’s a simple on-chain messaging contract that anyone can call, but whose events are monitored by P2P. The contract has a function `send(string text)` that emits an event (e.g., `Message`) containing the caller’s address and the provided text message. The text can encode a specific instruction or identifier (for example, a message indicating “please exit validator X”). The key point is that whatever the message contains, it is cryptographically **tied to the sender’s Ethereum address** via the event. * **Off-Chain Listening and Verification:** P2P’s backend service continuously listens for `Message` events from the P2pMessageSender contract. When a user wants to initiate a validator exit (withdraw), they will trigger an on-chain message via this contract (either directly or through an integrated UI/API call). The P2P service, upon detecting a new message event, checks the `sender` field of the event against its records of validator ownership. **Only if the event’s`msg.sender` matches the address authorized for that validator (i.e., the original staker’s address or a designated controller address) will P2P honor the request**. This ensures authenticity — a malicious party can’t impersonate the validator owner by simply calling the contract, because P2P will ignore messages from addresses that don’t match the expected owner. (The P2P docs note that only the preset controller or withdrawal address can initiate an exit via the smart contract; if the sender doesn’t match, the exit won’t proceed.) * **VEM Broadcast (Validator Exit Message):** Once a valid exit request is confirmed (i.e., a `Message` event from the real owner is received with an instruction to exit), P2P will perform the off-chain steps to exit the validator. Specifically, P2P will take the validator’s BLS key (which it manages) and craft a **Voluntary Exit Message (VEM)**, which is a signed message to the Ethereum consensus layer indicating the validator should exit. P2P then broadcasts this VEM to the Beacon Chain network. Broadcasting the VEM initiates the validator’s voluntary exit from active duty. After the exit is processed on-chain and the exit epoch is reached, the validator’s balance (32 ETH plus any rewards) becomes withdrawable to the user’s withdrawal address. The entire exit process is thus triggered by the user’s on-chain signal and executed by P2P, with the smart contract event acting as the trigger point for a secure off-chain action. In summary, the direct staking v1 architecture uses on-chain contracts to keep the process trust-minimized: **P2pEth2Depositor** lets users deposit to the beacon chain directly (no custodial intermediate), and **P2pMessageSender** provides a verified communication channel for exits or other signals. The user maintains ultimate control of funds via the withdrawal credential, while P2P handles the technical heavy lifting of validator operations and exit broadcasting. This separation of concerns provides a non-custodial staking service — P2P cannot move the user’s ETH, only facilitate its staking and unstaking per the user’s requests. ## Smart contract deployments ### P2pEth2Depositor (v3) > Returned by the `/deposit` endpoint; forwards to Beacon DepositContract. * [Mainnet](https://etherscan.io/address/0xa6cf239220a8a9939d3207dfd8f26cd5e4f36ae6) `0xa6cf239220a8a9939d3207dfd8f26cd5e4f36ae6` * [Hoodi](https://hoodi.etherscan.io/address/0xda2934020efd473fac5cd73bdde440a719bf4c59) `0xda2934020efd473fac5cd73bdde440a719bf4c59` ### P2pEth2Depositor (v2) * [Mainnet](https://etherscan.io/address/0x8e76a33f1aFf7EB15DE832810506814aF4789536) `0x8e76a33f1aFf7EB15DE832810506814aF4789536` * [Hoodi](https://hoodi.etherscan.io/address/0x44Cd8e2c24d3E11F0838a8Ea942C2D0da5BeD6C0) `0x44Cd8e2c24d3E11F0838a8Ea942C2D0da5BeD6C0` [Github](http://github.com/p2p-org/eth-staking-fee-distributor-contracts/blob/v2-audited/contracts/p2pEth2Depositor/P2pEth2Depositor.sol) [Audit](https://github.com/mixbytes/audits_public/tree/master/P2P.org/ETH2%20Depositor%20%26%20ETH%20Staking%20Fee%20Distributor%20\(v.2\)) ### P2pEth2Depositor (v1)) > Direct on-chain deposits (v1), still accepted. * [Mainnet](https://etherscan.io/address/0x4ca21e4d3a86e7399698f88686f5596dbe74adeb) `0x4ca21e4d3a86e7399698f88686f5596dbe74adeb` * [Hoodi](https://hoodi.etherscan.io/address/0x375e183efA05fb2bb66Cd0e7C3a05D9942046612) `0x375e183efA05fb2bb66Cd0e7C3a05D9942046612` [Github](https://github.com/p2p-org/p2p-eth2-depositor/blob/main/src/P2pEth2Depositor.sol) [Audit](https://github.com/mixbytes/audits_public/tree/master/P2P.org/ETH2%20Depositor%20%26%20ETH%20Staking%20Fee%20Distributor%20\(v.1\)) # Graph Transaction Signing Source: https://docs.p2p.org/docs/docgraph-transaction-signing Sign and broadcast Graph transactions built with the P2P.org Unified API. To sign and broadcast the transaction to the Gprah network, follow these steps: 1. Prepare unsigned transaction in JSON format. Obtain the `unsignedTransactionData` field from the Unified API (staking, approval, unstake, or withdrawal). This field contains the full **EIP-1559** transaction as a JSON string. 2. Sign the transaction using the following code: ```javascript theme={null} import { ethers } from 'ethers'; // Replace with your wallet mnemonic and Arbitrum RPC URL const MNEMONIC = ''; const RPC_URL = ''; // e.g., https://arb1.arbitrum.io/rpc const UNSIGNED_TX_JSON = ``; // Stringified JSON from API async function main() { if (!UNSIGNED_TX_JSON || UNSIGNED_TX_JSON.length < 10) { throw new Error("❌ UNSIGNED_TX_JSON is missing or invalid."); } // Parse the unsigned transaction data const txData = JSON.parse(UNSIGNED_TX_JSON); // Set up provider and wallet const provider = new ethers.providers.JsonRpcProvider(RPC_URL); const wallet = ethers.Wallet.fromMnemonic(MNEMONIC).connect(provider); // Prepare the EIP-1559 transaction const tx: ethers.providers.TransactionRequest = { from: txData.from, to: txData.to, data: txData.data, nonce: txData.nonce, gasLimit: ethers.BigNumber.from(txData.gasLimit), maxFeePerGas: ethers.BigNumber.from(txData.maxFeePerGas), maxPriorityFeePerGas: ethers.BigNumber.from(txData.maxPriorityFeePerGas), chainId: txData.chainId, type: txData.type, // 2 for EIP-1559 }; // Sign the transaction (returns raw transaction hex) const signedTx = await wallet.signTransaction(tx); console.log("✅ Signed The Graph (Arbitrum) Tx (hex):"); console.log(signedTx); } main().catch(console.error); ``` Upon successful execution, the script prints the signed transaction in hexadecimal format, ready to be broadcasted: ```bash theme={null} ✅ Signed The Graph (Arbitrum) Tx (hex): 0x... ``` 3. Broadcast the transaction to the Graph network by sending a POST request to [/api/v1/unified/transaction/broadcast](/reference/unified-transaction-send). # Energy Delegation Source: https://docs.p2p.org/docs/energy-delegation Delegate generated ENERGY to another TRON account without transferring ownership of the frozen TRX. Delegate generated `ENERGY` to another TRON account without transferring ownership of the underlying frozen TRX. ## What is Energy delegation? On TRON, every action that touches a smart contract (including TRC-20 transfers, e.g. USDT) consumes `ENERGY`. `ENERGY` is generated by freezing TRX via [`/staking/delegate`](/reference/tron-staking-delegate), the same mechanism used for `BANDWIDTH` and voting power. Energy delegation lets the account that froze the TRX (the **owner**) share the `ENERGY` it generates with a different account (the **receiver**), without moving the frozen TRX itself and without giving up ownership of it. The owner keeps the frozen balance, its voting rights, and can reclaim the delegated resource at any time after the lock period expires. This is the pattern used to cover gas costs for a large number of wallets from a single pool of frozen TRX — for example, an exchange or a custodian freezing TRX once and delegating `ENERGY` to hundreds of hot wallets so that each of them can pay for TRC-20 transfers without holding TRX of its own. Only `ENERGY` is supported for delegation in the current version. `BANDWIDTH` delegation is not available. ## The full flow Energy delegation sits between the freeze and unfreeze steps of the regular TRON staking flow: ``` freeze → delegate-resource → undelegate-resource → unfreeze → withdraw ```
Step Endpoint Description
1. Freeze `/staking/delegate` Freeze TRX and generate `ENERGY` on the owner account
2. Delegate resource `/staking/delegate-resource` Delegate generated `ENERGY` from the owner to a receiver account.
3. Undelegate resource `/staking/undelegate-resource` Reclaim previously delegated `ENERGY` back to the owner, once the delegation lock period has passed.
4. Unfreeze `/staking/undelegate` Unfreeze the owner's TRX
5. Withdraw `/staking/withdraw` Claim the unfrozen TRX back to the available balance
Steps 1, 4, and 5 are the standard freeze/unfreeze/withdraw operations already described in [Getting Started](/docs/staking-tron) and [Withdrawal](/docs/withdrawal-tron). This guide focuses on steps 2 and 3, which sit in between. Note that the owner does not have to unfreeze immediately after undelegating. `ENERGY` can be delegated and reclaimed many times over the lifetime of a single frozen balance — unfreezing is only required once the owner wants the underlying TRX back. ## Signing model As with any other operation on TRON, `/staking/delegate-resource` and `/staking/undelegate-resource` only build and return an **unsigned** transaction — the API never holds or uses your private key. The client is responsible for signing every transaction in the flow (freeze, delegate-resource, undelegate-resource, unfreeze, withdraw) individually with the [P2P Signer SDK](/docs/signer-sdk-overview) and broadcasting it via [/transaction/send](/reference/tron-transaction-send), following the steps described in [Sign and Broadcast Transaction](/docs/signing-transaction-tron). The owner address that signs the delegate/undelegate-resource transaction must be the same address that froze the TRX (`delegatorAddress`) — the receiver never signs anything as part of this flow. ## 1. Create delegate-resource request To delegate generated `ENERGY` to a receiver account, send a POST request to [`/api/v1/tron/{network}/staking/delegate-resource`](/reference/tron-staking-delegate-resource). Example request (for `testnet-nile` network): ```shell theme={null} curl --request POST \ --url 'https://api-test.p2p.org/api/v1/tron/testnet-nile/staking/delegate-resource' \ --header 'accept: application/json' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "delegatorAddress": "TVscj8F6wPZ92d1smGYjH9heZR1MaEcE9u", "amount": 1000000, "resource": "ENERGY" }' ``` * `delegatorAddress` — owner account address, i.e. the account that froze the TRX and generated the `ENERGY`. * `amount` — amount of resource to delegate in SUN. Minimum amount is 1 TRX (1 000 000 SUN) * `resource` — resource type to delegate. Only `ENERGY` is supported in the current version, defaults to `ENERGY`. Optional. Example response: ```json theme={null} { "error": null, "result": { "delegatorAddress": "TVscj8F6wPZ92d1smGYjH9heZR1MaEcE9u", "receiverAddress": "TNDzfERDpxLDS2w1q6yaFC7pzqaSQ3Bg3r", "amount": 10000000, "resource": "ENERGY", "lockPeriod": 28800, "unsignedTransaction": { "visible": false, "txID": "b7c4a2f1e6d9083c...", "raw_data_hex": "0a...", "raw_data": { "contract": [ { "parameter": { "value": { "owner_address": "41da53d324a354aa5924c7117caff3ead97910a5e1", "receiver_address": "41700ca9ad5f162f9de000558ff04eef8f99dce7e8", "balance": 10000000, "resource": "ENERGY" }, "type_url": "type.googleapis.com/protocol.DelegateResourceContract" }, "type": "DelegateResourceContract" } ], "ref_block_bytes": "0e0c", "ref_block_hash": "7db974037137d39c", "expiration": 1750000200000, "timestamp": 1750000140000 } }, "unsignedTransactionSerialized": "eyJ0eElEIjoidHJ4SWQifQ==", "createdAt": "2025-06-03T12:00:00.000Z" } } ``` * `delegatorAddress` — owner account address. * `receiverAddress` — account address receiving the delegated resource. * `amount` — amount of resource delegated, in SUN. * `resource` — resource type delegated: `ENERGY`. * `lockPeriod` — 28800, i.e. blocks, ≈ 24 h at 3 s per block. * `unsignedTransaction` — original unsigned transaction object returned by tronWeb. * `unsignedTransactionSerialized` — unsigned serialized transaction in Base64 format, ready for signing. * `createdAt` — timestamp of the transaction in the ISO 8601 format. ## Delegation lock TRON enforces a protocol-level lock on delegated resources: once `ENERGY` has been delegated to a receiver, it cannot be undelegated for **approximately 24 hours** from the moment the delegation transaction is confirmed. This is a network rule, and it applies per delegation — each new `/staking/delegate-resource` call starts its own 24-hour lock for the amount it delegates. Calling `/staking/undelegate-resource` before the lock period has elapsed will cause the transaction to fail on-chain. Plan any automated re-delegation or rebalancing logic around this window, and track the delegation's confirmation time so you know when it becomes eligible for undelegation. **The lock is amount-aware. **TRON keeps one lock per (owner, receiver) pair, so an older expired portion can coexist with a freshly locked one. Undelegating within the unlocked part succeeds; only a request reaching into the locked part is rejected. **The unlock time is machine-readable**. DelegationLockedException returns both the message and a structured field: ```json theme={null} "errors": [ { "property": "delegation", "constraints": { "unlocksAt": "2026-08-19T11:48:06.000Z" } } ] ``` Integrators should read `errors[0].constraints.unlocksAt` rather than parsing the message, and use it to schedule the retry instead of polling on a fixed 24-hour timer. ## 2. Create undelegate-resource request Once the lock period has passed, reclaim the delegated `ENERGY` back to the owner account by sending a POST request to [`/api/v1/tron/{network}/staking/undelegate-resource`](/reference/tron-staking-undelegate-resource). Example request (for `testnet-nile` network): ```shell theme={null} curl --request POST \ --url 'https://api-test.p2p.org/api/v1/tron/testnet-nile/staking/undelegate-resource' \ --header 'accept: application/json' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "delegatorAddress": "TVscj8F6wPZ92d1smGYjH9heZR1MaEcE9u", "amount": 1000000, "resource": "ENERGY" }' ``` * `delegatorAddress` — owner account address that originally delegated the resource. * `amount` — amount of resource to delegate in SUN. Minimum amount is 1 TRX (1 000 000 SUN) * `resource` — resource type to reclaim. Only `ENERGY` is supported, defaults to `ENERGY`. Optional. Example response: ```json theme={null} { "error": null, "result": { "delegatorAddress": "TVscj8F6wPZ92d1smGYjH9heZR1MaEcE9u", "receiverAddress": "TNDzfERDpxLDS2w1q6yaFC7pzqaSQ3Bg3r", "amount": 10000000, "resource": "ENERGY", "unsignedTransaction": { "visible": false, "txID": "d3f8b1a2c9e6740f...", "raw_data_hex": "0a...", "raw_data": { "contract": [ { "parameter": { "value": { "owner_address": "41da53d324a354aa5924c7117caff3ead97910a5e1", "receiver_address": "41700ca9ad5f162f9de000558ff04eef8f99dce7e8", "balance": 10000000, "resource": "ENERGY" }, "type_url": "type.googleapis.com/protocol.UnDelegateResourceContract" }, "type": "UnDelegateResourceContract" } ], "ref_block_bytes": "0e0c", "ref_block_hash": "7db974037137d39c", "expiration": 1750086600000, "timestamp": 1750086540000 } }, "unsignedTransactionSerialized": "eyJ0eElEIjoidHJ4SWQifQ==", "createdAt": "2025-06-03T12:00:00.000Z" } } ``` Field descriptions mirror the delegate-resource response above; the only difference is the underlying `UnDelegateResourceContract` type. ## Error codes `/staking/delegate-resource` and `/staking/undelegate-resource` can return the following `400 Bad request` errors in addition to the standard `401` and `429` responses. `/staking/delegate-resource` : | Code | Exception | Description | | ------ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | 125102 | `InvalidDelegatorAddressException` | Invalid delegator address provided. Please ensure the address is a valid TRON address format. | | 125123 | `InvalidReceiverException` | Invalid receiver address. The receiver must be a valid TRON address and must differ from the owner address. | | 125104 | `InvalidFreezeAmountException` | Freeze amount is below the minimum required threshold (1 TRX = 1,000,000 SUN). | | 125125 | `ResourceNotSupportedException` | Resource type is not supported. Only ENERGY delegation is available. | | 125124 | `ExceedsDelegatableMaxException` | Delegation amount exceeds the maximum delegatable resource for this account. Freeze more TRX or reduce the amount. | | 125121 | `DelegateResourceTransactionException` | Failed to create the resource delegation transaction. Verify the amount does not exceed the delegatable balance and that the account has frozen ENERGY. | `/staking/undelegate-resource` : | Code | Exception | Description | | ------ | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | 125102 | `InvalidDelegatorAddressException` | Invalid delegator address provided. Please ensure the address is a valid TRON address format. | | 125123 | `InvalidReceiverException` | Invalid receiver address. The receiver must be a valid TRON address and must differ from the owner address. | | 125105 | `InvalidUnfreezeAmountException` | Unfreeze amount exceeds the available frozen balance or is invalid. Please verify the account's frozen amount. | | 125125 | `ResourceNotSupportedException` | Resource type is not supported. Only ENERGY delegation is available. | | 125126 | `NoActiveDelegationException` | No active ENERGY delegation found for this account. There is nothing to undelegate. | | 125127 | `ExceedsDelegatedAmountException` | Undelegation amount exceeds the currently delegated ENERGY balance. Please reduce the amount. | | 125128 | `DelegationLockedException` | Delegation is still locked and cannot be undelegated until the lock period expires. | | 125122 | `UndelegateResourceTransactionException` | Failed to create the resource undelegation transaction. Locked delegations can only be undelegated after the lock period expires. | ## Testing on the Nile testnet Before delegating on mainnet, validate the full flow on TRON's Nile testnet using the `testnet-nile` network identifier. Because of the delegation lock, a full round trip through steps will take about 24 hours to complete. 1. **Fund a Nile testnet account.** Use the [Nile faucet](https://nileex.io/join/getJoinPage) to receive test TRX on your account. 2. **Freeze TRX for **`ENERGY`**.** Send a POST request to `/api/v1/tron/testnet-nile/staking/delegate` with `resource: "ENERGY"`, sign, and broadcast the transaction, as described in [Getting Started](/docs/staking-tron). 3. **Delegate the resource.** Send a POST request to: ``` https://api-test.p2p.org/api/v1/tron/testnet-nile/staking/delegate-resource ``` with your `delegatorAddress`, a `receiverAddress` you control (a second Nile testnet account), and the `amount` to delegate. 4. **Sign** transaction 5. **Broadcast** via POST `/tron/testnet-nile/transaction/send ` 6. **Verify the delegation.** The transaction confirms as DelegateResourceContract; getDelegatedResourceV2(owner, pool) shows the delegation with an expire\_time. 7. **Undelegate the resource. **Once the lock has expired, send a POST request to: ``` https://api-test.p2p.org/api/v1/tron/testnet-nile/staking/undelegate-resource ``` with the same `delegatorAddress`/`receiverAddress` pair and `amount`, then sign and broadcast. Testnet transactions use the same request/response shapes as mainnet. Only the `network` path parameter and the `api-test.p2p.org` host differ, so code validated on Nile carries over to production without changes. # Ethereum Staking 101 Source: https://docs.p2p.org/docs/ethereum-staking-a-comprehensive-guide What Ethereum proof-of-stake staking involves: deposits, validators, rewards and penalties. Ethereum staking represents the process of participating in the validation of transactions on the Ethereum blockchain, which has transitioned to a Proof-of-Stake (PoS) consensus mechanism following [The Merge](https://ethereum.org/en/roadmap/merge/) update. This is a crucial element of the Ethereum ecosystem as it not only contributes to the security of the network but also allows participants to earn rewards for their contribution. Here’s what you need to know to start staking Ethereum. ## Understanding Ethereum Staking Ethereum's transition to PoS invited participants, dubbed validators, to stake ETH as a security deposit to validate transactions and create new blocks. This mechanism enhances network security and energy efficiency compared to the former Proof-of-Work (PoW) system. ### What is Staking? Staking involves locking up a certain amount of cryptocurrency (in this case, Ether) to participate in the functioning of a blockchain network. This participation includes validating transactions and maintaining network security. Stakers, in return, receive rewards for their contributions. [Ethereum Launchpad](https://launchpad.ethereum.org/) serves as the official starting point for individuals aiming to become validators on the Ethereum network. It's a user-friendly platform designed to guide users through the staking process, ensuring they meet the necessary requirements and understand the commitments involved. Launchpad educates prospective validators on the risks and responsibilities of staking, providing a streamlined path from initial interest to active participation. This includes detailed instructions on setting up validator software, contributing to making the Ethereum network more decentralized and secure. ### What is a Deposit? A deposit in the context of Ethereum staking refers to the act of locking up 32 ETH as a commitment to become a network validator. This deposit is a prerequisite for participating in transaction validation and block proposal processes in Ethereum 2.0's PoS consensus mechanism. ### What is a Validator? A validator in the Ethereum network is a participant who stakes 32 ETH to validate transactions, propose new blocks, and maintain the network's security within its Proof-of-Stake (PoS) mechanism. Validators are crucial for the integrity and continuity of the blockchain, being rewarded for their contributions and penalized for any actions that jeopardize the network's operation. They run node software, enabling them to take part in the consensus process, ensuring Ethereum remains decentralized and secure. The balance of rewards and penalties incentivizes validators to act in the network's best interest. Validators are at the core of Ethereum's PoS consensus mechanism. They are selected to propose new blocks and validate transactions. For their service, validators earn rewards, but they also face penalties and slashing for downtime or malicious actions. ## Creating a Deposit Creating a deposit for staking in Ethereum 2.0 is a critical step towards becoming a validator. This process involves several key actions and requires attention to detail to avoid any errors that could potentially lead to financial loss or other issues. Here's a more detailed breakdown: * **Amount of ETH to Stake**: A minimum of 32 ETH is required for staking as a single validator. This is a fixed threshold set by the Ethereum 2.0 protocol to ensure validators have a vested interest in the network's security. * **Validator's Public Key**: The public key is part of the validator's cryptographic key pair (public and private keys) used for signing and verifying transactions and messages within the network. It identifies the validator on the network and is crucial for the operation and security of your validator node. * **Withdrawal Credentials**: These credentials are necessary for withdrawing staked ETH and rewards after the validator exits or is no longer active. Withdrawal credentials protect your funds by ensuring that only the entity in possession of the corresponding private key can access them. * **Signature from the Validator's Private Key**: The deposit must be signed with the validator's private key. This signature is a cryptographic proof that the validator is indeed the one making the deposit. It secures the transaction against unauthorized actions. **Timing Considerations** * **Deposit Transmission Time**: After being submitted, the deposit typically appears on the Ethereum blockchain within a few minutes, depending on the current network congestion. However, the Beacon Chain's recognition of the deposit and the transition from "Pending" to "Processed" status may take several hours or more during periods of high demand. * **Activation Queue**: After the deposit has been processed, validators enter a queue for activation. The waiting time in this queue can vary significantly, from a few days to several weeks, depending on the number of validators awaiting activation. The Ethereum protocol dynamically manages the number of validators that can be activated in each epoch (approximately 6.4 minutes long) to ensure gradual and controlled growth of the validator set. **Important Considerations** Double-Check All Inputted Data: Before confirming the deposit, it is essential to verify all information meticulously. Errors in the deposit transaction, such as incorrect public keys or withdrawal credentials, cannot be reversed and might result in the permanent loss of ETH or affect your ability to withdraw funds in the future. **Initiating the Staking Transaction**: * To begin staking, the deposit, along with the necessary data points, must be sent to the Ethereum 2.0 deposit contract. This contract is responsible for registering validators and managing their deposits. * Ensure you have enough ETH not only for the deposit but also to cover transaction fees. These fees vary based on network congestion and can be significant during periods of high demand. ## Deposit Statuses Deposits can have various statuses, reflecting the stage of the staking process: * **Pending**: This status indicates that your deposit has been made to the Ethereum 2.0 contract, but it hasn't been processed yet. Your ETH is waiting to be recognized by the network and included in the Beacon Chain. * **Processed**: At this point, your deposit has been acknowledged by the Beacon Chain and is included in its registry. The deposit is now waiting for the validator to be activated. * **Active**: Congratulations, your deposit is now linked to an active validator on the Ethereum network. This means you are participating in the consensus process, proposing and attesting to blocks. * **Exited**: This status indicates that your validator has ceased its active duties. This can happen voluntarily if you decide to stop staking or involuntarily if your validator is slashed for malicious activities or severe downtime. To track the status of your deposit and validator, the following tools are invaluable: * [Etherescan](https://etherscan.io/): Use this for initial deposit tracking to confirm your ETH has been successfully sent to the staking contract. Etherscan provides transaction receipts, including success or failure status, gas used, and more. * [Beaconcha.in](https://beaconcha.in/): A specialized Beacon Chain explorer that allows you to monitor the status of your deposit and validator in real-time. Beaconcha.in offers insights into validator queues, activation, performance metrics, and potential penalties. ## Validator The process of validator activation in Ethereum 2.0 is a critical step for those who wish to participate in the network's PoS consensus mechanism. Let’s expand on the key phases and responsibilities involved in this process to provide a more detailed view. ### Becoming a Validator 1. **Preparation**: Accumulate at least 32 ETH and familiarize yourself with Ethereum's staking mechanisms. 2. **Technical Setup**: Visit [Ethereum Launchpad](https://launchpad.ethereum.org/), follow the setup guide for execution and consensus clients, and understand the responsibilities and risks involved. 3. **Staking**: Commit your ETH through the Launchpad, completing the necessary steps to activate your validator status. Becoming a validator is a commitment that contributes to the security and functionality of the Ethereum network, offering rewards but also entailing risks and responsibilities. Potential validators should thoroughly research and prepare to ensure they contribute positively to the network's health. ### Validator Activation Process **Initial Stage: Deposit and Pending Status** * **Making the Deposit**: To initiate the journey of becoming a validator, a stake of 32 ETH must be deposited into the official Ethereum 2.0 staking contract. This step is generally swift, with the deposit appearing on the Ethereum blockchain within a few minutes under normal conditions. However, this timing can be affected by the current state of network congestion, potentially leading to delays. * **Pending Status**: Following the deposit, the transaction is processed by the Ethereum network. During this phase, the deposited ETH is locked, awaiting further action. The validator's status is set to "pending" until the Ethereum network acknowledges and includes the deposit in the Beacon Chain. The amount of time your deposit remains in this status can significantly vary, largely depending on the network's current workload and the queue of pending deposits awaiting recognition by the Beacon Chain. ### During Activation **Queued for Activation**: * **Entering the Queue**: After the deposit is recognized, the validator enters a queue. This is due to the protocol's design to limit the number of validators activated in each epoch, which helps manage the network’s growth and maintain its security. * **Preparation and Monitoring**: Validators in the queue should ensure their node is correctly set up, secure, and fully synchronized with the Ethereum 2.0 blockchain. Continuous monitoring is essential during this stage to avoid any issues that might delay activation or cause the node to miss its activation turn. ### After Activation **Active Participation**: * **Beginning of Duties**: Upon activation, validators start their roles in the network's consensus mechanism. This includes proposing new blocks and attesting to blocks they believe should be added to the blockchain. * **Security and Performance**: An active validator contributes to the overall security and efficiency of the network. It's crucial for validators to maintain a high online uptime and participate honestly in consensus activities to avoid penalties, known as slashing, which can result from actions like double voting or being offline for extended periods. **Earning Rewards**: * **Rewards for Participation**: Validators receive rewards for their active participation, specifically for proposing and attesting to blocks. The amount of rewards earned can vary based on several factors, including the total number of active validators and the validator's own performance. * **Penalties and Slashing**: To incentivize continuous participation and honesty, the Ethereum 2.0 network implements penalty mechanisms. Minor penalties are applied for being offline (inactivity leak), while more severe penalties (slashing) are enforced for malicious activities against the network. **Maintaining Validator Status**: * **Continuous Operation**: Validators are expected to run their nodes continuously. Regular software updates, secure key management, and adherence to best security practices are necessary to maintain validator health and optimize reward earning potential. * **Monitoring Tools**: Utilizing tools like beaconcha.in or other Ethereum 2.0 staking dashboards can help validators monitor their performance, projected rewards, and network status. These tools provide valuable insights that can help in maintaining optimal validator operation. Becoming and serving as a validator on the Ethereum 2.0 network is a significant commitment that entails responsibility, technical know-how, and ongoing management. However, it also offers the opportunity to contribute to the network’s security and earn rewards for playing a crucial role in the Ethereum ecosystem's evolution. ## Rewards The rewards mechanism in Ethereum 2.0 is designed to incentivize validators to act in the best interest of the network. By actively participating in the consensus mechanism, validators help secure the network and, in return, receive rewards for their contributions. Here's a deeper dive into the types of activities rewarded and how they are calculated: ### Types of Validator Rewards * **Block Proposals**: Validators are randomly selected to propose new blocks to the Beacon Chain. When a proposed block is successfully validated by other validators and added to the blockchain, the proposer receives a reward. This process is critical for the continuation of the blockchain and ensures validators are motivated to propose blocks accurately and efficiently. * **Attestations**: Attestation is the process by which validators vote on the validity and correctness of a block. By attesting to a block, validators confirm that they have seen and verified the block as being correct. Validators are rewarded for these attestations, especially when they quickly attest to blocks that are successfully included in the chain. This mechanism helps the network reach consensus more swiftly and securely. * **Sync Committees**: Validators may be selected to serve in sync committees, which are responsible for helping light clients stay up-to-date with the state of the blockchain. Sync committees provide signatures that light clients can use to verify the blockchain's state without needing to process every block. Participation in these committees is rewarded, as it plays a vital role in maintaining the network's accessibility and efficiency. ### Reward Calculation and Distribution * **Epoch-Based Calculation**: Rewards are calculated at the end of each epoch, which is a fixed period of time in the Ethereum 2.0 network. An epoch consists of multiple slots, with each slot representing an opportunity for a block to be proposed. * **Automatic Addition to Validator’s Balance**: Once rewards are calculated, they are automatically added to the validator's balance within the Beacon Chain. This process is seamless and does not require any action from the validator. ### Compounding of Rewards * **Stake Increase**: As rewards are added to the validator's balance, their total stake in the network increases. This increased stake can lead to higher rewards over time, as the validator's proportionate share of the total staked ETH grows. * **Long-Term Earning Potential**: The compounding effect of rewards means that validators who continuously perform their duties well can see significant growth in their staked ETH over time. This serves as an additional incentive for validators to maintain operational excellence and contribute positively to the network's health. ### Considerations for Maximizing Rewards * **Uptime and Performance**: Validators must ensure their node remains online and operates efficiently to maximize their rewards. Downtime or poor performance can lead to missed opportunities for block proposals and attestations, directly impacting reward potential. * **Security and Compliance**: Adhering to best security practices and following protocol rules is essential. Slashing penalties for malicious actions or protocol violations can severely reduce a validator’s stake and rewards. ## Penalties While rewards incentivize validators to act in the best interest of the network, penalties are designed to discourage and penalize undesirable actions. There are primarily two types of penalties: **1. Inactivity Leaks** * **Mechanism**: Inactivity leaks are designed to gradually reduce the stakes of validators who fail to participate in consensus activities, effectively decreasing their influence over the network. This penalty applies network-wide during extended periods of non-finality, affecting all validators but impacting inactive ones the most. * **Purpose**: The primary goal is to prevent inactive validators from causing long-term harm to the network's health. By slowly reducing their stakes, the network encourages validators to maintain a high level of uptime and participation. * **Recovery**: Validators affected by inactivity leaks can halt the penalty by returning to active participation in consensus activities, demonstrating the system's design to be forgiving and encouraging validators to rectify their status. **2. Slashing** * **Severe Misbehavior**: Slashing penalties are reserved for actions deemed explicitly harmful to the network's security, such as double signing or similar slashable offenses. These penalties are more severe than inactivity leaks, reflecting the seriousness of the actions. * **Proposer Slashing**: This type of slashing targets validators who propose two or more conflicting blocks for the same slot, an action that can lead to network instability or forks. * **Attester Slashing**: Validators who attest to contradictory block versions for the same epoch are subject to attester slashing. This behavior disrupts the consensus process and threatens network finality. * **Consequences**: Slashed validators suffer immediate and substantial stake reductions, part of which is burned and part distributed as rewards to whistleblowers who detected and reported the offense. Additionally, slashed validators are removed from the validator set, losing their ability to participate in consensus and earn rewards. ## Conclusion Ethereum staking is not only a way to earn rewards but also a method to contribute to the security and efficiency of the network. By becoming a validator, you play a direct role in the future of Ethereum. ## References * Ethereum Launchpad: [https://launchpad.ethereum.org/](https://launchpad.ethereum.org/) * Official FAQ: [https://launchpad.ethereum.org/en/faq](https://launchpad.ethereum.org/en/faq) * Validator Specification: [https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/validator.md#becoming-a-validator](https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/validator.md#becoming-a-validator) # FAQ Source: https://docs.p2p.org/docs/faq The main staking details from the P2P Validator can be found . ## What is Staking Cryptocurrency staking is the process of holding coins/tokens (stake) to earn rights to participate in the validation of transaction blocks, and in doing so, Validators are rewarded. ## What is DVT Staking API DVT Staking API is an innovative product designed specifically for institutions and intermediaries, enabling them to leverage the advantages of Distributed Validator Technology (DVT). Our API simplifies the process of integrating with the SSV protocol, automating the staking process for enhanced efficiency and ease. ## What is DVT Distributed Validator Technology (DVT), in the majority of PoS (proof-of-stake) systems, assigns each validator a single staking node. However, Distributed Validator Technology allows for the splitting of responsibilities and risks associated with a single validator node across multiple participants. The primary goals of DVT are to increase the security, decentralization, and resilience of the network by ensuring that no single point of failure exists. ## What are the advantages of using DVT **Enhanced Security and Fault Tolerance:** DVT's distributed nature means that your staking operations are not dependent on a single node operator. This significantly reduces the risk of system failures, security breaches, and the impact of potential attacks, as responsibilities and tasks are shared across multiple providers. **Diversification and Reduced Risk:** By spreading your staking across various operators and geographical locations, DVT minimizes the risks associated with relying on a single validator or location. This diversification ensures more stable and reliable staking outcomes. **Cost Efficiency:** DVT allows for more efficient management of staking operations. By distributing tasks across various nodes, operational costs can be optimized, translating into better cost-efficiency for the clients. **High Performance and Reliability:** With nodes distributed globally and managed by top-performing operators, DVT systems typically exhibit higher uptime and performance consistency, contributing to potentially greater staking rewards. ## What are the risks of using DVT **Technology Risks:** As with any technology reliant on software and online connectivity, there is always the risk of software bugs, vulnerabilities, or unforeseen technical issues that could affect the system's stability. **Market Risks:** A key consideration is the volatility of the SSV token price. Since users top-up the cluster balance with SSV tokens, a price decrease can lead to operators increasing their fee in SSV tokens to maintain their margins. This necessitates users to pay more and constantly monitor their balance to avoid liquidation risks. ## What is SSV Secret Shared Validator (SSV) is a technology that allows for the secure and decentralized operation of validator nodes in proof-of-stake (PoS) blockchain networks. ## What is EigenLayer EigenLayer is a protocol built on top of Ethereum designed to enhance the economic utility of staked ETH by allowing users to leverage their existing Ethereum stakes to secure additional protocols and applications without requiring additional collateral. ## What is AVS Actively Validated Services (AVS) refer to any system that necessitates its own distributed validation semantics for verification. This includes sidechains, data availability layers, new virtual machines, keeper networks, oracle networks, bridges, threshold cryptography schemes, and trusted execution environments. AVS is integrated into EigenLayer, essentially serving as node operators. # Get Started Source: https://docs.p2p.org/docs/get-started Start integrating P2P.org's staking capabilities into your platform in minutes. This guide will help you set up your environment and make your first API call. ## Quick Setup 1. **Get API Access** To get started with our APIs, you'll need to obtain authentication credentials. Visit our [Authentication Guide](/docs/authentication) for detailed instructions. 2. **Choose Your Environment** Production API endpoint: ``` https://api.p2p.org ``` Test environment endpoint: ``` https://api-test.p2p.org ``` 3. **Make Your First API Call** Try creating your first stake transaction using the Unified API (no funds required): ```bash bash theme={null} curl --request POST \ --url https://api-test.p2p.org/api/v1/unified/staking/stake \ --header 'accept: application/json' \ --header 'authorization: Bearer 8aAEXXJsogPZzpAKk1uedM7TYxPvGrBi' \ --header 'content-type: application/json' \ --data ' { "chain": "polkadot", "network": "westend", "stakerAddress": "5FU44bZkd8iza8Sw8xS9QeRamsj9MpcBiwvLBReVDLBmd25q", "amount": 1 } ' ``` This will return an unsigned transaction that you can review without committing any funds. ## Integration Steps ### 1. Authentication Setup * Follow our [Authentication Guide](/docs/authentication) to get your API token * Use JWT authentication for all API calls * Include token in Authorization header ### 2. Choose Your Product * [**Unified API**](/docs/unified-api-overview): Start here for multi-chain integration * [**Staking API**](/docs/staking-overview): Direct staking operations * [**Restaking API**](/docs/restaking-overview): Enhanced yield opportunities * [**DVT Staking API**](/docs/overview-dvt-solutions): Distributed validation * [**Data API**](/docs/overview): Analytics and monitoring ### 3. Test Your Integration * Use test networks for initial development * Validate responses and error handling * Monitor rate limits and usage ### 4. Go Live * Switch to production endpoints * Configure network-specific parameters * Set up monitoring and alerts ## Code Examples Looking for implementation examples? Check out our [Code Recipes](https://docs.p2p.org/recipes) for ready-to-use code samples and common integration patterns. Need help? Our [team](/docs/contacts) is available to assist with your integration. # Getting Started Source: https://docs.p2p.org/docs/getting-started-aptos Stake on Aptos programmatically with the P2P.org Staking API. Staking in the Aptos network using the Staking API consists of several main steps: 1. Create a staking request. 2. Sign and send the transaction to the network. [Get an authentication token](/docs/authentication) to start using Staking API. Request examples are provided using [cURL](https://curl.se/). ```mermaid theme={null} sequenceDiagram participant Client participant P2P API participant Blockchain Client->>P2P API: Create stake transaction P2P API-->>Client: Unsigned transaction Client->>Client: Sign transaction locally Client->>P2P API: Broadcast signed transaction P2P API->>Blockchain: Submit transaction Blockchain-->>P2P API: Transaction hash P2P API-->>Client: Transaction status ``` Send a POST request to [/api/v1/aptos/\{network}/staking/delegated/add](/reference/aptos-staking-delegated-add). Example request (for `mainnet` network): ```bash curl theme={null} curl --request POST \ --url https://api.p2p.org/api/v1/aptos/mainnet/staking/delegated/add \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'content-type: application/json' \ --data ' { "amount": 1100000000, "delegatorAddress": "0xd890b1115b39c8d182aeac6cae313cc713cd93922135f8a5893c34fce4e0f32a", "gas": { "unitPrice": 100, "maxGasLimit": 100, } } ' ``` ```typescript TypeScript theme={null} const response = await fetch( "https://api.p2p.org/api/v1/aptos/mainnet/staking/delegated/add", { method: "POST", headers: { "accept": "application/json", "authorization": "Bearer ", "content-type": "application/json", }, body: JSON.stringify({ amount: 1100000000, delegatorAddress: "0xd890b1115b39c8d182aeac6cae313cc713cd93922135f8a5893c34fce4e0f32a", gas: { unitPrice: 100, maxGasLimit: 100, }, }), } ); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.post( "https://api.p2p.org/api/v1/aptos/mainnet/staking/delegated/add", headers={ "accept": "application/json", "authorization": "Bearer ", "content-type": "application/json", }, json={ "amount": 1100000000, "delegatorAddress": "0xd890b1115b39c8d182aeac6cae313cc713cd93922135f8a5893c34fce4e0f32a", "gas": { "unitPrice": 100, "maxGasLimit": 100, }, }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) func main() { payload := map[string]interface{}{ "amount": 1100000000, "delegatorAddress": "0xd890b1115b39c8d182aeac6cae313cc713cd93922135f8a5893c34fce4e0f32a", "gas": map[string]interface{}{ "unitPrice": 100, "maxGasLimit": 100, }, } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.p2p.org/api/v1/aptos/mainnet/staking/delegated/add", bytes.NewBuffer(body)) req.Header.Set("accept", "application/json") req.Header.Set("authorization", "Bearer ") req.Header.Set("content-type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `amount` — amount of tokens to stake in Octas. The minimum stake amount is 11 APT (1 APT = 10⁸ Octas). * `delegatorAddress` — delegator stash account address which keeps tokens. * `gas` — optional; computational effort required to execute the transaction, measured in gas units. * `unitPrice` — price per unit of gas in Octas for processing the Aptos transaction. * `maxGasLimit` — maximum gas limit for the transaction. Example response: ```json theme={null} { "result": { "amount": 1100000000, "delegatorAddress": "0xd890b1115b39c8d182aeac6cae313cc713cd93922135f8a5893c34fce4e0f32a", "unsignedTransaction": "0xa571a5bc9b1bfd5fe58e6e09a5be251a3299985494d022959ce206f1f23f752e21000000000000000200000000000000000000000000000000000000000000000000000000000000010f64656c65676174696f6e5f706f6f6c096164645f7374616b650002207a2ddb6af66beb0d9987c6c9010cb9053454f067e16775a8ecf19961195c3d28080100000000000000400d0300000000006400000000000000e5742068000000000200", "createdAt": "2023-08-24T08:14:50.455Z" }, "error": null } ``` * `amount` — amount of tokens to stake in Octas. * `delegatorAddress` — delegator account address which keeps tokens. * `unsignedTransaction` — unsigned transaction in hexadecimal format. Sign the transaction and submit it to the blockchain to perform the called action. * `createdAt` — timestamp of the transaction in the ISO 8601 format. Use `unsignedTransaction` to [sign and send](/docs/signing-transaction-aptos) the signed transaction to the Aptos network. By broadcasting this transaction, you're adding the tokens to P2P.ORG delegation pool. To check the transaction status, send a GET request to [/api/v1/aptos/\{network}/transaction/status/\{transactionHash}](/reference/aptos-transaction-status). Example request (for `mainnet` network): ```bash curl theme={null} curl --request GET \ --url https://api.p2p.org/api/v1/aptos/mainnet/transaction/status/0x8d80b8ed85b578eeb873731101a926221701ee48218f6c9b83895f7ac1535641 \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'content-type: application/json' ``` ```typescript TypeScript theme={null} const response = await fetch( "https://api.p2p.org/api/v1/aptos/mainnet/transaction/status/0x8d80b8ed85b578eeb873731101a926221701ee48218f6c9b83895f7ac1535641", { method: "GET", headers: { "accept": "application/json", "authorization": "Bearer ", "content-type": "application/json", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( "https://api.p2p.org/api/v1/aptos/mainnet/transaction/status/0x8d80b8ed85b578eeb873731101a926221701ee48218f6c9b83895f7ac1535641", headers={ "accept": "application/json", "authorization": "Bearer ", "content-type": "application/json", }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.p2p.org/api/v1/aptos/mainnet/transaction/status/0x8d80b8ed85b578eeb873731101a926221701ee48218f6c9b83895f7ac1535641", nil) req.Header.Set("accept", "application/json") req.Header.Set("authorization", "Bearer ") req.Header.Set("content-type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `transactionHash` — hash of the transaction. Example response: ```json theme={null} { "result": { "createdAt": "2025-06-24T08:14:50.455Z", "gas": { "unitPrice": 100, "maxGasLimit": 100, "used": 100 }, "senderAddress": "0xd890b1115b39c8d182aeac6cae313cc713cd93922135f8a5893c34fce4e0f32a", "sequenceNumber": 42, "status": "success", "transactionHash": "0x8d80b8ed85b578eeb873731101a926221701ee48218f6c9b83895f7ac1535641" }, "error": {} } ``` * `createdAt` — timestamp of the transaction in the ISO 8601 format. * `gas` — computational effort required to execute the transaction, measured in gas units. * `unitPrice` — price per unit of gas in Octas for processing the Aptos transaction. * `maxGasLimit` — maximum gas limit for the transaction. * `used` — amount of gas spent for the transaction. * `senderAddress` — transaction sender address. * `sequenceNumber` — number of transactions that have been submitted and committed on-chain from the sender account. * `status` — transaction status: `pending`, `success` or `failed`. * `transactionHash` — hash of the transaction. # Getting Started Source: https://docs.p2p.org/docs/getting-started-celestia Stake on Celestia programmatically with the P2P.org Staking API. Staking in Celestia using the Staking API consists of several main steps: 1. Create a staking request. 2. Sign and send the transaction to the network. [Get an authentication token](/docs/authentication) to start using Staking API. Request examples are provided using [cURL](https://curl.se/). ```mermaid theme={null} sequenceDiagram participant Client participant P2P API participant Blockchain Client->>P2P API: Create stake transaction P2P API-->>Client: Unsigned transaction Client->>Client: Sign transaction locally Client->>P2P API: Broadcast signed transaction P2P API->>Blockchain: Submit transaction Blockchain-->>P2P API: Transaction hash P2P API-->>Client: Transaction status ``` Send a POST request to [/api/v1/celestia/\{network}/staking/stake](/reference/create-celestia-stake-transaction). Example request (for `celestia-mainnet-beta` network): ```bash curl theme={null} curl --request POST \ --url https://api-test.p2p.org/api/v1/celestia/celestia-mainnet-beta/staking/stake \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'content-type: application/json' \ --data ' { "stashAccountAddress": "celestia1yknsyf9ws4ugtv3r9g43kwqkne4zmrupae4xb2", "memo": "This is your memo message", "amount": 0.00420 }' ``` ```typescript TypeScript theme={null} const response = await fetch( "https://api-test.p2p.org/api/v1/celestia/celestia-mainnet-beta/staking/stake", { method: "POST", headers: { "accept": "application/json", "authorization": "Bearer ", "content-type": "application/json", }, body: JSON.stringify({ stashAccountAddress: "celestia1yknsyf9ws4ugtv3r9g43kwqkne4zmrupae4xb2", memo: "This is your memo message", amount: 0.00420, }), } ); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.post( "https://api-test.p2p.org/api/v1/celestia/celestia-mainnet-beta/staking/stake", headers={ "accept": "application/json", "authorization": "Bearer ", "content-type": "application/json", }, json={ "stashAccountAddress": "celestia1yknsyf9ws4ugtv3r9g43kwqkne4zmrupae4xb2", "memo": "This is your memo message", "amount": 0.00420, }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) func main() { payload := map[string]interface{}{ "stashAccountAddress": "celestia1yknsyf9ws4ugtv3r9g43kwqkne4zmrupae4xb2", "memo": "This is your memo message", "amount": 0.00420, } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api-test.p2p.org/api/v1/celestia/celestia-mainnet-beta/staking/stake", bytes.NewBuffer(body)) req.Header.Set("accept", "application/json") req.Header.Set("authorization", "Bearer ") req.Header.Set("content-type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `stashAccountAddress` — delegator stash account address which keeps tokens. * `memo` — arbitrary text data to add to the transactions. * `amount` — amount of tokens to stake in TIA. Example response: ```json theme={null} { "error": null, "result": { "amount": 0.0042, "currency": "tia", "validatorAddress": "celestiavaloper1r4kqtye4dzacmrwnh6f057p50pdjm8g59tlhhg", "stashAccountAddress": "celestia1yknsyf9ws4ugtv3r9g43kwqkne4zmrupae4xb2", "rewardDestinationAddress": "celestia1yknsyf9ws4ugtv3r9g43kwqkne4zmrupae4xb2", "transactionData": { "messages": [ { "typeUrl": "/cosmos.staking.v1beta1.MsgDelegate", "value": { "delegatorAddress": "celestia1yknsyf9ws4ugtv3r9g43kwqkne4zmrupae4xb2", "validatorAddress": "celestiavaloper1r4kqtye4dzacmrwnh6f057p50pdjm8g59tlhhg", "amount": { "denom": "utia", "amount": "4200" } } } ], "fee": { "amount": [ { "amount": "5953", "denom": "utia" } ], "gas": "238098" }, "memo": "The message", "encodedBody": "0a9b010a232f636f736d6f732e7374616b696e672e763162657461312e4d736744656c656761746512740a2d636f736d6f733179397765666d6c706d30646a6b74387373656576656b653068747466756b67326136306730721234636f736d6f7376616c6f70657231376c676730337a65397836786b613032667330686877346164347777673035686571723279381a0d0a057561746f6d120434323030120b546865206d657373616765", "encodedAuthInfo": "0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2103b0c7ac278e1941ac0b65d6bd45d541cae58aa584058fbbd822311b8718708c0e12040a020801182312130a0d0a057561746f6d1204353935331092c40e" }, "createdAt": "2024-02-12T11:21:40.810Z" } } ``` * `amount` — amount of tokens to stake. * `currency` — currency of tokens: * `tia` — TIA, Celestia native staking token. * `utia` — 1 TIA = 10⁶ uTIA, * `mtia` — 1 TIA = 10³ mTIA. * `validatorAddress` — validator address to which tokens are delegated. * `stashAccountAddress` — delegator stash account address which keeps tokens. * `rewardDestinationAddress` — reward destination account address. * `createdAt` — timestamp of the transaction in the ISO 8601 format. `transactionData` is the list of data fields to be used to construct the staking transactions: * `messages`: * `typeUrl` — Celestia network operation type. * `delegatorAddress` — delegator address. * `validatorAddress` — validator address. * `amount` — amount of tokens to stake. * `currency` — currency of tokens. * `fee` — fee charged for the transaction. * `amount` — amount of tokens. * `denom` — currency of tokens: `tia`, `utia`, or `mtia`. * `gas` — amount of gas spent for the transaction. * `memo` — arbitrary text data to add to the transactions. * `encodedBody` — processable transaction data encoded in the hexadecimal format. * `encodedAuthInfo` — authorization data, including fee, encoded in the hexadecimal format. Use `transactionData` to [sign and send](/docs/signing-transaction-celestia) the signed transaction to the Celestia network. To check the transaction status, send a GET request to [/api/v1/celestia/\{network}/transaction/status/\{transactionHash}](/reference/get-celestia-transaction-status). Example request (for `celestia-mainnet-beta` network): ```bash curl theme={null} curl --request GET \ --url https://api-test.p2p.org/api/v1/celestia/celestia-mainnet-beta/transaction/status/ADD7B2791E1959075D1836D4BCC71ED256CCD724459F9BD8862D85E205075D47 \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'content-type: application/json' ``` ```typescript TypeScript theme={null} const response = await fetch( "https://api-test.p2p.org/api/v1/celestia/celestia-mainnet-beta/transaction/status/ADD7B2791E1959075D1836D4BCC71ED256CCD724459F9BD8862D85E205075D47", { method: "GET", headers: { "accept": "application/json", "authorization": "Bearer ", "content-type": "application/json", }, } ); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( "https://api-test.p2p.org/api/v1/celestia/celestia-mainnet-beta/transaction/status/ADD7B2791E1959075D1836D4BCC71ED256CCD724459F9BD8862D85E205075D47", headers={ "accept": "application/json", "authorization": "Bearer ", "content-type": "application/json", }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api-test.p2p.org/api/v1/celestia/celestia-mainnet-beta/transaction/status/ADD7B2791E1959075D1836D4BCC71ED256CCD724459F9BD8862D85E205075D47", nil) req.Header.Set("accept", "application/json") req.Header.Set("authorization", "Bearer ") req.Header.Set("content-type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `transactionHash` — hash of the transaction. Example response: ```json theme={null} { "error": null, "result": { "status": "success", "blockId": 18575267, "fee": 0.005952, "gas": { "used": 202947, "wanted": 238047 }, "transactionHash": "ADD7B2791E1959075D1836D4BCC71ED256CCD724459F9BD8862D85E205075D47" } } ``` * `status` — transaction status: `success`, `failed`. * `blockId` — unique identifier of the block in which the transaction has been included. * `fee` — total fee in TIA charged for processing the transaction. * `gas` — computational effort required to execute the transaction, measured in gas units. * `used` — amount of gas spent for the transaction. * `wanted` — maximum gas limit that the transaction initiator was willing to consume for the transaction. * `transactionHash` — hash of the transaction. # Getting Started Source: https://docs.p2p.org/docs/getting-started-data-api Staking Data API provides methods to retrieve essential information about network, about validators and delegators within the network. To start using Staking Data API, [get an authentication token](/docs/authentication). > The section provides the examples of how to interact with the API for Ethereum data. ## Response format Every response is a JSON envelope with `result` and `error`. On success, `result` holds the payload and `error` is empty. On failure, `result` is `null` and `error` contains `code`, `message`, `name`, and `type`. Error responses also include `requestId` (the value of the `x-request-id` header) and `timestamp`. Quote `requestId` when contacting support so the request can be found in the server logs. ```json Error response theme={null} { "error": { "code": 100101, "message": "The data provided is not valid. Please check the request parameters and try again.", "name": "ValidationException", "type": "client" }, "result": null, "requestId": "90c16e9e-6e99-4651-a033-60290356df2f", "timestamp": "2026-05-20T22:29:03.126Z" } ``` Paginated endpoints accept `limit` (from 1 to 1000, default 50) and `offset` (default 0). Endpoints that accept `startAt` and `finishAt` reject a range longer than 31 days, and `finishNumber` must not be lower than `startNumber`. Not every endpoint is available on every network; see the **Supported networks** line for each method on the [Network](/docs/network), [Validator](/docs/validator), and [Delegator](/docs/data-delegator) pages. ## Using curl commands Request examples are provided using [cURL](https://curl.se/). To demonstrate the API usage scenarios, the examples cover: * retrieving the last staking period, * fetching active stakes for validators, * checking validator states, * obtaining rewards by type. ### Get Last Staking Period To retrieve the last staking period, send a GET request to [/api/v1/\{network}/data/network/last-staking-period](/reference/data-network-last-staking-period). Example request: ```bash curl theme={null} curl --request GET \ --url https://api.p2p.org/api/v1/ethereum/data/network/last-staking-period \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.p2p.org/api/v1/ethereum/data/network/last-staking-period", { headers: { "authorization": "Bearer ", }, }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( "https://api.p2p.org/api/v1/ethereum/data/network/last-staking-period", headers={ "authorization": "Bearer ", }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.p2p.org/api/v1/ethereum/data/network/last-staking-period", nil) req.Header.Set("authorization", "Bearer ") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` Example response: ```json theme={null} { "result": { "period": 244333, "updatedAt": "2023-08-24T08:23:18.830Z" }, "error": {} } ``` * `period` — last staking period. * `updatedAt` — timestamp of the last update to the queue. ### Get Delegator Stake To fetch the stake of a delegator for each validator, send a GET request to [/api/v1/\{network}/data/delegator/stakes](/reference/data-delegator-stakes). Note that there is a list of additional query params. Example request: ```bash curl theme={null} curl --request GET \ --url 'https://api.p2p.org/api/v1/ethereum/data/delegator/stakes?startNumber=298991&finishNumber=298991&limit=1000&address=0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb&addressType=deposit' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.p2p.org/api/v1/ethereum/data/delegator/stakes?startNumber=298991&finishNumber=298991&limit=1000&address=0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb&addressType=deposit", { headers: { "authorization": "Bearer ", }, }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( "https://api.p2p.org/api/v1/ethereum/data/delegator/stakes?startNumber=298991&finishNumber=298991&limit=1000&address=0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb&addressType=deposit", headers={ "authorization": "Bearer ", }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.p2p.org/api/v1/ethereum/data/delegator/stakes?startNumber=298991&finishNumber=298991&limit=1000&address=0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb&addressType=deposit", nil) req.Header.Set("authorization", "Bearer ") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `startNumber` — start number of the staking period. * `finishNumber` — finish number of the staking period. Cannot be lower than `startNumber`. * `limit` — number of resources that a single response page contains. * `address` — delegator address in the required network. * `addressType` — delegator address type in the required network. If omitted, the default for the network is used: * `deposit` — Ethereum (default). * `withdrawal` — Ethereum. * `delegator` — Solana (default), Sui, Cardano, Celestia. * `stake_account` — Solana. * `nominator` — Polkadot (default), Kusama (default), TON. * `nominator_reward_account` — Polkadot, Kusama. Passing an `addressType` that the network does not support returns `400`. * `validatorAddress` — validator address in the required network for this delegator. * `groupBy` — group the output data: `stakingPeriod`, `day`, or `all`. `day` cannot be combined with `startNumber` / `finishNumber`; use `startAt` / `finishAt` instead, otherwise the request returns `400`. * `skip` — set to `validator` to exclude the breakdown by validator from the report. Example response: ```json theme={null} { "result": { "limit": 50, "offset": 0, "list": [ { "stakingPeriod": 298991, "stakingPeriodStart": "2023-11-01T06:43:17.000Z", "stakingPeriodEnd": "2020-11-01T07:12:27.000Z", "stake": 1.82282, "validator": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17" } ] }, "error": {} } ``` * `limit` — number of resources that a single response page contains. * `offset` — number of resources to exclude from the beginning of a response. * `list`: * `stakingPeriod` — number of the staking period. * `stakingPeriodStart` — timestamp of the staking period start in the ISO 8601 format. * `stakingPeriodEnd` — timestamp of the staking period finish in the ISO 8601 format. * `stake` — total stake balance of the delegator. On Ethereum this includes ETH that is still in the deposit or activation queue. * `activeStake` — optional; Ethereum only, returned only with `groupBy=day`. The part of `stake` that is already active on the Beacon Chain. * `inactiveStake` — optional; Ethereum only, returned only with `groupBy=day`. The part of `stake` that is still waiting to be activated. * `validator` — validator address. ### Get Validator State and Statuses To retrieve validator state, send a GET request to [/api/v1/\{network}/data/validator/state](/reference/data-validator-state). Example request: ```bash curl theme={null} curl --request GET \ --url 'https://api.p2p.org/api/v1/ethereum/data/validator/state?address=0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.p2p.org/api/v1/ethereum/data/validator/state?address=0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", { headers: { "authorization": "Bearer ", }, }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( "https://api.p2p.org/api/v1/ethereum/data/validator/state?address=0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", headers={ "authorization": "Bearer ", }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.p2p.org/api/v1/ethereum/data/validator/state?address=0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", nil) req.Header.Set("authorization", "Bearer ") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `address` — validator address in the required network. For the Ethereum network, it is a public validator key. Example response: ```json theme={null} { "result": { "state": "string", "activatedAt": "string", "activatedStakingPeriodNum": 0 }, "error": {} } ``` * `state` — validator state. * `activatedAt` — timestamp of the validator activated date in the ISO 8601 format. * `activatedStakingPeriodNum` — timestamp of the validator activated staking period. You can also retrieve the information about validator statuses by sending a GET request to [/api/v1/\{network}/data/validator/statuses](/reference/data-validator-statuses) . Example request: ```bash curl theme={null} curl --request GET \ --url https://api.p2p.org/api/v1/ethereum/data/validator/statuses \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.p2p.org/api/v1/ethereum/data/validator/statuses", { headers: { "authorization": "Bearer ", }, }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( "https://api.p2p.org/api/v1/ethereum/data/validator/statuses", headers={ "authorization": "Bearer ", }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.p2p.org/api/v1/ethereum/data/validator/statuses", nil) req.Header.Set("authorization", "Bearer ") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` Example response: ```json theme={null} { "result": { "total": 3, "by_status": [ { "pending_initialized": 0, "pending_queued": 0, "active_ongoing": 2, "active_slashed": 0, "exited_unslashed": 0, "exited_slashed": 0, "withdrawal_possible": 1, "withdrawal_done": 0 } ] }, "error": {} } ``` * `total` — total count of validators. * `by_status`: * `pending_initialized` — number of validators which are initialized but not yet in the queue for activation. * `pending_queued` — number of validators that entered a queue for activation. * `active_ongoing` — number of validators that were activated and currently participate in attesting and proposing blocks. * `active_slashed` — number of validators that were slashed due to misbehaviors. * `exited_unslashed` — number of validators that have exited the network without being slashed and are no longer acting as validators. * `exited_slashed` — number of validators that have exited the network after being slashed and are no longer acting as validators. * `withdrawal_possible` — number of validators having a non-zero balance. * `withdrawal_done` — number of validators completed the withdrawal process. ### Get Delegator Rewards To fetch a list of delegators rewards by type, send a GET request to [/api/v1/\{network}/data/delegator/rewards](/reference/data-delegator-rewards). Note that there is a list of additional query params, e.g., grouping the data by date. Example request: ```bash curl theme={null} curl --request GET \ --url 'https://api.p2p.org/api/v1/ethereum/data/delegator/rewards?startAt=2024-07-01T00%3A00%3A00&finishAt=2024-07-23T00%3A00%3A00&limit=100&address=0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb&addressType=deposit&groupBy=day' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.p2p.org/api/v1/ethereum/data/delegator/rewards?startAt=2024-07-01T00%3A00%3A00&finishAt=2024-07-23T00%3A00%3A00&limit=100&address=0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb&addressType=deposit&groupBy=day", { headers: { "authorization": "Bearer ", }, }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( "https://api.p2p.org/api/v1/ethereum/data/delegator/rewards?startAt=2024-07-01T00%3A00%3A00&finishAt=2024-07-23T00%3A00%3A00&limit=100&address=0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb&addressType=deposit&groupBy=day", headers={ "authorization": "Bearer ", }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.p2p.org/api/v1/ethereum/data/delegator/rewards?startAt=2024-07-01T00%3A00%3A00&finishAt=2024-07-23T00%3A00%3A00&limit=100&address=0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb&addressType=deposit&groupBy=day", nil) req.Header.Set("authorization", "Bearer ") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `startAt` — timestamp of the report data period start in the ISO 8601 format. If not specified, the default value is the one month ago. * `finishAt` — timestamp of the report data period finish in the ISO 8601 format. If not specified, the default value is the current date. * `limit` — number of resources that a single response page contains. * `address` — delegator address in the required network. * `addressType` — delegator address type in the required network. If omitted, the default for the network is used: * `deposit` — Ethereum (default). * `withdrawal` — Ethereum. * `delegator` — Solana (default), Sui, Cardano, Celestia. * `stake_account` — Solana. * `nominator` — Polkadot (default), Kusama (default), TON. * `nominator_reward_account` — Polkadot, Kusama. Passing an `addressType` that the network does not support returns `400`. * `validatorAddress` — validator address in the required network for this delegator. * `groupBy` — group the output data: * `stakingPeriod` — aggregate the results by epoch. * `day` — aggregate the results by days. * `all` — aggregate the results over the entire period. * `skip` — set to `validator` to exclude the breakdown by validator from the report. * `rewardTypes` — filter rewards by type. Pass a single value or repeat the key (`?rewardTypes=consensus&rewardTypes=execution`). Some networks exclude certain reward types by default; pass `rewardTypes=all` to return every type. Example response: ```json theme={null} { "result": { "limit": 50, "offset": 0, "list": [ { "stakingPeriod": 298991, "stakingPeriodStart": "2023-11-01T06:43:17.000Z", "stakingPeriodEnd": "2020-11-01T07:12:27.000Z", "rewards": [ { "type": "consensus", "amount": 0.63722, "currency": "ETH", "recipient": "string" } ] } ] }, "error": {} } ``` * `limit` — number of resources that a single response page contains. * `offset` — number of resources to exclude from the beginning of a response. * `list`: * `stakingPeriod` — number of the staking period. * `stakingPeriodStart` — timestamp of the staking period start in the ISO 8601 format. * `stakingPeriodEnd` — timestamp of the staking period finish in the ISO 8601 format. * `rewards`: * `type` — rewards type: `consensus` or `execution`. * `amount` — amount of tokens in the stake. * `currency` — currency of the tokens. * `recipient` — rewards recipient address. ## Using Python code This section demonstrates how to use Python with the Staking Data API. The examples for Ethereum data cover: * fetching the delegator daily rewards, * retrieving the summary for a delegator and validator. ### Get Delegator Rewards To fetch the daily rewards for a delegator, use the following code with the API endpoint [/api/v1/\{network}/data/delegator/rewards](/reference/data-delegator-rewards): ```python Python theme={null} import requests import datetime import pickle # API endpoint. API_URL = 'https://api.p2p.org/api/v1/ethereum/data/delegator/rewards' # Include your Bearer token for authentication and specify the delegator address. ACCESS_TOKEN = 'YOUR_ACCESS_TOKEN' DELEGATOR_ADDRESS = 'DELEGATOR_ADDRESS' def fetch_rewards_for_date(session, date, address): """Fetch rewards for a specific date.""" params = { 'startAt': date.strftime('%Y-%m-%dT00:00:00') ,# Start time in the ISO 8601 format for each day. 'finishAt': (date + datetime.timedelta(days=1)).strftime('%Y-%m-%dT00:00:00'), # Finish time in the ISO 8601 format for the next day. 'limit': 1000, # Number of resources per page. 'offset': 0, # Number of resources to exclude from the beginning; the default value is 0. 'address': address, # Delegator address in the Ethereum network. 'addressType': 'deposit', # Delegator address type in the required network. For Ethereum it can be `deposit` or `withdrawal`. 'skip': 'validator', # Excludes breakdown by validator from the data report. To include validator, specify the empty value. 'groupBy': 'stakingPeriod' # Group the results by `stakingPeriod`, `day` or `all` (the entire period). } rewards = [] while True: try: response = session.get(API_URL, params=params) response.raise_for_status() result = response.json() new_rows = result.get('result', {}).get('list', []) if new_rows: rewards.extend(new_rows) params['offset'] += 1000 else: break except requests.RequestException as e: print(f"An error occurred: {e}") break return rewards def main(): # Setup session session = requests.Session() session.headers.update({'Authorization': f'Bearer {ACCESS_TOKEN}'}) # Define date range start_date = datetime.date(year=2024, month=1, day=1) end_date = datetime.date.today() date_range = (start_date + datetime.timedelta(days=d) for d in range((end_date - start_date).days + 1)) all_rewards = [] # Fetch rewards for each date for date in date_range: print(f"Fetching rewards for {date}") daily_rewards = fetch_rewards_for_date(session, date, DELEGATOR_ADDRESS) all_rewards.extend(daily_rewards) # Save rewards to file with open('dump.pkl', 'wb') as file: pickle.dump(all_rewards, file) # Calculate total amount total_amount = sum(reward['amount'] for epoch in all_rewards for reward in epoch.get('rewards', [])) print(f"Total Amount: {total_amount}") if __name__ == '__main__': main() ``` This script gathers daily rewards data from a start date to the current date, saves the data to a file, and calculates the total rewards amount. ### Get Delegator Summary To fetch the lifetime stake, rewards, and APY for a delegator, use the following code with the API endpoint [/api/v1/\{network}/data/delegator/summary](/reference/data-delegator-summary): ```python Python theme={null} import requests # API endpoint. url = "https://api.p2p.org/api/v1/{network}/data/delegator/summary" # Include your Bearer token for authentication. headers = { "Authorization": "Bearer ", "Content-Type": "application/json" } # Specify the following parameters: params = { "address": "0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb", # Delegator address. "addressType": "deposit", # Delegator address type in the required network. For Ethereum it can be `deposit` or `withdrawal`. "network": "ethereum", # Network name. "startAt": "2024-04-17T15:00:00.000Z", # Report data start time in the ISO 8601 format. "finishAt": "2024-04-19T15:00:00.000Z", # Report data finish time in the ISO 8601 format. "groupBy": "all", # Aggregate the results by `stakingPeriod` or `day`. To aggregate the results for the entire period, set `all`. "skip": "validator" # Exclude breakdown by validator from the report. To include validators in the output, remove the parameter or pass an empty value. } try: # Make the request. response = requests.get(url.format(network=params["network"]), headers=headers, params=params) # Check the response status. response.raise_for_status() # Parse the JSON response. data = response.json() # Check if the response contains data. if data and "result" in data: print("Delegator Summary:") print("Gross APY:", data["result"]["list"][0]["grossApy"]) print("Net APY:", data["result"]["list"][0]["netApy"]) print("Stake:", data["result"]["list"][0]["stake"], data["result"]["list"][0]["currency"]) print("Rewards:") for reward in data["result"]["list"][0]["rewards"]: print("Type:", reward["type"]) print("Amount:", reward["amount"], reward["currency"]) print("---") else: print("Empty response received.") except requests.exceptions.HTTPError as http_err: print(f"HTTP error occurred: {http_err}") except requests.exceptions.RequestException as req_err: print(f"Request exception occurred: {req_err}") except ValueError as val_err: print(f"Value error occurred: {val_err}") except Exception as err: print(f"Other error occurred: {err}") ``` Example output (with breaking down the result by validators): ```bash theme={null} Gross APY: 0.0312 Net APY: 0.0287 Stake: 1411 ETH Rewards: Type: consensus Amount: 5.195236601009028 ETH --- Type: execution Amount: 1.188704036616951 ETH --- ``` * `Gross APY` — delegator annual percentage yield (APY) before the validator fee (`grossApy`). * `Net APY` — delegator APY after the validator fee (`netApy`). * `Stake` — total stake balance of the delegator. * `Rewards`: * `Type` — rewards type: `consensus` or `execution`. * `Amount` — amount of tokens in the stake. ### Get Validator Summary To retrieve the validator summary, use the following code with the API endpoint [/api/v1/\{network}/data/validator/summary](/reference/data-validator-summary): ```python Python theme={null} import requests # API endpoint. url = "https://api.p2p.org/api/v1/{network}/data/validator/summary" # Include your Bearer token for authentication. headers = { "Authorization": "Bearer ", "Content-Type": "application/json" } # Specify the following parameters: params = { "address": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", # Validator address in the required network. For the Ethereum network, it is a public validator key. "network": "ethereum", # Network name. "startAt": "2024-04-17T15:00:00.000Z", # Report data start time in the ISO 8601 format. "finishAt": "2024-04-19T15:00:00.000Z", # Report data finish time in the ISO 8601 format. "limit": 50, # Number of resources that a single response page contains. "offset": 0 # Number of resources to exclude from the beginning of a response. } try: # Make the request. response = requests.get(url.format(network=params["network"]), headers=headers, params=params) # Check the response status. response.raise_for_status() # Parse the JSON response. data = response.json() # Check if the response contains data. if data and "result" in data: validator_summary = data["result"] # Print the validator summary information. print("Validator Summary:") print("---") # Iterate over each item in the list. for item in validator_summary["list"]: print("Staking Period:", item["stakingPeriod"]) print("Start:", item["stakingPeriodStart"]) print("End:", item["stakingPeriodEnd"]) print("APY:", item["apy"]) print("Stake:", item["stake"], "ETH") # Print the rewards information. print("Rewards:") for reward in item["rewards"]: print("Amount:", reward["amount"], reward["currency"]) print("---") else: print("Empty response received.") except requests.exceptions.HTTPError as http_err: print(f"HTTP error occurred: {http_err}") except requests.exceptions.RequestException as req_err: print(f"Request exception occurred: {req_err}") except ValueError as val_err: print(f"Value error occurred: {val_err}") except Exception as err: print(f"Other error occurred: {err}") ``` Example output (without breaking down the results by validators): ```bash theme={null} Validator Summary: --- Staking Period: 277903 Start: 2024-04-19 14:59:35 End: 2024-04-19 15:05:59 APY: 0.024850512 Stake: 32.01709 ETH Rewards: Amount: 9.683e-06 ETH --- Staking Period: 277902 Start: 2024-04-19 14:53:11 End: 2024-04-19 14:59:35 APY: 0.024770953 Stake: 32.01708 ETH Rewards: Amount: 9.652e-06 ETH --- ``` * `Staking Period` — number of the staking period. * `Start` and `End` — timestamp of the staking period start and finish in the ISO 8601 format. * `APY` — validator annual percentage yield (APY). * `Stake` — total stake balance of the validator. * `Rewards`: * `Amount` — amount of tokens in the stake. # Getting started Source: https://docs.p2p.org/docs/getting-started-ssv-31 Prerequisites and setup for staking on the SSV network with the SSV 3.1 flow. To start staking on the SSV network using our SSV 3.1, there are a few essential **prerequisites** to ensure a smooth and efficient setup: * **Minimum Stake Requirement**: You will need a minimum of 32 ETH for each staking validator you wish to run. * **API Key Request**: [Get an authentication token](/docs/authentication) to start using DVT Staking API. We created an API endpoint to return the transaction data required to interact with our proxy smart contract. We also provide convenient request examples using [cURL](https://curl.se/) to guide you through the process. While the use of the API method is not mandatory for the smart contract interaction, we recommend it for a faster integration. **Please note** On May 7, 2025, the major Ethereum network upgrade was carried out that brought many benefits for clients who use ETH or SSV validators. **P2P.ORG recommends** upgrading to the `0x02` withdrawal credentials to enhance your functionality. It can be done in two ways: * By [consolidating multiple existing validators](/docs/validator-consolidation) into a single one * By [creating a new `0x02`-enabled validator](/docs/new-validator-creation) via simplified 3.1 smart contract flow For more details, see [Pectra Upgrade overview](/docs/pectra-overview). However, upgrading to the new address format is not necessary, all existing endpoints remain supported. You can follow the `0x01` creation flow below, if you need fine-grained control over validator count, effective balance per validator, or you intend to manually broadcast deposit transactions. ## Staking via [SSV Proxy Smart Contract](/docs/unlimited-unified-api#p2porg-dvt-staking-architecture-overview-1) and API 1. Create a serialized transaction for depositing the stake amount to the SSV proxy smart contract by sending a GET request to [/api/v1/eth/staking/ssv/p2p/deposit](/reference/p2p-transaction-deposit). Example request: ```bash curl theme={null} curl --request POST \ --url https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposit \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'content-type: application/json' \ --data ' { "amount": 32, "amountPerValidator": "32000000000", "withdrawalCredentialsType": "0x01", "withdrawalAddress": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", "feeRecipientAddress": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17" } ' ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposit", { method: "POST", headers: { "authorization": "Bearer ", "content-type": "application/json", }, body: JSON.stringify({ "amount": 32, "amountPerValidator": "32000000000", "withdrawalCredentialsType": "0x01", "withdrawalAddress": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", "feeRecipientAddress": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17" }), }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.post( "https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposit", headers={ "authorization": "Bearer ", "content-type": "application/json", }, json={ "amount": 32, "amountPerValidator": "32000000000", "withdrawalCredentialsType": "0x01", "withdrawalAddress": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", "feeRecipientAddress": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17" }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) func main() { payload := map[string]interface{}{ "amount": 32, "amountPerValidator": "32000000000", "withdrawalCredentialsType": "0x01", "withdrawalAddress": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", "feeRecipientAddress": "0x39D02C253dA1d9F85ddbEB3B6Dc30bc1EcBbFA17", } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposit", bytes.NewBuffer(body)) req.Header.Set("authorization", "Bearer ") req.Header.Set("content-type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `amount` — amount of ETH to deposit. The value must be in multiples of 32 ETH. * `amountPerValidator` — amount of tokens to stake in Gwei per validator. Note that if the amount is more than 32 ETH (1 ETH = 10⁹ Gwei), the `withdrawalCredentialsType` parameter defaults to `0x02`. * `withdrawalCredentialsType` — withdrawal credentials preferred format: `0x01` or `0x02`. * `withdrawalAddress` — withdrawal address for the validators. * `feeRecipientAddress` — Eth1 address that receives the fee recipient rewards. Example response: ```json theme={null} { "error": null, "result": { "serializeTx": "0x02f902cf0580021e830186a094681a1b3441c6bfb12f91651efd9f02c83c0702938901bc16d674ec800000b902a44f498c730000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030b4b5f251eac53f34eb1da2b6659d35db303e408c4c830156cf090441564474f356abd023a0e679aa97cefeed850abb27000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000009c7d4b4595402ed44167c74f9f7c7720ab5528e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060a307b2e1661bcc38d482a69603713baf332ad36adf837392e9f20a96b77a03ca415a46261fc62b8f5d4aa69a0d70e2cf0d23e3396bd387cfde18d392a2c7ba822d3f8a60d075366625dcb3c88cd59ebc8f7ab1fc312c6b8b625a57bf7e1b6e6f000000000000000000000000000000000000000000000000000000000000000178ae581ccc8bdf1fde00cff35b2278e1c10a9ed2fc18632373dac118a03cfacdc0", "to": "0x681a1b3441c6BFb12f91651EFD9F02c83c070293", "gasLimit": "0.0000000000001", "data": "0x4f498c730000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030b4b5f251eac53f34eb1da2b6659d35db303e408c4c830156cf090441564474f356abd023a0e679aa97cefeed850abb27000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000009c7d4b4595402ed44167c74f9f7c7720ab5528e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060a307b2e1661bcc38d482a69603713baf332ad36adf837392e9f20a96b77a03ca415a46261fc62b8f5d4aa69a0d70e2cf0d23e3396bd387cfde18d392a2c7ba822d3f8a60d075366625dcb3c88cd59ebc8f7ab1fc312c6b8b625a57bf7e1b6e6f000000000000000000000000000000000000000000000000000000000000000178ae581ccc8bdf1fde00cff35b2278e1c10a9ed2fc18632373dac118a03cfacd", "value": "32.0", "chainId": 1, "type": 2, "maxFeePerGas": "0.00000000000000003", "maxPriorityFeePerGas": "2", "marker": "8bb5a5d4-145c-4a22-b792-3fd98565bdd0" } } ``` * `serializeTx` — serialized unsigned transaction. * `to` — recipient address for this transaction. * `gasLimit` — maximum gas limit for this block. * `data` — transaction data. * `value` — amount this transaction is sending in Wei. * `chainId` — chain ID this transaction is authorized on, as specified by [EIP-155](https://eips.ethereum.org/EIPS/eip-155). * `type` — [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) type of this transaction envelope. * `maxFeePerGas` — maximum price per unit of gas this transaction will pay for the combined [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee and this transaction's priority fee in Wei. * `maxPriorityFeePerGas` — price per unit of gas in Wei, which is added to the [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee. This added fee is used to incentivize miners to prioritize this transaction. * `marker` — additional identifier of the deposit transaction. You can use it to search the deposits with [Get List Deposits](/reference/p2p-transaction-deposit-list) endpoint. 2. Use `serializeTx` from the previous step to [sign and send](/docs/signing-transaction-eth) the signed transaction to the Ethereum network. By broadcasting this transaction, you are depositing the requested stake amount in the Ethereum deposit smart contract by using the SSV proxy smart contract. Also, the smart contract will register the newly created validators on the SSV protocol. After your deposit is successful, your validator will enter the activation queue and soon start performing the validator tasks. You can check the status of your validator on the [SSV network explorer](https://goerli.explorer.ssv.network/validators). 3. Check the list of validators created via your authentication token and their status by sending a GET request to [/api/v1/eth/staking/ssv/p2p/deposits](/reference/p2p-transaction-deposit). Example request: ```bash curl theme={null} curl --request GET \ --url https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposits?limit=50&offset=0&type=withdrawalAddress&withdrawalAddress=0xe3bb3cc1fd44fced699e5eae6fbbc643a0fbcca0 \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposits?limit=50&offset=0&type=withdrawalAddress&withdrawalAddress=0xe3bb3cc1fd44fced699e5eae6fbbc643a0fbcca0", { headers: { "authorization": "Bearer ", }, }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( "https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposits?limit=50&offset=0&type=withdrawalAddress&withdrawalAddress=0xe3bb3cc1fd44fced699e5eae6fbbc643a0fbcca0", headers={ "authorization": "Bearer ", }, ) print(response.json()) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposits?limit=50&offset=0&type=withdrawalAddress&withdrawalAddress=0xe3bb3cc1fd44fced699e5eae6fbbc643a0fbcca0", nil) req.Header.Set("authorization", "Bearer ") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(string(respBody)) } ``` * `limit` — number of resources that a single response page contains. * `offset` — number of resources to exclude from a response. * `type` — type of filter: * `withdrawalAddress` — withdrawal address for the validators. * `transactionHash` — hash of the deposit transaction. * `depositIds` — list of the deposit IDs. * `markers` — additional identifier of the deposit transaction. Example response: ```json theme={null} { "error": null, "result": { "list": [ { "deposit": { "depositId": "", "marker": "faee00fc-f282-43eb-a15f-27d2ed97479e", "transactionHash": "", "senderAddress": "", "withdrawalAddress": "0xe3bb3cc1fd44fced699e5eae6fbbc643a0fbcca0", "feeRecipientAddress": "", "totalAmount": "", "unprocessedAmount": "18273" }, "validator": { "pubkey": "0xcd5ff4fbeafeb6bcdc5dde3ba41d88638eb7b14e52ee3beaf0d32ae3abb303ff8814258ecaa49c5ed0bd7dd48bb0ca502ac89c0bcd36dc7b01a00b8db1e4adec", "status": "unknown", "amount": "1991", "withdrawalCredentials": "...", "signature": "...", "depositMessageRoot": "...", "depositDataRoot": "...", "forkVersion": "10000910", "depositCliVersion": "2.7.0", "eth2NetworkName": "hoodi" } }, ], "limit": 50, "offset": 0, "totalCount": 17, "totalAmount": "18273", "unprocessedAmount": "18273", "markers": [ { "marker": "faee00fc-f282-43eb-a15f-27d2ed97479e", "processStatus": "confirmed", "address": ",,,", "amount": "400", "amountPerValidator": "40000000000", "feeRecipientAddress": "..." } ] } } ``` * `deposit`: * `depositId` — unique identifier for the deposit initiated by the user. * `marker`— unique identifier of the deposit transaction * `transactionHash` — hash of the transaction associated with the user's validator deposit. * `senderAddress` — address used to interact with the SSV proxy smart contract and transfer the requested stake amount. * `withdrawalAddress` — withdrawal address for the validators. * `feeRecipientAddress` — the address that receives the fee recipient rewards. * `totalAmount` — total amount currently staked for the specified deposit ID. * `unprocessedAmount` — amount associated with validators that are not yet activated for the specified deposit ID * `validator`: * `pubkey` — validator public key. * `status` — current status of the validator * `amount` — amount of ETH, denominated in gwei, that is being deposited. * `limit` — number of resources that a single response page contains. * `offset` — number of resources to exclude from a response. * `totalCount` — total number of validators relevant to the current request, based on applied filters. * `totalAmount` — total staked amount for the current request. * `unprocessedAmount` — cumulative staked amount across all validators that are not yet activated for the current request. * `markers` — Unique identifiers generated after creating a deposit request, tracking transactions and deposited funds. # Incremental Staking (Top-ups) Source: https://docs.p2p.org/docs/incremental-staking-tops-up Pectra Upgrade brought to the Ethereum staking many new features, such as increasing of the validator maximum effective balance in particular. This now makes it possible to , as well as and , up to a total of 2048 ETH. Contributing to the validator's total stake is especially convenient, as the general top-up flow supports an amount starting from 1 ETH, whilst to create a new validator, at least 32 ETH are required. To increase the stake of your validator: 1. Create an increment request. 2. Check the status of the request. 3. Sign and broadcast the transaction. Request examples are provided using [cURL](https://curl.se/). The flow is supported for both Ethereum and SSV validators via the same endpoint. ## 1. Create increment request > The flow applies only to active validators using the `0x02` type of withdrawal credentials. 1. Prepare `id` that is an arbitrary UUID. Generate it in one of the following ways: * [Online UUID Generator](https://www.uuidgenerator.net/) * [uuid npm package](https://www.npmjs.com/package/uuid) 2. Send a POST request to [/api/v1/eth/staking/direct/increment-request/create](/reference/eth-increment-request-create). Example request: ```bash bash theme={null} curl --request POST \ --url https://api.p2p.org/api/v1/eth/staking/direct/increment-request/create \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ "id": "f293e499-9188-4e01-8e7c-621038967b06", "pubkeys": [ "0xffC08FcD7cFeF5c70fB2b0e1f2A8EaA690AaE2bDFfa5dBEc4dEef31DcC0B19eB1f9Cebe3E2fe9eefBD9a1BDF6FD89b39" ], "amountPerValidator": "10000000000", "withdrawalAddress": "0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb" }' ``` * `id` — arbitrary UUID. You can later use that UUID to check the status of the top-up operation. * `pubkeys` — list of validators' public keys. * `amountPerValidator` — amount of tokens to stake in Gwei per validator. * `withdrawalAddress` — withdrawal address for the validators. Example response: ```json JSON theme={null} { "error": null, "result": true } ``` ## 2. Check request status Check the status of the top-up operation by sending a GET request to [/api/v1/eth/staking/direct/increment-request/status/\{id}](/reference/eth-increment-request-status). Example request: ```bash bash theme={null} curl --request GET \ --url https://api.p2p.org/api/v1/eth/staking/direct/increment-request/status/f293e499-9188-4e01-8e7c-621038967b06 \ --header 'accept: application/json' --header 'Authorization: Bearer ' ``` Example response: ```json JSON theme={null} { "error": null, "result": { "id": "f293e499-9188-4e01-8e7c-621038967b06", "status": "ready", "withdrawalAddress": "0x338EF19fA2eC0fc4d1277B1307a613fA1FBbc0cb", "amountPerValidator": "1000000000", "pubkeys": [ "0xffC08FcD7cFeF5c70fB2b0e1f2A8EaA690AaE2bDFfa5dBEc4dEef31DcC0B19eB1f9Cebe3E2fe9eefBD9a1BDF6FD89b39" ], "depositData": [], "incrementTxs": [ { "serializeTx": "0x02f901d983088bb0808404521e85847d276925839896809400000000219ab540356cbb839cbe05303d7705fa880de0b6b3a7640000b901a422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120bd3d5b43a2f35b2c8ab809dcdaf619ab1a068d944e02359e5b6cd1eb5c69daea0000000000000000000000000000000000000000000000000000000000000030a3ee378399d9eb0aa126594965866e8785033005709e2816c3969963d99cdce11ee6b0ff3c6410aca6b1cb2e820b28420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200200000000000000000000005419e9924ecd177418639cdce74cc3929c1472e80000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0", "to": "0x00000000219ab540356cBB839Cbe05303d7705Fa", "gasLimit": "10000000", "data": "0x22895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120bd3d5b43a2f35b2c8ab809dcdaf619ab1a068d944e02359e5b6cd1eb5c69daea0000000000000000000000000000000000000000000000000000000000000030a3ee378399d9eb0aa126594965866e8785033005709e2816c3969963d99cdce11ee6b0ff3c6410aca6b1cb2e820b28420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200200000000000000000000005419e9924ecd177418639cdce74cc3929c1472e80000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "value": "1000000000000000000", "chainId": 560048, "type": 2, "maxFeePerGas": "2099734821", "maxPriorityFeePerGas": "72490629" } ], "createdAt": "2023-08-24T08:14:50.455Z" } } ``` * `id` — arbitrary UUID of the top-up request. * `status` — current status of the top-up request: * `init` — request is created. * `processing` — request is in progress. * `ready` — backend has the deposit data for the top-up request. * `cancel` — something went wrong, and the deposit data was not created. * `withdrawalAddress` — withdrawal address for the validators. * `pubkeys` — list of validators' public keys. * `amountPerValidator` — amount of tokens to stake in Gwei per validator. * `depositData` for each validator: * `pubkey` — validator public key. * `signature` — validator signature. * `depositDataRoot` — SHA-256 hash of the SSZ-encoded `depositData` object. Used as a protection against malformed input. * `withdrawalCredentials`— withdrawal address credentials, passed in the expected format by the Ethereum deposit smart contract. * `amount` — amount of ETH, denominated in Gwei, that is being deposited. * `depositMessageRoot` — cryptographic hash of the Merkle tree's root, ensuring the integrity and authenticity of the deposit data. * `forkVersion` — version of the network fork that the deposit is intended for. It helps in aligning the deposit with a specific version of the protocol. * `eth2NetworkName` — name of the Ethereum 2.0 network where the deposit is made. * `depositCliVersion` — version of the deposit command-line interface (CLI) tool that was used to generate the deposit data. * `incrementTxs` — list of serialized transaction payloads to increment the validator stake. * `serializeTx` — serialized unsigned transaction. * `to` — recipient address for this transaction. * `gasLimit` — maximum gas limit for this block. * `data` — transaction data. * `value` — amount this transaction is sending in Wei. * `chainId` — chain ID this transaction is authorized on, as specified by [EIP-155](https://eips.ethereum.org/EIPS/eip-155). * `type` — [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) type of this transaction envelope. * `maxFeePerGas` — maximum price per unit of gas this transaction will pay for the combined [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee and this transaction's priority fee in Wei. * `maxPriorityFeePerGas` — price per unit of gas in Wei, which is added to the [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee. This added fee is used to incentivize miners to prioritize this transaction. * `createdAt` — timestamp of the transaction in the ISO 8601 format. ## 3. Sign and broadcast transaction Prepare `serializeTx` from the previous step to [sign](/docs/signing-transaction-eth) and broadcast the signed transaction to the Ethereum network. After the transaction is processed in the chain's queue, the validator's stake will be topped up. # Initial Staking Source: https://docs.p2p.org/docs/initial-staking Create 0x02 validators through the simplified smart contract flow introduced with the Pectra upgrade. For setting up validators, P2P.ORG has introduced a new **simplified smart contract flow** — validators created this way receive the `0x02` type of withdrawal credentials. Compared to the previous `0x01` type, this brings the following benefits: * Flexible effective balance from 32 ETH up to 2048 ETH * New staking options such as [incremental top-ups](/docs/incremental-staking-top-up) and auto compounding * Simplified flow with fewer steps and reduced manual deposit data handling All changes are supported for both Ethereum and SSV staking APIs. To upgrade to the `0x02` credentials type, you can either [consolidate multiple existing validators](/docs/validator-consolidation) into a single one or set up a new `0x02`-enabled validator by following the steps below: 1. Prepare a proxy staking transaction. 2. Sign and broadcast the transaction. Request examples are provided using [cURL](https://curl.se/). **Upgrade is just an option** For clients currently using the `0x01` validator setup, all existing endpoints remain supported. If you need to create a new validator via the previous `0x01` flow, refer to the guides for the [Ethereum](/docs/staking-ethereum) or [SSV](/docs/overview-dvt-solutions) networks respectively. Upgrading to the `0x02` type of withdrawal credentials is not necessary, but only recommended for enhanced functionality. ## 1. Prepare staking proxy transaction > This flow is supported for Ethereum Direct staking only and applies exclusively to validators using `0x02` type of withdrawal credentials. Send a GET request to [/api/v1/eth/staking/direct/p2p/deposit](/reference/p2p-transaction-direct-deposit) with the following query parameters specified: `amount`, `withdrawalAddress`, `controllerAddress` and `feeRecipientAddress`. By calling this endpoint, you are receiving a transaction fully constructed by Ethereum Smart Contract 3.1 which is ready for signing and broadcasting. Example request: ```bash bash theme={null} curl --request GET \ --url "https://api-test.p2p.org/api/v1/eth/staking/direct/p2p/deposit?amount=32&withdrawalAddress=0x8eB2277e54A2A7db9b7c0D9FA9B78dcB35f74866&controllerAddress=0x8eB2277e54A2A7db9b7c0D9FA9B78dcB35f74866&feeRecipientAddress=0x3202B5B0602274197CcB22B8c02CEC9539990c7c" \ --header 'Authorization: Bearer ' ``` * `amount` — total amount of ETH to deposit. Note that the maximum effective balance is 2048 ETH, so it is recommended specifying no more than 1920 ETH in order to save the opportunity to auto-compound the received rewards. * `amountPerValidator` — amount of tokens to stake in Gwei per validator. * `withdrawalAddress` — withdrawal address for the validators. * `controllerAddress` — controller address for the validators. * `feeRecipientAddress` — address that receives priority fees. Example response: ```json JSON theme={null} { "error": null, "result": { "serializeTx": "0x02f9025701808312c36c850727b59762839896809423be839a14cec3d6d716d904f09368bbf9c750eb8903782dace9d9000000b90224a49b131b0200000000000000000000008eb2277e54a2a7db9b7c0d9fa9b78dcb35f74866000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000003fcd8d9acac042095dfba53f4c40c74d19e2e9d9000000000000000000000000000000000000000000000000000000000000251c0000000000000000000000003202b5b0602274197ccb22b8c02cec9539990c7c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000e86b78000ed3b7b820ac6a39b46602356daee0602180fefa69a94774c3209a9f5ad1df42aa2c426b1494a39afc4ee79a528dca95807f5a1b518c8fb0a4df1be92b8a8b6e5f4fccf35f3975f9cf64aa92f4896f2553a5ba9fb0b13cabb25f21a2881f027d9703bfa57f45a514fff7bcc1b8020c2a3ab0581cfbb748dfe612a14acef40bc61de068cf3117e8227ab1315e0302f97e17d5b8948aa8071645b27ad74f669783d363fbc7e9dd58c01e535aff418b2fdbff56a55212e25efbc6b963bb048d98ecc1d03020f890bbf7518fd2463afb82ffdf452863268bdae4eae18d6108dce9ef803669f466000000000000000000000000000000000000000000000000c0", "to": "0x23BE839a14cEc3D6D716D904f09368Bbf9c750eb", "gasLimit": "10000000", "data": "0xa49b131b0200000000000000000000008eb2277e54a2a7db9b7c0d9fa9b78dcb35f74866000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000003fcd8d9acac042095dfba53f4c40c74d19e2e9d9000000000000000000000000000000000000000000000000000000000000251c0000000000000000000000003202b5b0602274197ccb22b8c02cec9539990c7c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000e86b78000ed3b7b820ac6a39b46602356daee0602180fefa69a94774c3209a9f5ad1df42aa2c426b1494a39afc4ee79a528dca95807f5a1b518c8fb0a4df1be92b8a8b6e5f4fccf35f3975f9cf64aa92f4896f2553a5ba9fb0b13cabb25f21a2881f027d9703bfa57f45a514fff7bcc1b8020c2a3ab0581cfbb748dfe612a14acef40bc61de068cf3117e8227ab1315e0302f97e17d5b8948aa8071645b27ad74f669783d363fbc7e9dd58c01e535aff418b2fdbff56a55212e25efbc6b963bb048d98ecc1d03020f890bbf7518fd2463afb82ffdf452863268bdae4eae18d6108dce9ef803669f466000000000000000000000000000000000000000000000000", "value": "64000000000000000000", "chainId": 1, "type": 2, "maxFeePerGas": "30730983266", "maxPriorityFeePerGas": "1229676", "marker": "8bb5a5d4-145c-4a22-b792-3fd98565bdd0" } } ``` * `serializeTx` — serialized unsigned transaction. * `to` — recipient address for this transaction. * `gasLimit` — maximum gas limit for this block. * `data` — transaction data. * `value` — amount this transaction is sending in Wei. * `chainId` — chain ID this transaction is authorized on, as specified by [EIP-155](https://eips.ethereum.org/EIPS/eip-155). * `type` — [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) type of this transaction envelope. * `maxFeePerGas` — maximum price per unit of gas this transaction will pay for the combined [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee and this transaction's priority fee in Wei. * `maxPriorityFeePerGas` — price per unit of gas in Wei, which is added to the [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee. This added fee is used to incentivize miners to prioritize this transaction. * `marker` — additional identifier of the deposit transaction. You can use it to search the deposits with [Get List Deposits](/reference/p2p-transaction-deposit-list) endpoint. ## 2. Sign and broadcast transaction Prepare `serializeTx` from the previous step and use the example code below to sign and broadcast the transaction to the Ethereum network: ```bash bash theme={null} require("dotenv").config(); const ethers = require("ethers"); async function signAndBroadcast() { console.log("Started"); // Enter the serialized transaction const rawTransaction = process.env.RAW_TRANSACTION; // Enter the private key of the address used to transfer the stake amount const privateKey = process.env.PRIVATE_KEY; // Enter the selected RPC URL const rpcURL = process.env.RPC_URL; // Initialize the provider using the RPC URL const provider = new ethers.providers.JsonRpcProvider(rpcURL); // Initialize a new Wallet instance const wallet = new ethers.Wallet(privateKey, provider); // Parse the raw transaction const tx = ethers.utils.parseTransaction(rawTransaction); tx.nonce = await provider.getTransactionCount(wallet.address); // Enter the max fee per gas and prirorty fee tx.maxFeePerGas = ethers.utils.parseUnits( process.env.MAX_FEE_PER_GAS_IN_GWEI, "gwei" ); tx.maxPriorityFeePerGas = ethers.utils.parseUnits( process.env.MAX_PRIORITY_FEE_IN_GWEI, "gwei" ); // Sign the transaction const signedTransaction = await wallet.signTransaction(tx); // Send the signed transaction const transactionResponse = await provider.sendTransaction(signedTransaction); return transactionResponse; } signAndBroadcast() .then((transactionResponse) => { console.log( "Transaction broadcasted, transaction hash:", transactionResponse.hash ); }) .catch((error) => { console.error("Error:", error); }) .finally(() => { console.log("Finished"); }); ``` After the transaction is processed in the chain's queue, the new validator with `0x02` type of withdrawal credentials will be set up. ## 3. Get list of deposits Check the list of validators created via your authentication token and their status by sending a GET request to [/api/v1/eth/staking/direct/p2p/deposits](/reference/p2p-transaction-direct-deposit-list). Example request: ```bash bash theme={null} curl --request GET \ --url https://api.p2p.org/api/v1/eth/staking/ssv/p2p/deposits?limit=50&offset=0&type=withdrawalAddress&withdrawalAddress=0xe3bb3cc1fd44fced699e5eae6fbbc643a0fbcca0 \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` * `limit` — number of resources that a single response page contains. * `offset` — number of resources to exclude from a response. * `type` — type of filter: * `withdrawalAddress` — withdrawal address for the validators. * `transactionHash` — hash of the deposit transaction. * `depositIds` — list of the deposit IDs. * `markers` — additional identifier of the deposit transaction. Example response: ```json JSON theme={null} { "error": null, "result": { "list": [ { "deposit": { "depositId": "", "marker": "faee00fc-f282-43eb-a15f-27d2ed97479e", "transactionHash": "", "senderAddress": "", "withdrawalAddress": "0xe3bb3cc1fd44fced699e5eae6fbbc643a0fbcca0", "feeRecipientAddress": "", "totalAmount": "", "unprocessedAmount": "18273" }, "validator": { "pubkey": "0xcd5ff4fbeafeb6bcdc5dde3ba41d88638eb7b14e52ee3beaf0d32ae3abb303ff8814258ecaa49c5ed0bd7dd48bb0ca502ac89c0bcd36dc7b01a00b8db1e4adec", "status": "unknown", "amount": "1991", "withdrawalCredentials": "...", "signature": "...", "depositMessageRoot": "...", "depositDataRoot": "...", "forkVersion": "10000910", "depositCliVersion": "2.7.0", "eth2NetworkName": "hoodi" } }, ], "limit": 50, "offset": 0, "totalCount": 17, "totalAmount": "18273", "unprocessedAmount": "18273", "markers": [ { "marker": "faee00fc-f282-43eb-a15f-27d2ed97479e", "processStatus": "confirmed", "address": ",,,", "amount": "400", "amountPerValidator": "40000000000", "feeRecipientAddress": "..." } ] } } ``` * `deposit`: * `depositId` — unique identifier for the deposit initiated by the user. * `marker`— unique identifier of the deposit transaction * `transactionHash` — hash of the transaction associated with the user's validator deposit. * `senderAddress` — address used to interact with the SSV proxy smart contract and transfer the requested stake amount. * `withdrawalAddress` — withdrawal address for the validators. * `feeRecipientAddress` — the address that receives the fee recipient rewards. * `totalAmount` — total amount currently staked for the specified deposit ID. * `unprocessedAmount` — amount associated with validators that are not yet activated for the specified deposit ID * `validator`: * `pubkey` — validator public key. * `status` — current status of the validator * `amount` — amount of ETH, denominated in gwei, that is being deposited. * `limit` — number of resources that a single response page contains. * `offset` — number of resources to exclude from a response. * `totalCount` — total number of validators relevant to the current request, based on applied filters. * `totalAmount` — total staked amount for the current request. * `unprocessedAmount` — cumulative staked amount across all validators that are not yet activated for the current request. * `markers` — Unique identifiers generated after creating a deposit request, tracking transactions and deposited funds. # Aggregated Docs for LLMs Source: https://docs.p2p.org/docs/llms Reference the entire P2P.ORG API documentation in your AI tool ## Add P2P.ORG Docs to Your LLM Adding P2P.ORG docs to your LLM lets you interact with our docs and get the most accurate answers to your questions. Access the outline of the docs, including links to all markdown pages, here: * [https://docs.p2p.org/llms.txt](https://docs.p2p.org/llms.txt). To access the full content of these pages, you can also access this aggregated markdown file containing the whole documentation: 1. Go to [p2p\_aggregated\_docs.md](https://github.com/AndreGZommerfelds/p2p-docs-aggregator/blob/main/p2p_aggregated_docs.md) 2. Attach the file to your LLM, or reference it directly in the AI tool ## Example Using Cursor [Cursor](https://www.cursor.com/) is an AI code editor that makes it easy to write code while integrating with P2P.ORG APIs. Here is how to setup Cursor to build with our documentation. This will let you interact with our docs directly and get the most accurate answers to your questions. 1. Go to Cursor Settings > Features > Docs and click "+ Add new doc" 2. Paste [this URL](https://raw.githubusercontent.com/AndreGZommerfelds/p2p-docs-aggregator/refs/heads/main/p2p_aggregated_docs.md) 3. Change the name to P2P, and leave everything else the same. 4. Wait until the docs have been fully indexed. You can check the progress in the Cursor Settings. 5. Now you can reference the P2P docs in your prompt using `@P2P` # Using MCP to Read API Docs Source: https://docs.p2p.org/docs/mcp Read the P2P.ORG API documentation using our MCP server The P2P.ORG Model Context Protocol (MCP) server connects AI-powered coding tools like Cursor or Claude Code to P2P.ORG’s API documentation, giving LLMs context for the entire API documentation. This enables developers to easily explore APIs, clarify technical questions, and streamline integrations with confidence. ## Exploring the Model Context Protocol (MCP) MCP is an open-source standard that enables AI systems to securely engage with external services and data. By leveraging the P2P.ORG MCP server, AI tools can provide context-aware support that simplifies integration and enhances development efficiency. Key benefits include: * Providing rich context to AI systems for accurate interaction with P2P.ORG’s platform. * Simplifying the process of clarifying and answering technical questions about APIs and features. * Facilitating smoother and faster integration with P2P.ORG’s ecosystem through tailored support. **Note**: The P2P.ORG MCP server currently supports only read and search functionalities. Direct API execution is not yet available. ## Configuring the P2P.ORG MCP Server To connect your AI tools to P2P.ORG’s resources, direct your AI tools to the MCP server at * `https://docs.p2p.org/mcp` P2P.ORG hosts a remote MCP server at this URL. Configure your AI development tools to connect to this server: **Add to `~/.cursor/mcp.json`:** ```json theme={null} { "mcpServers": { "p2p-staking": { "url": "https://docs.p2p.org/mcp" } } } ``` **Add to `~/.codeium/windsurf/mcp_config.json`:** ```json theme={null} { "mcpServers": { "p2p-staking": { "url": "https://docs.p2p.org/mcp" } } } ``` **Add to `claude_desktop_config.json`:** ```json theme={null} { "mcpServers": { "p2p-staking": { "url": "https://docs.p2p.org/mcp" } } } ``` ### Testing Your P2P.ORG MCP Setup Once configured, you can test your MCP server connection: 1. **Open your AI editor** (Cursor, Windsurf, etc.) 2. **Start a new chat** with the AI assistant 3. **Ask about P2P.ORG** - try questions like: * "How do I \[common use case]?" * "Show me an example of \[API functionality]" * "Create a \[integration type] using P2P.ORG" The AI should now have access to your P2P.ORG account data and documentation through the MCP server. ## Alternative: Using Context7 MCP Context7 MCP setup: Alternatively, you can reference our API documentation directly using the Context7 MCP: * See setup instructions here: [https://github.com/upstash/context7](https://github.com/upstash/context7) * Reference our documentation: [Context7 - P2P.ORG API Documentation Hub](https://context7.com/context7/p2p) ### Testing Your Context7 MCP Setup Once you have set up Context7 MCP, you can ask your LLM to use it to check the P2P.ORG documentation. Here are some prompt examples: * `Please read the P2P.ORG API documentation with context7 MCP and prepare for any questions I might have.` * `Please read the P2P.ORG API documentation with context7 MCP and tell me what are the two different ways to integrate with SSV. Make a concise table overview.` * `Please read the P2P.ORG API documentation with context7 MCP and tell me how many networks are supported in the Unified API. Make a concise table overview.` *** AI tools can make mistakes, so double-check the answer, and in case of doubt contact us: [Contacts](/docs/contacts). # Morpho Source: https://docs.p2p.org/docs/morpho Morpho, a non-custodial lending protocol, available through the P2P.org DeFi API. Morpho is a decentralized, non-custodial lending protocol that operates on Ethereum and other EVM-compatible networks. The protocol consists of two primary components: * Morpho Markets providing collateral and borrowable asset pairs, * Morpho Vaults providing managed yield strategies that allocate deposits across multiple markets. The P2P.ORG DeFi API supports Morpho on the Ethereum mainnet and Base network. Some strategies (e.g., Steakhouse Financial) are available on both networks. To retrieve the complete list of available Morpho strategies, send a GET request to the dedicated P2P.ORG [/api/defi/v1/earning/strategies](/reference/get-strategies) endpoint. ## Integration prerequisites Morpho operates through a proxy-based model. Each user creates their own proxy contract, which subsequently facilitates revenue sharing between parties. Since a new P2P Morpho proxy is deployed deterministically at deposit time, the smart contract receiving the deposit does not exist on-chain before the first deposit. Accordingly, the pre-calculated address must be whitelisted in advance. If your platform uses whitelists restricting which contracts users can deposit to, follow the steps below to ensure successful integration. A custom identifier linked to your merchant account is required. [Get an authentication token](/docs/authentication) to start using DeFi API. ### 1. Whitelist required addresses Before any USDT or USDC deposit can be made, three addresses must be whitelisted in your system: | # | Name | Address | | - | --------------------- | -------------------------------------------- | | 1 | P2pMorphoProxyFactory | `0x3FE77F22a81c787B2E8dBF366F963f772E799568` | | 2 | USDT Token (ERC-20) | `0xdAC17F958D2ee523a2206206994597C13D831ec7` | | 3 | Permit2 | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | > **Note for USDC:** For the USDC token address, follow the same whitelisting pattern (`0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`) *** ### 2. Whitelist future proxy address The proxy address is derived deterministically from your depositor wallet address — it does not exist on-chain until the first deposit is made. To get your pre-calculated proxy address, call the `predictP2pLendingProxyAddress` read function on the P2pMorphoProxyFactory contract directly on Etherscan: 1. Go to the contract's **Read Contract** tab on Etherscan: [0x3FE77F22a81c787B2E8dBF366F963f772E799568 – Read Contract](https://etherscan.io/address/0x3fe77f22a81c787b2e8dbf366f963f772e799568#readContract). 2. Find the `predictP2pLendingProxyAddress` function. 3. Enter the following parameters: * `_clientAddress`: your depositor wallet address. * `_clientBasisPoints`: `9000`. 4. Click **Query**. The returned address is your future proxy address. Whitelist the returned proxy address **before** attempting the deposit. *** ### 3. Configure transaction authorization policy (TAP) This step is required to allow USDT → Permit2 approvals through your system. Without it, the approval transaction will be blocked at the policy layer. > **Note:** The steps below describe the flow for Fireblocks. The actual UI for your integration may use slightly different naming or layout, but the overall process and intent remain the same. 1. Refer to the **Fireblocks Console → Settings → Governance → Transaction Authorization Policy** section. 2. Go to the **"Approve"** section (not "Transfers"). 3. Add a new rule: | Field | Value | | ----------------------- | -------------------------------------------------------- | | **Asset** | USDT (ERC-20 on Ethereum) | | **Action** | Approve | | **Spender / Contract** | `0x000000000022d473030f116ddee9f6b43ac78ba3` (Permit2) | | **From Wallet(s)** | Your staking wallet address | | **Amount** | `Unlimited` — or set a limit | | **dApp URL (optional)** | `https://widget.p2p.org` — add to tightly restrict scope | 4. Add another rule with USDC (ERC-20 on Ethereum) as the asset, keeping all other fields the same. 5. Save the policy before proceeding. *** ### 4. Check deposit > **Note:** The steps below describe the general deposit flow via the P2P widget. Exact button labels and screen layout for your system may vary — follow the same overall process as described. With all whitelisting and TAP configuration complete: 1. Open the P2P.ORG widget at [**https://widget.p2p.org**](https://widget.p2p.org/). 2. Connect via Fireblocks. 3. Select **USDT** (or **USDC**) on **Ethereum**. 4. Initiate the deposit. The approval and deposit transactions should now route through Fireblocks without being blocked. # Deposit Source: https://docs.p2p.org/docs/morpho-staking This is a detailed tutorial on how to make a deposit to one of the Morpho's vaults through the DeFi API. To learn more about the flow in general, refer to the [Getting Started](/docs/defi-api-getting-started) section. As an example of using the DeFi API with the Morpho integration, let us consider making the deposit to the Steakhouse USDC vault on the Base network. The process is the following: 1. Get the schema with the step list for obtaining approval. 2. Create the permission request for the permit2 smart contract. 3. Get the schema with the step list for initiating the deposit. 4. Create the permission request for the Morpho bundler. 5. Create the permission request for the P2P.ORG proxy contract. 6. Prepare the final deposit transaction. 7. Broadcast the deposit transaction to the Base network. Before starting, ensure you have met the [prerequisites for successful integration](/docs/morpho-staking). Request examples are provided using [cURL](https://curl.se/) **This is an example for the Steakhouse USDC vault. Note that for other vaults, the number of steps may vary.** This tutorial covers the most complex flow involving construction of three transactions to make a deposit. For the majority of strategies, one transaction is sufficient. ## 1. Get schema for approval Before depositing assets into the vault, first grant permissions to the smart contracts involved in the integration to manage tokens. To check the action schema and retrieve a list of steps required for approval, send a GET request to [/api/defi/v1/transactions/schema](/reference/get-transaction-schema). Example request (for `base` chain and `Steakhouse USDC` strategy): ```bash bash theme={null} curl --request GET \ --url GET 'https://edge.p2p.org/api/defi/v1/transactions/schema?action=approvePermit2&chain=base&protocol=morpho&strategy=Steakhouse%20USDC' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' --header 'x-client-hash: ' ``` * `action` — operation type; in this case `approvePermit2`. * `chain` — blockchain network, e.g., `base`. * `protocol`— integration name, e.g., `morpho`. * `strategy` — vault name, e.g., `Steakhouse USDC`. * `x-client-hash` — unique identifier of the user or merchant participating in the integration; obtained with the [authentication token](/docs/authentication). Example response: ```json JSON theme={null} { "error": null, "result": { "action": "approvePermit2", "steps": [ { "index": 0, "name": "finalPermit2AllowanceTransaction", "description": "Approve token for Permit2 (ERC20 approve to max uint256 for PERMIT2 spender)", "inputs": [ { "key": "asset", "type": "string", "description": "Asset symbol (e.g., USDC, USDT)", "required": true }, { "key": "amount", "type": "string", "description": "Optional allowance amount in token smallest unit. If omitted, max uint256 is used.", "required": false } ], "outputs": [ { "key": "json", "type": "object", "description": "Built approve transaction data (to, data, value, chainId)" }, { "key": "encoded", "type": "string", "description": "Built approve transaction as encoded serialized transaction" } ], "groupId": 0, "canExecuteInParallel": false, "isUnsignedTransaction": true } ], "stepGroups": [ [ 0 ] ] }, "meta": { "time": 34, "requestId": "e94c3511-5efe-4c3d-aa0f-38d427c3afa0" } } ``` * `action` — operation type; in this case `approvePermit2`. * `steps` — schema with the list of steps describing which transactions you have to construct using the [Craft Transaction Step](/reference/craft-transaction-step) endpoint. * `index` — step number in the execution order. * `name` — transaction type corresponding to the `stepName` request parameter of the [Craft Transaction Step](/reference/craft-transaction-step) endpoint. * `description` — detailed information on the step purpose. * `inputs` — list of request parameters required to construct a transaction, e.g., wallet address, amount of tokens, etc. It may include transactions that require a signature as well. * `key` — parameter name. * `type` — parameter object format. * `description` — detailed information on the parameter. * `required` — boolean flag indicating if this parameter is necessary for the request. * `outputs` — list of fields expected in the transaction response. * `key` — raw transaction payload ready for signing, either `encoded` or `json` (optional). * `type` — transaction payload format. * `description` — detailed information on the payload contents. * `groupID` — identifier of the group to which this step belongs. * `canExecuteInParallel` — boolean flag indicating if it is possible to perform this step asynchronously. By executing in parallel, you can initiate the construction of multiple transactions at the same time. * `true` — step can be performed asynchronously. * `false` — step can only be performed synchronously. * `isUnsignedTransaction` — boolean flag indicating if the result of the step is a transaction that must be signed and broadcasted. * `stepGroups` — groups of steps that can be executed in parallel; currently is not operational. * `meta` — API request attributes: * `time` — request execution time in ms. * `requestId` — unique identifier of the request. **Approval action summary** In this schema, the `steps` response array includes one step called `finalPermit2AllowanceTransaction`. It means that to approve token management, you have to prepare only one transaction and then sign and broadcast it. ## 2. Create permission request for permit2 contract 1. To allow Morpho's [Permit2 contract](https://docs.uniswap.org/contracts/permit2/overview) to perform any operations with the specified amount of tokens, send a POST request to [/api/defi/v1/transactions/steps/craft](/reference/craft-transaction-step). Example request (for `base` chain and `Steakhouse USDC` strategy): ```bash bash theme={null} curl --request GET \ --url GET 'https://edge.p2p.org/api/defi/v1/transactions/steps/craft' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "chain": "base", "action": "approvePermit2", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x0000000000000000000000000000000000000001", "options": { "executionMode": "sync" }, "stepName": "finalPermit2AllowanceTransaction", "payload": { "asset": "USDC", "amount": "10000" } } ' ``` * `action` — operation type. * `chain` — blockchain network. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `options.executeMode` — DeFi API processing mode. The recommended value for each step can be found in the schema's `canExecuteInParallel` field. * `sync` — synchronous; default for some networks so that the blockchain does not reverse the block in which the transaction is included. In this mode, before continuing with other steps, the user has to wait until the API operation is fully completed and the transaction is returned. * `async` — asynchronous. In this mode, after sending the request, the user can continue with other steps according to the schema and periodically check the request status with the [Get Step Crafting Status](/reference/get-step-status) endpoint. * `stepName` — name of the transaction to construct. In this case, it is `finalPermit2AllowanceTransaction`. * `payload` — transaction data: * `amount` — amount of tokens the permit2 contract is allowed to interact with. You can specify any amount, but not less than the deposit amount. * `asset` — token denomination, e.g., `USDC`. Example response: ```json JSON theme={null} { "error": null, "result": { "operationId": "mock-operation-id-permit2-abc123", "chain": "base", "action": "approvePermit2", "status": "READY_FOR_SIGN", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x0000000000000000000000000000000000000001", "stepList": [ { "index": 0, "type": "transaction", "name": "finalPermit2AllowanceTransaction", "description": "Approve token for Permit2 (ERC20 approve to max uint256 for PERMIT2 spender)", "payload": "****", "verifierMeta": { "signature": "0x0000000000000000000000000000000000000000000000000000000000000000" } } ], "payload": "****", "verifierMeta": { "signature": "0x0000000000000000000000000000000000000000000000000000000000000000" }, "createdAt": "2026-02-13T12:00:00.000Z", "updatedAt": "2026-02-13T12:00:00.000Z" }, "meta": { "time": 100, "requestId": "mock-request-id-permit2-xyz789" } } ``` * `operationId` — unique identifier of the transaction. * `chain` — blockchain network. * `action` — operation type. * `status` — current transaction status: `AWAITING_CRAFT`, `IN_PROGRESS`, `READY_FOR_SIGN` or `DONE`. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `stepList` — list of fields with the step results obtained according to the schema: * `index` — step number in the execution order. * `type` — object format. * `name` — name of the transaction to construct. In this case, it is `finalPermit2AllowanceTransaction`. * `description` — detailed information on the step purpose. * `payload` — raw transaction object ready for signing represented in two formats: `encoded` and `json` (optional). * `verifierMeta` — data object received from P2P.ORG's verifier module. * `signature` — signature provided by the verifier module, which confirms that the transaction was crafted and then cryptographically verified by P2P.ORG. * `payload` — final transaction payload represented either in `encoded` or `json` (optional) format. * `verifierMeta` — final transaction data object received from the verifier module. * `createdAt` — timestamp of the transaction creation in the ISO 8601 format. * `updatedAt` — timestamp of the transaction update in the ISO 8601 format. * `meta` — API request attributes: * `time` — request execution time in ms. * `requestId` — unique identifier of the request. 2. [Sign](/docs/defi-api-signing-transaction#for-final-transactions) the raw `json` or `encoded` object in the `result.payload` field. 3. [Broadcast](/docs/defi-api-signing-transaction#3-broadcast-signed-final-transaction) it to the network by sending a POST request to [/api/defi/v1/transactions/steps/broadcast](/reference/broadcast-transaction). By broadcasting this transaction, you're granting permission to the permit2 smart contract to interact with the specified amount of tokens at your wallet address. ## 3. Get schema for deposit The deposit action may require a various number of steps depending on the target chain and vault. To retrieve a list of steps for the preferred strategy, send a GET request to [/api/defi/v1/transactions/schema](/reference/get-transaction-schema). Example request (for `base` chain and `Steakhouse USDC` strategy): ```bash bash theme={null} curl --request GET \ --url GET 'https://edge.p2p.org/api/defi/v1/transactions/schema?action=deposit&chain=base&protocol=morpho&strategy=Steakhouse%20USDC' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` * `action` — operation type; in this case `deposit`. * `chain` — blockchain network. * `protocol`— integration name. * `strategy` — vault name. * `x-client-hash` — unique identifier of the user or merchant participating in the integration; obtained with the authentication token. Example response: ```json JSON theme={null} { "error": null, "result": { "action": "deposit", "steps": [ { "index": 0, "name": "permit2SingleDetailsForMorpho", "description": "Prepare Permit2 SingleDetails for Morpho bundler", "inputs": [ { "key": "amount", "type": "string", "description": "Deposit amount in token smallest unit", "required": true }, { "key": "asset", "type": "string", "description": "Asset symbol (e.g., USDC, USDT)", "required": true } ], "outputs": [ { "key": "json", "type": "object", "description": "Permit2 SingleDetails data (amountInTokens, spenderAddress, sigDeadline, nonce, chainId, tokenContractAddress)" }, { "key": "encoded", "type": "string", "description": "Built Permit2 SingleDetails for Morpho bundler as hashed typed data" } ], "groupId": 0, "canExecuteInParallel": false }, { "index": 1, "name": "permit2SingleDetailsForP2pProxy", "description": "Prepare Permit2 SingleDetails for P2P Proxy", "inputs": [ { "key": "amount", "type": "string", "description": "Deposit amount in token smallest unit", "required": true }, { "key": "asset", "type": "string", "description": "Asset symbol (e.g., USDC, USDT)", "required": true }, { "key": "operationId", "type": "string", "description": "Encrypted transaction ID (operationId) from previous step (step 0)", "required": true } ], "outputs": [ { "key": "json", "type": "object", "description": "Permit2 SingleDetails data (amountInTokens, spenderAddress, sigDeadline, nonce, chainId, tokenContractAddress)" }, { "key": "encoded", "type": "string", "description": "Built Permit2 SingleDetails for P2P Proxy as hashed typed data" } ], "groupId": 0, "canExecuteInParallel": false }, { "index": 2, "name": "finalDepositTransaction", "description": "Create final deposit transaction", "inputs": [ { "key": "amount", "type": "string", "description": "Deposit amount in token smallest unit", "required": true }, { "key": "asset", "type": "string", "description": "Asset symbol (e.g., USDC, USDT)", "required": true }, { "key": "permit2SignatureForMorpho", "type": "string", "description": "Signed Permit2 SingleDetails for Morpho bundler (from step 0)", "required": true, "requiresSignature": true, "signatureSource": { "stepIndex": 0, "stepName": "permit2SingleDetailsForMorpho", "outputKey": "encoded" } }, { "key": "permit2SignatureForP2pProxy", "type": "string", "description": "Signed Permit2 SingleDetails for P2P Proxy (from step 1)", "required": true, "requiresSignature": true, "signatureSource": { "stepIndex": 1, "stepName": "permit2SingleDetailsForP2pProxy", "outputKey": "encoded" } }, { "key": "operationId", "type": "string", "description": "Encrypted transaction ID (operationId) from previous step (step 0)", "required": true } ], "outputs": [ { "key": "json", "type": "object", "description": "Built deposit transaction data (to, data, value, chainId)" }, { "key": "encoded", "type": "string", "description": "Built deposit transaction as encoded function data" } ], "inputReferences": [ { "stepIndex": 0, "outputKey": "encoded" }, { "stepIndex": 1, "outputKey": "encoded" }, { "stepIndex": 0, "outputKey": "operationId" } ], "groupId": 1, "canExecuteInParallel": false } ], "stepGroups": [ [ 0, 1 ], [ 2 ] ] }, "meta": { "time": 13, "requestId": "6b1c6203-e455-4093-ab3e-271127be16a5" } } ``` * `action` — operation type. * `steps` — schema with the list of steps describing which transactions you have to construct using the [Craft Transaction Step](/reference/craft-transaction-step) endpoint. * `index` — step number in the execution order. * `name` — transaction type corresponding to the `stepName` request parameter of the [Craft Transaction Step](/reference/craft-transaction-step) endpoint. * `description` — detailed information on the step purpose. * `inputs` — list of request parameters required to construct a transaction, e.g., wallet address, amount of tokens, etc. It may include transactions that require a signature as well. * `key` — parameter name. * `type` — parameter object format. * `description` — detailed information on the parameter. * `required` — boolean flag indicating if this parameter is necessary for the request. * `requiresSignature` — boolean flag indicating if the parameter is a transaction itself that must be signed. * `signatureSource` — detailed information on the step at which the transaction requiring a signature was received: * `stepIndex` — step number. * `stepName` — transaction type. * `outputKey` — resulting object format. * `outputs` — list of fields expected in the transaction response for the specific step. * `key` — raw transaction payload ready for signing. * `type` — object format, which is either `encoded` or `json` (optional). * `description` — detailed information on the payload contents. * `groupID` — identifier of the group to which this step belongs. * `canExecuteInParallel` — boolean flag indicating if it is possible to perform this step asynchronously. By executing in parallel, you can initiate the construction of multiple transactions at the same time. * `true` — step can be performed asynchronously. * `false` — step can only be performed synchronously. * `inputReferences` — references to `key` from previous steps, either from the request or response. * `stepIndex` — step number. * `outputKey` — name of the specific `key` parameter from the step output. * `stepGroups` — groups of steps that can be executed in parallel; currently is not operational. * `meta` — API request attributes: * `time` — request execution time in ms. * `requestId` — unique identifier of the request. **Deposit action summary** In this schema, the `steps` response array includes three steps ordered from 0 to 2. It means that the process of depositing assets to a vault requires three actions: 1. Allow transferring funds from the user wallet to the P2P proxy contract address. 2. Allow transferring funds from the P2P proxy contract address to the vault address. 3. Combine the two previous transactions to obtain the raw deposit transaction. For each step, a signature is required. The third and final transaction must have the payloads of the previous two. To complete the deposit action, broadcast the final transaction to the network. Accordingly, these transactions can be constructed only consequently, since the value of the `canExecuteInParallel` parameter is `false`. ## 4. Create permission request for the vault 1. Construct the first intermediate `permit2SingleDetailsForMorpho` transaction according to the schema. To allow the Morpho bundler to withdraw assets from the P2P proxy address to the vault address, send a POST request to [/api/defi/v1/transactions/steps/craft](/reference/craft-transaction-step). Example request (for `base` chain and `Steakhouse USDC` strategy): ```bash bash theme={null} curl --request GET \ --url GET 'https://edge.p2p.org/api/defi/v1/transactions/steps/craft' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "chain": "base", "action": "deposit", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x991c468AbcE2b4DD627a6210C145373EbABdd186", "options": { "executionMode": "sync" }, "stepName": "permit2SingleDetailsForMorpho", "payload": { "amount": "10000", "asset": "USDC" } }' ``` * `chain` — blockchain network. * `action` — operation type. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `options.executeMode` — DeFi API processing mode. The recommended value for each step can be found in the schema's `canExecuteInParallel` field. * `stepName` — step name corresponding to the `name` response parameter of the [Get Transaction Step Schema](/reference/get-transaction-schema) endpoint. In this case, it is `permit2SingleDetailsForMorpho`. * `payload` — transaction data: * `amount` — amount of tokens to deposit. * `asset` — token denomination, e.g., `USDC`. Example response: ```json JSON theme={null} { "error": null, "result": { "operationId": "iR4WV/aWs1Ll+SiOObmZdONXggOufzS5kEYdyxmZMWd8gb6Iu9KILvUWCbatR75x", "chain": "base", "action": "deposit", "status": "IN_PROGRESS", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x991c468AbcE2b4DD627a6210C145373EbABdd186", "stepList": [ { "index": 0, "type": "signature_data", "name": "permit2SingleDetailsForMorpho", "description": "Prepare Permit2 SingleDetails for Morpho bundler", "payload": { "json": { "domain": { "name": "Permit2", "chainId": 8453, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "types": { "PermitDetails": [ { "name": "token", "type": "address" }, { "name": "amount", "type": "uint160" }, { "name": "expiration", "type": "uint48" }, { "name": "nonce", "type": "uint48" } ], "PermitSingle": [ { "name": "details", "type": "PermitDetails" }, { "name": "spender", "type": "address" }, { "name": "sigDeadline", "type": "uint256" } ] }, "primaryType": "PermitSingle", "message": { "details": { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "10000", "expiration": "281474976710655", "nonce": 0 }, "spender": "0x23055618898e202386e6c13955a58D3C68200BFB", "sigDeadline": "1767173669" } }, "encoded": "0x8675b0c734ce70a49ffcef60f6cbffd3c747091ccf99c783c0fdbec686b29c11" }, "verifierMeta": { "signature": "0x1942c3fdf677c44a333f121e9105d3930a9aac60f393094dba29c9aaa1e230cb" }, "outputKeys": [ "json", "encoded" ] }, null, null ], "payload": null, "verifierMeta": null, "createdAt": "2025-12-24T09:34:29.324Z", "updatedAt": "2025-12-24T09:49:53.551Z" }, "meta": { "time": 3212, "requestId": "25ae64b3-44d0-4bfe-b583-78717ba5581b" } } ``` * `operationId` — unique identifier of the transaction; required for the next steps. * `chain` — blockchain network. * `action` — operation type. * `status` — current transaction status: `AWAITING_CRAFT`, `IN_PROGRESS`, `READY_FOR_SIGN` or `DONE`. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `stepList` — list of fields with the step results obtained according to the schema. > For schemes with multiple steps: if a step has not been performed yet, the field will have `null`. In this example, the response contains data for the step 0 only. * `index` — step number in the execution order. * `type` — object format. * `name` — step name corresponding to the `name` response field of the [Get Transaction Step Schema](/reference/get-transaction-schema) endpoint. * `description` — detailed information on the step purpose. * `payload` — raw transaction object ready for signing represented in two formats: `encoded` and `json` (optional). * `verifierMeta` — data object received from P2P.ORG's verifier module. * `signature` — signature provided by the verifier module, which confirms that the transaction was crafted and then cryptographically verified by P2P.ORG. * `payload` — final transaction object. `null` until there are schema steps yet to be performed. * `verifierMeta` — final transaction data object received from the verifier module. `null` until there are schema steps yet to be performed. * `createdAt` — timestamp of the transaction creation in the ISO 8601 format. * `updatedAt` — timestamp of the transaction update in the ISO 8601 format. * `meta` — API request attributes: * `time` — request execution time in ms. * `requestId` — unique identifier of the request. 2. [Sign](/docs/defi-api-signing-transaction#for-intermediate-transaction) the intermediate `json` or `encoded` transaction object from the step 0 `payload` field. ## 5. Create permission request for P2P.ORG smart contract 1. Construct the second `permit2SingleDetailsForP2pProxy` intermediate transaction according to the schema. To allow a P2P proxy contract to withdraw assets from your wallet address, send a POST request to [/api/defi/v1/transactions/steps/craft](/reference/craft-transaction-step). Mind specifying the correct `stepName` according to the scheme's step order as well as the `operationID` of the previous transaction. Example request (for `base` chain and `Steakhouse USDC` strategy): ```bash bash theme={null} curl --request GET \ --url GET 'https://edge.p2p.org/api/defi/v1/transactions/steps/craft' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "chain": "base", "action": "deposit", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x991c468AbcE2b4DD627a6210C145373EbABdd186", "options": { "executionMode": "sync" }, "stepName": "permit2SingleDetailsForP2pProxy", "payload": { "amount": "10000", "asset": "USDC", "operationId": "iR4WV/aWs1Ll+SiOObmZdONXggOufzS5kEYdyxmZMWd8gb6Iu9KILvUWCbatR75x" } }' ``` * `chain` — blockchain network. * `action` — operation type. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `options.executeMode` — DeFi API request processing mode. The recommended value for each step can be found in the schema's `canExecuteInParallel` field. * `stepName` — step name corresponding to the `name` response parameter of the [Get Transaction Step Schema](/reference/get-transaction-schema) endpoint. In this case, it is `permit2SingleDetailsForP2pProxy`. * `payload` — transaction data: * `amount` — amount of tokens to deposit. * `asset` — token denomination, e.g., `USDC`. * `operationId` — unique identifier of the `permit2SingleDetailsForMorpho` transaction. Example response: ```json JSON theme={null} { "error": null, "result": { "operationId": "iR4WV/aWs1Ll+SiOObmZdONXggOufzS5kEYdyxmZMWd8gb6Iu9KILvUWCbatR75x", "chain": "base", "action": "deposit", "status": "IN_PROGRESS", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x991c468AbcE2b4DD627a6210C145373EbABdd186", "stepList": [ { "index": 0, "type": "signature_data", "name": "permit2SingleDetailsForMorpho", "description": "Prepare Permit2 SingleDetails for Morpho bundler", "payload": { "json": { "domain": { "name": "Permit2", "chainId": 8453, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "types": { "PermitDetails": [ { "name": "token", "type": "address" }, { "name": "amount", "type": "uint160" }, { "name": "expiration", "type": "uint48" }, { "name": "nonce", "type": "uint48" } ], "PermitSingle": [ { "name": "details", "type": "PermitDetails" }, { "name": "spender", "type": "address" }, { "name": "sigDeadline", "type": "uint256" } ] }, "primaryType": "PermitSingle", "message": { "details": { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "10000", "expiration": "281474976710655", "nonce": 0 }, "spender": "0x23055618898e202386e6c13955a58D3C68200BFB", "sigDeadline": "1767173669" } }, "encoded": "0x8675b0c734ce70a49ffcef60f6cbffd3c747091ccf99c783c0fdbec686b29c11" }, "verifierMeta": { "signature": "0x1942c3fdf677c44a333f121e9105d3930a9aac60f393094dba29c9aaa1e230cb" }, "outputKeys": [ "json", "encoded" ] }, { "index": 1, "type": "signature_data", "name": "permit2SingleDetailsForP2pProxy", "description": "Prepare Permit2 SingleDetails for P2P Proxy", "payload": { "json": { "domain": { "name": "Permit2", "chainId": 8453, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "types": { "PermitDetails": [ { "name": "token", "type": "address" }, { "name": "amount", "type": "uint160" }, { "name": "expiration", "type": "uint48" }, { "name": "nonce", "type": "uint48" } ], "PermitSingle": [ { "name": "details", "type": "PermitDetails" }, { "name": "spender", "type": "address" }, { "name": "sigDeadline", "type": "uint256" } ] }, "primaryType": "PermitSingle", "message": { "details": { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "10000", "expiration": "281474976710655", "nonce": 0 }, "spender": "0x9A01117B3AAB083C93cdd67F9ab4359cB21FE94E", "sigDeadline": "1767173669" } }, "encoded": "0xcb57b7905a95cdeea99e1ad7c2a95f079aefe1eb951c4c7b6c14bc72c7aae6c5" }, "verifierMeta": { "signature": "0x1d07766debe8ff9f44d04023ded4e2d1ff750109e3975a6f54ac1697db28d8de" }, "outputKeys": [ "json", "encoded" ] }, null ], "payload": null, "verifierMeta": null, "createdAt": "2025-12-24T09:34:29.324Z", "updatedAt": "2025-12-24T09:49:53.551Z" }, "meta": { "time": 3212, "requestId": "25ae64b3-44d0-4bfe-b583-78717ba5581b" } } ``` * `operationId` — unique identifier of the `permit2SingleDetailsForMorpho` transaction. * `chain` — blockchain network. * `action` — operation type. * `status` — current transaction status: `AWAITING_CRAFT`, `IN_PROGRESS`, `READY_FOR_SIGN` or `DONE`. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `stepList` — list of fields with the step results obtained according to the schema. > For schemes with multiple steps: if a step has not been performed yet, the field will have `null`. In this example, the response contains data for steps 0 and 1. * `index` — step number in the execution order. * `type` — parameter object format. * `name` — step name corresponding to the `name` response field of the [Get Transaction Step Schema](/reference/get-transaction-schema) endpoint. * `description` — detailed information on the step purpose. * `payload` — raw transaction object ready for signing represented in two formats: `encoded` and `json` (optional). * `verifierMeta` — data object received from P2P.ORG's verifier module. * `signature` — signature provided by the verifier module, which confirms that the transaction was crafted and then cryptographically verified by P2P.ORG. * `payload` — final transaction object. `null` until there are schema steps yet to be performed. * `verifierMeta` — final transaction data object received from the verifier module. `null` until there are schema steps yet to be performed. * `createdAt` — timestamp of the transaction creation in the ISO 8601 format. * `updatedAt` — timestamp of the transaction update in the ISO 8601 format. * `meta` — API request attributes: * `time` — request execution time in ms. * `requestId` — request identifier; used as a correlating ID in logs. 2. [Sign](/docs/defi-api-signing-transaction#for-intermediate-transaction) the intermediate `json` or `encoded` transaction object from the step 1 `payload` field. ## 6. Prepare final deposit transaction Construct the third `finalDepositTransaction` transaction according to the schema. To prepare the final deposit transaction, send a POST request to [/api/defi/v1/transactions/steps/craft](/reference/craft-transaction-step). Mind specifying the correct `stepName` according to the scheme's step order and the `operationID` from the step 0. Example request (for `base` chain and `Steakhouse USDC` strategy): ```bash bash theme={null} curl --request GET \ --url GET 'https://edge.p2p.org/api/defi/v1/transactions/steps/craft' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "chain": "base", "action": "deposit", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x991c468AbcE2b4DD627a6210C145373EbABdd186", "options": { "executionMode": "sync" }, "stepName": "finalDepositTransaction", "payload": { "amount": "10000", "asset": "USDC", "operationId": "iR4WV/aWs1Ll+SiOObmZdONXggOufzS5kEYdyxmZMWd8gb6Iu9KILvUWCbatR75x", "permit2SignatureForMorpho": "0x831ad90a76e7f88fda3d35d454ecc6482c456cd180d1243d60b53286e5a41400239b17261d50bd79f301e177a5243ebee57a24728039b03d7cc76cb3b101068f1c", "permit2SignatureForP2pProxy": "0xf4de5fcf3866909788c28a69cfad101a602c77f81da47ad4c8e32d28a93703452944eb65d6a13a2522ec4de30e42bdfb44e983dd3d7b25fa27d60dbf1a377f181b" } }' ``` * `chain` — blockchain network. * `action` — operation type. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `options.executeMode` — DeFi API request processing mode. The recommended value for each step can be found in the schema's `canExecuteInParallel` field. * `stepName` — step name corresponding to the `name` response parameter of the [Get Transaction Step Schema](/reference/get-transaction-schema) endpoint. In this case, it is `finalDepositTransaction`. * `payload` — transaction data: * `amount` — amount of tokens to deposit. * `asset` — token denomination, e.g., `USDC`. * `operationId` — unique identifier of the `permit2SingleDetailsForMorpho` transaction. * `permit2SignatureForMorpho` — signed transaction obtained at step 0. * `permit2SignatureForP2pProxy` — signed transaction obtained at step 1. Example response: ```json JSON theme={null} { "error": null, "result": { "operationId": "iR4WV/aWs1Ll+SiOObmZdONXggOufzS5kEYdyxmZMWd8gb6Iu9KILvUWCbatR75x", "chain": "base", "action": "deposit", "status": "READY_FOR_SIGN", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x991c468AbcE2b4DD627a6210C145373EbABdd186", "stepList": [ { "index": 0, "type": "signature_data", "name": "permit2SingleDetailsForMorpho", "description": "Prepare Permit2 SingleDetails for Morpho bundler", "payload": { "json": { "domain": { "name": "Permit2", "chainId": 8453, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "types": { "PermitDetails": [ { "name": "token", "type": "address" }, { "name": "amount", "type": "uint160" }, { "name": "expiration", "type": "uint48" }, { "name": "nonce", "type": "uint48" } ], "PermitSingle": [ { "name": "details", "type": "PermitDetails" }, { "name": "spender", "type": "address" }, { "name": "sigDeadline", "type": "uint256" } ] }, "primaryType": "PermitSingle", "message": { "details": { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "10000", "expiration": "281474976710655", "nonce": 0 }, "spender": "0x23055618898e202386e6c13955a58D3C68200BFB", "sigDeadline": "1767173669" } }, "encoded": "0x8675b0c734ce70a49ffcef60f6cbffd3c747091ccf99c783c0fdbec686b29c11" }, "verifierMeta": { "signature": "0x1942c3fdf677c44a333f121e9105d3930a9aac60f393094dba29c9aaa1e230cb" }, "outputKeys": [ "json", "encoded" ] }, { "index": 1, "type": "signature_data", "name": "permit2SingleDetailsForP2pProxy", "description": "Prepare Permit2 SingleDetails for P2P Proxy", "payload": { "json": { "domain": { "name": "Permit2", "chainId": 8453, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "types": { "PermitDetails": [ { "name": "token", "type": "address" }, { "name": "amount", "type": "uint160" }, { "name": "expiration", "type": "uint48" }, { "name": "nonce", "type": "uint48" } ], "PermitSingle": [ { "name": "details", "type": "PermitDetails" }, { "name": "spender", "type": "address" }, { "name": "sigDeadline", "type": "uint256" } ] }, "primaryType": "PermitSingle", "message": { "details": { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "10000", "expiration": "281474976710655", "nonce": 0 }, "spender": "0x9A01117B3AAB083C93cdd67F9ab4359cB21FE94E", "sigDeadline": "1767173669" } }, "encoded": "0xcb57b7905a95cdeea99e1ad7c2a95f079aefe1eb951c4c7b6c14bc72c7aae6c5" }, "verifierMeta": { "signature": "0x1d07766debe8ff9f44d04023ded4e2d1ff750109e3975a6f54ac1697db28d8de" }, "outputKeys": [ "json", "encoded" ] }, { "index": 2, "type": "transaction", "name": "finalDepositTransaction", "description": "Create final deposit transaction", "payload": { "json": { "domain": { "name": "Morpho", "chainId": 8453, "verifyingContract": "0x23055618898e202386e6c13955a58D3C68200BFB" }, "types": { "Deposit": [ { "name": "bundlerAddress", "type": "address" }, { "name": "multicallData", "type": "bytes" }, { "name": "p2pProxyPermitSingle", "type": "bytes" }, { "name": "permit2SignatureForP2pProxy", "type": "bytes" }, { "name": "feeInBasisPoints", "type": "uint256" }, { "name": "feeSignatureDeadline", "type": "uint256" }, { "name": "feeSignature", "type": "bytes" } ] }, "primaryType": "Deposit", "message": { "bundlerAddress": "0x23055618898e202386e6c13955a58D3C68200BFB", "multicallData": "0xac9650d8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000184af504202000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000ffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023055618898e202386e6c13955a58d3c68200bfb000000000000000000000000000000000000000000000000000000006954ee25000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041831ad90a76e7f88fda3d35d454ecc6482c456cd180d1243d60b53286e5a41400239b17261d50bd79f301e177a5243ebee57a24728039b03d7cc76cb3b101068f1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004454c53ef0000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000846ef5eeae000000000000000000000000beef010f9cb27031ad51e3333f9af9c6b122818300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000020f8ebb4ebb46f0000000000000000000000009a01117b3aab083c93cdd67f9ab4359cb21fe94e00000000000000000000000000000000000000000000000000000000", "p2pProxyPermitSingle": "0x000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000ffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a01117b3aab083c93cdd67f9ab4359cb21fe94e000000000000000000000000000000000000000000000000000000006954ee25", "permit2SignatureForP2pProxy": "0xf4de5fcf3866909788c28a69cfad101a602c77f81da47ad4c8e32d28a93703452944eb65d6a13a2522ec4de30e42bdfb44e983dd3d7b25fa27d60dbf1a377f181b", "feeInBasisPoints": "10000", "feeSignatureDeadline": "1767173669", "feeSignature": "0x0f6bbd829715c699631306b3526a7b54de1a20a1ae8f98e80b0800012887c6c1762226b7999f019399f7b68f70cdafb9779bfa23bda3348f66d0da76f6befc7c1b" } }, "encoded": "0xa6c26af400000000000000000000000023055618898e202386e6c13955a58d3c68200bfb0000000000000000000000000000000000000000000000000000000000000180000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000ffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a01117b3aab083c93cdd67f9ab4359cb21fe94e000000000000000000000000000000000000000000000000000000006954ee2500000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000006954ee2500000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000003a4ac9650d8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000184af504202000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000ffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023055618898e202386e6c13955a58d3c68200bfb000000000000000000000000000000000000000000000000000000006954ee25000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041831ad90a76e7f88fda3d35d454ecc6482c456cd180d1243d60b53286e5a41400239b17261d50bd79f301e177a5243ebee57a24728039b03d7cc76cb3b101068f1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004454c53ef0000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000846ef5eeae000000000000000000000000beef010f9cb27031ad51e3333f9af9c6b122818300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000020f8ebb4ebb46f0000000000000000000000009a01117b3aab083c93cdd67f9ab4359cb21fe94e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f4de5fcf3866909788c28a69cfad101a602c77f81da47ad4c8e32d28a93703452944eb65d6a13a2522ec4de30e42bdfb44e983dd3d7b25fa27d60dbf1a377f181b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410f6bbd829715c699631306b3526a7b54de1a20a1ae8f98e80b0800012887c6c1762226b7999f019399f7b68f70cdafb9779bfa23bda3348f66d0da76f6befc7c1b00000000000000000000000000000000000000000000000000000000000000" }, "verifierMeta": { "signature": "0x7ef4832cc53ca9f909eee4a56ced5314de5ec23ff5074ba4b7a95932ab7252d5" }, "inputReferences": [ { "stepIndex": 0, "outputKey": "encoded" }, { "stepIndex": 1, "outputKey": "encoded" }, { "stepIndex": 0, "outputKey": "operationId" } ], "outputKeys": [ "json", "encoded" ] } ], "payload": { "json": { "domain": { "name": "Morpho", "chainId": 8453, "verifyingContract": "0x23055618898e202386e6c13955a58D3C68200BFB" }, "types": { "Deposit": [ { "name": "bundlerAddress", "type": "address" }, { "name": "multicallData", "type": "bytes" }, { "name": "p2pProxyPermitSingle", "type": "bytes" }, { "name": "permit2SignatureForP2pProxy", "type": "bytes" }, { "name": "feeInBasisPoints", "type": "uint256" }, { "name": "feeSignatureDeadline", "type": "uint256" }, { "name": "feeSignature", "type": "bytes" } ] }, "primaryType": "Deposit", "message": { "bundlerAddress": "0x23055618898e202386e6c13955a58D3C68200BFB", "multicallData": "0xac9650d8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000184af504202000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000ffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023055618898e202386e6c13955a58d3c68200bfb000000000000000000000000000000000000000000000000000000006954ee25000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041831ad90a76e7f88fda3d35d454ecc6482c456cd180d1243d60b53286e5a41400239b17261d50bd79f301e177a5243ebee57a24728039b03d7cc76cb3b101068f1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004454c53ef0000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000846ef5eeae000000000000000000000000beef010f9cb27031ad51e3333f9af9c6b122818300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000020f8ebb4ebb46f0000000000000000000000009a01117b3aab083c93cdd67f9ab4359cb21fe94e00000000000000000000000000000000000000000000000000000000", "p2pProxyPermitSingle": "0x000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000ffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a01117b3aab083c93cdd67f9ab4359cb21fe94e000000000000000000000000000000000000000000000000000000006954ee25", "permit2SignatureForP2pProxy": "0xf4de5fcf3866909788c28a69cfad101a602c77f81da47ad4c8e32d28a93703452944eb65d6a13a2522ec4de30e42bdfb44e983dd3d7b25fa27d60dbf1a377f181b", "feeInBasisPoints": "10000", "feeSignatureDeadline": "1767173669", "feeSignature": "0x0f6bbd829715c699631306b3526a7b54de1a20a1ae8f98e80b0800012887c6c1762226b7999f019399f7b68f70cdafb9779bfa23bda3348f66d0da76f6befc7c1b" } }, "encoded": "0xa6c26af400000000000000000000000023055618898e202386e6c13955a58d3c68200bfb0000000000000000000000000000000000000000000000000000000000000180000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000ffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a01117b3aab083c93cdd67f9ab4359cb21fe94e000000000000000000000000000000000000000000000000000000006954ee2500000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000006954ee2500000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000003a4ac9650d8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000184af504202000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000ffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023055618898e202386e6c13955a58d3c68200bfb000000000000000000000000000000000000000000000000000000006954ee25000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041831ad90a76e7f88fda3d35d454ecc6482c456cd180d1243d60b53286e5a41400239b17261d50bd79f301e177a5243ebee57a24728039b03d7cc76cb3b101068f1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004454c53ef0000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000846ef5eeae000000000000000000000000beef010f9cb27031ad51e3333f9af9c6b122818300000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000020f8ebb4ebb46f0000000000000000000000009a01117b3aab083c93cdd67f9ab4359cb21fe94e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f4de5fcf3866909788c28a69cfad101a602c77f81da47ad4c8e32d28a93703452944eb65d6a13a2522ec4de30e42bdfb44e983dd3d7b25fa27d60dbf1a377f181b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410f6bbd829715c699631306b3526a7b54de1a20a1ae8f98e80b0800012887c6c1762226b7999f019399f7b68f70cdafb9779bfa23bda3348f66d0da76f6befc7c1b00000000000000000000000000000000000000000000000000000000000000" }, "verifierMeta": { "signature": "0x7ef4832cc53ca9f909eee4a56ced5314de5ec23ff5074ba4b7a95932ab7252d5" }, "createdAt": "2025-12-24T09:34:29.324Z", "updatedAt": "2025-12-24T09:49:53.551Z" }, "meta": { "time": 3212, "requestId": "25ae64b3-44d0-4bfe-b583-78717ba5581b" } } ``` * `operationId` — unique identifier of the `permit2SingleDetailsForMorpho` transaction. * `chain` — blockchain network. * `action` — operation type. * `status` — current transaction status: `AWAITING_CRAFT`, `IN_PROGRESS`, `READY_FOR_SIGN` or `DONE`. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `stepList` — list of fields with the step results obtained according to the schema. * `index` — step number in the execution order. * `type` — parameter object format. * `name` — step name corresponding to the `name` response field of the [Get Transaction Step Schema](/reference/get-transaction-schema) endpoint. * `description` — detailed information on the step purpose. * `payload` — raw transaction object ready for signing represented in two formats: `encoded` and `json` (optional). * `verifierMeta` — data object received from P2P.ORG's verifier module. * `signature` — signature provided by the verifier module, which confirms that the transaction was crafted and then cryptographically verified by P2P.ORG. * `payload` — final deposit transaction payload represented either in `encoded` an optional `json` format. * `verifierMeta` — final transaction data object received from the verifier module. * `createdAt` — timestamp of the transaction creation in the ISO 8601 format. * `updatedAt` — timestamp of the transaction update in the ISO 8601 format. * `meta` — API request attributes: * `time` — request execution time in ms. * `requestId` — request identifier; used as a correlating ID in logs. ## 7. Sign and broadcast final deposit transaction The final deposit transaction now contains all the `payload` objects from the previous intermediate transactions. 1. [Sign](/docs/defi-api-signing-transaction#for-final-transactions) the `json` or `encoded` transaction object from the `result.payload` field. 2. [Broadcast](/docs/defi-api-signing-transaction#3-broadcast-signed-final-transaction) it to the network by sending a POST request to [/api/defi/v1/transactions/steps/broadcast](/reference/broadcast-transaction). By broadcasting this transaction, you're making the deposit to the Steakhouse USDC vault on the Base blockchain. # Withdrawal Source: https://docs.p2p.org/docs/morpho-withdrawal Withdraw from a Morpho vault using the P2P.org DeFi API. To initiate a withdrawal from a Morpho's vault using the DeFi API, follow these steps: 1. Retrieve the schema with the step list to check which transactions are required. 2. Prepare the withdrawal transaction. 3. Sign and broadcast the withdrawal transaction to the network. Both partial and full withdrawal are possible. Request examples are provided using [cURL](https://curl.se/). ## 1. Get schema To retrieve a list of steps required to perform a withdrawal, send a GET request to [/api/defi/v1/transactions/schema](/reference/get-transaction-schema). Example request (for `base` chain and `Steakhouse USDC` strategy): ```bash bash theme={null} curl --request GET \ --url GET 'https://edge.p2p.org/api/defi/v1/transactions/schema?action=withdraw&chain=base&protocol=morpho&strategy=Steakhouse%20USDC' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' ``` * `action` — operation type; in this case `withdraw`. * `chain` — blockchain network, e.g., `base`. * `protocol`— integration name, e.g., `morpho`. * `strategy` — vault name, e.g., `Steakhouse USDC`. * `x-client-hash` — unique identifier of the user or merchant participating in the integration; obtained with the [authentication token](/docs/authentication). Example response: ```json JSON theme={null} { "error": null, "result": { "action": "withdraw", "steps": [ { "index": 0, "name": "finalWithdrawTransaction", "description": "Create final withdraw transaction", "inputs": [ { "key": "amount", "type": "string", "description": "Withdraw amount in token smallest unit", "required": true }, { "key": "asset", "type": "string", "description": "Asset symbol (e.g., USDC, USDT)", "required": true }, { "key": "proxyAddress", "type": "string", "description": "P2P Proxy address", "required": true } ], "outputs": [ { "key": "json", "type": "object", "description": "Built withdraw transaction data (to, data, value, chainId)" }, { "key": "encoded", "type": "string", "description": "Built withdraw transaction as encoded function data" } ], "groupId": 0, "canExecuteInParallel": false, "isUnsignedTransaction": true } ], "stepGroups": [ [ 0 ] ] }, "meta": { "time": 18, "requestId": "5406b88f-b94d-4027-97cd-29af077cbef1" } } ``` * `action` — operation type. * `steps` — schema with the list of steps describing which transactions you have to construct using the [Craft Transaction Step](/reference/craft-transaction-step) endpoint. * `index` — step number in the execution order. * `name` — transaction type corresponding to the `stepName` request parameter of the [Craft Transaction Step](/reference/craft-transaction-step) endpoint. * `description` — detailed information on the step purpose. * `inputs` — list of request parameters required to construct a transaction, e.g., wallet address, amount of tokens, etc. It can include the transactions that require a signature as well. * `key` — parameter name. * `type` — parameter object format. * `description` — detailed information on the parameter. * `required` — boolean flag indicating if this parameter is necessary for the request. * `outputs` — list of fields expected in the transaction response. * `key` — raw transaction payload ready for signing which is `encoded` or an optional`json`. * `type` — transaction payload format. * `description` — detailed information on the payload contents. * `groupID` — identifier of the group to which this step belongs. * `canExecuteInParallel` — boolean flag indicating if it is possible to perform this step asynchronously. By executing in parallel, you can initiate the construction of multiple transactions at the same time. * `true` — step can be performed asynchronously. * `false` — step can only be performed synchronously. * `isUnsignedTransaction` — boolean flag indicating if the result of the step is a transaction that must be signed and broadcasted. * `stepGroups` — groups of steps that can be executed in parallel; currently is not operational. * `meta` — API request attributes: * `time` — request execution time in ms. * `requestId` — unique identifier of the request. **Summary** In this schema example, the `steps` response array includes one step called `finalWithdrawTransaction`. It means that to withdraw funds from the vault, you have to prepare only one transaction and then sign and broadcast it. ## 2. Prepare withdrawal transaction To withdraw funds from the vault, send a POST request to [/api/defi/v1/transactions/steps/craft](/reference/craft-transaction-step). Example request (for `base` chain and `Steakhouse USDC` strategy): ```bash bash theme={null} curl --request GET \ --url GET 'https://edge.p2p.org/api/defi/v1/transactions/steps/craft' \ --header 'accept: application/json' \ --header 'authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "chain": "base", "action": "withdraw", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x0000000000000000000000000000000000000001", "options": { "executionMode": "sync" }, "stepName": "finalWithdrawTransaction", "payload": { "amount": "100000", "asset": "USDC", "proxyAddress": "0x0000000000000000000000000000000000000001" } }' ``` * `chain` — blockchain network. * `action` — operation type. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `options.executeMode` — DeFi API request processing mode. The recommended value can be found in the schema's `canExecuteInParallel` field. * `sync` — synchronous; default for some networks so that the blockchain does not reverse the block in which the transaction is included. In this mode, before continuing with other steps, you have to wait until the API operation is fully completed and the transaction is returned. * `async` — asynchronous. In this mode, after sending the request, you can continue with other steps according to the schema and periodically check the request status with the [Get Step Crafting Status](/reference/get-step-status) endpoint. * `stepName` — step name corresponding to the `name` response field of the [Get Transaction Step Schema](/reference/get-transaction-schema) endpoint. In this case, it is `finalWithdrawTransaction`. * `payload` — transaction data: * `amount` — amount of tokens to withdraw. * `asset` — token denomination, e.g., `USDC`. * `proxyAddress` — P2P.ORG proxy address used to perform the deposit. To retrieve this address, send a GET response to the [Get User Positions](/reference/get-user-positions) endpoint. Example response: ```json JSON theme={null} { "error": null, "result": { "operationId": "mock-operation-id-abc123", "chain": "base", "action": "withdraw", "status": "READY_FOR_SIGN", "protocol": "morpho", "strategy": "Steakhouse USDC", "userAddress": "0x0000000000000000000000000000000000000001", "stepList": [ { "index": 0, "type": "transaction", "name": "finalWithdrawTransaction", "description": "Create final withdraw transaction", "payload": { "json": { "chainId": 8453, "to": "0x0000000000000000000000000000000000000002", "nonce": 0, "value": "0", "data": "0xdeadbeef", "maxFeePerGas": "1000000000", "maxPriorityFeePerGas": "100000000", "gas": "300000", "type": "eip1559" }, "encoded": "0x02f8..." }, "verifierMeta": { "signature": "0x0000000000000000000000000000000000000000000000000000000000000000" } } ], "payload": { "json": { "chainId": 8453, "to": "0x0000000000000000000000000000000000000002", "nonce": 0, "value": "0", "data": "0xdeadbeef", "maxFeePerGas": "1000000000", "maxPriorityFeePerGas": "100000000", "gas": "300000", "type": "eip1559" }, "encoded": "0x02f8..." }, "verifierMeta": { "signature": "0x0000000000000000000000000000000000000000000000000000000000000000" }, "createdAt": "2026-02-13T12:00:00.000Z", "updatedAt": "2026-02-13T12:00:00.000Z" }, "meta": { "time": 100, "requestId": "mock-request-id-xyz789" } } ``` * `operationId` — unique identifier of the transaction. * `chain` — blockchain network. * `action` — operation type. * `status` — current transaction status: `AWAITING_CRAFT`, `IN_PROGRESS`, `READY_FOR_SIGN` or `DONE`. * `protocol`— integration name. * `strategy` — vault name. * `userAddress` — user wallet address. * `stepList` — list of fields with the step results obtained according to the schema: * `index` — step number in the execution order. * `type` — field object format. * `name` — step name corresponding to the `name` response field of the [Get Transaction Step Schema](/reference/get-transaction-schema) endpoint. * `description` — detailed information on the step purpose. * `payload` — raw transaction object ready for signing represented in two formats: `encoded` and `json` (optional). * `verifierMeta` — data object received from P2P.ORG's verifier module. * `signature` — signature provided by the verifier module, which confirms that the transaction was crafted and then cryptographically verified by P2P.ORG. * `payload` — final transaction payload represented either in `encoded` or `json` (optional) format. * `verifierMeta` — final transaction data object received from the verifier module. * `createdAt` — timestamp of the transaction creation in the ISO 8601 format. * `updatedAt` — timestamp of the transaction update in the ISO 8601 format. * `meta` — API request attributes: * `time` — request execution time in ms. * `requestId` — unique identifier of the request. ## 3. Sign and broadcast transaction 1. [Sign](/docs/defi-api-signing-transaction#for-final-transactions) the final `json` or `encoded` transaction object from the `result.payload` field. 2. [Broadcast](/docs/defi-api-signing-transaction#3-broadcast-signed-final-transaction) it to the network by sending a POST request to [/api/defi/v1/transactions/steps/broadcast](/reference/broadcast-transaction). # Near Transaction Signing Source: https://docs.p2p.org/docs/near-signing-transaction Sign and broadcast Near transactions built with the P2P.org Unified API. To sign and broadcast the transaction to the Near network, follow these steps: 1. Prepare unsigned transaction in Base64 encrypted format. 2. Sign the transaction using the following code: ```javascript theme={null} import { KeyPair, Signer } from "near-api-js"; import { signTransaction, Transaction } from "near-api-js/lib/transaction"; import * as sha256 from "js-sha256"; import { KeyPairString, PublicKey } from "near-api-js/lib/utils"; // Private key (ensure this is securely stored) const PRIVATE_KEY = "ed25519:YOUR_PRIVATE_KEY_HERE"; // Unsigned transaction in hexadecimal format const RAW_TX_HEX = "YOUR_UNSIGNED_TRANSACTION_HEX"; // Create a signer instance using the private key function createSigner(privateKey: string): Signer { const keyPair = KeyPair.fromString(privateKey as KeyPairString); return { async getPublicKey() { return keyPair.getPublicKey(); }, async signMessage(message: Uint8Array) { const hash = new Uint8Array(sha256.sha256.array(message)); const signature = keyPair.sign(hash); return { signature: signature.signature, publicKey: keyPair.getPublicKey(), }; }, createKey(accountId: string, networkId?: string): Promise {}, }; } // Function to sign the transaction async function signTransactionWithSigner( tx: Transaction, signer: Signer ): Promise { const [, signedTx] = await signTransaction(tx, signer); const signedTxSerialized = signedTx.encode(); return Buffer.from(signedTxSerialized).toString("hex"); } // Process the transaction signing async function processTransaction() { if (!RAW_TX_HEX) { throw new Error("❌ Error: RAW_TX_HEX is empty. Insert your transaction!"); } // Create signer with private key const signer = createSigner(PRIVATE_KEY); // Decode transaction from hex format const tx = Transaction.decode(Buffer.from(RAW_TX_HEX, "hex")); // Sign the transaction const signedTxHex = await signTransactionWithSigner(tx, signer); console.log("✅ Signed Transaction (Hex):", signedTxHex); } // Execute signing process processTransaction().catch(console.error); ``` Upon successful execution, the script prints the signed transaction in the hexadecimal format, ready to be broadcasted: ```bash theme={null} ✅ Signed Transaction (Hex): 0x... ``` 3. Broadcast the transaction to the Near network by sending a POST request to [/api/v1/unified/transaction/broadcast](/reference/unified-transaction-send). # Network Source: https://docs.p2p.org/docs/network Staking Data API provides methods for retrieving various data related to the network. This section describes the available network methods along with descriptions and recommendations for their usage. ## Methods ### Get Supply Data **Endpoint:** [GET /api/v1/\{network}/data/network/supply-data](/reference/data-network-supply-data) **Supported networks:** Ethereum. **Description:** Retrieve network supply data, offering insights into the overall supply dynamics of the network. Utilize this endpoint to collect information on the network's supply and analyze the data to improve your comprehension of the network's economic state. ### Get Last Staking Period **Endpoint:** [GET /api/v1/\{network}/data/network/last-staking-period](/reference/data-network-last-staking-period) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, Celestia. **Description:** Retrieve information about the last staking period (epoch, era etc.) on the network. Utilize this endpoint to retrieve historical data on the network's staking periods and analyze the information to discern trends or alterations in staking behavior. ### Get Network APY **Endpoint:** [GET /api/v1/\{network}/data/network/apy](/reference/data-network-apy) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, Celestia. **Description:** Retrieve the Annual Percentage Yield (APY) for the network based on the specified staking period (epoch, era etc.). Utilize this endpoint to compute and track the APY for various staking periods, and grasp the fluctuations in network APY over time to facilitate well-informed staking decisions. ### Get Network APR **Endpoint:** [GET /api/v1/\{network}/data/network/apr](/reference/data-network-apr) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, Celestia. **Description:** Retrieve the Annual Percentage Rate (APR) for the network based on the specified staking period (epoch, era, etc.). Utilize this endpoint to compute and track the APR for various staking periods, and grasp the fluctuations in network APR over time to facilitate well-informed staking decisions. ### Get Validators Count **Endpoint:** [GET /api/v1/\{network}/data/network/validators-count](/reference/data-network-validator-count) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, Celestia. **Description:** Retrieve the count of validators on the network for the specified staking period (epoch, era etc.). Utilize this endpoint to oversee the count of participating validators in the network and assess validator count data for evaluations of network health. ### Get Delegators Count **Endpoint:** [GET /api/v1/\{network}/data/network/delegators-count](/reference/data-network-delegators-count) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, Celestia. **Description:** Retrieve the count of delegators on the network for the specified staking period (epoch, era etc.). Utilize this endpoint to evaluate the involvement of delegators in staking and analyze the data on delegators count to comprehend trends in user participation. ### Get Network Stake **Endpoint:** [GET /api/v1/\{network}/data/network/stake](/reference/data-network-stake) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, Celestia. **Description:** Retrieve information about the network's total stake for the specified staking period (epoch, era etc.). Utilize this endpoint to obtain data on the overall staking amount in the network. ### Get Network Rewards **Endpoint:** [GET /api/v1/\{network}/data/network/rewards](/reference/data-network-rewards) **Supported networks:** Ethereum, Solana, Polkadot, Kusama, Sui, Celestia. **Description:** Retrieve information about the network's rewards for the specified staking period (epoch, era etc.). Use this endpoint to retrieve data on rewards distributed across various staking periods, and analyze the reward data to comprehend the economic incentives within the network. ### Get Exit Queue **Endpoint:** [GET /api/v1/\{network}/data/network/exit-queue](/reference/data-network-exit-queue) **Supported networks:** Ethereum. **Description:** This method allows you to retrieve information in days about the network exit queue. The exit queue contains validators who have requested to exit the network, and they must wait for a certain period before safely exit. Utilize this endpoint to track validators within the exit queue and comprehend the waiting duration necessary for validators to safely exit. ### Get Staking Queue **Endpoint:** [GET /api/v1/\{network}/data/network/staking-queue](/reference/data-network-staking-queue) **Supported networks:** Ethereum. **Description:** Retrieve information about the network staking queue in days, which provides insights into the current staking activities on the network. Use this endpoint to evaluate staking activities and analyze the staking queue data to gain insights into the current state of network staking. ### Get Block Rewards **Endpoint:** [GET /api/v1/\{network}/data/network/block/rewards](/reference/data-network-block-rewards) **Supported networks:** Solana. **Description:** Retrieve Solana block rewards (block production fees, voting rewards, and staking rewards) by block date. Optional query parameters: * `address` — return only rewards paid to this recipient address. * `rewardType` — return only rewards of one type: `Fee`, `Voting`, `Staking`, or `DeactivatedStake`. If omitted, rewards of every type are returned. Each record contains `stakingPeriodNumber`, `blockDate`, `blockDateTime`, `recipient`, `rewardType`, `amount`, and `processingDateTime` (when the record was loaded into the data warehouse). # Chains Supported Source: https://docs.p2p.org/docs/networks-supported Check the list of chains supported P2P.org provides comprehensive staking and data services across multiple blockchain networks. This page provides an overview of network support across our different API products. ## Unified API & Staking API Support | Chains | Unified API | Staking API | | ---------------------- | ----------- | ----------- | | Aptos | ✅ | ✅ | | Cardano | ✅ | ❌ | | Celestia | ✅ | ✅ | | Cosmos (ATOM) | ✅ | ✅ | | Ethereum | ✅ | ✅ | | Hyperliquid | ✅ | ✅ | | Kusama | ✅ | ✅ | | Monad | ✅ | ✅ | | Near | ✅ | ❌ | | Polkadot | ✅ | ✅ | | Polygon (mainnet only) | ✅ | ✅ | | Sei | ✅ | ✅ | | Solana | ✅ | ✅ | | Sui | ✅ | ✅ | | Story Protocol | ✅ | ❌ | | Tezos | ✅ | ❌ | | The Graph | ✅ | ❌ | | TON | ✅ | ✅ | | TRON | ✅ | ✅ | ## Data API Support Our Data API provides comprehensive analytics across multiple networks with varying data scope and latency. For complete Data API coverage, visit our [Data API Chains Support](/docs/networks-supported-data-api) page. ## Additional API Products ### Restaking API Maximize yield through restaking capabilities. Currently supports EigenLayer on Ethereum. For more details, visit our [Restaking Overview](/docs/restaking-overview). ### DVT Staking API Distributed Validator Technology (DVT) for yield opportunities with enhanced security and reliability. Currently supports Ethereum through the SSV protocol. Learn more in our [DVT Solutions Overview](/docs/overview-dvt-solutions). ### Pooled Staking API Offer Ethereum staking to your users without the traditional 32 ETH minimum. For more details, visit our [Pooled Staking Overview](/docs/pooled-staking-overview). Need help? Our [team](/docs/contacts) is available to assist with your integration. # Chains Supported Source: https://docs.p2p.org/docs/networks-supported-data-api Check the list of chains supported | Chains | Network | Data scope | Time Aggregations | Historical Depth | Latency | | -------- | ------- | -------------------------------------------------- | ----------------- | ---------------- | --------- | | Ethereum | Mainnet | Network,
Validator,
Delegator | Epoch, Daily | No Limit | 2-4 hours | | Cardano | Mainnet | Validator,
Delegator (only P2P) | Daily | 3 months | 1 day | | Celestia | Mainnet | Network,
Validator,
Delegator | Daily | 3 months | 6 hours | | Kusama | Mainnet | Network,
Validator,
Delegator | Era, Daily | 3 months | 2 hours | | Polkadot | Mainnet | Network,
Validator,
Delegator (only P2P) | Era, Daily | 3 months | 2 hours | | Solana | Mainnet | Network,
Validator,
Delegator | Epoch | Up to epoch #132 | 24 hours | | TON | Mainnet | Delegator (only P2P) | Epoch, Daily | 3 months | 2 hours | | Sui | Mainnet | Network, Validator, Delegator | Epoch, Daily | 3 months | 2 hours | Not every endpoint is available on every chain. Each method page lists the supported networks per endpoint: [Network](/docs/network), [Validator](/docs/validator), [Delegator](/docs/data-delegator). # Chains Supported Source: https://docs.p2p.org/docs/networks-supported-staking-api Check the list of chains supported The table below summarizes all networks supported in the Staking API and the `network` parameter that may be required for the staking endpoints: | Protocol | `network` - mainnet | `network` - testnet | | ----------------------------------------- | -------------------------- | --------------------------------------- | | [Aptos](/docs/overview-aptos) | `mainnet` | `testnet` | | [Celestia](/docs/overview-celestia) | `celestia-mainnet-beta` | `celestia-mocha-testnet` | | [Cosmos (ATOM)](/docs/overview-cosmos) | `cosmoshub-4` | - | | [Ethereum](/docs/overview-eth) | use `api.p2p.org` base URL | use `api-test.p2p.org` base URL (Hoodi) | | [Hyperliquid](/docs/overview-hyperliquid) | `mainnet` | `testnet` | | [Kusama](/docs/overview-polkadot) | - | `kusama` | | [Monad](/docs/overview-monad) | `mainnet` | `testnet` | | [Polkadot](/docs/overview-polkadot) | `mainnet` | `westend` (Westend) | | [Polygon](/docs/overview-polygon) | use `api.p2p.org` base URL | use `api-test.p2p.org` base URL (Hoodi) | | [Sei](/docs/overview-sei) | `pacific-1` | `atlantic-2` | | [Solana](/docs/overview-solana) | `mainnet-beta` | `testnet` (Testnet) | | [Sui](/docs/overview-sui) | `mainnet` | `testnet` | | [Story Protocol](/docs/overview-story) | `mainnet` | `aeneid` | | [TON](/docs/overview-ton) | `mainnet` (TON Whales) | `testnet` (TON Whales) | | [TRON](/docs/overview-tron) | `mainnet` | `testnet-nile` | ## Testing and production environments We provide two distinct endpoints for testing and production environments: * Production: \<[https://api.p2p.org](https://api.p2p.org)> * Testing: \<[https://api-test.p2p.org](https://api-test.p2p.org)> # Overview Source: https://docs.p2p.org/docs/overview Our Staking Data API service is specifically designed to provide comprehensive data information regarding your staking activities, allowing you to stay up-to-date with the status of your assets. ## Features * **Coverage:** Currently, we support five networks in the Staking Data API — Ethereum, Kusama, Polkadot, Solana, Sui. Additionally, we operate in more than 20 networks, continually adding data for all networks we support. Moreover, we have universal EVM and Cosmos SDK indexers and can promptly integrate any new networks. * **Cross-Network Integration:** Our architecture is built with flexibility in mind. Our API allows you to streamline all your networks through a unified single data integration, saving considerable resource time. * **Data Latency:** Our data latency ranges from 30 minutes to 1 day depending on the network. We continuously boost our data latency and focus on achieving near real-time data by using streaming infrastructure. * **Speed and Volume:** We can provide a millisecond response for any data in the Staking Data API with a 200 RPS load. * **Quality:** As we internally use all the data for accounting and analytical purposes, we conduct thousands of data quality tests. Additionally, we have a disaster recovery architecture for the Staking Data API and most of the data pipelines. * **Customization:** We can promptly implement new API methods and customize existing ones to meet your specific requests. # Overview for AI Tools ✨ Source: https://docs.p2p.org/docs/overview-ai Using AI tools to consume P2P.ORG's API documentation P2P.ORG provides multiple ways to consume our documentation with AI tools to help you build applications faster.