-
Notifications
You must be signed in to change notification settings - Fork 181
feat(typescript): add optional builderCode to x402 client and server #768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ethanoroshiba
wants to merge
5
commits into
main
Choose a base branch
from
ethanoroshiba/x402-builder-codes
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
45df013
add builder code support on x402 primitives
ethanoroshiba 0251fd8
explicit empty string handling and fix ci
ethanoroshiba 4548c33
address review
ethanoroshiba 2fdb77c
address further review
ethanoroshiba f2cf4ba
further review
ethanoroshiba File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@coinbase/cdp-sdk": minor | ||
| --- | ||
|
|
||
| Add optional `builderCode` on `CdpX402Client` and `createX402Server` to auto-attach the x402 `builder-code` extension for ERC-8021 on-chain attribution. The client accepts a single service code or an array of them; the server advertises its app code on every route with an EVM payment option. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /* | ||
| * Validation helpers for the x402 builder-code extension. | ||
| * | ||
| * Upstream validates codes with `BUILDER_CODE_PATTERN.test()`, which coerces its | ||
| * argument — `42` and `["my_app"]` both stringify into something the pattern | ||
| * accepts. Config values reaching the SDK are not always typed (a `configPath` | ||
| * file is untyped JSON), so the type is checked here before the pattern runs. | ||
| */ | ||
|
|
||
| import { BUILDER_CODE_PATTERN } from "@x402/extensions/builder-code"; | ||
|
|
||
| /** Shared tail of every builder-code rejection message. */ | ||
| const CODE_REQUIREMENT = | ||
| "Must be a string of 1-32 characters, lowercase alphanumeric and underscores only."; | ||
|
|
||
| /** | ||
| * Asserts that a value is a syntactically valid builder code. | ||
| * | ||
| * @param code - Candidate builder code, possibly from untyped JSON. | ||
| * @throws If `code` is not a string matching `^[a-z0-9_]{1,32}$`. | ||
| */ | ||
| export function assertBuilderCode(code: unknown): asserts code is string { | ||
| if (typeof code !== "string" || !BUILDER_CODE_PATTERN.test(code)) { | ||
| throw new Error(`Invalid builder code: ${JSON.stringify(code)}. ${CODE_REQUIREMENT}`); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Normalizes a client `builderCode` config value into a non-empty array of | ||
| * validated service codes. | ||
| * | ||
| * @param builderCode - A single service code, or an array of them. | ||
| * @returns The validated service codes. | ||
| * @throws If any code is invalid, or if the array is empty — an empty array | ||
| * would otherwise register an extension that attaches no attribution. | ||
| */ | ||
| export function toServiceBuilderCodes(builderCode: unknown): string[] { | ||
| const codes = Array.isArray(builderCode) ? builderCode : [builderCode]; | ||
| if (codes.length === 0) { | ||
| throw new Error( | ||
| "Invalid builder code: []. Supply at least one code, or omit builderCode to leave the extension unset.", | ||
| ); | ||
| } | ||
| for (const code of codes) { | ||
| assertBuilderCode(code); | ||
| } | ||
| return codes; | ||
| } |
45 changes: 45 additions & 0 deletions
45
typescript/packages/cdp-sdk/src/x402/client.extensions.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| /* | ||
| * Extension-registry behavior of CdpX402Client against the real `x402Client` | ||
| * base class. `client.test.ts` mocks `@x402/core/client`, so it cannot observe | ||
| * how same-key registrations collapse. | ||
| */ | ||
|
|
||
| import { BuilderCodeClientExtension } from "@x402/extensions/builder-code"; | ||
| import { describe, expect, it } from "vitest"; | ||
|
|
||
| import { CdpX402Client } from "./client.js"; | ||
|
|
||
| import type { ClientExtension } from "@x402/core/client"; | ||
|
|
||
| /** | ||
| * Collects the builder-code extensions currently registered on a client. | ||
| * | ||
| * @param client - Client to inspect. | ||
| * @returns Every registered extension keyed `"builder-code"`. | ||
| */ | ||
| function builderCodeExtensions(client: CdpX402Client): ClientExtension[] { | ||
| return client.getExtensions().filter(extension => extension.key === "builder-code"); | ||
| } | ||
|
|
||
| describe("CdpX402Client extension registry", () => { | ||
| it("registers no builder-code extension when builderCode is omitted", () => { | ||
| expect(builderCodeExtensions(new CdpX402Client())).toHaveLength(0); | ||
| }); | ||
|
|
||
| it("registers exactly one builder-code extension from builderCode", () => { | ||
| const client = new CdpX402Client({ builderCode: "my_client" }); | ||
|
|
||
| expect(builderCodeExtensions(client)).toHaveLength(1); | ||
| }); | ||
|
|
||
| it("lets a manually registered builder-code extension replace the configured one", () => { | ||
| const client = new CdpX402Client({ builderCode: "my_client" }); | ||
| const custom = new BuilderCodeClientExtension("my_override"); | ||
|
|
||
| client.registerExtension(custom); | ||
|
|
||
| // The registry is keyed by extension key, so the caller's later | ||
| // registration replaces the one built in the constructor. | ||
| expect(builderCodeExtensions(client)).toEqual([custom]); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think we should change this and always attribute the client regardless of the server, dont see a good reason to drop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Patched here: x402-foundation/x402#2994