Back to skills
extension
Category: Productivity & OfficeAPI key required

Soho

Initiate payments on the SOHO Pay credit layer using EIP-712 signatures.

personAuthor: max-clinchhubclawhub

SOHO Pay — Credit Layer Payments

This skill orchestrates payments through the SOHO Pay Creditor smart contract using the spendWithAuthorization EIP-712 flow.

This skill is manual-invocation only. It must not be triggered autonomously by a model. Every invocation requires explicit user confirmation.


Architecture — Three-Party Separation

┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│  Wallet Signer   │     │   SoHo (Credit)  │     │   Blockchain     │
│  (user/operator) │     │                  │     │   (Base)         │
│                  │     │  Credit checks   │     │                  │
│  Signs EIP-712   │────▶│  JIT funding     │────▶│  Settlement      │
│  Owns keys       │     │  Authorization   │     │  Creditor.sol    │
│                  │     │                  │     │                  │
│  MPC / HSM /     │     │  NEVER signs     │     │                  │
│  Turnkey / Privy │     │  NEVER holds keys│     │                  │
└──────────────────┘     └──────────────────┘     └──────────────────┘

SoHo is a credit layer only. It does not custody, hold, generate, or control any signing keys or key shards, and it does not produce EIP-712 signatures.

All EIP-712 signatures come from a separate Wallet Signer owned and controlled by the user or agent operator (e.g., MPC provider like Turnkey/Privy/Fireblocks, or a user wallet).

The skill orchestrates between:

  • Wallet Signer — signing (user/operator-controlled)
  • SoHo — credit checks + just-in-time funding (credit layer only)
  • Blockchain — settlement

Usage

node scripts/pay.js <amount> <merchantAddress> [payerAddress]

| Argument | Description | | ----------------- | ------------------------------------------------------------------ | | amount | Decimal USDC value (e.g. 10, 0.5) | | merchantAddress | Explicit 0x-prefixed EVM address (checksummed). Never a name. | | payerAddress | Required when SIGNER_PROVIDER=WALLET_SIGNER_REMOTE |

Example — Base Sepolia with local key (dev only)

export RPC_URL=https://sepolia.base.org
export CHAIN_ID=84532
export SIGNER_PROVIDER=LOCAL_PRIVATE_KEY
export SOHO_DEV_PRIVATE_KEY=0xabc...

node scripts/pay.js 10 0x1234567890abcdef1234567890abcdef12345678

Example — Base Sepolia with remote wallet signer (production pattern)

export RPC_URL=https://sepolia.base.org
export CHAIN_ID=84532
export SIGNER_PROVIDER=WALLET_SIGNER_REMOTE
export WALLET_SIGNER_SERVICE_URL=https://your-mpc-signer.example.com
export SIGNER_SERVICE_AUTH_TOKEN=sk_live_...

node scripts/pay.js 10 0xMERCHANT... 0xPAYER...

Workflow

  1. Validate config — all env vars checked with Zod at startup; unknown chains rejected.
  2. Parse inputs — amount and explicit merchant address (no name-to-address generation).
  3. Pre-flight credit checks — SoHo credit layer verifies borrower registration, active status, credit limit.
  4. Sign EIP-712 — Wallet Signer (user-controlled) produces signature. SoHo never signs.
  5. Submit tx — call spendWithAuthorization on the Creditor contract.
  6. Return result — transaction hash + block number.

Supported Networks

| Network | Chain ID | Status | | ------------- | -------- | ------------------------------------------ | | Base Sepolia | 84532 | Supported | | Base Mainnet | 8453 | Requires SOHO_MAINNET_CONFIRM=YES |

Unknown chain IDs are rejected at config validation time.

Contract Addresses (Base Sepolia)

| Contract | Address | | ---------------- | -------------------------------------------- | | Creditor | 0x1867a19816f38ec31ec3af1be15fd7104f161978 | | Borrower Manager | 0x76e51158015e869ab2875fa17b87383d8886e93c | | USDC (test) | 0x55b8ff660d4f0f176de84f325d39a773a7a3bda7 |


Security Model

Key Custody Boundary

| Entity | Role | Holds keys? | | -------------- | ------------------------------------ | ----------- | | Wallet Signer | Produces EIP-712 signatures | Yes — user/operator-controlled | | SoHo | Credit authorization + JIT funding | No — credit layer only | | This skill | Orchestration between the above | No — passes typed data to signer |

SoHo must NEVER custody, hold, generate, or control any signing keys or key shards.

Wallet Signer Providers

| Provider | When to use | Risk level | | ------------------------ | -------------------------- | ---------- | | WALLET_SIGNER_REMOTE | Production / Mainnet | Low — keys stay in user's MPC/HSM (Turnkey, Privy, Fireblocks, etc.) | | LOCAL_PRIVATE_KEY | Dev / Testnet only | High — raw key in env |

  • Default and recommended: WALLET_SIGNER_REMOTE. The skill sends EIP-712 typed data to the user's wallet signing service and never touches private keys.
  • Dev-only fallback: LOCAL_PRIVATE_KEY is gated to testnet chain IDs. Using it on mainnet requires DEV_ALLOW_LOCAL_KEY=YES and is strongly discouraged.

Mainnet Safety Gate

Transactions on Base Mainnet (CHAIN_ID=8453) are refused unless SOHO_MAINNET_CONFIRM=YES is set. This prevents accidental mainnet spends during development.

Recipient Address Policy

The skill never derives or generates an address from a merchant name. The merchantAddress argument must be an explicit, valid, checksummed EVM address. Any non-address input is rejected with an error.

Invocation Policy

  • invocation: manual — the skill cannot be triggered autonomously.
  • require_confirmation: true — the orchestrator must obtain user confirmation before execution.
  • A runtime guard rejects execution if SOHO_AUTONOMOUS=true is detected in the environment.

Threat Model & Mitigations

| Threat | Mitigation | | ------ | ---------- | | Signer compromise | Default to WALLET_SIGNER_REMOTE (MPC/HSM); keys never leave the user's signing service. Local key gated to testnet only. | | Replay attacks | Random 32-byte nonce per transaction; validAfter / expiry window (10 min). On-chain nonce check in Creditor contract. | | Wrong merchant address | Address generation from names removed. Only explicit checksummed EVM addresses accepted; checksum validated before signing. | | Accidental mainnet transaction | SOHO_MAINNET_CONFIRM=YES safety gate required for chain ID 8453. | | Autonomous invocation with signing authority | invocation: manual in metadata; require_confirmation: true; runtime guard blocks SOHO_AUTONOMOUS=true. | | SoHo used as signer | Architecture enforces SoHo = credit-only. No signing key env vars reference SoHo as a signer. Skill never passes keys to SoHo API. | | Undeclared env var dependencies | All env vars declared in skill.json with types and sensitivity flags. | | MITM on wallet signer service | Use HTTPS for WALLET_SIGNER_SERVICE_URL; bearer token auth via SIGNER_SERVICE_AUTH_TOKEN. |


Environment Variables

Required (all modes)

| Variable | Example | Description | | ----------------- | -------------------------------- | ---------------------------------------- | | RPC_URL | https://sepolia.base.org | JSON-RPC endpoint | | CHAIN_ID | 84532 | 84532 (Sepolia) or 8453 (Mainnet) | | SIGNER_PROVIDER | WALLET_SIGNER_REMOTE | WALLET_SIGNER_REMOTE or LOCAL_PRIVATE_KEY |

Required for WALLET_SIGNER_REMOTE

| Variable | Description | Sensitive | | ---------------------------- | ---------------------------------------------------- | --------- | | WALLET_SIGNER_SERVICE_URL | Base URL of user/operator's wallet signing service | No | | SIGNER_SERVICE_AUTH_TOKEN | Bearer auth token for the wallet signing service | Yes |

SoHo Credit Layer

| Variable | Description | Sensitive | | -------------- | ----------------------------------------------------- | --------- | | SOHO_API_URL | SoHo credit-layer API (credit checks, JIT funding) | No |

Required for LOCAL_PRIVATE_KEY (dev / testnet only)

| Variable | Description | Sensitive | | ---------------------- | -------------------------------------------- | --------- | | SOHO_DEV_PRIVATE_KEY | User/operator's raw hex private key (NOT a SoHo key) | Yes |

Conditional

| Variable | When required | | ---------------------- | ----------------------------------------------------- | | SOHO_MAINNET_CONFIRM | Must be YES when CHAIN_ID=8453 | | DEV_ALLOW_LOCAL_KEY | Set YES to allow local key on non-testnet (dangerous) |


Dependencies

Install with:

npm install

| Package | Purpose | | -------- | ---------------------------------- | | ethers | EVM interactions, EIP-712 signing | | dotenv | Load .env files | | zod | Env var validation at startup |