@tesser-payments/sdk-ts 0.1.0 → 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 +11 -4
- package/dist/index.js +49 -9
- package/dist/internal/types.d.ts +5 -3
- package/dist/internal/version.d.ts +1 -1
- package/dist/signer.d.ts +5 -4
- package/dist/signing/unsigned-payload.d.ts +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,6 @@ const signer = new LocalSigner({
|
|
|
24
24
|
privateKey: process.env.SIGNING_PRIVATE_KEY!,
|
|
25
25
|
enclaveId: process.env.SIGNING_ENCLAVE_ID!,
|
|
26
26
|
},
|
|
27
|
-
apiBaseUrl: 'https://api.tesser.xyz', // sandbox: https://api-sandbox.tesser.xyz
|
|
28
27
|
});
|
|
29
28
|
|
|
30
29
|
const { signature } = await signer.signCreateWallet({
|
|
@@ -50,8 +49,8 @@ const res = await fetch(`${API_BASE_URL}/v1/accounts/wallets`, {
|
|
|
50
49
|
`signature` is `base64(JSON.stringify({ body, stamp }))` — drop it into the
|
|
51
50
|
Tesser API request body as the documented `signature` field.
|
|
52
51
|
|
|
53
|
-
`
|
|
54
|
-
`https://api-sandbox.tesser.xyz
|
|
52
|
+
`apiBaseUrl` is optional; pass it to use only that URL (`https://api.tesser.xyz`
|
|
53
|
+
or `https://api-sandbox.tesser.xyz`; a trailing `/` or `/v1` is removed). Pass an
|
|
55
54
|
optional `fetch` implementation to configure a proxy.
|
|
56
55
|
|
|
57
56
|
`signCreateWallet` signs a wallet-creation request. For `stablecoin_ethereum`,
|
|
@@ -59,6 +58,13 @@ it first fetches the request to sign from the Tesser API and checks that it
|
|
|
59
58
|
matches your parameters. The private key never leaves your process. Tesser
|
|
60
59
|
currently supports managed wallets for `stablecoin_ethereum` only.
|
|
61
60
|
|
|
61
|
+
`LocalSigner({ signing })` works with production and sandbox signing keys.
|
|
62
|
+
Without `apiBaseUrl`, the first `stablecoin_ethereum` `signCreateWallet` tries
|
|
63
|
+
production and, if production does not accept your signing key, sandbox, and
|
|
64
|
+
remembers it for later calls. Set `apiBaseUrl` if your network allows only one
|
|
65
|
+
Tesser host, or if you create a new signer per request (each new signer detects
|
|
66
|
+
again). Send your own API requests to the environment of your signing key.
|
|
67
|
+
|
|
62
68
|
**Private-key handling.** `signer.signing` exposes only `{ publicKey, enclaveId }`.
|
|
63
69
|
The private key lives on the instance as an ECMA private field and is not
|
|
64
70
|
reachable via property access, `console.log`, or `JSON.stringify`. The SDK still
|
|
@@ -141,7 +147,8 @@ the request `signCreateWallet` makes to Tesser for `stablecoin_ethereum`.
|
|
|
141
147
|
`errors[]` (with `errorCode` for the first one). `TesserConnectionError`,
|
|
142
148
|
`TesserTimeoutError`, and a `TesserAPIError` with status 429 or 5xx are safe to
|
|
143
149
|
retry; a `TesserAPIError` with any other 4xx status fails the same way on
|
|
144
|
-
retry.
|
|
150
|
+
retry. Without `apiBaseUrl`, if neither production nor sandbox accepts your
|
|
151
|
+
signing key, the `TesserAPIError` from production is thrown.
|
|
145
152
|
|
|
146
153
|
Each error carries the original failure as `.cause` and can be discriminated via
|
|
147
154
|
`instanceof` or the static `.is()` guard:
|
package/dist/index.js
CHANGED
|
@@ -21473,7 +21473,7 @@ async function stamp(keys, body) {
|
|
|
21473
21473
|
}
|
|
21474
21474
|
|
|
21475
21475
|
// src/internal/version.ts
|
|
21476
|
-
var SDK_VERSION = "0.
|
|
21476
|
+
var SDK_VERSION = "0.2.0";
|
|
21477
21477
|
|
|
21478
21478
|
// src/signing/unsigned-payload.ts
|
|
21479
21479
|
var UNSIGNED_PAYLOAD_PATH = "/v1/accounts/wallets/unsigned-payload";
|
|
@@ -21482,12 +21482,27 @@ var ETHEREUM_ADDRESS_FORMAT = "ADDRESS_FORMAT_ETHEREUM";
|
|
|
21482
21482
|
var UNSIGNED_PAYLOAD_TIMEOUT_MS = 1e4;
|
|
21483
21483
|
var LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
21484
21484
|
var MAX_FAILURE_CHAIN_DEPTH = 10;
|
|
21485
|
-
|
|
21485
|
+
var PRODUCTION_API_BASE_URL = "https://api.tesser.xyz";
|
|
21486
|
+
var SANDBOX_API_BASE_URL = "https://api-sandbox.tesser.xyz";
|
|
21487
|
+
var DEFAULT_API_BASE_URLS = [PRODUCTION_API_BASE_URL, SANDBOX_API_BASE_URL];
|
|
21488
|
+
var STAMP_REJECTED_ERROR_CODE = "signing-4010";
|
|
21489
|
+
function createTesserApiConnection({
|
|
21490
|
+
apiBaseUrl,
|
|
21491
|
+
fetch: injectedFetch,
|
|
21492
|
+
defaultBaseUrls = DEFAULT_API_BASE_URLS
|
|
21493
|
+
}) {
|
|
21486
21494
|
return {
|
|
21487
|
-
|
|
21488
|
-
|
|
21495
|
+
fetch: resolveFetchImplementation(injectedFetch),
|
|
21496
|
+
defaultBaseUrls,
|
|
21497
|
+
resolvedBaseUrl: resolveExplicitApiBaseUrl(apiBaseUrl)
|
|
21489
21498
|
};
|
|
21490
21499
|
}
|
|
21500
|
+
function resolveExplicitApiBaseUrl(apiBaseUrl) {
|
|
21501
|
+
if (apiBaseUrl === undefined) {
|
|
21502
|
+
return;
|
|
21503
|
+
}
|
|
21504
|
+
return normalizeApiBaseUrl(apiBaseUrl);
|
|
21505
|
+
}
|
|
21491
21506
|
function resolveFetchImplementation(injectedFetch) {
|
|
21492
21507
|
if (injectedFetch !== undefined) {
|
|
21493
21508
|
return injectedFetch;
|
|
@@ -21533,9 +21548,35 @@ function parseUrl(value) {
|
|
|
21533
21548
|
}
|
|
21534
21549
|
async function fetchCheckedCreateWalletAccountsBody(options) {
|
|
21535
21550
|
const { api, signing, params } = options;
|
|
21551
|
+
if (api.resolvedBaseUrl !== undefined) {
|
|
21552
|
+
return fetchCheckedBodyFrom(api.resolvedBaseUrl, api.fetch, signing, params);
|
|
21553
|
+
}
|
|
21554
|
+
return detectEnvironmentAndFetchCheckedBody({ api, signing, params });
|
|
21555
|
+
}
|
|
21556
|
+
async function detectEnvironmentAndFetchCheckedBody(options) {
|
|
21557
|
+
const { api, signing, params } = options;
|
|
21558
|
+
let firstStampRejection;
|
|
21559
|
+
for (const baseUrl of api.defaultBaseUrls) {
|
|
21560
|
+
try {
|
|
21561
|
+
const body = await fetchCheckedBodyFrom(baseUrl, api.fetch, signing, params);
|
|
21562
|
+
api.resolvedBaseUrl = baseUrl;
|
|
21563
|
+
return body;
|
|
21564
|
+
} catch (error) {
|
|
21565
|
+
if (!isStampRejected(error)) {
|
|
21566
|
+
throw error;
|
|
21567
|
+
}
|
|
21568
|
+
firstStampRejection ??= error;
|
|
21569
|
+
}
|
|
21570
|
+
}
|
|
21571
|
+
throw firstStampRejection;
|
|
21572
|
+
}
|
|
21573
|
+
function isStampRejected(error) {
|
|
21574
|
+
return error instanceof TesserAPIError && error.status === 401 && error.errorCode === STAMP_REJECTED_ERROR_CODE;
|
|
21575
|
+
}
|
|
21576
|
+
async function fetchCheckedBodyFrom(baseUrl, fetchImplementation, signing, params) {
|
|
21536
21577
|
const query = buildUnsignedPayloadQuery(signing, params);
|
|
21537
21578
|
const stamped = await stamp({ publicKey: signing.publicKey, privateKey: signing.privateKey }, buildCanonicalUnsignedPayloadMessage(query));
|
|
21538
|
-
const response = await sendUnsignedPayloadRequest(
|
|
21579
|
+
const response = await sendUnsignedPayloadRequest(fetchImplementation, buildUnsignedPayloadUrl(baseUrl, query), stamped);
|
|
21539
21580
|
const body = parseUnsignedPayloadBody(response);
|
|
21540
21581
|
assertUnsignedPayloadMatchesRequest(body, signing, params);
|
|
21541
21582
|
return body;
|
|
@@ -21556,7 +21597,7 @@ function buildCanonicalUnsignedPayloadMessage(query) {
|
|
|
21556
21597
|
timestampMs: query.timestampMs
|
|
21557
21598
|
});
|
|
21558
21599
|
}
|
|
21559
|
-
function buildUnsignedPayloadUrl(
|
|
21600
|
+
function buildUnsignedPayloadUrl(baseUrl, query) {
|
|
21560
21601
|
const queryEntries = [
|
|
21561
21602
|
["organizationId", query.organizationId],
|
|
21562
21603
|
["name", query.name],
|
|
@@ -21564,10 +21605,9 @@ function buildUnsignedPayloadUrl(api, query) {
|
|
|
21564
21605
|
["timestampMs", query.timestampMs]
|
|
21565
21606
|
];
|
|
21566
21607
|
const queryString = queryEntries.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&");
|
|
21567
|
-
return `${
|
|
21608
|
+
return `${baseUrl}${UNSIGNED_PAYLOAD_PATH}?${queryString}`;
|
|
21568
21609
|
}
|
|
21569
|
-
async function sendUnsignedPayloadRequest(
|
|
21570
|
-
const fetchImplementation = api.fetch;
|
|
21610
|
+
async function sendUnsignedPayloadRequest(fetchImplementation, url, stamped) {
|
|
21571
21611
|
try {
|
|
21572
21612
|
const response = await fetchImplementation(url, {
|
|
21573
21613
|
method: "GET",
|
package/dist/internal/types.d.ts
CHANGED
|
@@ -50,10 +50,12 @@ export interface SignedStepResult {
|
|
|
50
50
|
export interface LocalSignerOptions {
|
|
51
51
|
signing: SigningConfig;
|
|
52
52
|
/**
|
|
53
|
-
* Tesser API base URL
|
|
54
|
-
*
|
|
53
|
+
* Optional Tesser API base URL. Leave it unset and the signer finds the environment
|
|
54
|
+
* your signing key belongs to, production or sandbox. When set, only this URL is used:
|
|
55
|
+
* `https://api.tesser.xyz` in production, `https://api-sandbox.tesser.xyz` in sandbox.
|
|
56
|
+
* A trailing `/` or `/v1` is removed.
|
|
55
57
|
*/
|
|
56
|
-
apiBaseUrl
|
|
58
|
+
apiBaseUrl?: string | undefined;
|
|
57
59
|
/**
|
|
58
60
|
* `fetch` implementation used for requests to Tesser. Inject one to configure a
|
|
59
61
|
* proxy. Defaults to the global `fetch`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "0.
|
|
1
|
+
export declare const SDK_VERSION = "0.2.0";
|
package/dist/signer.d.ts
CHANGED
|
@@ -12,15 +12,15 @@ export interface PublicSigningInfo {
|
|
|
12
12
|
* Produces locally-signed activity payloads for Tesser API operations.
|
|
13
13
|
*
|
|
14
14
|
* Supports `signCreateWallet` and `signStep`. Construction validates that the
|
|
15
|
-
* signing keys are non-blank and that `apiBaseUrl
|
|
15
|
+
* signing keys are non-blank and that `apiBaseUrl`, if set, is an https URL.
|
|
16
16
|
*
|
|
17
17
|
* Key handling: the private key is stored in an ECMA private field (`#privateKey`)
|
|
18
18
|
* and is not reachable via `signer.signing.privateKey` or any other public
|
|
19
19
|
* surface. `signer.signing` exposes only `{ publicKey, enclaveId }`, so logging
|
|
20
20
|
* `signer` or its `signing` property will not leak the private key.
|
|
21
21
|
*
|
|
22
|
-
* Thread-safety:
|
|
23
|
-
*
|
|
22
|
+
* Thread-safety: the only state is the environment remembered after the first
|
|
23
|
+
* Ethereum wallet request; a single LocalSigner may be shared across concurrent calls.
|
|
24
24
|
*/
|
|
25
25
|
export declare class LocalSigner {
|
|
26
26
|
#private;
|
|
@@ -39,7 +39,8 @@ export declare class LocalSigner {
|
|
|
39
39
|
* Tesser does not match your parameters.
|
|
40
40
|
* @throws {TesserAPIError} if Tesser answers with an error status or an
|
|
41
41
|
* invalid response. Safe to retry for status 429 or 5xx; other 4xx statuses
|
|
42
|
-
* fail the same way on retry.
|
|
42
|
+
* fail the same way on retry. Without `apiBaseUrl`, if neither production nor
|
|
43
|
+
* sandbox accepts your signing key, the error from production is thrown.
|
|
43
44
|
* @throws {TesserConnectionError} if the request to Tesser fails at the
|
|
44
45
|
* network level. Safe to retry.
|
|
45
46
|
* @throws {TesserTimeoutError} if the request to Tesser times out. Safe to retry.
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { CreateWalletParams, SigningConfig } from '../internal/types.js';
|
|
2
2
|
export interface TesserApiConnection {
|
|
3
|
-
baseUrl: string;
|
|
4
3
|
fetch: typeof fetch;
|
|
4
|
+
defaultBaseUrls: readonly string[];
|
|
5
|
+
resolvedBaseUrl: string | undefined;
|
|
5
6
|
}
|
|
6
|
-
export declare function createTesserApiConnection(
|
|
7
|
+
export declare function createTesserApiConnection({ apiBaseUrl, fetch: injectedFetch, defaultBaseUrls, }: {
|
|
7
8
|
apiBaseUrl: unknown;
|
|
8
9
|
fetch: typeof fetch | undefined;
|
|
10
|
+
defaultBaseUrls?: readonly string[];
|
|
9
11
|
}): TesserApiConnection;
|
|
10
12
|
export declare function normalizeApiBaseUrl(apiBaseUrl: unknown): string;
|
|
11
13
|
export declare function fetchCheckedCreateWalletAccountsBody(options: {
|
package/package.json
CHANGED