Skip to main content

Summary

DA Builder supports two EIP-7702 integration paths: TrustedProposer for fast setup and TrustlessProposer for account-owner authorization on every delegated call.

Choose An Integration Path

Start with TrustedProposer unless your application already requires account-owner authorization for every delegated call.
PathUse this whenDo not use this whenIntegration workSecurity model
TrustedProposerYou need the fastest way to test DA Builder with a dedicated EOAThe EOA controls assets, admin permissions, or protocol authorityDelegate to Spire’s TrustedProposer, switch RPC, fund GasTankThe EOA trusts Spire’s DA Builder service with delegated execution
TrustlessProposerEvery delegated call must be authorized by the account ownerYou need a no-code or minimal-code trialDelegate to TrustlessProposer, switch RPC, fund GasTank, sign EIP-712 proposer payloadsDA Builder aggregates calls, but the proposer contract verifies owner authorization before execution

Network Configuration

NetworkTrustedProposerTrustlessProposerGasTankRPC
Mainnet0xC09f597034f654283a05B058EE1306534b8378680x1b3068A7dC934cCEBF7784cBd9266D80948a98A10x2565c0A726cB0f2F79cd16510c117B4da6a6534bhttps://da-builder.mainnet.spire.dev/
Sepolia0x9ccc2f3ecdE026230e11a5c8799ac7524f2bb2940x51922A1e2A010418bD709870Cd1ff13615070fa80x18Fa15ea0A34a7c4BCA01bf7263b2a9Ac0D32e92https://da-builder.sepolia.spire.dev/

TrustedProposer Workflow

TrustedProposer is the fastest way to use DA Builder. The transaction sender can keep submitting normal signed transactions after setup.
  1. Create or choose a dedicated EOA.
  2. Deposit ETH into the Spire GasTank for that EOA.
  3. Submit an EIP-7702 authorization that delegates the EOA to Spire’s TrustedProposer.
  4. Send transaction submissions to the DA Builder RPC endpoint.
  5. Store the returned transaction hash.
  6. Poll eth_getTransactionReceipt or dab_transactionStatus.
  7. Use direct RPC fallback for outages, unsupported methods, or non-aggregated operational paths.

TrustedProposer Delegation With Foundry

#!/usr/bin/env bash
set -euo pipefail

RPC_URL="https://da-builder.mainnet.spire.dev/"
DELEGATE="0xC09f597034f654283a05B058EE1306534b837868"

SIGNED_AUTH="$(cast wallet sign-auth "$DELEGATE" \
  --private-key "$EOA_PK" \
  --rpc-url "$RPC_URL")"

cast send "$(cast az)" \
  --private-key "$EOA_PK" \
  --auth "$SIGNED_AUTH" \
  --rpc-url "$RPC_URL"

TrustlessProposer Workflow

TrustlessProposer is for applications that need DA Builder aggregation without trusting Spire to choose arbitrary delegated execution.
  1. Delegate the EOA to TrustlessProposer.
  2. Fund the Spire GasTank.
  3. Update the application to sign proposer call payloads.
  4. Submit the signed proposer call through DA Builder.
  5. Poll status and implement direct RPC fallback.

IProposer Interface

interface IProposer {
    function onCall(address _target, bytes calldata _data, uint256 _value) external returns (bool);
}

TrustlessProposer Security Requirements

RequirementPurpose
EIP-712 signatureConfirms the account owner authorized the delegated call
NoncePrevents replay attacks
DeadlinePrevents stale transactions
Gas limitPrevents unexpected delegated execution cost
Fixed storage layoutReduces EIP-7702 storage collision risk when account code changes
For a complete implementation, see TrustlessProposer.sol↗.

TrustlessProposer Delegation With Foundry

#!/usr/bin/env bash
set -euo pipefail

RPC_URL="https://da-builder.mainnet.spire.dev/"
DELEGATE="0x1b3068A7dC934cCEBF7784cBd9266D80948a98A1"

SIGNED_AUTH="$(cast wallet sign-auth "$DELEGATE" \
  --private-key "$EOA_PK" \
  --rpc-url "$RPC_URL")"

cast send "$(cast az)" \
  --private-key "$EOA_PK" \
  --auth "$SIGNED_AUTH" \
  --rpc-url "$RPC_URL"

TrustlessProposer Example Call

This example builds the exact calldata shape used by TrustlessProposer.onCall(...) and uses ethers v6.
import { AbiCoder, Interface, Wallet, ethers } from "ethers";

const trustlessProposerAbi = [
  "function nestedNonce() view returns (uint256)",
  "function onCall(address target, bytes data, uint256 value) returns (bool)",
];

const greeterAbi = ["function setGreeting(string greeting)"];

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const signer = new Wallet(process.env.PRIVATE_KEY!, provider);

const trustlessProposerAddress = "0x1b3068A7dC934cCEBF7784cBd9266D80948a98A1";
const greeterAddress = "0xYourPublicGreeterAddress";
const chainId = 1;
const gasLimit = 1_000_000n;
const deadline = 999_999_999_999n;
const value = 0n;

const trustlessProposer = new ethers.Contract(
  trustlessProposerAddress,
  trustlessProposerAbi,
  provider,
);

const greeter = new Interface(greeterAbi);
const greeting = "hello from DA Builder";
const calldata = greeter.encodeFunctionData("setGreeting", [greeting]);
const nonce = await trustlessProposer.nestedNonce();

// The signer must control the delegated EOA.
const signature = await signer.signTypedData(
  {
    name: "TrustlessProposer",
    version: "1",
    chainId,
    verifyingContract: trustlessProposerAddress,
  },
  {
    Call: [
      { name: "deadline", type: "uint256" },
      { name: "nonce", type: "uint256" },
      { name: "target", type: "address" },
      { name: "value", type: "uint256" },
      { name: "calldata", type: "bytes" },
      { name: "gasLimit", type: "uint256" },
    ],
  },
  {
    deadline,
    nonce,
    target: greeterAddress,
    value,
    calldata,
    gasLimit,
  },
);

const encodedData = AbiCoder.defaultAbiCoder().encode(
  ["bytes", "uint256", "uint256", "bytes", "uint256"],
  [signature, deadline, nonce, calldata, gasLimit],
);

const functionCall = new Interface(trustlessProposerAbi).encodeFunctionData("onCall", [
  greeterAddress,
  encodedData,
  value,
]);

const tx = await signer.sendTransaction({
  to: trustlessProposerAddress,
  data: functionCall
});

const receipt = await tx.wait();
console.log(receipt?.status);

Next Steps