@coinbase/agentkit 0.0.0-nightly-20250915210424 → 0.0.0-nightly-20250917210422
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/README.md +9 -1
- package/dist/action-providers/pyth/pythActionProvider.d.ts +2 -2
- package/dist/action-providers/pyth/pythActionProvider.js +83 -31
- package/dist/action-providers/pyth/pythActionProvider.test.js +178 -26
- package/dist/action-providers/pyth/schemas.d.ts +6 -0
- package/dist/action-providers/pyth/schemas.js +9 -1
- package/dist/action-providers/x402/schemas.d.ts +7 -0
- package/dist/action-providers/x402/schemas.js +11 -1
- package/dist/action-providers/x402/utils.d.ts +55 -0
- package/dist/action-providers/x402/utils.js +160 -0
- package/dist/action-providers/x402/x402ActionProvider.d.ts +9 -9
- package/dist/action-providers/x402/x402ActionProvider.js +158 -39
- package/dist/action-providers/x402/x402ActionProvider.test.js +116 -10
- package/dist/wallet-providers/cdpEvmWalletProvider.d.ts +7 -0
- package/dist/wallet-providers/cdpEvmWalletProvider.js +9 -0
- package/dist/wallet-providers/cdpEvmWalletProvider.test.js +7 -0
- package/dist/wallet-providers/cdpSmartWalletProvider.d.ts +7 -0
- package/dist/wallet-providers/cdpSmartWalletProvider.js +13 -1
- package/dist/wallet-providers/cdpSmartWalletProvider.test.js +6 -2
- package/dist/wallet-providers/evmWalletProvider.d.ts +9 -2
- package/dist/wallet-providers/evmWalletProvider.js +4 -0
- package/dist/wallet-providers/legacyCdpSmartWalletProvider.d.ts +9 -0
- package/dist/wallet-providers/legacyCdpSmartWalletProvider.js +11 -0
- package/dist/wallet-providers/legacyCdpWalletProvider.d.ts +7 -0
- package/dist/wallet-providers/legacyCdpWalletProvider.js +16 -0
- package/dist/wallet-providers/legacyCdpWalletProvider.test.js +6 -0
- package/dist/wallet-providers/privyEvmDelegatedEmbeddedWalletProvider.d.ts +7 -0
- package/dist/wallet-providers/privyEvmDelegatedEmbeddedWalletProvider.js +27 -0
- package/dist/wallet-providers/privyEvmWalletProvider.test.js +11 -0
- package/dist/wallet-providers/viemWalletProvider.d.ts +7 -0
- package/dist/wallet-providers/viemWalletProvider.js +16 -0
- package/dist/wallet-providers/viemWalletProvider.test.js +9 -0
- package/dist/wallet-providers/zeroDevWalletProvider.d.ts +7 -0
- package/dist/wallet-providers/zeroDevWalletProvider.js +12 -0
- package/dist/wallet-providers/zeroDevWalletProvider.test.js +10 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -407,7 +407,7 @@ const agent = createReactAgent({
|
|
|
407
407
|
</tr>
|
|
408
408
|
<tr>
|
|
409
409
|
<td width="200"><code>fetch_price_feed_id</code></td>
|
|
410
|
-
<td width="768">Retrieves the unique price feed identifier for a given
|
|
410
|
+
<td width="768">Retrieves the unique price feed identifier for a given asset symbol.</td>
|
|
411
411
|
</tr>
|
|
412
412
|
</table>
|
|
413
413
|
</details>
|
|
@@ -541,6 +541,10 @@ const agent = createReactAgent({
|
|
|
541
541
|
<details>
|
|
542
542
|
<summary><strong>x402</strong></summary>
|
|
543
543
|
<table width="100%">
|
|
544
|
+
<tr>
|
|
545
|
+
<td width="200"><code>discover_x402_services</code></td>
|
|
546
|
+
<td width="768">Discover available x402 services with optional filtering by maximum USDC price.</td>
|
|
547
|
+
</tr>
|
|
544
548
|
<tr>
|
|
545
549
|
<td width="200"><code>make_http_request</code></td>
|
|
546
550
|
<td width="768">Makes a basic HTTP request to an API endpoint. If the endpoint requires payment (returns 402),
|
|
@@ -553,6 +557,10 @@ it will return payment details that can be used on retry.</td>
|
|
|
553
557
|
<tr>
|
|
554
558
|
<td width="200"><code>make_http_request_with_x402</code></td>
|
|
555
559
|
<td width="768">Combines make_http_request and retry_http_request_with_x402 into a single step.</td>
|
|
560
|
+
</tr>
|
|
561
|
+
</table>
|
|
562
|
+
</details>
|
|
563
|
+
<details>
|
|
556
564
|
<summary><strong>ZeroX</strong></summary>
|
|
557
565
|
<table width="100%">
|
|
558
566
|
<tr>
|
|
@@ -13,14 +13,14 @@ export declare class PythActionProvider extends ActionProvider {
|
|
|
13
13
|
* Fetch the price feed ID for a given token symbol from Pyth.
|
|
14
14
|
*
|
|
15
15
|
* @param args - The arguments for the action.
|
|
16
|
-
* @returns The price feed ID as
|
|
16
|
+
* @returns The price feed ID as stringified JSON.
|
|
17
17
|
*/
|
|
18
18
|
fetchPriceFeed(args: z.infer<typeof PythFetchPriceFeedIDSchema>): Promise<string>;
|
|
19
19
|
/**
|
|
20
20
|
* Fetches the price from Pyth given a Pyth price feed ID.
|
|
21
21
|
*
|
|
22
22
|
* @param args - The arguments for the action.
|
|
23
|
-
* @returns The price as
|
|
23
|
+
* @returns The price as stringified JSON.
|
|
24
24
|
*/
|
|
25
25
|
fetchPrice(args: z.infer<typeof PythFetchPriceSchema>): Promise<string>;
|
|
26
26
|
/**
|
|
@@ -34,68 +34,120 @@ class PythActionProvider extends actionProvider_1.ActionProvider {
|
|
|
34
34
|
* Fetch the price feed ID for a given token symbol from Pyth.
|
|
35
35
|
*
|
|
36
36
|
* @param args - The arguments for the action.
|
|
37
|
-
* @returns The price feed ID as
|
|
37
|
+
* @returns The price feed ID as stringified JSON.
|
|
38
38
|
*/
|
|
39
39
|
async fetchPriceFeed(args) {
|
|
40
|
-
|
|
41
|
-
// This is temporary until proper new API link is provided after talking to the Pyth team
|
|
42
|
-
if (args.tokenSymbol.toUpperCase() === "ETH") {
|
|
43
|
-
return "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace";
|
|
44
|
-
}
|
|
45
|
-
const url = `https://hermes.pyth.network/v2/price_feeds?query=${args.tokenSymbol}&asset_type=crypto`;
|
|
40
|
+
const url = `https://hermes.pyth.network/v2/price_feeds?query=${args.tokenSymbol}&asset_type=${args.assetType}`;
|
|
46
41
|
const response = await fetch(url);
|
|
47
42
|
if (!response.ok) {
|
|
48
|
-
|
|
43
|
+
return JSON.stringify({
|
|
44
|
+
success: false,
|
|
45
|
+
error: `HTTP error! status: ${response.status}`,
|
|
46
|
+
});
|
|
49
47
|
}
|
|
50
48
|
const data = await response.json();
|
|
51
49
|
if (data.length === 0) {
|
|
52
|
-
|
|
50
|
+
return JSON.stringify({
|
|
51
|
+
success: false,
|
|
52
|
+
error: `No price feed found for ${args.tokenSymbol}`,
|
|
53
|
+
});
|
|
53
54
|
}
|
|
54
55
|
const filteredData = data.filter(
|
|
55
56
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
-
(item) => item.attributes.base.toLowerCase() === args.tokenSymbol.toLowerCase()
|
|
57
|
+
(item) => item.attributes.base.toLowerCase() === args.tokenSymbol.toLowerCase() &&
|
|
58
|
+
item.attributes.quote_currency.toLowerCase() === args.quoteCurrency.toLowerCase());
|
|
57
59
|
if (filteredData.length === 0) {
|
|
58
|
-
|
|
60
|
+
return JSON.stringify({
|
|
61
|
+
success: false,
|
|
62
|
+
error: `No price feed found for ${args.tokenSymbol}/${args.quoteCurrency}`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// For equities, select the regular feed over special market hours feeds
|
|
66
|
+
let selectedFeed = filteredData[0];
|
|
67
|
+
if (args.assetType === "equity") {
|
|
68
|
+
// Look for regular market feed (no PRE, POST, ON, EXT suffixes)
|
|
69
|
+
const regularMarketFeed = filteredData.find(
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
+
(item) => !item.attributes.symbol.includes(".PRE") &&
|
|
72
|
+
!item.attributes.symbol.includes(".POST") &&
|
|
73
|
+
!item.attributes.symbol.includes(".ON") &&
|
|
74
|
+
!item.attributes.symbol.includes(".EXT"));
|
|
75
|
+
if (regularMarketFeed) {
|
|
76
|
+
selectedFeed = regularMarketFeed;
|
|
77
|
+
}
|
|
59
78
|
}
|
|
60
|
-
return
|
|
79
|
+
return JSON.stringify({
|
|
80
|
+
success: true,
|
|
81
|
+
priceFeedID: selectedFeed.id,
|
|
82
|
+
tokenSymbol: args.tokenSymbol,
|
|
83
|
+
quoteCurrency: args.quoteCurrency,
|
|
84
|
+
feedType: selectedFeed.attributes.display_symbol,
|
|
85
|
+
});
|
|
61
86
|
}
|
|
62
87
|
/**
|
|
63
88
|
* Fetches the price from Pyth given a Pyth price feed ID.
|
|
64
89
|
*
|
|
65
90
|
* @param args - The arguments for the action.
|
|
66
|
-
* @returns The price as
|
|
91
|
+
* @returns The price as stringified JSON.
|
|
67
92
|
*/
|
|
68
93
|
async fetchPrice(args) {
|
|
69
94
|
const url = `https://hermes.pyth.network/v2/updates/price/latest?ids[]=${args.priceFeedID}`;
|
|
70
95
|
const response = await fetch(url);
|
|
71
96
|
if (!response.ok) {
|
|
72
|
-
|
|
97
|
+
return JSON.stringify({
|
|
98
|
+
success: false,
|
|
99
|
+
error: `HTTP error! status: ${response.status}`,
|
|
100
|
+
});
|
|
73
101
|
}
|
|
74
102
|
const data = await response.json();
|
|
75
103
|
const parsedData = data.parsed;
|
|
76
104
|
if (parsedData.length === 0) {
|
|
77
|
-
|
|
105
|
+
return JSON.stringify({
|
|
106
|
+
success: false,
|
|
107
|
+
error: `No price data found for ${args.priceFeedID}`,
|
|
108
|
+
});
|
|
78
109
|
}
|
|
110
|
+
// Helper function to format price
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
112
|
+
const formatPrice = (priceInfo) => {
|
|
113
|
+
const price = BigInt(priceInfo.price);
|
|
114
|
+
const exponent = priceInfo.expo;
|
|
115
|
+
if (exponent < 0) {
|
|
116
|
+
const adjustedPrice = price * BigInt(100);
|
|
117
|
+
const divisor = BigInt(10) ** BigInt(-exponent);
|
|
118
|
+
const scaledPrice = adjustedPrice / BigInt(divisor);
|
|
119
|
+
const priceStr = scaledPrice.toString();
|
|
120
|
+
const formattedPrice = `${priceStr.slice(0, -2)}.${priceStr.slice(-2)}`;
|
|
121
|
+
return formattedPrice.startsWith(".") ? `0${formattedPrice}` : formattedPrice;
|
|
122
|
+
}
|
|
123
|
+
const scaledPrice = price / BigInt(10) ** BigInt(exponent);
|
|
124
|
+
return scaledPrice.toString();
|
|
125
|
+
};
|
|
79
126
|
const priceInfo = parsedData[0].price;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const scaledPrice = adjustedPrice / BigInt(divisor);
|
|
86
|
-
const priceStr = scaledPrice.toString();
|
|
87
|
-
const formattedPrice = `${priceStr.slice(0, -2)}.${priceStr.slice(-2)}`;
|
|
88
|
-
return formattedPrice.startsWith(".") ? `0${formattedPrice}` : formattedPrice;
|
|
89
|
-
}
|
|
90
|
-
const scaledPrice = price / BigInt(10) ** BigInt(exponent);
|
|
91
|
-
return scaledPrice.toString();
|
|
127
|
+
return JSON.stringify({
|
|
128
|
+
success: true,
|
|
129
|
+
priceFeedID: args.priceFeedID,
|
|
130
|
+
price: formatPrice(priceInfo),
|
|
131
|
+
});
|
|
92
132
|
}
|
|
93
133
|
}
|
|
94
134
|
exports.PythActionProvider = PythActionProvider;
|
|
95
135
|
__decorate([
|
|
96
136
|
(0, actionDecorator_1.CreateAction)({
|
|
97
137
|
name: "fetch_price_feed",
|
|
98
|
-
description:
|
|
138
|
+
description: `Fetch the price feed ID for a given token symbol from Pyth.
|
|
139
|
+
|
|
140
|
+
Inputs:
|
|
141
|
+
- tokenSymbol: The asset ticker/symbol to fetch the price feed ID for (e.g. BTC, ETH, COIN, XAU, EUR, etc.)
|
|
142
|
+
- quoteCurrency: The quote currency to filter by (defaults to USD)
|
|
143
|
+
- assetType: The asset type to search for (crypto, equity, fx, metal) - defaults to crypto
|
|
144
|
+
|
|
145
|
+
Examples:
|
|
146
|
+
- Crypto: BTC, ETH, SOL
|
|
147
|
+
- Equities: COIN, AAPL, TSLA
|
|
148
|
+
- FX: EUR, GBP, JPY
|
|
149
|
+
- Metals: XAU (Gold), XAG (Silver), XPT (Platinum), XPD (Palladium)
|
|
150
|
+
`,
|
|
99
151
|
schema: schemas_1.PythFetchPriceFeedIDSchema,
|
|
100
152
|
}),
|
|
101
153
|
__metadata("design:type", Function),
|
|
@@ -105,16 +157,16 @@ __decorate([
|
|
|
105
157
|
__decorate([
|
|
106
158
|
(0, actionDecorator_1.CreateAction)({
|
|
107
159
|
name: "fetch_price",
|
|
108
|
-
description: `Fetch the price of a
|
|
160
|
+
description: `Fetch the price of a price feed from Pyth.
|
|
109
161
|
|
|
110
162
|
Inputs:
|
|
111
|
-
-
|
|
163
|
+
- priceFeedID: Price feed ID (string)
|
|
112
164
|
|
|
113
165
|
Important notes:
|
|
114
166
|
- Do not assume that a random ID is a Pyth price feed ID. If you are confused, ask a clarifying question.
|
|
115
167
|
- This action only fetches price inputs from Pyth price feeds. No other source.
|
|
116
168
|
- If you are asked to fetch the price from Pyth for a ticker symbol such as BTC, you must first use the pyth_fetch_price_feed_id
|
|
117
|
-
action to retrieve the price feed ID before invoking the
|
|
169
|
+
action to retrieve the price feed ID before invoking the pyth_fetch_price action
|
|
118
170
|
`,
|
|
119
171
|
schema: schemas_1.PythFetchPriceSchema,
|
|
120
172
|
}),
|
|
@@ -9,39 +9,180 @@ describe("PythActionProvider", () => {
|
|
|
9
9
|
jest.resetAllMocks().restoreAllMocks();
|
|
10
10
|
});
|
|
11
11
|
describe("fetchPriceFeed", () => {
|
|
12
|
-
it("should return the first price feed ID that matches the input token symbol", async () => {
|
|
12
|
+
it("should return the first price feed ID that matches the input token symbol for crypto", async () => {
|
|
13
13
|
fetchMock.mockResolvedValueOnce({
|
|
14
14
|
ok: true,
|
|
15
|
-
json: async () => [
|
|
15
|
+
json: async () => [
|
|
16
|
+
{ id: "some-price-feed-id", attributes: { base: "BTC", quote_currency: "USD" } },
|
|
17
|
+
],
|
|
16
18
|
});
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
+
const result = await provider.fetchPriceFeed({
|
|
20
|
+
tokenSymbol: "BTC",
|
|
21
|
+
assetType: "crypto",
|
|
22
|
+
quoteCurrency: "USD",
|
|
23
|
+
});
|
|
24
|
+
const parsed = JSON.parse(result);
|
|
25
|
+
expect(parsed.success).toBe(true);
|
|
26
|
+
expect(parsed.priceFeedID).toEqual("some-price-feed-id");
|
|
27
|
+
});
|
|
28
|
+
it("should return the first price feed ID that matches the input token symbol for metals", async () => {
|
|
29
|
+
fetchMock.mockResolvedValueOnce({
|
|
30
|
+
ok: true,
|
|
31
|
+
json: async () => [
|
|
32
|
+
{ id: "gold-price-feed-id", attributes: { base: "XAU", quote_currency: "USD" } },
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
const result = await provider.fetchPriceFeed({
|
|
36
|
+
tokenSymbol: "XAU",
|
|
37
|
+
assetType: "metal",
|
|
38
|
+
quoteCurrency: "USD",
|
|
39
|
+
});
|
|
40
|
+
const parsed = JSON.parse(result);
|
|
41
|
+
expect(parsed.success).toBe(true);
|
|
42
|
+
expect(parsed.priceFeedID).toEqual("gold-price-feed-id");
|
|
43
|
+
});
|
|
44
|
+
it("should return the first price feed ID that matches the input token symbol for commodities", async () => {
|
|
45
|
+
fetchMock.mockResolvedValueOnce({
|
|
46
|
+
ok: true,
|
|
47
|
+
json: async () => [
|
|
48
|
+
{ id: "oil-price-feed-id", attributes: { base: "WTI", quote_currency: "USD" } },
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
const result = await provider.fetchPriceFeed({
|
|
52
|
+
tokenSymbol: "WTI",
|
|
53
|
+
assetType: "metal",
|
|
54
|
+
quoteCurrency: "USD",
|
|
55
|
+
});
|
|
56
|
+
const parsed = JSON.parse(result);
|
|
57
|
+
expect(parsed.success).toBe(true);
|
|
58
|
+
expect(parsed.priceFeedID).toEqual("oil-price-feed-id");
|
|
59
|
+
});
|
|
60
|
+
it("should return the first price feed ID that matches the input token symbol for equities", async () => {
|
|
61
|
+
fetchMock.mockResolvedValueOnce({
|
|
62
|
+
ok: true,
|
|
63
|
+
json: async () => [
|
|
64
|
+
{
|
|
65
|
+
id: "apple-price-feed-id",
|
|
66
|
+
attributes: {
|
|
67
|
+
base: "AAPL",
|
|
68
|
+
quote_currency: "USD",
|
|
69
|
+
symbol: "Equity.US.AAPL/USD",
|
|
70
|
+
display_symbol: "AAPL/USD",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
const result = await provider.fetchPriceFeed({
|
|
76
|
+
tokenSymbol: "AAPL",
|
|
77
|
+
assetType: "equity",
|
|
78
|
+
quoteCurrency: "USD",
|
|
79
|
+
});
|
|
80
|
+
const parsed = JSON.parse(result);
|
|
81
|
+
expect(parsed.success).toBe(true);
|
|
82
|
+
expect(parsed.priceFeedID).toEqual("apple-price-feed-id");
|
|
19
83
|
});
|
|
20
|
-
it("should
|
|
84
|
+
it("should return the first price feed ID that matches the input token symbol for FX", async () => {
|
|
21
85
|
fetchMock.mockResolvedValueOnce({
|
|
22
86
|
ok: true,
|
|
23
|
-
json: async () => [
|
|
87
|
+
json: async () => [
|
|
88
|
+
{ id: "eur-price-feed-id", attributes: { base: "EUR", quote_currency: "USD" } },
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
const result = await provider.fetchPriceFeed({
|
|
92
|
+
tokenSymbol: "EUR",
|
|
93
|
+
assetType: "fx",
|
|
94
|
+
quoteCurrency: "USD",
|
|
24
95
|
});
|
|
25
|
-
|
|
96
|
+
const parsed = JSON.parse(result);
|
|
97
|
+
expect(parsed.success).toBe(true);
|
|
98
|
+
expect(parsed.priceFeedID).toEqual("eur-price-feed-id");
|
|
26
99
|
});
|
|
27
|
-
it("should return
|
|
28
|
-
|
|
29
|
-
|
|
100
|
+
it("should return error if no price feed is found", async () => {
|
|
101
|
+
fetchMock.mockResolvedValueOnce({
|
|
102
|
+
ok: true,
|
|
103
|
+
json: async () => [
|
|
104
|
+
{ id: "some-price-feed-id", attributes: { base: "BTC", quote_currency: "USD" } },
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
const result = await provider.fetchPriceFeed({
|
|
108
|
+
tokenSymbol: "SOL",
|
|
109
|
+
assetType: "crypto",
|
|
110
|
+
quoteCurrency: "USD",
|
|
111
|
+
});
|
|
112
|
+
const parsed = JSON.parse(result);
|
|
113
|
+
expect(parsed.success).toBe(false);
|
|
114
|
+
expect(parsed.error).toContain("No price feed found for SOL/USD");
|
|
30
115
|
});
|
|
31
|
-
it("should
|
|
116
|
+
it("should return error if the response is not ok", async () => {
|
|
32
117
|
fetchMock.mockResolvedValueOnce({
|
|
33
118
|
ok: false,
|
|
34
119
|
status: 404,
|
|
35
120
|
});
|
|
36
|
-
await
|
|
121
|
+
const result = await provider.fetchPriceFeed({
|
|
122
|
+
tokenSymbol: "BTC",
|
|
123
|
+
assetType: "crypto",
|
|
124
|
+
quoteCurrency: "USD",
|
|
125
|
+
});
|
|
126
|
+
const parsed = JSON.parse(result);
|
|
127
|
+
expect(parsed.success).toBe(false);
|
|
128
|
+
expect(parsed.error).toContain("HTTP error! status: 404");
|
|
37
129
|
});
|
|
38
|
-
it("should
|
|
130
|
+
it("should return error if response is ok but no data is returned", async () => {
|
|
39
131
|
fetchMock.mockResolvedValueOnce({
|
|
40
132
|
ok: true,
|
|
41
133
|
json: async () => [],
|
|
42
134
|
});
|
|
43
|
-
const
|
|
44
|
-
|
|
135
|
+
const result = await provider.fetchPriceFeed({
|
|
136
|
+
tokenSymbol: "BTC",
|
|
137
|
+
assetType: "crypto",
|
|
138
|
+
quoteCurrency: "USD",
|
|
139
|
+
});
|
|
140
|
+
const parsed = JSON.parse(result);
|
|
141
|
+
expect(parsed.success).toBe(false);
|
|
142
|
+
expect(parsed.error).toContain("No price feed found for BTC");
|
|
143
|
+
});
|
|
144
|
+
it("should prefer regular market hours feed for equities over pre/post market", async () => {
|
|
145
|
+
fetchMock.mockResolvedValueOnce({
|
|
146
|
+
ok: true,
|
|
147
|
+
json: async () => [
|
|
148
|
+
{
|
|
149
|
+
id: "post-market-feed-id",
|
|
150
|
+
attributes: {
|
|
151
|
+
base: "COIN",
|
|
152
|
+
quote_currency: "USD",
|
|
153
|
+
symbol: "Equity.US.COIN/USD.POST",
|
|
154
|
+
display_symbol: "COIN/USD POST MARKET",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "regular-market-feed-id",
|
|
159
|
+
attributes: {
|
|
160
|
+
base: "COIN",
|
|
161
|
+
quote_currency: "USD",
|
|
162
|
+
symbol: "Equity.US.COIN/USD",
|
|
163
|
+
display_symbol: "COIN/USD",
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "pre-market-feed-id",
|
|
168
|
+
attributes: {
|
|
169
|
+
base: "COIN",
|
|
170
|
+
quote_currency: "USD",
|
|
171
|
+
symbol: "Equity.US.COIN/USD.PRE",
|
|
172
|
+
display_symbol: "COIN/USD PRE MARKET",
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
});
|
|
177
|
+
const result = await provider.fetchPriceFeed({
|
|
178
|
+
tokenSymbol: "COIN",
|
|
179
|
+
assetType: "equity",
|
|
180
|
+
quoteCurrency: "USD",
|
|
181
|
+
});
|
|
182
|
+
const parsed = JSON.parse(result);
|
|
183
|
+
expect(parsed.success).toBe(true);
|
|
184
|
+
expect(parsed.priceFeedID).toEqual("regular-market-feed-id");
|
|
185
|
+
expect(parsed.feedType).toEqual("COIN/USD");
|
|
45
186
|
});
|
|
46
187
|
});
|
|
47
188
|
describe("fetchPrice", () => {
|
|
@@ -59,8 +200,10 @@ describe("PythActionProvider", () => {
|
|
|
59
200
|
],
|
|
60
201
|
}),
|
|
61
202
|
});
|
|
62
|
-
const
|
|
63
|
-
|
|
203
|
+
const result = await provider.fetchPrice({ priceFeedID: "some-price-feed-id" });
|
|
204
|
+
const parsed = JSON.parse(result);
|
|
205
|
+
expect(parsed.success).toBe(true);
|
|
206
|
+
expect(parsed.price).toEqual("1");
|
|
64
207
|
});
|
|
65
208
|
it("should return the price for a given price feed ID with a negative exponent", async () => {
|
|
66
209
|
fetchMock.mockResolvedValueOnce({
|
|
@@ -76,9 +219,10 @@ describe("PythActionProvider", () => {
|
|
|
76
219
|
],
|
|
77
220
|
}),
|
|
78
221
|
});
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
expect(
|
|
222
|
+
const result = await provider.fetchPrice({ priceFeedID: "some-price-feed-id" });
|
|
223
|
+
const parsed = JSON.parse(result);
|
|
224
|
+
expect(parsed.success).toBe(true);
|
|
225
|
+
expect(parsed.price).toEqual("1.00");
|
|
82
226
|
});
|
|
83
227
|
it("should handle scaled price starting with a decimal", async () => {
|
|
84
228
|
fetchMock.mockResolvedValueOnce({
|
|
@@ -94,24 +238,32 @@ describe("PythActionProvider", () => {
|
|
|
94
238
|
],
|
|
95
239
|
}),
|
|
96
240
|
});
|
|
97
|
-
const
|
|
98
|
-
|
|
241
|
+
const result = await provider.fetchPrice({ priceFeedID: "some-price-feed-id" });
|
|
242
|
+
const parsed = JSON.parse(result);
|
|
243
|
+
expect(parsed.success).toBe(true);
|
|
244
|
+
expect(parsed.price).toEqual("0.25");
|
|
99
245
|
});
|
|
100
|
-
it("should
|
|
246
|
+
it("should return error if there is no price data", async () => {
|
|
101
247
|
fetchMock.mockResolvedValueOnce({
|
|
102
248
|
ok: true,
|
|
103
249
|
json: async () => ({
|
|
104
250
|
parsed: [],
|
|
105
251
|
}),
|
|
106
252
|
});
|
|
107
|
-
await
|
|
253
|
+
const result = await provider.fetchPrice({ priceFeedID: "some-price-feed-id" });
|
|
254
|
+
const parsed = JSON.parse(result);
|
|
255
|
+
expect(parsed.success).toBe(false);
|
|
256
|
+
expect(parsed.error).toContain("No price data found for some-price-feed-id");
|
|
108
257
|
});
|
|
109
|
-
it("should
|
|
258
|
+
it("should return error if response is not ok", async () => {
|
|
110
259
|
fetchMock.mockResolvedValueOnce({
|
|
111
260
|
ok: false,
|
|
112
261
|
status: 404,
|
|
113
262
|
});
|
|
114
|
-
await
|
|
263
|
+
const result = await provider.fetchPrice({ priceFeedID: "some-price-feed-id" });
|
|
264
|
+
const parsed = JSON.parse(result);
|
|
265
|
+
expect(parsed.success).toBe(false);
|
|
266
|
+
expect(parsed.error).toContain("HTTP error! status: 404");
|
|
115
267
|
});
|
|
116
268
|
});
|
|
117
269
|
});
|
|
@@ -4,10 +4,16 @@ import { z } from "zod";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const PythFetchPriceFeedIDSchema: z.ZodObject<{
|
|
6
6
|
tokenSymbol: z.ZodString;
|
|
7
|
+
quoteCurrency: z.ZodDefault<z.ZodString>;
|
|
8
|
+
assetType: z.ZodDefault<z.ZodEnum<["crypto", "equity", "fx", "metal"]>>;
|
|
7
9
|
}, "strict", z.ZodTypeAny, {
|
|
8
10
|
tokenSymbol: string;
|
|
11
|
+
quoteCurrency: string;
|
|
12
|
+
assetType: "crypto" | "equity" | "fx" | "metal";
|
|
9
13
|
}, {
|
|
10
14
|
tokenSymbol: string;
|
|
15
|
+
quoteCurrency?: string | undefined;
|
|
16
|
+
assetType?: "crypto" | "equity" | "fx" | "metal" | undefined;
|
|
11
17
|
}>;
|
|
12
18
|
/**
|
|
13
19
|
* Input schema for Pyth fetch price action.
|
|
@@ -7,7 +7,15 @@ const zod_1 = require("zod");
|
|
|
7
7
|
*/
|
|
8
8
|
exports.PythFetchPriceFeedIDSchema = zod_1.z
|
|
9
9
|
.object({
|
|
10
|
-
tokenSymbol: zod_1.z.string().describe("The
|
|
10
|
+
tokenSymbol: zod_1.z.string().describe("The asset ticker/symbol to fetch the price feed ID for"),
|
|
11
|
+
quoteCurrency: zod_1.z
|
|
12
|
+
.string()
|
|
13
|
+
.default("USD")
|
|
14
|
+
.describe("The quote currency to filter by (defaults to USD)"),
|
|
15
|
+
assetType: zod_1.z
|
|
16
|
+
.enum(["crypto", "equity", "fx", "metal"])
|
|
17
|
+
.default("crypto")
|
|
18
|
+
.describe("The asset type to search for (crypto, equity, fx, metal)"),
|
|
11
19
|
})
|
|
12
20
|
.strict();
|
|
13
21
|
/**
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export declare const ListX402ServicesSchema: z.ZodObject<{
|
|
3
|
+
maxUsdcPrice: z.ZodOptional<z.ZodNumber>;
|
|
4
|
+
}, "strip", z.ZodTypeAny, {
|
|
5
|
+
maxUsdcPrice?: number | undefined;
|
|
6
|
+
}, {
|
|
7
|
+
maxUsdcPrice?: number | undefined;
|
|
8
|
+
}>;
|
|
2
9
|
export declare const HttpRequestSchema: z.ZodObject<{
|
|
3
10
|
url: z.ZodString;
|
|
4
11
|
method: z.ZodDefault<z.ZodNullable<z.ZodEnum<["GET", "POST", "PUT", "DELETE", "PATCH"]>>>;
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DirectX402RequestSchema = exports.RetryWithX402Schema = exports.HttpRequestSchema = void 0;
|
|
3
|
+
exports.DirectX402RequestSchema = exports.RetryWithX402Schema = exports.HttpRequestSchema = exports.ListX402ServicesSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
// Schema for listing x402 services
|
|
6
|
+
exports.ListX402ServicesSchema = zod_1.z
|
|
7
|
+
.object({
|
|
8
|
+
maxUsdcPrice: zod_1.z
|
|
9
|
+
.number()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Optional maximum price in USDC whole units (e.g., 0.1 for 0.10 USDC). Only USDC payment options will be considered when this filter is applied."),
|
|
12
|
+
})
|
|
13
|
+
.strip()
|
|
14
|
+
.describe("Parameters for listing x402 services with optional filtering");
|
|
5
15
|
// Schema for initial HTTP request
|
|
6
16
|
exports.HttpRequestSchema = zod_1.z
|
|
7
17
|
.object({
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Network } from "../../network";
|
|
2
|
+
import { AxiosError } from "axios";
|
|
3
|
+
import { EvmWalletProvider } from "../../wallet-providers";
|
|
4
|
+
/**
|
|
5
|
+
* Supported network types for x402 protocol
|
|
6
|
+
*/
|
|
7
|
+
export type X402Network = "base" | "base-sepolia" | "solana" | "solana-devnet";
|
|
8
|
+
/**
|
|
9
|
+
* Converts the internal network ID to the format expected by the x402 protocol.
|
|
10
|
+
*
|
|
11
|
+
* @param network - The network to convert
|
|
12
|
+
* @returns The network ID in x402 format
|
|
13
|
+
* @throws Error if the network is not supported
|
|
14
|
+
*/
|
|
15
|
+
export declare function getX402Network(network: Network): X402Network | string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Helper method to handle HTTP errors consistently.
|
|
18
|
+
*
|
|
19
|
+
* @param error - The axios error to handle
|
|
20
|
+
* @param url - The URL that was being accessed when the error occurred
|
|
21
|
+
* @returns A JSON string containing formatted error details
|
|
22
|
+
*/
|
|
23
|
+
export declare function handleHttpError(error: AxiosError, url: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Formats a payment option into a human-readable string.
|
|
26
|
+
*
|
|
27
|
+
* @param option - The payment option to format
|
|
28
|
+
* @param option.asset - The asset address or identifier
|
|
29
|
+
* @param option.maxAmountRequired - The maximum amount required for the payment
|
|
30
|
+
* @param option.network - The network identifier
|
|
31
|
+
* @param walletProvider - The wallet provider for token details lookup
|
|
32
|
+
* @returns A formatted string like "0.1 USDC on base"
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatPaymentOption(option: {
|
|
35
|
+
asset: string;
|
|
36
|
+
maxAmountRequired: string;
|
|
37
|
+
network: string;
|
|
38
|
+
}, walletProvider: EvmWalletProvider): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Checks if an asset is USDC on any supported network.
|
|
41
|
+
*
|
|
42
|
+
* @param asset - The asset address or identifier
|
|
43
|
+
* @param walletProvider - The wallet provider for network context
|
|
44
|
+
* @returns True if the asset is USDC, false otherwise
|
|
45
|
+
*/
|
|
46
|
+
export declare function isUsdcAsset(asset: string, walletProvider: EvmWalletProvider): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Converts whole units to atomic units for a given asset.
|
|
49
|
+
*
|
|
50
|
+
* @param wholeUnits - The amount in whole units (e.g., 0.1 for 0.1 USDC)
|
|
51
|
+
* @param asset - The asset address or identifier
|
|
52
|
+
* @param walletProvider - The wallet provider for token details lookup
|
|
53
|
+
* @returns The amount in atomic units as a string, or null if conversion fails
|
|
54
|
+
*/
|
|
55
|
+
export declare function convertWholeUnitsToAtomic(wholeUnits: number, asset: string, walletProvider: EvmWalletProvider): Promise<string | null>;
|