infra-sdk-typescript 0.1.3 → 0.2.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/README.md +256 -254
- package/dist/index.cjs +51 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -29
- package/dist/index.d.ts +55 -29
- package/dist/index.js +51 -9
- package/dist/index.js.map +1 -1
- package/package.json +77 -77
package/README.md
CHANGED
|
@@ -1,254 +1,256 @@
|
|
|
1
|
-
# infra-sdk-typescript
|
|
2
|
-
|
|
3
|
-
The official TypeScript SDK for the [Convexity Infra API](https://docs.withconvexity.com).
|
|
4
|
-
It covers three services: **Wallet**, **Tokenization**, and **Blockchain Events** (the
|
|
5
|
-
Indexer).
|
|
6
|
-
|
|
7
|
-
What you get:
|
|
8
|
-
|
|
9
|
-
- Types for every request and response, including all three of the pagination envelopes
|
|
10
|
-
the API uses.
|
|
11
|
-
- Auth handled for you. Hand the client an API key and it mints, caches, and refreshes
|
|
12
|
-
OAuth bearer tokens in the background.
|
|
13
|
-
- Retries with backoff on rate limits (`429`, honoring `retryAfter`) and upstream outages
|
|
14
|
-
(`503`). Connection failures get retried too, but only when a retry is safe.
|
|
15
|
-
- `Idempotency-Key` headers generated automatically on the wallet operations that move
|
|
16
|
-
value. You can pass your own instead.
|
|
17
|
-
- Real error classes (`RateLimitError`, `NotFoundError`, `PaymentRequiredError`, and so
|
|
18
|
-
on) so you're not switching on status codes.
|
|
19
|
-
- Signature verification for both webhook schemes: Wallet outcome webhooks and Indexer
|
|
20
|
-
event deliveries.
|
|
21
|
-
- No runtime dependencies. It's built on native `fetch` and `node:crypto`, and ships dual
|
|
22
|
-
ESM/CJS builds with full `.d.ts` types.
|
|
23
|
-
|
|
24
|
-
## Requirements
|
|
25
|
-
|
|
26
|
-
- Node.js 18 or later.
|
|
27
|
-
- A Convexity Infra API key. There's no self-service signup yet, so email
|
|
28
|
-
[infra@withconvexity.com](mailto:infra@withconvexity.com) with your business details and
|
|
29
|
-
you'll get back a test key (`sk_test_...`) and a live key (`sk_live_...`).
|
|
30
|
-
|
|
31
|
-
## Install
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npm install infra-sdk-typescript
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Quickstart
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
import { InfraClient } from "infra-sdk-typescript";
|
|
41
|
-
|
|
42
|
-
const client = new InfraClient({ apiKey: process.env.INFRA_API_KEY! });
|
|
43
|
-
|
|
44
|
-
const balance = await client.wallet.getBalance();
|
|
45
|
-
console.log(`Business balance: $${balance.balance}`);
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
On the first call, the client trades your API key for a short-lived OAuth bearer token
|
|
49
|
-
(`POST /v1/oauth/token`) and caches it. It refreshes that token before it expires, and
|
|
50
|
-
retries once if it ever gets a surprise `401`. You shouldn't need to touch tokens yourself.
|
|
51
|
-
|
|
52
|
-
There are fuller examples in [`examples/`](./examples):
|
|
53
|
-
|
|
54
|
-
- [`basic-usage.ts`](./examples/basic-usage.ts) derives a wallet, transfers, and polls for
|
|
55
|
-
completion.
|
|
56
|
-
- [`pagination.ts`](./examples/pagination.ts) shows all three ways to consume a paginated
|
|
57
|
-
list.
|
|
58
|
-
- [`tokenization-yield-lifecycle.ts`](./examples/tokenization-yield-lifecycle.ts) walks a
|
|
59
|
-
yield-bearing token from issue through mint, coupon payment, and principal redemption.
|
|
60
|
-
- [`indexer-subscription.ts`](./examples/indexer-subscription.ts) subscribes to on-chain
|
|
61
|
-
events and manages the subscription afterward.
|
|
62
|
-
- [`webhook-verification.ts`](./examples/webhook-verification.ts) verifies both signature
|
|
63
|
-
schemes inside a plain `node:http` server.
|
|
64
|
-
|
|
65
|
-
## Resources
|
|
66
|
-
|
|
67
|
-
There's one namespace per service:
|
|
68
|
-
|
|
69
|
-
| Namespace | Service | Docs |
|
|
70
|
-
|---|---|---|
|
|
71
|
-
| `client.wallet` | HD wallets, on-chain transfers, sponsored signing, USD gas balance | [Wallet overview](https://docs.withconvexity.com/api-reference/wallet/overview) |
|
|
72
|
-
| `client.tokenization` | Issue, mint/burn/transfer tokenized assets; yield lifecycle | [Tokenization overview](https://docs.withconvexity.com/api-reference/tokenization/overview) |
|
|
73
|
-
| `client.indexer` | Subscribe to decoded on-chain events, delivered to your webhook | [Blockchain Events overview](https://docs.withconvexity.com/api-reference/indexer/overview) |
|
|
74
|
-
|
|
75
|
-
The methods map 1:1 onto the API. Check the JSDoc on each resource, or the type
|
|
76
|
-
definitions, for full parameter and return shapes. A few things worth calling out:
|
|
77
|
-
|
|
78
|
-
### Async transfers
|
|
79
|
-
|
|
80
|
-
[`initiateTransfer`](./src/resources/wallet/index.ts) and
|
|
81
|
-
[`signTransaction`](./src/resources/wallet/index.ts) are async by default. They hand back a
|
|
82
|
-
`PENDING` transaction right away (HTTP `202`) and the on-chain execution keeps going in the
|
|
83
|
-
background.
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
const transfer = await client.wallet.initiateTransfer({
|
|
87
|
-
fromAddress: 12, // HD addressIndex, or a full "0x..." address
|
|
88
|
-
toAddress: "0x2222222222222222222222222222222222222222",
|
|
89
|
-
amount: "1000000",
|
|
90
|
-
chainType: "EVM",
|
|
91
|
-
chainId: 8453,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Poll until it settles...
|
|
95
|
-
let final = await client.wallet.getTransaction(transfer.id);
|
|
96
|
-
while (final.status === "PENDING" || final.status === "PROCESSING") {
|
|
97
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
98
|
-
final = await client.wallet.getTransaction(transfer.id);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ...or take the outcome at a webhook instead: pass webhookUrl/webhookSecret on the
|
|
102
|
-
// initiate call, then verify deliveries with `verifyWalletWebhookSignature`.
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
If you want the old synchronous behavior, pass `waitForCompletion: true`. That holds the
|
|
106
|
-
request open until the transaction settles and returns the final result.
|
|
107
|
-
|
|
108
|
-
### Pagination
|
|
109
|
-
|
|
110
|
-
The API hands back three different pagination envelopes depending on which service you're
|
|
111
|
-
hitting. The SDK flattens all of them into a single `Page<T>`:
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
// Iterate every item; later pages are fetched as you go.
|
|
115
|
-
for await (const token of await client.tokenization.listTokens()) {
|
|
116
|
-
console.log(token.ticker);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Or walk it a page at a time:
|
|
120
|
-
let page = await client.wallet.getTransactionHistory({ status: "COMPLETED" });
|
|
121
|
-
for (;;) {
|
|
122
|
-
for (const tx of page.data) handle(tx);
|
|
123
|
-
if (!page.hasNextPage()) break;
|
|
124
|
-
page = await page.getNextPage();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Or just pull the whole thing into memory, which is fine for small collections:
|
|
128
|
-
const holders = await (await client.tokenization.getTokenHolders({ tokenId })).toArray();
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### Idempotency
|
|
132
|
-
|
|
133
|
-
`initiateTransfer`, `signTransaction`, and `generateWallets` all require an
|
|
134
|
-
`Idempotency-Key` header
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
console.log(
|
|
156
|
-
} else if (err instanceof
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
console.log(err.
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
npm
|
|
247
|
-
npm
|
|
248
|
-
npm run
|
|
249
|
-
npm
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
1
|
+
# infra-sdk-typescript
|
|
2
|
+
|
|
3
|
+
The official TypeScript SDK for the [Convexity Infra API](https://docs.withconvexity.com).
|
|
4
|
+
It covers three services: **Wallet**, **Tokenization**, and **Blockchain Events** (the
|
|
5
|
+
Indexer).
|
|
6
|
+
|
|
7
|
+
What you get:
|
|
8
|
+
|
|
9
|
+
- Types for every request and response, including all three of the pagination envelopes
|
|
10
|
+
the API uses.
|
|
11
|
+
- Auth handled for you. Hand the client an API key and it mints, caches, and refreshes
|
|
12
|
+
OAuth bearer tokens in the background.
|
|
13
|
+
- Retries with backoff on rate limits (`429`, honoring `retryAfter`) and upstream outages
|
|
14
|
+
(`503`). Connection failures get retried too, but only when a retry is safe.
|
|
15
|
+
- `Idempotency-Key` headers generated automatically on the wallet operations that move
|
|
16
|
+
value. You can pass your own instead.
|
|
17
|
+
- Real error classes (`RateLimitError`, `NotFoundError`, `PaymentRequiredError`, and so
|
|
18
|
+
on) so you're not switching on status codes.
|
|
19
|
+
- Signature verification for both webhook schemes: Wallet outcome webhooks and Indexer
|
|
20
|
+
event deliveries.
|
|
21
|
+
- No runtime dependencies. It's built on native `fetch` and `node:crypto`, and ships dual
|
|
22
|
+
ESM/CJS builds with full `.d.ts` types.
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- Node.js 18 or later.
|
|
27
|
+
- A Convexity Infra API key. There's no self-service signup yet, so email
|
|
28
|
+
[infra@withconvexity.com](mailto:infra@withconvexity.com) with your business details and
|
|
29
|
+
you'll get back a test key (`sk_test_...`) and a live key (`sk_live_...`).
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install infra-sdk-typescript
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quickstart
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { InfraClient } from "infra-sdk-typescript";
|
|
41
|
+
|
|
42
|
+
const client = new InfraClient({ apiKey: process.env.INFRA_API_KEY! });
|
|
43
|
+
|
|
44
|
+
const balance = await client.wallet.getBalance();
|
|
45
|
+
console.log(`Business balance: $${balance.balance}`);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
On the first call, the client trades your API key for a short-lived OAuth bearer token
|
|
49
|
+
(`POST /v1/oauth/token`) and caches it. It refreshes that token before it expires, and
|
|
50
|
+
retries once if it ever gets a surprise `401`. You shouldn't need to touch tokens yourself.
|
|
51
|
+
|
|
52
|
+
There are fuller examples in [`examples/`](./examples):
|
|
53
|
+
|
|
54
|
+
- [`basic-usage.ts`](./examples/basic-usage.ts) derives a wallet, transfers, and polls for
|
|
55
|
+
completion.
|
|
56
|
+
- [`pagination.ts`](./examples/pagination.ts) shows all three ways to consume a paginated
|
|
57
|
+
list.
|
|
58
|
+
- [`tokenization-yield-lifecycle.ts`](./examples/tokenization-yield-lifecycle.ts) walks a
|
|
59
|
+
yield-bearing token from issue through mint, coupon payment, and principal redemption.
|
|
60
|
+
- [`indexer-subscription.ts`](./examples/indexer-subscription.ts) subscribes to on-chain
|
|
61
|
+
events and manages the subscription afterward.
|
|
62
|
+
- [`webhook-verification.ts`](./examples/webhook-verification.ts) verifies both signature
|
|
63
|
+
schemes inside a plain `node:http` server.
|
|
64
|
+
|
|
65
|
+
## Resources
|
|
66
|
+
|
|
67
|
+
There's one namespace per service:
|
|
68
|
+
|
|
69
|
+
| Namespace | Service | Docs |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `client.wallet` | HD wallets, on-chain transfers, sponsored signing, USD gas balance | [Wallet overview](https://docs.withconvexity.com/api-reference/wallet/overview) |
|
|
72
|
+
| `client.tokenization` | Issue, mint/burn/transfer tokenized assets; yield lifecycle | [Tokenization overview](https://docs.withconvexity.com/api-reference/tokenization/overview) |
|
|
73
|
+
| `client.indexer` | Subscribe to decoded on-chain events, delivered to your webhook | [Blockchain Events overview](https://docs.withconvexity.com/api-reference/indexer/overview) |
|
|
74
|
+
|
|
75
|
+
The methods map 1:1 onto the API. Check the JSDoc on each resource, or the type
|
|
76
|
+
definitions, for full parameter and return shapes. A few things worth calling out:
|
|
77
|
+
|
|
78
|
+
### Async transfers
|
|
79
|
+
|
|
80
|
+
[`initiateTransfer`](./src/resources/wallet/index.ts) and
|
|
81
|
+
[`signTransaction`](./src/resources/wallet/index.ts) are async by default. They hand back a
|
|
82
|
+
`PENDING` transaction right away (HTTP `202`) and the on-chain execution keeps going in the
|
|
83
|
+
background.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const transfer = await client.wallet.initiateTransfer({
|
|
87
|
+
fromAddress: 12, // HD addressIndex, or a full "0x..." address
|
|
88
|
+
toAddress: "0x2222222222222222222222222222222222222222",
|
|
89
|
+
amount: "1000000",
|
|
90
|
+
chainType: "EVM",
|
|
91
|
+
chainId: 8453,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Poll until it settles...
|
|
95
|
+
let final = await client.wallet.getTransaction(transfer.id);
|
|
96
|
+
while (final.status === "PENDING" || final.status === "PROCESSING") {
|
|
97
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
98
|
+
final = await client.wallet.getTransaction(transfer.id);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ...or take the outcome at a webhook instead: pass webhookUrl/webhookSecret on the
|
|
102
|
+
// initiate call, then verify deliveries with `verifyWalletWebhookSignature`.
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If you want the old synchronous behavior, pass `waitForCompletion: true`. That holds the
|
|
106
|
+
request open until the transaction settles and returns the final result.
|
|
107
|
+
|
|
108
|
+
### Pagination
|
|
109
|
+
|
|
110
|
+
The API hands back three different pagination envelopes depending on which service you're
|
|
111
|
+
hitting. The SDK flattens all of them into a single `Page<T>`:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// Iterate every item; later pages are fetched as you go.
|
|
115
|
+
for await (const token of await client.tokenization.listTokens()) {
|
|
116
|
+
console.log(token.ticker);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Or walk it a page at a time:
|
|
120
|
+
let page = await client.wallet.getTransactionHistory({ status: "COMPLETED" });
|
|
121
|
+
for (;;) {
|
|
122
|
+
for (const tx of page.data) handle(tx);
|
|
123
|
+
if (!page.hasNextPage()) break;
|
|
124
|
+
page = await page.getNextPage();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Or just pull the whole thing into memory, which is fine for small collections:
|
|
128
|
+
const holders = await (await client.tokenization.getTokenHolders({ tokenId })).toArray();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Idempotency
|
|
132
|
+
|
|
133
|
+
`initiateTransfer`, `signTransaction`, and `generateWallets` all require an
|
|
134
|
+
`Idempotency-Key` header, as do the tokenization write methods (`createToken`, `mintToken`,
|
|
135
|
+
`burnToken`, `transferToken`, `forcedTransfer`, `registerWallet`, `distributeYield`,
|
|
136
|
+
`payCoupon`, `claimYield`, `redeemPrincipal`). The SDK generates a UUIDv4 per call. Pass your own when you need
|
|
137
|
+
a retry to line up across two separate SDK calls, like after a process restart:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
await client.wallet.initiateTransfer(params, { idempotencyKey: "my-own-key" });
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Errors
|
|
144
|
+
|
|
145
|
+
Everything the SDK throws extends `InfraError`. HTTP failures extend `InfraAPIError` and
|
|
146
|
+
line up with the status code table in the API docs:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { RateLimitError, PaymentRequiredError, UnprocessableEntityError, InfraAPIError } from "infra-sdk-typescript";
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
await client.wallet.initiateTransfer(params);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
if (err instanceof RateLimitError) {
|
|
155
|
+
console.log(`Throttled — retry after ${err.retryAfterSeconds}s`);
|
|
156
|
+
} else if (err instanceof PaymentRequiredError) {
|
|
157
|
+
console.log("Insufficient gas balance");
|
|
158
|
+
} else if (err instanceof UnprocessableEntityError) {
|
|
159
|
+
// A waitForCompletion: true transfer that reverts on-chain still carries the failed
|
|
160
|
+
// transaction. Read it off the raw response body if you need the details.
|
|
161
|
+
console.log((err.body as { data?: unknown }).data);
|
|
162
|
+
} else if (err instanceof InfraAPIError) {
|
|
163
|
+
console.log(err.status, err.message, err.errors);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`429` and `503` get retried for you (twice by default). Everything else throws right away,
|
|
169
|
+
because sending the identical request again won't change the answer.
|
|
170
|
+
|
|
171
|
+
### Retries, timeouts, and connection safety
|
|
172
|
+
|
|
173
|
+
- `429` and `503` are always retried. The server never executed the request in either
|
|
174
|
+
case, so there's nothing to double up on. `429` retries wait for whatever `retryAfter`
|
|
175
|
+
says.
|
|
176
|
+
- Connection failures are murkier. If a request times out, or DNS fails, or TLS breaks, or
|
|
177
|
+
the socket aborts, you can't tell whether the server already ran it. So the SDK only
|
|
178
|
+
retries those on `GET` requests and on requests carrying an `Idempotency-Key`, where a
|
|
179
|
+
duplicate provably can't hurt.
|
|
180
|
+
- Set these on the constructor, or override them per call:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const client = new InfraClient({
|
|
184
|
+
apiKey: process.env.INFRA_API_KEY!,
|
|
185
|
+
timeoutMs: 30_000, // default
|
|
186
|
+
maxRetries: 2, // default
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
await client.wallet.getBalance({ timeoutMs: 5_000, signal: myAbortController.signal });
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Webhooks
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import { verifyWalletWebhookSignature, verifyIndexerWebhookSignature } from "infra-sdk-typescript";
|
|
196
|
+
|
|
197
|
+
// Wallet outcome webhooks (wallet.transfer.completed / .failed, wallet.sign.completed / .failed):
|
|
198
|
+
// HMAC-SHA256("<timestamp>.<rawBody>"), with a 5-minute replay window by default.
|
|
199
|
+
verifyWalletWebhookSignature({
|
|
200
|
+
payload: rawBody, // exactly the bytes you received — don't JSON.parse and re-stringify
|
|
201
|
+
timestampHeader: req.headers["x-wallet-timestamp"],
|
|
202
|
+
signatureHeader: req.headers["x-wallet-signature"],
|
|
203
|
+
secret: webhookSecret, // whatever you passed as `webhookSecret` on the originating call
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Indexer (Blockchain Events) deliveries: HMAC-SHA256(rawBody), no timestamp component.
|
|
207
|
+
verifyIndexerWebhookSignature({
|
|
208
|
+
payload: rawBody,
|
|
209
|
+
signatureHeader: req.headers["x-indexer-signature"],
|
|
210
|
+
secret: signingSecret, // returned once, when you create the subscription or rotate the secret
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Both throw `WebhookSignatureVerificationError` when verification fails. There's a complete
|
|
215
|
+
`node:http` handler in [`examples/webhook-verification.ts`](./examples/webhook-verification.ts).
|
|
216
|
+
|
|
217
|
+
### Escape hatch
|
|
218
|
+
|
|
219
|
+
Every resource method is a thin wrapper around `client.request()`, which carries the same
|
|
220
|
+
auth, retry, and timeout behavior. If this version of the SDK doesn't wrap an endpoint yet,
|
|
221
|
+
call it directly:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
const data = await client.request<{ enabled: boolean }>({
|
|
225
|
+
method: "GET",
|
|
226
|
+
path: "/v1/wallet/hd/enabled",
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Configuration reference
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
new InfraClient({
|
|
234
|
+
apiKey: string; // required — sk_live_... or sk_test_...
|
|
235
|
+
baseURL?: string; // default "https://api.withconvexity.com"
|
|
236
|
+
scope?: string; // space-separated OAuth scopes; omit for full access
|
|
237
|
+
timeoutMs?: number; // default 30000
|
|
238
|
+
maxRetries?: number; // default 2
|
|
239
|
+
fetch?: typeof fetch; // override for testing
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Development
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
npm install
|
|
247
|
+
npm run typecheck # tsc --noEmit
|
|
248
|
+
npm run lint # eslint
|
|
249
|
+
npm test # vitest
|
|
250
|
+
npm run build # tsup -> dist/ (ESM + CJS + .d.ts)
|
|
251
|
+
npm run ci # all of the above, in order
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
MIT. See [LICENSE](./LICENSE).
|
package/dist/index.cjs
CHANGED
|
@@ -572,11 +572,13 @@ var TokenizationResource = class extends APIResource {
|
|
|
572
572
|
* `YIELD_BEARING` token.
|
|
573
573
|
*/
|
|
574
574
|
async createToken(params, overrides = {}) {
|
|
575
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
575
576
|
return this.client.request({
|
|
576
577
|
method: "POST",
|
|
577
578
|
path: "/v1/tokens",
|
|
578
579
|
body: params,
|
|
579
|
-
|
|
580
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
581
|
+
...rest
|
|
580
582
|
});
|
|
581
583
|
}
|
|
582
584
|
/**
|
|
@@ -606,38 +608,61 @@ var TokenizationResource = class extends APIResource {
|
|
|
606
608
|
}
|
|
607
609
|
/** Mints new units of a token to a recipient address. */
|
|
608
610
|
async mintToken(params, overrides = {}) {
|
|
611
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
609
612
|
return this.client.request({
|
|
610
613
|
method: "POST",
|
|
611
614
|
path: "/v1/tokens/mint",
|
|
612
615
|
body: params,
|
|
613
|
-
|
|
616
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
617
|
+
...rest
|
|
614
618
|
});
|
|
615
619
|
}
|
|
616
620
|
/** Burns units of a token from a holder address. */
|
|
617
621
|
async burnToken(params, overrides = {}) {
|
|
622
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
618
623
|
return this.client.request({
|
|
619
624
|
method: "POST",
|
|
620
625
|
path: "/v1/tokens/burn",
|
|
621
626
|
body: params,
|
|
622
|
-
|
|
627
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
628
|
+
...rest
|
|
623
629
|
});
|
|
624
630
|
}
|
|
625
631
|
/** Transfers token units between holder addresses. */
|
|
626
632
|
async transferToken(params, overrides = {}) {
|
|
633
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
627
634
|
return this.client.request({
|
|
628
635
|
method: "POST",
|
|
629
636
|
path: "/v1/tokens/transfer",
|
|
630
637
|
body: params,
|
|
631
|
-
|
|
638
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
639
|
+
...rest
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Force-transfers token units out of a holder's address without the holder's signature
|
|
644
|
+
* (issuer/admin compliance action, e.g. recovery or court order). An `Idempotency-Key` is
|
|
645
|
+
* generated per call unless you pass your own.
|
|
646
|
+
*/
|
|
647
|
+
async forcedTransfer(params, overrides = {}) {
|
|
648
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
649
|
+
return this.client.request({
|
|
650
|
+
method: "POST",
|
|
651
|
+
path: "/v1/tokens/forced-transfer",
|
|
652
|
+
body: params,
|
|
653
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
654
|
+
...rest
|
|
632
655
|
});
|
|
633
656
|
}
|
|
634
657
|
/** Registers (whitelists) a wallet address so it can hold a given token. */
|
|
635
658
|
async registerWallet(params, overrides = {}) {
|
|
659
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
636
660
|
return this.client.request({
|
|
637
661
|
method: "POST",
|
|
638
662
|
path: "/v1/tokens/register-wallet",
|
|
639
663
|
body: params,
|
|
640
|
-
|
|
664
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
665
|
+
...rest
|
|
641
666
|
});
|
|
642
667
|
}
|
|
643
668
|
/** Returns the paginated list of holder wallet addresses and balances for a token on a chain. */
|
|
@@ -650,6 +675,15 @@ var TokenizationResource = class extends APIResource {
|
|
|
650
675
|
}).then((raw) => pageFromFlatResponse(raw, fetchPage));
|
|
651
676
|
return fetchPage({ page: params.page, pageSize: params.pageSize });
|
|
652
677
|
}
|
|
678
|
+
/** Returns a single wallet's balance of a token on a chain. */
|
|
679
|
+
async getWalletBalance(params, overrides = {}) {
|
|
680
|
+
return this.client.request({
|
|
681
|
+
method: "GET",
|
|
682
|
+
path: "/v1/tokens/balance",
|
|
683
|
+
query: { ...params },
|
|
684
|
+
...overrides
|
|
685
|
+
});
|
|
686
|
+
}
|
|
653
687
|
/** Fetches a single token transaction by its reference. */
|
|
654
688
|
async getTokenTransaction(ref, overrides = {}) {
|
|
655
689
|
return this.client.request({
|
|
@@ -679,38 +713,46 @@ var TokenizationResource = class extends APIResource {
|
|
|
679
713
|
}
|
|
680
714
|
/** Funds and distributes a yield payout to holders. Unclaimed funds may be reclaimed after `reclaimAfter`. */
|
|
681
715
|
async distributeYield(params, overrides = {}) {
|
|
716
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
682
717
|
return this.client.request({
|
|
683
718
|
method: "POST",
|
|
684
719
|
path: "/v1/tokens/yield/distribute",
|
|
685
720
|
body: params,
|
|
686
|
-
|
|
721
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
722
|
+
...rest
|
|
687
723
|
});
|
|
688
724
|
}
|
|
689
725
|
/** Pays the scheduled coupon for a yield-bearing token. Set `pushYield` to push funds rather than let investors claim. */
|
|
690
726
|
async payCoupon(params, overrides = {}) {
|
|
727
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
691
728
|
return this.client.request({
|
|
692
729
|
method: "POST",
|
|
693
730
|
path: "/v1/tokens/yield/pay-coupon",
|
|
694
731
|
body: params,
|
|
695
|
-
|
|
732
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
733
|
+
...rest
|
|
696
734
|
});
|
|
697
735
|
}
|
|
698
736
|
/** Claims an investor's yield for a given distribution snapshot. */
|
|
699
737
|
async claimYield(params, overrides = {}) {
|
|
738
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
700
739
|
return this.client.request({
|
|
701
740
|
method: "POST",
|
|
702
741
|
path: "/v1/tokens/yield/claim",
|
|
703
742
|
body: params,
|
|
704
|
-
|
|
743
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
744
|
+
...rest
|
|
705
745
|
});
|
|
706
746
|
}
|
|
707
747
|
/** Redeems principal to holders at maturity. Set `pushYield` to push funds rather than let investors claim. */
|
|
708
748
|
async redeemPrincipal(params, overrides = {}) {
|
|
749
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
709
750
|
return this.client.request({
|
|
710
751
|
method: "POST",
|
|
711
752
|
path: "/v1/tokens/yield/redeem",
|
|
712
753
|
body: params,
|
|
713
|
-
|
|
754
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
755
|
+
...rest
|
|
714
756
|
});
|
|
715
757
|
}
|
|
716
758
|
};
|