Skip to content

buy_skill fails on-chain (ConstraintHasOne) whenever creatorWallet is omitted, exactly as its own tool description instructs #125

Description

@mega123-art

Found during QA of #103 (Hermes step A.5).

Repro

Call buy_skill after a successful verify_skill in the same session, omitting creatorWallet — which the tool's own description explicitly invites: "The wallet address of the skill creator (to receive payment). If unknown, leave undefined."

{"name":"buy_skill","arguments":{"skillId":"Gbzs1pwQXrayFoFizpWSfFqvgCuzNWHUC4Noc57FXazy"}}

Result — every time, on every skill tried (reproduced on 2 different skills from 2 different creators):

Failed to buy skill: Simulation failed.
Message: Transaction simulation failed: Error processing Instruction 1: custom program error: 0x7d1.
Logs:
...
Program log: AnchorError caused by account: config. Error Code: ConstraintHasOne. Error Number: 2001.
Program log: Left:
Program log: 8QWrZjNNFzngKWCLCrFkAy7ydnagrBSVYdJFyEvw9agh   <- the skill's real creator
Program log: Right:
Program log: 7zRzFeGuCY8aS5Z7DCF2ZBMxUXw1LKZo6m4DC8oQ9mDK   <- the BUYER's own wallet
...

Passing the correct creatorWallet explicitly makes the buy succeed (tx confirmed on devnet, balance drops, SKILL.md installs). So the on-chain program and guard logic are both fine — the bug is entirely in how the client resolves the default.

Root cause

packages/core/src/skill-market/index.ts:325:

const creatorWallet = (args?.creatorWallet as string) || defaultCreatorWallet;

defaultCreatorWallet is wired from mcp-stdio.ts:70 as the connected wallet's own address (createAgentMcpServer(conn, wallet, address, ...)), not a lookup of the skill's actual on-chain creator. So omitting creatorWallet silently substitutes the buyer's own address into the creator slot of buyItemIx, which the on-chain program's has_one constraint on config.creator then rejects.

Impact

Any caller that follows the tool's own instructions (omit creatorWallet when unknown) cannot buy anything. In practice this is most callers — creatorWallet is only known if the LLM happens to carry it over from an earlier search_skills result in the same context. This blocks the entire buy flow for any host/agent that doesn't thread that field through by hand.

Suggested fix

Either:

  1. Look up the skill's real creator on-chain (the config PDA already stores it) when creatorWallet is omitted, instead of defaulting to the buyer's own address, or
  2. Drop the parameter entirely and always resolve the creator server-side — search_skills/verify_skill already return the creator address, so the client never needs to supply it.

Also worth updating the buy_skill tool description, since it currently tells the agent to do the one thing that guarantees failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions