# Submit complete PulseChain blocks to BlockFerret

Permissionless signed submission. No enrollment or API key is needed. Build
blocks containing any transaction supported by the active PulseChain fork:
native transfers, swaps, token/NFT actions, protocol calls and contract creation.
The complete-block endpoint is separate from the ordinary private wallet RPC.

## Endpoints and identity

- GET `https://rpc.blockferret.win/v1/builder-context`: current public template.
- POST `https://rpc.blockferret.win/builders`: signed builder JSON-RPC.
- Chain: PulseChain, chain ID 369. Block format: Capella execution payload.

Each POST must include `X-Flashbots-Signature: ADDRESS:SIGNATURE`. EIP-191
personal-sign the UTF-8 hexadecimal string of Keccak-256 of the exact HTTP body,
including its `0x` prefix. With ethers 6: `await wallet.signMessage(id(body))`.
Do not sign decoded hash bytes or change the body after signing. This follows
the [Flashbots authentication convention](https://docs.flashbots.net/flashbots-auction/advanced/rpc-endpoint#authentication).
The authentication wallet can be unfunded. Keep its private key locally; the
website and API never need it. Fund and sign your actual payment transactions
with an outside account. No upfront enrollment payment or deposit is required.

## Obtain a usable template

Fetch context just before constructing your block. Require
`permissionlessBuilders: true`, `genericBuilderTransactions: true` and
`templateAvailable: true`, and respect
`expiresAt`. HTTP 503 or a false template flag means there is no usable template
now. Templates exist only during eligible relay duty windows. Back off and retry
a future window.

The context includes:

| Field | Meaning / representation |
| --- | --- |
| `slot` | Decimal JSON number: target proposer duty |
| `proposerPubkey` | 48-byte hex proposer public key |
| `parentHash`, `prevRandao` | 32-byte hex execution attributes |
| `blockNumber`, `timestamp`, `gasLimit` | Decimal JSON numbers; encode as hex quantities inside the payload |
| `feeRecipient`, `extraData`, `baseFeePerGas` | Exact payload attributes; base fee is a hex quantity |
| `withdrawals` | Ordered Capella wire withdrawals: preserve them exactly |
| `publicTransactions` | Signed public transactions to retain in order, with unchanged execution outcomes |
| `validatorRecipient`, `builderWallet` | Recipients for your funded payments |
| `baselinePayoutValueWei` | Decimal string: unsigned original payout value; replay still decides validator payment |
| `payoutGasLimit` | Decimal number: baseline payout gas limit, for reference |
| `snapshotHash`, `snapshotRevision`, `baselineBlockHash` | Baseline identity; rebuild when it changes |
| `expiresAt`, `executionLimits` | Expiry and scope |

Only transactions independently verified as already indexed by our public
execution node appear in the template. Our signed captures and payout are
omitted. Private wallet transaction bytes are never exported; a template that
cannot be provided without exposing private transactions is unavailable.

## Build and pay

Retain `publicTransactions` byte for byte and in order, and preserve the template's
execution attributes, fee recipient and withdrawals. You may insert your own
transactions before, between or after them, provided retained transactions keep
their status, logs and output and do not use more gas. Swaps and other contract
calls use their normal execution gas. Include funded native PLS payments to
BlockFerret and the validator, directly or through contract execution.

Legacy (including unprotected legacy), EIP-2930 access-list and EIP-1559
transactions are supported. Replay-protected signatures must use chain ID 369.
The active Capella path does not support blob or EIP-7702 envelopes. Check
`transactionCapabilities` in current context for the supported formats.

Payment to BlockFerret: `0x3Ec102407BffeABB8a494deeAeEFAc74Be4f5ea1`.
Final validator payment: `validatorRecipient` from the current context.

Our independent replay requires BOTH:

1. Aggregate native net earnings across all BlockFerret accounts are strictly
   greater than in the latest own candidate.
2. Actual validator payment covers the latest own candidate's amount plus any
   additional ordinary transaction priority fees.

Gas, spending and real balance changes are included. Self-funding, withdrawals
and returned principal do not count as external earnings. Ordinary priority
tips go to the validator; they are not BlockFerret's commission. There is no
fixed commission or automatic 40% claim on your entire profit. Among eligible
evaluated candidates, validator payment ranks first, then BlockFerret net
earnings. An exact tie keeps the current choice.

Independent replay validates the entire block and its payments. New execution
must preserve BlockFerret-owned state and assets; it cannot spend our account
signatures or consume our token approvals. Protected user transactions cannot
be censored or have their outcomes changed. Complete-block submission does not
grant access to private orderflow or enable a separate searcher-bundle endpoint.

Use your own PulseChain execution infrastructure to build and replay the actual
complete block. Accurate roots, gas accounting and block hash are required;
the example client cannot construct those from a transaction list alone.

## Submit and track

Request body shape (the capitalized value denotes your complete payload):

```js
{
  jsonrpc: "2.0",
  id: 1,
  method: "blockferret_sendCandidateBlock",
  params: [{ slot: CURRENT_SLOT, proposerPubkey: CURRENT_PROPOSER,
             executionPayload: YOUR_COMPLETE_CAPELLA_PAYLOAD }]
}
```

The payload requires `parentHash`, `feeRecipient`, `stateRoot`, `receiptsRoot`,
`logsBloom`, `prevRandao`, `blockNumber`, `gasLimit`, `gasUsed`, `timestamp`,
`extraData`, `baseFeePerGas`, `blockHash`, `transactions` and `withdrawals`.
All payload numeric quantities use hex strings. Withdrawals use Capella wire
fields `index`, `validatorIndex`, `address`, `amount`. Preserve their values
and order exactly. `slot` outside the payload is a decimal JSON number.

Acceptance returns `submissionId`, `bundleHash`, state and mode. Check status
with the same authentication identity and a newly signed body:

```json
{"jsonrpc":"2.0","id":1,"method":"blockferret_getSubmissionStatus","params":[{"submissionId":"0xYOUR_32_BYTE_SUBMISSION_ID"}]}
```

Acceptance is not selection, a relay win or canonical inclusion. Rebuild when
the parent or baseline changes. Verify the exact block hash and receipts
on-chain. There is no complete-block cancellation method.
We never automatically broadcast your candidate transactions publicly.

## Example client

Download [submit-builder.mjs](https://blockferret.win/assets/examples/submit-builder.mjs)
to a local working directory, then install Node.js 20+ and ethers 6:

```sh
npm install ethers@6
node submit-builder.mjs context
```

Prepare `candidate.json` as `{ "slot": ..., "proposerPubkey": "0x...",
"executionPayload": ... }` using your local block-building infrastructure.
Set `BUILDER_AUTH_KEY` in your local process environment using a secure method
appropriate to your operating system. Do not put keys in candidate JSON, paste
them into chat or send them to the website. Then:

```sh
node submit-builder.mjs submit candidate.json
node submit-builder.mjs status 0xYOUR_SUBMISSION_ID
```

The client signs the exact sent body and checks current duty, retained transactions and
attributes before submission. Local shape checks cannot replace independent
execution or the economic comparison. The client only uses context and builder
routes, with no public transaction broadcast fallback.

## Limits and errors

8 MiB maximum request; 10,000 transactions per block. Builder requests share
600 requests/minute across submission and context, with 60/minute per signing
address and two concurrent request slots. Complete blocks have a global cap of
16 queue entries and 16 MiB queue accounting at the current 32 MiB default.
Wallet HTTP capacity is separate and wallet work is scheduled first.

- HTTP 401: missing or invalid authentication signature.
- HTTP 429: request, identity or concurrency limit; back off.
- HTTP 503: no fresh context, parent changed, or service not ready; retry later.
- JSON-RPC `-32602`: invalid request/candidate parameters or stale target.
- JSON-RPC `-32005`: queue capacity reached.

Queue lifetime is at most two minutes. Parent changes and duty deadlines often
invalidate a candidate sooner. The queue is currently in memory; a restart may
require resubmission. These limits protect service capacity, but do not promise
DDoS immunity or inclusion in any particular block.
