@tesser-payments/sdk-ts 0.0.4 → 0.1.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 +56 -39
- package/dist/index.d.ts +1 -1
- package/dist/index.js +348 -26
- package/dist/internal/errors.d.ts +5 -5
- package/dist/internal/types.d.ts +11 -1
- package/dist/internal/version.d.ts +1 -0
- package/dist/signer.d.ts +21 -5
- package/dist/signing/create-wallet.d.ts +2 -1
- package/dist/signing/network-type.d.ts +2 -0
- package/dist/signing/unsigned-payload.d.ts +15 -0
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# @tesser-payments/sdk-ts
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
it. Bring your own client.
|
|
3
|
+
TypeScript SDK for the Tesser API: sign `signCreateWallet` and `signStep`
|
|
4
|
+
requests locally with your signing key. The SDK does not handle OAuth or submit
|
|
5
|
+
requests for you; bring your own client.
|
|
7
6
|
|
|
8
7
|
## Install
|
|
9
8
|
|
|
@@ -12,9 +11,7 @@ bun add @tesser-payments/sdk-ts @tesser-payments/types
|
|
|
12
11
|
```
|
|
13
12
|
|
|
14
13
|
`@tesser-payments/types` is a peer dependency — install it alongside the SDK.
|
|
15
|
-
|
|
16
|
-
`signCreateWallet` and `signStep` activities) and is installed automatically;
|
|
17
|
-
its Apache-2.0 license aligns with the SDK's. Apache 2.0 licensed.
|
|
14
|
+
Apache 2.0 licensed.
|
|
18
15
|
|
|
19
16
|
## Quick start
|
|
20
17
|
|
|
@@ -25,8 +22,9 @@ const signer = new LocalSigner({
|
|
|
25
22
|
signing: {
|
|
26
23
|
publicKey: process.env.SIGNING_PUBLIC_KEY!, // 33-byte compressed P-256
|
|
27
24
|
privateKey: process.env.SIGNING_PRIVATE_KEY!,
|
|
28
|
-
enclaveId: process.env.SIGNING_ENCLAVE_ID!,
|
|
25
|
+
enclaveId: process.env.SIGNING_ENCLAVE_ID!,
|
|
29
26
|
},
|
|
27
|
+
apiBaseUrl: 'https://api.tesser.xyz', // sandbox: https://api-sandbox.tesser.xyz
|
|
30
28
|
});
|
|
31
29
|
|
|
32
30
|
const { signature } = await signer.signCreateWallet({
|
|
@@ -52,6 +50,15 @@ const res = await fetch(`${API_BASE_URL}/v1/accounts/wallets`, {
|
|
|
52
50
|
`signature` is `base64(JSON.stringify({ body, stamp }))` — drop it into the
|
|
53
51
|
Tesser API request body as the documented `signature` field.
|
|
54
52
|
|
|
53
|
+
`LocalSigner` requires the Tesser API base URL (`https://api.tesser.xyz` or
|
|
54
|
+
`https://api-sandbox.tesser.xyz`); a trailing `/` or `/v1` is removed. Pass an
|
|
55
|
+
optional `fetch` implementation to configure a proxy.
|
|
56
|
+
|
|
57
|
+
`signCreateWallet` signs a wallet-creation request. For `stablecoin_ethereum`,
|
|
58
|
+
it first fetches the request to sign from the Tesser API and checks that it
|
|
59
|
+
matches your parameters. The private key never leaves your process. Tesser
|
|
60
|
+
currently supports managed wallets for `stablecoin_ethereum` only.
|
|
61
|
+
|
|
55
62
|
**Private-key handling.** `signer.signing` exposes only `{ publicKey, enclaveId }`.
|
|
56
63
|
The private key lives on the instance as an ECMA private field and is not
|
|
57
64
|
reachable via property access, `console.log`, or `JSON.stringify`. The SDK still
|
|
@@ -59,8 +66,7 @@ uses it internally to stamp activities — you just can't pull it back out.
|
|
|
59
66
|
|
|
60
67
|
## `signStep`
|
|
61
68
|
|
|
62
|
-
`signStep`
|
|
63
|
-
single rebalance step. Pass the unsigned transaction Tesser returned, the
|
|
69
|
+
`signStep` signs a single rebalance step. Pass the unsigned transaction Tesser returned, the
|
|
64
70
|
on-chain `signWith` address, and the network — that's all the SDK needs to
|
|
65
71
|
produce a signature:
|
|
66
72
|
|
|
@@ -71,9 +77,7 @@ const step: StepForSigning = {
|
|
|
71
77
|
unsignedTransaction: '0x02...', // hex-encoded raw tx Tesser returned
|
|
72
78
|
signWith: '0xabc...', // account.crypto_wallet_address from
|
|
73
79
|
// GET /v1/accounts/{from_account_id}
|
|
74
|
-
network: 'BASE_SEPOLIA',
|
|
75
|
-
// ETHEREUM_SEPOLIA | POLYGON |
|
|
76
|
-
// POLYGON_AMOY | SOLANA
|
|
80
|
+
network: 'BASE_SEPOLIA',
|
|
77
81
|
};
|
|
78
82
|
|
|
79
83
|
const signed = await signer.signStep(step);
|
|
@@ -92,29 +96,54 @@ await fetch(
|
|
|
92
96
|
```
|
|
93
97
|
|
|
94
98
|
`signStep` returns `{ signature, metadata }`. The `signature` field is what
|
|
95
|
-
the Tesser API needs; `metadata` carries the
|
|
96
|
-
|
|
99
|
+
the Tesser API needs; `metadata` carries the signed body and stamp header value
|
|
100
|
+
for debugging.
|
|
97
101
|
|
|
98
102
|
The `network` field is typed as `SupportedNetwork`, derived from the SDK's
|
|
99
|
-
network
|
|
103
|
+
supported-network table — so a typo like `'BASE-SEPOLIA'` (hyphen) is a
|
|
100
104
|
compile-time error. If you're pulling the value out of a webhook payload or
|
|
101
105
|
env var, cast it at that boundary.
|
|
102
106
|
|
|
107
|
+
Supported networks are `BASE`, `BASE_SEPOLIA`, `ETHEREUM`, `ETHEREUM_SEPOLIA`,
|
|
108
|
+
`POLYGON`, `POLYGON_AMOY`, `SOLANA`, `TEMPO`, and `TEMPO_MODERATO`.
|
|
109
|
+
|
|
110
|
+
### Tempo transfers
|
|
111
|
+
|
|
112
|
+
1. Pass the Tempo transaction Tesser provides (`0x76...`), your wallet
|
|
113
|
+
address, and `TEMPO` or `TEMPO_MODERATO` to `signStep`.
|
|
114
|
+
2. The SDK authorizes the transaction with your API key, without changing
|
|
115
|
+
it. Submit the returned `signature` to the Tesser signing endpoint.
|
|
116
|
+
3. Tesser completes the transfer, broadcasts it, and pays the network fee.
|
|
117
|
+
|
|
118
|
+
Unknown networks raise `TesserConfigError` before anything is signed.
|
|
119
|
+
|
|
103
120
|
## Error handling
|
|
104
121
|
|
|
105
122
|
The SDK mirrors Tesser's documented error envelope
|
|
106
123
|
(<https://docs.tesser.xyz/overviews/errors>) rather than inventing a custom
|
|
107
|
-
HTTP-status taxonomy
|
|
108
|
-
errors are exported:
|
|
124
|
+
HTTP-status taxonomy:
|
|
109
125
|
|
|
110
126
|
```
|
|
111
127
|
TesserError (base; .cause set on all)
|
|
112
128
|
├── TesserConfigError bad input at construction or call site
|
|
113
|
-
|
|
114
|
-
|
|
129
|
+
├── TesserAPIError Tesser answered with an error status or
|
|
130
|
+
│ an invalid response
|
|
131
|
+
├── TesserConnectionError the request to Tesser failed at the network level
|
|
132
|
+
├── TesserTimeoutError the request to Tesser timed out
|
|
133
|
+
└── TesserSigningError signing failure, or a fetched request that
|
|
134
|
+
│ does not match your parameters
|
|
135
|
+
└── StampError the signing key could not sign
|
|
115
136
|
```
|
|
116
137
|
|
|
117
|
-
|
|
138
|
+
`TesserAPIError`, `TesserConnectionError`, and `TesserTimeoutError` come from
|
|
139
|
+
the request `signCreateWallet` makes to Tesser for `stablecoin_ethereum`.
|
|
140
|
+
`TesserAPIError` carries `status`, `headers`, `requestId`, and the parsed
|
|
141
|
+
`errors[]` (with `errorCode` for the first one). `TesserConnectionError`,
|
|
142
|
+
`TesserTimeoutError`, and a `TesserAPIError` with status 429 or 5xx are safe to
|
|
143
|
+
retry; a `TesserAPIError` with any other 4xx status fails the same way on
|
|
144
|
+
retry.
|
|
145
|
+
|
|
146
|
+
Each error carries the original failure as `.cause` and can be discriminated via
|
|
118
147
|
`instanceof` or the static `.is()` guard:
|
|
119
148
|
|
|
120
149
|
```typescript
|
|
@@ -124,7 +153,7 @@ try {
|
|
|
124
153
|
await signer.signStep(step);
|
|
125
154
|
} catch (err) {
|
|
126
155
|
if (StampError.is(err)) {
|
|
127
|
-
//
|
|
156
|
+
// The key could not sign — usually a key-format problem.
|
|
128
157
|
}
|
|
129
158
|
if (TesserError.is(err)) {
|
|
130
159
|
console.error('Signing failed', { name: err.name, cause: err.cause });
|
|
@@ -133,29 +162,19 @@ try {
|
|
|
133
162
|
}
|
|
134
163
|
```
|
|
135
164
|
|
|
136
|
-
HTTP-side errors (`TesserAPIError`, `TesserConnectionError`,
|
|
137
|
-
`TesserTimeoutError`) will return when the optional bundled client lands in a
|
|
138
|
-
future version. Until then, model HTTP failures with whatever your `fetch`
|
|
139
|
-
wrapper produces.
|
|
140
|
-
|
|
141
165
|
## Setup notes
|
|
142
166
|
|
|
143
|
-
`signing.publicKey` must be the **33-byte compressed** form
|
|
167
|
+
`signing.publicKey` must be the **33-byte compressed** form Tesser uses for
|
|
144
168
|
API-key auth lookups (66 hex chars, prefix `02` or `03`). Dashboards that
|
|
145
169
|
display the 65-byte uncompressed form need to be compressed before use:
|
|
146
170
|
|
|
147
171
|
```sh
|
|
148
|
-
|
|
149
|
-
'const c = require("@turnkey/crypto"); \
|
|
150
|
-
const u = process.argv[1].replace(/^0x/i,""); \
|
|
151
|
-
const b = Buffer.from(u, "hex"); \
|
|
152
|
-
console.log(Buffer.from(c.compressRawPublicKey(b)).toString("hex"))' \
|
|
172
|
+
node -e 'const u = process.argv[1].replace(/^0x/i, ""); console.log(require("node:crypto").ECDH.convertKey(u, "prime256v1", "hex", "hex", "compressed"))' \
|
|
153
173
|
04...your-uncompressed-key...
|
|
154
174
|
```
|
|
155
175
|
|
|
156
|
-
If a key is registered
|
|
157
|
-
|
|
158
|
-
correct — re-register the key in compressed form to fix.
|
|
176
|
+
If a key is registered in uncompressed form, signing is rejected even when the
|
|
177
|
+
keypair is logically correct — re-register the key in compressed form to fix.
|
|
159
178
|
|
|
160
179
|
## Examples
|
|
161
180
|
|
|
@@ -173,9 +192,7 @@ copy [`.env.example`](./.env.example) and fill in the values first.
|
|
|
173
192
|
## Out of scope
|
|
174
193
|
|
|
175
194
|
- **`TesserClient`** — the previous bundled HTTP client (`getAccountAddress`,
|
|
176
|
-
retry/timeout/logger plumbing) is intentionally not
|
|
177
|
-
re-add it later as an opt-in `LocalSignerOptions.client` field; that change
|
|
178
|
-
is purely additive and will not break v0.0.1 call sites.
|
|
195
|
+
retry/timeout/logger plumbing) is intentionally not included.
|
|
179
196
|
- **Webhook signature verification** — the header name and HMAC algorithm
|
|
180
197
|
aren't currently documented in public Tesser docs.
|
|
181
198
|
- **Solana / Stellar `signCreateWallet`** — exported as `@experimental` types;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ export { LocalSigner } from './signer.js';
|
|
|
2
2
|
export type { PublicSigningInfo } from './signer.js';
|
|
3
3
|
export type { CreateWalletParams, LocalSignerOptions, SignedResult, SignedResultMetadata, SignedStepResult, SignedStepResultMetadata, SigningConfig, StepForSigning, WalletType, } from './internal/types.js';
|
|
4
4
|
export type { SupportedNetwork } from './signing/network-type.js';
|
|
5
|
-
export { StampError, TesserConfigError, TesserError, TesserSigningError, } from './internal/errors.js';
|
|
5
|
+
export { StampError, TesserAPIError, TesserConfigError, TesserConnectionError, TesserError, TesserSigningError, TesserTimeoutError, } from './internal/errors.js';
|
|
6
6
|
export type { TesserErrorDetail } from './internal/errors.js';
|
package/dist/index.js
CHANGED
|
@@ -18273,6 +18273,47 @@ class TesserConfigError extends TesserError {
|
|
|
18273
18273
|
return err instanceof TesserConfigError;
|
|
18274
18274
|
}
|
|
18275
18275
|
}
|
|
18276
|
+
|
|
18277
|
+
class TesserAPIError extends TesserError {
|
|
18278
|
+
name = "TesserAPIError";
|
|
18279
|
+
status;
|
|
18280
|
+
headers;
|
|
18281
|
+
requestId;
|
|
18282
|
+
errors;
|
|
18283
|
+
constructor(message, options) {
|
|
18284
|
+
super(message, { cause: options.cause });
|
|
18285
|
+
this.status = options.status;
|
|
18286
|
+
if (options.headers !== undefined)
|
|
18287
|
+
this.headers = options.headers;
|
|
18288
|
+
if (options.requestId !== undefined)
|
|
18289
|
+
this.requestId = options.requestId;
|
|
18290
|
+
this.errors = options.errors ?? [];
|
|
18291
|
+
}
|
|
18292
|
+
get errorCode() {
|
|
18293
|
+
return this.errors[0]?.errorCode;
|
|
18294
|
+
}
|
|
18295
|
+
hasCode(...codes) {
|
|
18296
|
+
return this.errors.some((e) => codes.includes(e.errorCode));
|
|
18297
|
+
}
|
|
18298
|
+
static is(err) {
|
|
18299
|
+
return err instanceof TesserAPIError;
|
|
18300
|
+
}
|
|
18301
|
+
}
|
|
18302
|
+
|
|
18303
|
+
class TesserConnectionError extends TesserError {
|
|
18304
|
+
name = "TesserConnectionError";
|
|
18305
|
+
static is(err) {
|
|
18306
|
+
return err instanceof TesserConnectionError;
|
|
18307
|
+
}
|
|
18308
|
+
}
|
|
18309
|
+
|
|
18310
|
+
class TesserTimeoutError extends TesserError {
|
|
18311
|
+
name = "TesserTimeoutError";
|
|
18312
|
+
static is(err) {
|
|
18313
|
+
return err instanceof TesserTimeoutError;
|
|
18314
|
+
}
|
|
18315
|
+
}
|
|
18316
|
+
|
|
18276
18317
|
class TesserSigningError extends TesserError {
|
|
18277
18318
|
name = "TesserSigningError";
|
|
18278
18319
|
static is(err) {
|
|
@@ -19814,7 +19855,7 @@ var DNS = "dns";
|
|
|
19814
19855
|
var DN = "dn";
|
|
19815
19856
|
var EMAIL = "email";
|
|
19816
19857
|
var IP = "ip";
|
|
19817
|
-
var
|
|
19858
|
+
var URL2 = "url";
|
|
19818
19859
|
var GUID = "guid";
|
|
19819
19860
|
var UPN = "upn";
|
|
19820
19861
|
var REGISTERED_ID = "id";
|
|
@@ -19870,7 +19911,7 @@ class GeneralName extends AsnData {
|
|
|
19870
19911
|
});
|
|
19871
19912
|
break;
|
|
19872
19913
|
}
|
|
19873
|
-
case
|
|
19914
|
+
case URL2:
|
|
19874
19915
|
name = new asn1X509__namespace.GeneralName({ uniformResourceIdentifier: args[1] });
|
|
19875
19916
|
break;
|
|
19876
19917
|
default:
|
|
@@ -19894,7 +19935,7 @@ class GeneralName extends AsnData {
|
|
|
19894
19935
|
this.type = IP;
|
|
19895
19936
|
this.value = asn.iPAddress;
|
|
19896
19937
|
} else if (asn.uniformResourceIdentifier != null) {
|
|
19897
|
-
this.type =
|
|
19938
|
+
this.type = URL2;
|
|
19898
19939
|
this.value = asn.uniformResourceIdentifier;
|
|
19899
19940
|
} else if (asn.registeredID != null) {
|
|
19900
19941
|
this.type = REGISTERED_ID;
|
|
@@ -19941,7 +19982,7 @@ class GeneralName extends AsnData {
|
|
|
19941
19982
|
case IP:
|
|
19942
19983
|
case REGISTERED_ID:
|
|
19943
19984
|
case UPN:
|
|
19944
|
-
case
|
|
19985
|
+
case URL2:
|
|
19945
19986
|
type = this.type.toUpperCase();
|
|
19946
19987
|
break;
|
|
19947
19988
|
case EMAIL:
|
|
@@ -21431,16 +21472,273 @@ async function stamp(keys, body) {
|
|
|
21431
21472
|
}
|
|
21432
21473
|
}
|
|
21433
21474
|
|
|
21475
|
+
// src/internal/version.ts
|
|
21476
|
+
var SDK_VERSION = "0.1.0";
|
|
21477
|
+
|
|
21478
|
+
// src/signing/unsigned-payload.ts
|
|
21479
|
+
var UNSIGNED_PAYLOAD_PATH = "/v1/accounts/wallets/unsigned-payload";
|
|
21480
|
+
var CREATE_WALLET_ACCOUNTS_ACTIVITY_TYPE = "ACTIVITY_TYPE_CREATE_WALLET_ACCOUNTS";
|
|
21481
|
+
var ETHEREUM_ADDRESS_FORMAT = "ADDRESS_FORMAT_ETHEREUM";
|
|
21482
|
+
var UNSIGNED_PAYLOAD_TIMEOUT_MS = 1e4;
|
|
21483
|
+
var LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
21484
|
+
var MAX_FAILURE_CHAIN_DEPTH = 10;
|
|
21485
|
+
function createTesserApiConnection(options) {
|
|
21486
|
+
return {
|
|
21487
|
+
baseUrl: normalizeApiBaseUrl(options.apiBaseUrl),
|
|
21488
|
+
fetch: resolveFetchImplementation(options.fetch)
|
|
21489
|
+
};
|
|
21490
|
+
}
|
|
21491
|
+
function resolveFetchImplementation(injectedFetch) {
|
|
21492
|
+
if (injectedFetch !== undefined) {
|
|
21493
|
+
return injectedFetch;
|
|
21494
|
+
}
|
|
21495
|
+
return globalThis.fetch;
|
|
21496
|
+
}
|
|
21497
|
+
function normalizeApiBaseUrl(apiBaseUrl) {
|
|
21498
|
+
if (typeof apiBaseUrl !== "string") {
|
|
21499
|
+
throw invalidApiBaseUrlError();
|
|
21500
|
+
}
|
|
21501
|
+
const withoutTrailingSlash = removeSuffix(apiBaseUrl.trim(), "/");
|
|
21502
|
+
const normalized = removeSuffix(removeSuffix(withoutTrailingSlash, "/v1"), "/");
|
|
21503
|
+
if (!isHttpsOrLoopbackHttpUrl(normalized)) {
|
|
21504
|
+
throw invalidApiBaseUrlError();
|
|
21505
|
+
}
|
|
21506
|
+
return normalized;
|
|
21507
|
+
}
|
|
21508
|
+
function invalidApiBaseUrlError() {
|
|
21509
|
+
return new TesserConfigError("LocalSigner: apiBaseUrl must be an https URL, for example https://api.tesser.xyz");
|
|
21510
|
+
}
|
|
21511
|
+
function removeSuffix(value, suffix) {
|
|
21512
|
+
if (value.endsWith(suffix)) {
|
|
21513
|
+
return value.slice(0, value.length - suffix.length);
|
|
21514
|
+
}
|
|
21515
|
+
return value;
|
|
21516
|
+
}
|
|
21517
|
+
function isHttpsOrLoopbackHttpUrl(value) {
|
|
21518
|
+
const url = parseUrl(value);
|
|
21519
|
+
if (url === undefined || url.hostname === "") {
|
|
21520
|
+
return false;
|
|
21521
|
+
}
|
|
21522
|
+
if (url.protocol === "https:") {
|
|
21523
|
+
return true;
|
|
21524
|
+
}
|
|
21525
|
+
return url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname.toLowerCase());
|
|
21526
|
+
}
|
|
21527
|
+
function parseUrl(value) {
|
|
21528
|
+
try {
|
|
21529
|
+
return new URL(value);
|
|
21530
|
+
} catch {
|
|
21531
|
+
return;
|
|
21532
|
+
}
|
|
21533
|
+
}
|
|
21534
|
+
async function fetchCheckedCreateWalletAccountsBody(options) {
|
|
21535
|
+
const { api, signing, params } = options;
|
|
21536
|
+
const query = buildUnsignedPayloadQuery(signing, params);
|
|
21537
|
+
const stamped = await stamp({ publicKey: signing.publicKey, privateKey: signing.privateKey }, buildCanonicalUnsignedPayloadMessage(query));
|
|
21538
|
+
const response = await sendUnsignedPayloadRequest(api, buildUnsignedPayloadUrl(api, query), stamped);
|
|
21539
|
+
const body = parseUnsignedPayloadBody(response);
|
|
21540
|
+
assertUnsignedPayloadMatchesRequest(body, signing, params);
|
|
21541
|
+
return body;
|
|
21542
|
+
}
|
|
21543
|
+
function buildUnsignedPayloadQuery(signing, params) {
|
|
21544
|
+
return {
|
|
21545
|
+
organizationId: signing.enclaveId,
|
|
21546
|
+
name: params.name,
|
|
21547
|
+
type: params.type,
|
|
21548
|
+
timestampMs: String(Date.now())
|
|
21549
|
+
};
|
|
21550
|
+
}
|
|
21551
|
+
function buildCanonicalUnsignedPayloadMessage(query) {
|
|
21552
|
+
return JSON.stringify({
|
|
21553
|
+
organizationId: query.organizationId,
|
|
21554
|
+
name: query.name,
|
|
21555
|
+
type: query.type,
|
|
21556
|
+
timestampMs: query.timestampMs
|
|
21557
|
+
});
|
|
21558
|
+
}
|
|
21559
|
+
function buildUnsignedPayloadUrl(api, query) {
|
|
21560
|
+
const queryEntries = [
|
|
21561
|
+
["organizationId", query.organizationId],
|
|
21562
|
+
["name", query.name],
|
|
21563
|
+
["type", query.type],
|
|
21564
|
+
["timestampMs", query.timestampMs]
|
|
21565
|
+
];
|
|
21566
|
+
const queryString = queryEntries.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&");
|
|
21567
|
+
return `${api.baseUrl}${UNSIGNED_PAYLOAD_PATH}?${queryString}`;
|
|
21568
|
+
}
|
|
21569
|
+
async function sendUnsignedPayloadRequest(api, url, stamped) {
|
|
21570
|
+
const fetchImplementation = api.fetch;
|
|
21571
|
+
try {
|
|
21572
|
+
const response = await fetchImplementation(url, {
|
|
21573
|
+
method: "GET",
|
|
21574
|
+
headers: {
|
|
21575
|
+
Accept: "application/json",
|
|
21576
|
+
[stamped.stampHeaderName]: stamped.stampHeaderValue,
|
|
21577
|
+
"User-Agent": `tesser-sdk-ts/${SDK_VERSION}`
|
|
21578
|
+
},
|
|
21579
|
+
signal: AbortSignal.timeout(UNSIGNED_PAYLOAD_TIMEOUT_MS)
|
|
21580
|
+
});
|
|
21581
|
+
const text = await response.text();
|
|
21582
|
+
return { status: response.status, headers: response.headers, text };
|
|
21583
|
+
} catch (cause) {
|
|
21584
|
+
throw toTransportError(cause);
|
|
21585
|
+
}
|
|
21586
|
+
}
|
|
21587
|
+
function toTransportError(cause) {
|
|
21588
|
+
if (isTimeoutFailure(cause)) {
|
|
21589
|
+
return new TesserTimeoutError(`Unsigned payload request timed out after ${UNSIGNED_PAYLOAD_TIMEOUT_MS} ms`, { cause });
|
|
21590
|
+
}
|
|
21591
|
+
return new TesserConnectionError(`Unsigned payload request failed: ${describeTransportFailure(cause)}`, { cause });
|
|
21592
|
+
}
|
|
21593
|
+
function isTimeoutFailure(cause) {
|
|
21594
|
+
return readErrorName(cause) === "TimeoutError";
|
|
21595
|
+
}
|
|
21596
|
+
function describeTransportFailure(cause) {
|
|
21597
|
+
const failureChain = collectFailureChain(cause);
|
|
21598
|
+
const messages = failureChain.map(readErrorMessage).filter(isNonBlank);
|
|
21599
|
+
if (messages.length > 0) {
|
|
21600
|
+
return [...new Set(messages)].join(": ");
|
|
21601
|
+
}
|
|
21602
|
+
const names2 = failureChain.map(readErrorName).filter(isNonBlank);
|
|
21603
|
+
return names2[names2.length - 1] ?? "Unknown transport failure";
|
|
21604
|
+
}
|
|
21605
|
+
function collectFailureChain(cause) {
|
|
21606
|
+
const failureChain = [];
|
|
21607
|
+
let current = cause;
|
|
21608
|
+
while (isObject(current) && failureChain.length < MAX_FAILURE_CHAIN_DEPTH) {
|
|
21609
|
+
if (failureChain.includes(current)) {
|
|
21610
|
+
break;
|
|
21611
|
+
}
|
|
21612
|
+
failureChain.push(current);
|
|
21613
|
+
current = current.cause;
|
|
21614
|
+
}
|
|
21615
|
+
return failureChain;
|
|
21616
|
+
}
|
|
21617
|
+
function readErrorMessage(failure) {
|
|
21618
|
+
return readStringField(failure, "message");
|
|
21619
|
+
}
|
|
21620
|
+
function readErrorName(failure) {
|
|
21621
|
+
return readStringField(failure, "name");
|
|
21622
|
+
}
|
|
21623
|
+
function readStringField(source, key) {
|
|
21624
|
+
if (!isObject(source)) {
|
|
21625
|
+
return;
|
|
21626
|
+
}
|
|
21627
|
+
const value = source[key];
|
|
21628
|
+
if (typeof value !== "string") {
|
|
21629
|
+
return;
|
|
21630
|
+
}
|
|
21631
|
+
return value;
|
|
21632
|
+
}
|
|
21633
|
+
function isNonBlank(value) {
|
|
21634
|
+
return value !== undefined && value.trim() !== "";
|
|
21635
|
+
}
|
|
21636
|
+
function isObject(value) {
|
|
21637
|
+
return typeof value === "object" && value !== null;
|
|
21638
|
+
}
|
|
21639
|
+
function readGatewayRequestId(headers) {
|
|
21640
|
+
return headers.get("zp-rid") ?? headers.get("x-request-id") ?? undefined;
|
|
21641
|
+
}
|
|
21642
|
+
function parseUnsignedPayloadBody(response) {
|
|
21643
|
+
if (response.status < 200 || response.status > 299) {
|
|
21644
|
+
throw buildUnsignedPayloadApiError(response);
|
|
21645
|
+
}
|
|
21646
|
+
const body = readResponseDataBody(response.text);
|
|
21647
|
+
if (body === undefined) {
|
|
21648
|
+
throw new TesserAPIError("Unsigned payload response is not valid: data.body is missing", buildApiErrorOptions(response, []));
|
|
21649
|
+
}
|
|
21650
|
+
return body;
|
|
21651
|
+
}
|
|
21652
|
+
function readResponseDataBody(text) {
|
|
21653
|
+
const envelope = parseJsonObject(text);
|
|
21654
|
+
if (envelope === undefined || !isObject(envelope.data)) {
|
|
21655
|
+
return;
|
|
21656
|
+
}
|
|
21657
|
+
return readStringField(envelope.data, "body");
|
|
21658
|
+
}
|
|
21659
|
+
function buildUnsignedPayloadApiError(response) {
|
|
21660
|
+
return new TesserAPIError(`Unsigned payload request returned HTTP ${response.status}: ${response.text}`, buildApiErrorOptions(response, parseErrorDetails(response.text)));
|
|
21661
|
+
}
|
|
21662
|
+
function buildApiErrorOptions(response, errors) {
|
|
21663
|
+
const requestId = readGatewayRequestId(response.headers);
|
|
21664
|
+
if (requestId === undefined) {
|
|
21665
|
+
return { status: response.status, headers: response.headers, errors };
|
|
21666
|
+
}
|
|
21667
|
+
return { status: response.status, headers: response.headers, requestId, errors };
|
|
21668
|
+
}
|
|
21669
|
+
function parseErrorDetails(text) {
|
|
21670
|
+
const envelope = parseJsonObject(text);
|
|
21671
|
+
if (envelope === undefined || !Array.isArray(envelope.errors)) {
|
|
21672
|
+
return [];
|
|
21673
|
+
}
|
|
21674
|
+
return envelope.errors.flatMap(toErrorDetail);
|
|
21675
|
+
}
|
|
21676
|
+
function toErrorDetail(entry) {
|
|
21677
|
+
const errorCode = readStringField(entry, "error_code");
|
|
21678
|
+
const errorMessage = readStringField(entry, "error_message");
|
|
21679
|
+
if (errorCode === undefined || errorMessage === undefined) {
|
|
21680
|
+
return [];
|
|
21681
|
+
}
|
|
21682
|
+
const uiMessage = readStringField(entry, "ui_message");
|
|
21683
|
+
if (uiMessage === undefined) {
|
|
21684
|
+
return [{ errorCode, errorMessage }];
|
|
21685
|
+
}
|
|
21686
|
+
return [{ errorCode, errorMessage, uiMessage }];
|
|
21687
|
+
}
|
|
21688
|
+
function parseJsonObject(text) {
|
|
21689
|
+
try {
|
|
21690
|
+
const parsed = JSON.parse(text);
|
|
21691
|
+
if (isObject(parsed) && !Array.isArray(parsed)) {
|
|
21692
|
+
return parsed;
|
|
21693
|
+
}
|
|
21694
|
+
return;
|
|
21695
|
+
} catch {
|
|
21696
|
+
return;
|
|
21697
|
+
}
|
|
21698
|
+
}
|
|
21699
|
+
function assertUnsignedPayloadMatchesRequest(body, signing, params) {
|
|
21700
|
+
const payload = parseJsonObject(body);
|
|
21701
|
+
if (payload === undefined) {
|
|
21702
|
+
throw rejectUnsignedPayload("body is not a JSON object");
|
|
21703
|
+
}
|
|
21704
|
+
if (readStringField(payload, "type") !== CREATE_WALLET_ACCOUNTS_ACTIVITY_TYPE) {
|
|
21705
|
+
throw rejectUnsignedPayload(`type is not ${CREATE_WALLET_ACCOUNTS_ACTIVITY_TYPE}`);
|
|
21706
|
+
}
|
|
21707
|
+
if (readStringField(payload, "organizationId") !== signing.enclaveId) {
|
|
21708
|
+
throw rejectUnsignedPayload("organizationId does not match the signing enclaveId");
|
|
21709
|
+
}
|
|
21710
|
+
const account = readSingleAccount(payload);
|
|
21711
|
+
if (readStringField(account, "name") !== params.name) {
|
|
21712
|
+
throw rejectUnsignedPayload("account name does not match the requested wallet name");
|
|
21713
|
+
}
|
|
21714
|
+
if (readStringField(account, "addressFormat") !== ETHEREUM_ADDRESS_FORMAT) {
|
|
21715
|
+
throw rejectUnsignedPayload(`account addressFormat is not ${ETHEREUM_ADDRESS_FORMAT}`);
|
|
21716
|
+
}
|
|
21717
|
+
}
|
|
21718
|
+
function readSingleAccount(payload) {
|
|
21719
|
+
const parameters = payload.parameters;
|
|
21720
|
+
if (!isObject(parameters)) {
|
|
21721
|
+
throw rejectUnsignedPayload("parameters is missing");
|
|
21722
|
+
}
|
|
21723
|
+
const accounts = parameters.accounts;
|
|
21724
|
+
if (!Array.isArray(accounts)) {
|
|
21725
|
+
throw rejectUnsignedPayload("parameters.accounts is missing");
|
|
21726
|
+
}
|
|
21727
|
+
if (accounts.length !== 1) {
|
|
21728
|
+
throw rejectUnsignedPayload(`expected exactly one account, got ${accounts.length}`);
|
|
21729
|
+
}
|
|
21730
|
+
const account = accounts[0];
|
|
21731
|
+
if (!isObject(account) || Array.isArray(account)) {
|
|
21732
|
+
throw rejectUnsignedPayload("account is not a JSON object");
|
|
21733
|
+
}
|
|
21734
|
+
return account;
|
|
21735
|
+
}
|
|
21736
|
+
function rejectUnsignedPayload(reason) {
|
|
21737
|
+
return new TesserSigningError(`Unsigned payload rejected: ${reason}`);
|
|
21738
|
+
}
|
|
21739
|
+
|
|
21434
21740
|
// src/signing/create-wallet.ts
|
|
21435
21741
|
var WALLET_TYPE_ACCOUNTS = {
|
|
21436
|
-
stablecoin_ethereum: [
|
|
21437
|
-
{
|
|
21438
|
-
curve: "CURVE_SECP256K1",
|
|
21439
|
-
pathFormat: "PATH_FORMAT_BIP32",
|
|
21440
|
-
path: "m/44'/60'/0'/0/0",
|
|
21441
|
-
addressFormat: "ADDRESS_FORMAT_ETHEREUM"
|
|
21442
|
-
}
|
|
21443
|
-
],
|
|
21444
21742
|
stablecoin_solana: [
|
|
21445
21743
|
{
|
|
21446
21744
|
curve: "CURVE_ED25519",
|
|
@@ -21458,21 +21756,16 @@ var WALLET_TYPE_ACCOUNTS = {
|
|
|
21458
21756
|
}
|
|
21459
21757
|
]
|
|
21460
21758
|
};
|
|
21461
|
-
var VALID_WALLET_TYPES =
|
|
21462
|
-
|
|
21759
|
+
var VALID_WALLET_TYPES = [
|
|
21760
|
+
"stablecoin_ethereum",
|
|
21761
|
+
"stablecoin_solana",
|
|
21762
|
+
"stablecoin_stellar"
|
|
21763
|
+
];
|
|
21764
|
+
async function signCreateWallet(signing, params, api) {
|
|
21463
21765
|
if (!VALID_WALLET_TYPES.includes(params.type)) {
|
|
21464
21766
|
throw new TesserConfigError(`signCreateWallet: type '${String(params.type)}' is not a valid WalletType. ` + `Valid values: ${VALID_WALLET_TYPES.join(", ")}.`);
|
|
21465
21767
|
}
|
|
21466
|
-
const
|
|
21467
|
-
const body = JSON.stringify({
|
|
21468
|
-
type: "ACTIVITY_TYPE_CREATE_WALLET",
|
|
21469
|
-
timestampMs: String(Date.now()),
|
|
21470
|
-
organizationId: signing.enclaveId,
|
|
21471
|
-
parameters: {
|
|
21472
|
-
walletName: params.name,
|
|
21473
|
-
accounts
|
|
21474
|
-
}
|
|
21475
|
-
});
|
|
21768
|
+
const body = await resolveCreateWalletBody({ signing, params, api });
|
|
21476
21769
|
const result = await stamp({ publicKey: signing.publicKey, privateKey: signing.privateKey }, body);
|
|
21477
21770
|
const signature = Buffer.from(JSON.stringify({ body, stamp: result.stampHeaderValue })).toString("base64");
|
|
21478
21771
|
return {
|
|
@@ -21484,6 +21777,28 @@ async function signCreateWallet(signing, params) {
|
|
|
21484
21777
|
}
|
|
21485
21778
|
};
|
|
21486
21779
|
}
|
|
21780
|
+
async function resolveCreateWalletBody(options) {
|
|
21781
|
+
const { signing, params, api } = options;
|
|
21782
|
+
if (params.type === "stablecoin_ethereum") {
|
|
21783
|
+
return fetchCheckedCreateWalletAccountsBody({ api, signing, params });
|
|
21784
|
+
}
|
|
21785
|
+
return buildCreateWalletBody({
|
|
21786
|
+
enclaveId: signing.enclaveId,
|
|
21787
|
+
name: params.name,
|
|
21788
|
+
type: params.type
|
|
21789
|
+
});
|
|
21790
|
+
}
|
|
21791
|
+
function buildCreateWalletBody(options) {
|
|
21792
|
+
return JSON.stringify({
|
|
21793
|
+
type: "ACTIVITY_TYPE_CREATE_WALLET",
|
|
21794
|
+
timestampMs: String(Date.now()),
|
|
21795
|
+
organizationId: options.enclaveId,
|
|
21796
|
+
parameters: {
|
|
21797
|
+
walletName: options.name,
|
|
21798
|
+
accounts: WALLET_TYPE_ACCOUNTS[options.type]
|
|
21799
|
+
}
|
|
21800
|
+
});
|
|
21801
|
+
}
|
|
21487
21802
|
|
|
21488
21803
|
// src/signing/network-type.ts
|
|
21489
21804
|
var NETWORK_TO_TURNKEY_TYPE = {
|
|
@@ -21493,7 +21808,9 @@ var NETWORK_TO_TURNKEY_TYPE = {
|
|
|
21493
21808
|
ETHEREUM_SEPOLIA: "TRANSACTION_TYPE_ETHEREUM",
|
|
21494
21809
|
POLYGON: "TRANSACTION_TYPE_ETHEREUM",
|
|
21495
21810
|
POLYGON_AMOY: "TRANSACTION_TYPE_ETHEREUM",
|
|
21496
|
-
SOLANA: "TRANSACTION_TYPE_SOLANA"
|
|
21811
|
+
SOLANA: "TRANSACTION_TYPE_SOLANA",
|
|
21812
|
+
TEMPO: "TRANSACTION_TYPE_TEMPO",
|
|
21813
|
+
TEMPO_MODERATO: "TRANSACTION_TYPE_TEMPO"
|
|
21497
21814
|
};
|
|
21498
21815
|
function networkToTurnkeyType(network) {
|
|
21499
21816
|
const turnkeyType = NETWORK_TO_TURNKEY_TYPE[network];
|
|
@@ -21534,11 +21851,13 @@ async function signStep(signing, step) {
|
|
|
21534
21851
|
class LocalSigner {
|
|
21535
21852
|
signing;
|
|
21536
21853
|
#privateKey;
|
|
21854
|
+
#api;
|
|
21537
21855
|
constructor(options) {
|
|
21538
21856
|
const s = options.signing;
|
|
21539
21857
|
if (!s || !s.publicKey || !s.privateKey || !s.enclaveId) {
|
|
21540
21858
|
throw new TesserConfigError("LocalSigner: signing.publicKey, signing.privateKey, and signing.enclaveId are required");
|
|
21541
21859
|
}
|
|
21860
|
+
this.#api = createTesserApiConnection({ apiBaseUrl: options.apiBaseUrl, fetch: options.fetch });
|
|
21542
21861
|
this.signing = { publicKey: s.publicKey, enclaveId: s.enclaveId };
|
|
21543
21862
|
this.#privateKey = s.privateKey;
|
|
21544
21863
|
}
|
|
@@ -21547,7 +21866,7 @@ class LocalSigner {
|
|
|
21547
21866
|
publicKey: this.signing.publicKey,
|
|
21548
21867
|
privateKey: this.#privateKey,
|
|
21549
21868
|
enclaveId: this.signing.enclaveId
|
|
21550
|
-
}, params);
|
|
21869
|
+
}, params, this.#api);
|
|
21551
21870
|
}
|
|
21552
21871
|
signStep(step) {
|
|
21553
21872
|
return signStep({
|
|
@@ -21558,9 +21877,12 @@ class LocalSigner {
|
|
|
21558
21877
|
}
|
|
21559
21878
|
}
|
|
21560
21879
|
export {
|
|
21880
|
+
TesserTimeoutError,
|
|
21561
21881
|
TesserSigningError,
|
|
21562
21882
|
TesserError,
|
|
21883
|
+
TesserConnectionError,
|
|
21563
21884
|
TesserConfigError,
|
|
21885
|
+
TesserAPIError,
|
|
21564
21886
|
StampError,
|
|
21565
21887
|
LocalSigner
|
|
21566
21888
|
};
|
|
@@ -28,11 +28,11 @@ export interface TesserAPIErrorOptions {
|
|
|
28
28
|
errors?: readonly TesserErrorDetail[];
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* Thrown
|
|
32
|
-
* `errors[]` envelope so consumers can branch on Tesser's
|
|
33
|
-
* codes (e.g. `accounts-3005`, `idempotency-1001`) rather
|
|
34
|
-
* alone. For responses that are not Tesser-shaped (e.g., a
|
|
35
|
-
* page), `errors` is the empty array.
|
|
31
|
+
* Thrown when Tesser answers with an error status or an invalid response.
|
|
32
|
+
* Carries the parsed `errors[]` envelope so consumers can branch on Tesser's
|
|
33
|
+
* documented error codes (e.g. `accounts-3005`, `idempotency-1001`) rather
|
|
34
|
+
* than HTTP status alone. For responses that are not Tesser-shaped (e.g., a
|
|
35
|
+
* Cloudflare 502 page), `errors` is the empty array.
|
|
36
36
|
*/
|
|
37
37
|
export declare class TesserAPIError extends TesserError {
|
|
38
38
|
readonly name: string;
|
package/dist/internal/types.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface StepForSigning {
|
|
|
34
34
|
unsignedTransaction: string;
|
|
35
35
|
/** On-chain address from GET /v1/accounts/{id} → crypto_wallet_address */
|
|
36
36
|
signWith: string;
|
|
37
|
-
/** Network identifier; one of the supported
|
|
37
|
+
/** Network identifier; one of the supported networks. */
|
|
38
38
|
network: SupportedNetwork;
|
|
39
39
|
}
|
|
40
40
|
export interface SignedStepResultMetadata {
|
|
@@ -49,4 +49,14 @@ export interface SignedStepResult {
|
|
|
49
49
|
}
|
|
50
50
|
export interface LocalSignerOptions {
|
|
51
51
|
signing: SigningConfig;
|
|
52
|
+
/**
|
|
53
|
+
* Tesser API base URL: `https://api.tesser.xyz` in production,
|
|
54
|
+
* `https://api-sandbox.tesser.xyz` in sandbox. A trailing `/` or `/v1` is removed.
|
|
55
|
+
*/
|
|
56
|
+
apiBaseUrl: string;
|
|
57
|
+
/**
|
|
58
|
+
* `fetch` implementation used for requests to Tesser. Inject one to configure a
|
|
59
|
+
* proxy. Defaults to the global `fetch`.
|
|
60
|
+
*/
|
|
61
|
+
fetch?: typeof fetch;
|
|
52
62
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SDK_VERSION = "0.1.0";
|
package/dist/signer.d.ts
CHANGED
|
@@ -11,9 +11,8 @@ export interface PublicSigningInfo {
|
|
|
11
11
|
/**
|
|
12
12
|
* Produces locally-signed activity payloads for Tesser API operations.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* the addition is purely additive and does not break existing call sites.
|
|
14
|
+
* Supports `signCreateWallet` and `signStep`. Construction validates that the
|
|
15
|
+
* signing keys are non-blank and that `apiBaseUrl` is an https URL.
|
|
17
16
|
*
|
|
18
17
|
* Key handling: the private key is stored in an ECMA private field (`#privateKey`)
|
|
19
18
|
* and is not reachable via `signer.signing.privateKey` or any other public
|
|
@@ -27,8 +26,25 @@ export declare class LocalSigner {
|
|
|
27
26
|
#private;
|
|
28
27
|
readonly signing: PublicSigningInfo;
|
|
29
28
|
constructor(options: LocalSignerOptions);
|
|
30
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Signs a wallet-creation request.
|
|
31
|
+
*
|
|
32
|
+
* For `stablecoin_ethereum` the SDK first fetches the request to sign from the
|
|
33
|
+
* Tesser API and checks that it matches your parameters. Other wallet types are
|
|
34
|
+
* signed locally without a request to Tesser. The private key never leaves the
|
|
35
|
+
* process.
|
|
36
|
+
*
|
|
37
|
+
* @throws {TesserConfigError} if the wallet type is not valid.
|
|
38
|
+
* @throws {TesserSigningError} if signing fails or the request fetched from
|
|
39
|
+
* Tesser does not match your parameters.
|
|
40
|
+
* @throws {TesserAPIError} if Tesser answers with an error status or an
|
|
41
|
+
* invalid response. Safe to retry for status 429 or 5xx; other 4xx statuses
|
|
42
|
+
* fail the same way on retry.
|
|
43
|
+
* @throws {TesserConnectionError} if the request to Tesser fails at the
|
|
44
|
+
* network level. Safe to retry.
|
|
45
|
+
* @throws {TesserTimeoutError} if the request to Tesser times out. Safe to retry.
|
|
46
|
+
*/
|
|
31
47
|
signCreateWallet(params: CreateWalletParams): Promise<SignedResult>;
|
|
32
|
-
/**
|
|
48
|
+
/** Signs a single transfer step. The private key never leaves the process. */
|
|
33
49
|
signStep(step: StepForSigning): Promise<SignedStepResult>;
|
|
34
50
|
}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { CreateWalletParams, SignedResult, SigningConfig } from '../internal/types.js';
|
|
2
|
-
|
|
2
|
+
import { type TesserApiConnection } from './unsigned-payload.js';
|
|
3
|
+
export declare function signCreateWallet(signing: SigningConfig, params: CreateWalletParams, api: TesserApiConnection): Promise<SignedResult>;
|
|
@@ -6,6 +6,8 @@ declare const NETWORK_TO_TURNKEY_TYPE: {
|
|
|
6
6
|
readonly POLYGON: "TRANSACTION_TYPE_ETHEREUM";
|
|
7
7
|
readonly POLYGON_AMOY: "TRANSACTION_TYPE_ETHEREUM";
|
|
8
8
|
readonly SOLANA: "TRANSACTION_TYPE_SOLANA";
|
|
9
|
+
readonly TEMPO: "TRANSACTION_TYPE_TEMPO";
|
|
10
|
+
readonly TEMPO_MODERATO: "TRANSACTION_TYPE_TEMPO";
|
|
9
11
|
};
|
|
10
12
|
export type SupportedNetwork = keyof typeof NETWORK_TO_TURNKEY_TYPE;
|
|
11
13
|
export declare function networkToTurnkeyType(network: string): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CreateWalletParams, SigningConfig } from '../internal/types.js';
|
|
2
|
+
export interface TesserApiConnection {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
fetch: typeof fetch;
|
|
5
|
+
}
|
|
6
|
+
export declare function createTesserApiConnection(options: {
|
|
7
|
+
apiBaseUrl: unknown;
|
|
8
|
+
fetch: typeof fetch | undefined;
|
|
9
|
+
}): TesserApiConnection;
|
|
10
|
+
export declare function normalizeApiBaseUrl(apiBaseUrl: unknown): string;
|
|
11
|
+
export declare function fetchCheckedCreateWalletAccountsBody(options: {
|
|
12
|
+
api: TesserApiConnection;
|
|
13
|
+
signing: SigningConfig;
|
|
14
|
+
params: CreateWalletParams;
|
|
15
|
+
}): Promise<string>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tesser-payments/sdk-ts",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for Tesser API: sign signCreateWallet and signStep requests locally. No OAuth — bring your own client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -41,14 +41,17 @@
|
|
|
41
41
|
},
|
|
42
42
|
"overrides": {
|
|
43
43
|
"ws": "^8.21.0",
|
|
44
|
-
"vite": "^6.4.
|
|
44
|
+
"vite": "^6.4.5",
|
|
45
|
+
"postcss": "^8.5.18",
|
|
46
|
+
"source-map-js": "^1.2.2",
|
|
47
|
+
"zod": "^4.6.6"
|
|
45
48
|
},
|
|
46
49
|
"devDependencies": {
|
|
47
50
|
"@biomejs/biome": "^1.9",
|
|
48
51
|
"@tesser-payments/types": "*",
|
|
49
52
|
"@types/node": "^20",
|
|
50
53
|
"typescript": "^5.4",
|
|
51
|
-
"vitest": "^
|
|
54
|
+
"vitest": "^4.1.12"
|
|
52
55
|
},
|
|
53
56
|
"trustedDependencies": []
|
|
54
57
|
}
|