synthra-x402 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts +12 -0
- package/dist/client/index.js +22 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/server/index.d.ts +16 -0
- package/dist/server/index.js +47 -0
- package/package.json +18 -0
- package/scripts/test-agent.ts +47 -0
- package/src/client/index.ts +30 -0
- package/src/index.ts +2 -0
- package/src/server/index.ts +62 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { x402Client } from "@x402-avm/core/client";
|
|
2
|
+
import type { ClientAvmSigner } from "@x402-avm/avm";
|
|
3
|
+
export interface SynthraClientConfig {
|
|
4
|
+
signer: ClientAvmSigner;
|
|
5
|
+
network?: "testnet" | "mainnet";
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates a wrapped fetch client that automatically handles x402 payments
|
|
9
|
+
* using prepaid USDC on Algorand.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createSynthraClient(config: SynthraClientConfig): x402Client;
|
|
12
|
+
export type { ClientAvmSigner };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSynthraClient = void 0;
|
|
4
|
+
const client_1 = require("@x402-avm/core/client");
|
|
5
|
+
const client_2 = require("@x402-avm/avm/exact/client");
|
|
6
|
+
const avm_1 = require("@x402-avm/avm");
|
|
7
|
+
/**
|
|
8
|
+
* Creates a wrapped fetch client that automatically handles x402 payments
|
|
9
|
+
* using prepaid USDC on Algorand.
|
|
10
|
+
*/
|
|
11
|
+
function createSynthraClient(config) {
|
|
12
|
+
const isMainnet = config.network === "mainnet";
|
|
13
|
+
const caip2 = isMainnet ? avm_1.ALGORAND_MAINNET_CAIP2 : avm_1.ALGORAND_TESTNET_CAIP2;
|
|
14
|
+
const usdcId = isMainnet ? avm_1.USDC_MAINNET_ASA_ID : avm_1.USDC_TESTNET_ASA_ID;
|
|
15
|
+
// Initialize the base fetch client
|
|
16
|
+
const client = new client_1.x402Client();
|
|
17
|
+
// Register the AVM scheme
|
|
18
|
+
(0, client_2.registerExactAvmScheme)(client, { signer: config.signer });
|
|
19
|
+
// The x402Client has a .fetch() method that automatically handles the 402 flow
|
|
20
|
+
return client;
|
|
21
|
+
}
|
|
22
|
+
exports.createSynthraClient = createSynthraClient;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./client"), exports);
|
|
18
|
+
__exportStar(require("./server"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SynthraApiConfig {
|
|
2
|
+
payTo: string;
|
|
3
|
+
priceUsdc: number;
|
|
4
|
+
network?: "testnet" | "mainnet";
|
|
5
|
+
discovery?: {
|
|
6
|
+
description?: string;
|
|
7
|
+
tags?: string[];
|
|
8
|
+
inputSchema?: any;
|
|
9
|
+
outputSchema?: any;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Express middleware that protects an endpoint with x402 using USDC.
|
|
14
|
+
* Automatically declares discovery info to the Facilitator.
|
|
15
|
+
*/
|
|
16
|
+
export declare function synthraApiAuth(config: SynthraApiConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.synthraApiAuth = void 0;
|
|
4
|
+
const express_1 = require("@x402-avm/express");
|
|
5
|
+
const server_1 = require("@x402-avm/avm/exact/server");
|
|
6
|
+
const extensions_1 = require("@x402-avm/extensions");
|
|
7
|
+
const server_2 = require("@x402-avm/core/server");
|
|
8
|
+
const avm_1 = require("@x402-avm/avm");
|
|
9
|
+
// Create the shared resource server
|
|
10
|
+
const server = new server_2.x402ResourceServer();
|
|
11
|
+
(0, server_1.registerExactAvmScheme)(server);
|
|
12
|
+
/**
|
|
13
|
+
* Express middleware that protects an endpoint with x402 using USDC.
|
|
14
|
+
* Automatically declares discovery info to the Facilitator.
|
|
15
|
+
*/
|
|
16
|
+
function synthraApiAuth(config) {
|
|
17
|
+
const isMainnet = config.network === "mainnet";
|
|
18
|
+
const caip2 = isMainnet ? avm_1.ALGORAND_MAINNET_CAIP2 : avm_1.ALGORAND_TESTNET_CAIP2;
|
|
19
|
+
const usdcId = isMainnet ? avm_1.USDC_MAINNET_ASA_ID : avm_1.USDC_TESTNET_ASA_ID;
|
|
20
|
+
// Convert friendly USDC amount (e.g. 0.50) to micro-USDC units (e.g. 500000)
|
|
21
|
+
// USDC has 6 decimals
|
|
22
|
+
const rawAmount = Math.floor(config.priceUsdc * 1_000_000);
|
|
23
|
+
// Prepare extensions
|
|
24
|
+
let extensions = {};
|
|
25
|
+
if (config.discovery) {
|
|
26
|
+
const discoveryConfig = {
|
|
27
|
+
...config.discovery,
|
|
28
|
+
price: { amount: config.priceUsdc, currency: "USDC" }
|
|
29
|
+
};
|
|
30
|
+
Object.assign(extensions, (0, extensions_1.declareDiscoveryExtension)(discoveryConfig));
|
|
31
|
+
}
|
|
32
|
+
// Define a wildcard route configuration
|
|
33
|
+
const routes = {
|
|
34
|
+
"*": {
|
|
35
|
+
accepts: {
|
|
36
|
+
scheme: "exact",
|
|
37
|
+
network: caip2,
|
|
38
|
+
price: String(rawAmount),
|
|
39
|
+
asset: String(usdcId),
|
|
40
|
+
payTo: config.payTo,
|
|
41
|
+
},
|
|
42
|
+
extensions
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return (0, express_1.paymentMiddleware)(routes, server);
|
|
46
|
+
}
|
|
47
|
+
exports.synthraApiAuth = synthraApiAuth;
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "synthra-x402",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Synthra API SDK using x402 and Algorand USDC",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@x402-avm/avm": "^2.6.1",
|
|
12
|
+
"@x402-avm/core": "^2.6.1",
|
|
13
|
+
"@x402-avm/express": "^2.6.1",
|
|
14
|
+
"@x402-avm/extensions": "^2.6.1",
|
|
15
|
+
"@x402-avm/fetch": "^2.6.1",
|
|
16
|
+
"algosdk": "^3.5.2"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import algosdk from "algosdk";
|
|
2
|
+
import { createSynthraClient } from "../src/client";
|
|
3
|
+
import type { ClientAvmSigner } from "../src/client";
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
console.log("Initializing Agent...");
|
|
7
|
+
|
|
8
|
+
// Normally the agent has a funded private key. For simulation, we generate one.
|
|
9
|
+
const account = algosdk.generateAccount();
|
|
10
|
+
const secretKey = account.sk;
|
|
11
|
+
const address = account.addr;
|
|
12
|
+
|
|
13
|
+
console.log("Agent Address:", address);
|
|
14
|
+
|
|
15
|
+
// Implement the ClientAvmSigner interface for the Agent
|
|
16
|
+
const agentSigner: ClientAvmSigner = {
|
|
17
|
+
address,
|
|
18
|
+
signTransactions: async (txns, indexesToSign) => {
|
|
19
|
+
return txns.map((txnBytes, i) => {
|
|
20
|
+
if (indexesToSign && !indexesToSign.includes(i)) return null;
|
|
21
|
+
const decoded = algosdk.decodeUnsignedTransaction(txnBytes);
|
|
22
|
+
return algosdk.signTransaction(decoded, secretKey).blob;
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Create the API SDK Client
|
|
28
|
+
const fetchClient = createSynthraClient({
|
|
29
|
+
signer: agentSigner,
|
|
30
|
+
network: "testnet"
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log("Fetching paid API endpoint...");
|
|
34
|
+
try {
|
|
35
|
+
// This will intercept the 402, sign the transaction using agentSigner,
|
|
36
|
+
// get the x402 Macaroon prepaid token, and replay the request.
|
|
37
|
+
const res = await fetchClient("http://localhost:8080/api/weather-demo");
|
|
38
|
+
const data = await res.json();
|
|
39
|
+
console.log("API Response:", data);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
// Note: We expect this to fail with "insufficient funds" since the generated account is empty,
|
|
42
|
+
// but the SDK flow will be fully tested.
|
|
43
|
+
console.error("Test Output:", (error as Error).message);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { x402Client } from "@x402-avm/core/client";
|
|
2
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
3
|
+
import { ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2, USDC_TESTNET_ASA_ID, USDC_MAINNET_ASA_ID } from "@x402-avm/avm";
|
|
4
|
+
import type { ClientAvmSigner } from "@x402-avm/avm";
|
|
5
|
+
|
|
6
|
+
export interface SynthraClientConfig {
|
|
7
|
+
signer: ClientAvmSigner;
|
|
8
|
+
network?: "testnet" | "mainnet";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates a wrapped fetch client that automatically handles x402 payments
|
|
13
|
+
* using prepaid USDC on Algorand.
|
|
14
|
+
*/
|
|
15
|
+
export function createSynthraClient(config: SynthraClientConfig) {
|
|
16
|
+
const isMainnet = config.network === "mainnet";
|
|
17
|
+
const caip2 = isMainnet ? ALGORAND_MAINNET_CAIP2 : ALGORAND_TESTNET_CAIP2;
|
|
18
|
+
const usdcId = isMainnet ? USDC_MAINNET_ASA_ID : USDC_TESTNET_ASA_ID;
|
|
19
|
+
|
|
20
|
+
// Initialize the base fetch client
|
|
21
|
+
const client = new x402Client();
|
|
22
|
+
|
|
23
|
+
// Register the AVM scheme
|
|
24
|
+
registerExactAvmScheme(client, { signer: config.signer });
|
|
25
|
+
|
|
26
|
+
// The x402Client has a .fetch() method that automatically handles the 402 flow
|
|
27
|
+
return client;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type { ClientAvmSigner };
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { paymentMiddleware } from "@x402-avm/express";
|
|
2
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/server";
|
|
3
|
+
import { declareDiscoveryExtension } from "@x402-avm/extensions";
|
|
4
|
+
import { x402ResourceServer } from "@x402-avm/core/server";
|
|
5
|
+
import { ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2, USDC_TESTNET_ASA_ID, USDC_MAINNET_ASA_ID } from "@x402-avm/avm";
|
|
6
|
+
import type { Network } from "@x402-avm/core/types";
|
|
7
|
+
|
|
8
|
+
// Create the shared resource server
|
|
9
|
+
const server = new x402ResourceServer();
|
|
10
|
+
registerExactAvmScheme(server);
|
|
11
|
+
|
|
12
|
+
export interface SynthraApiConfig {
|
|
13
|
+
payTo: string;
|
|
14
|
+
priceUsdc: number;
|
|
15
|
+
network?: "testnet" | "mainnet";
|
|
16
|
+
discovery?: {
|
|
17
|
+
description?: string;
|
|
18
|
+
tags?: string[];
|
|
19
|
+
inputSchema?: any;
|
|
20
|
+
outputSchema?: any;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Express middleware that protects an endpoint with x402 using USDC.
|
|
26
|
+
* Automatically declares discovery info to the Facilitator.
|
|
27
|
+
*/
|
|
28
|
+
export function synthraApiAuth(config: SynthraApiConfig) {
|
|
29
|
+
const isMainnet = config.network === "mainnet";
|
|
30
|
+
const caip2 = isMainnet ? ALGORAND_MAINNET_CAIP2 : ALGORAND_TESTNET_CAIP2;
|
|
31
|
+
const usdcId = isMainnet ? USDC_MAINNET_ASA_ID : USDC_TESTNET_ASA_ID;
|
|
32
|
+
|
|
33
|
+
// Convert friendly USDC amount (e.g. 0.50) to micro-USDC units (e.g. 500000)
|
|
34
|
+
// USDC has 6 decimals
|
|
35
|
+
const rawAmount = Math.floor(config.priceUsdc * 1_000_000);
|
|
36
|
+
|
|
37
|
+
// Prepare extensions
|
|
38
|
+
let extensions: Record<string, unknown> = {};
|
|
39
|
+
if (config.discovery) {
|
|
40
|
+
const discoveryConfig = {
|
|
41
|
+
...config.discovery,
|
|
42
|
+
price: { amount: config.priceUsdc, currency: "USDC" }
|
|
43
|
+
};
|
|
44
|
+
Object.assign(extensions, declareDiscoveryExtension(discoveryConfig));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Define a wildcard route configuration
|
|
48
|
+
const routes = {
|
|
49
|
+
"*": {
|
|
50
|
+
accepts: {
|
|
51
|
+
scheme: "exact",
|
|
52
|
+
network: caip2 as Network,
|
|
53
|
+
price: String(rawAmount),
|
|
54
|
+
asset: String(usdcId),
|
|
55
|
+
payTo: config.payTo,
|
|
56
|
+
},
|
|
57
|
+
extensions
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return paymentMiddleware(routes, server);
|
|
62
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"]
|
|
15
|
+
}
|