Skip to Content
Dial v1 live 🎉

EVM Invoicing

Status: partly runnable, last checked 2026-11-02. The verification below runs for anyone. The invoice pattern needs access to the private spawn-proto repo. Testnet only.

Verify a prediction yourself

This computes a CREATE2 address locally and compares it with the factory’s own answer on Sepolia. It uses only public tools and needs no Dial code or account. The values are the test vector from spawn-proto.

npm install viem
verify.mjs
import { createPublicClient, getCreate2Address, http, parseAbi } from 'viem' import { sepolia } from 'viem/chains' const factory = '0xBd9680E4dD16Bc3bebE8557F47FFA54184679334' const salt = '0xc037ee33b1d559f7d95f9bed05d88af5d3569b5fe00ac1b6a4b38dfff85b5636' const initCodeHash = '0xcf6ca536764062ad2d04a612020107da8826bdbb4de1566dd7f012ab61f9136f' const local = getCreate2Address({ from: factory, salt, bytecodeHash: initCodeHash }) const client = createPublicClient({ chain: sepolia, transport: http() }) const onchain = await client.readContract({ address: factory, abi: parseAbi(['function computeAddress(bytes32, bytes32) view returns (address)']), functionName: 'computeAddress', args: [salt, initCodeHash], }) console.log({ local, onchain, match: local === onchain })

Both values should be 0x2041F7F77d7FDf50199F516e7e433194f3faD2d8.

What this proves: the formula and the deployed factory agree. What it doesn’t prove: that a program ever ran. Nothing is deployed at this address, because it’s a test vector. Don’t send funds to it.

How the invoice pattern works

  1. Create. The pattern creates a Request Network invoice, then builds forwarder init code with the beneficiary, payment reference and fee.
  2. Predict. It computes the CREATE2 address and stores the salt and init code. Losing them means losing the ability to deploy at that address.
  3. Share. The payer gets the address, a QR code and an ethereum: payment URI.
  4. Watch. A signed Alchemy webhook reports activity at the address.
  5. Deploy. The service deploys the forwarder, which forwards the balance during construction.
  6. Reconcile. Webhooks update the stored state.

Using the package

With repo access, the entry point on main is createInvoiceSpawn from @decoperations/spawn-proto-patterns-invoice, published privately to GitHub Packages. Its createxAddress option is why the next warning matters.

Don’t pass the canonical CreateX address and then fund the result. The prediction may not match the deployed address. See the open issue. Use the Sepolia repo factory for testing on dev, with Sepolia ETH only. These forwarders don’t handle ERC-20 tokens, so never send USDC to them.

Testnet safety

  • Use Sepolia ETH, and never real funds.
  • Keep the salt and init code until the program has finished.
  • Treat “funds arrived” and “executed” as separate states.
  • Check the forwarder’s balance and the beneficiary’s receipt on an explorer. A webhook is only a signal.

Troubleshooting

SymptomLikely cause
Addresses don’t matchWrong factory, wrong chain, a different salt, or changed constructor arguments
Deploy revertsContract already deployed at that address, or a failed transfer in the constructor
Webhook never firesAddress not registered with the watcher, or wrong network
Second payment revertsGenericForwarderMinimal has no receive function

Next: read the full EVM adapter page.

Explore invoicing: Programmable Invoicing and invoicing on Ethereum

Last updated on