@joai/warps-wallet-remote 1.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 +77 -0
- package/dist/index.d.cts +77 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +311 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +286 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @joai/warps-wallet-remote
|
|
2
|
+
|
|
3
|
+
Generic remote wallet provider package for Warps SDK.
|
|
4
|
+
|
|
5
|
+
This package is intentionally backend-agnostic. It supports any remote signer service by configuration.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @joai/warps-wallet-remote
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { WarpClient, WarpChainName } from '@joai/warps'
|
|
17
|
+
import { createRemoteWalletProvider } from '@joai/warps-wallet-remote'
|
|
18
|
+
|
|
19
|
+
const providerKey = 'myRemoteSigner'
|
|
20
|
+
|
|
21
|
+
const client = new WarpClient({
|
|
22
|
+
env: 'devnet',
|
|
23
|
+
user: {
|
|
24
|
+
id: 'agent-1',
|
|
25
|
+
wallets: {
|
|
26
|
+
[WarpChainName.Ethereum]: {
|
|
27
|
+
provider: providerKey,
|
|
28
|
+
address: '0x...',
|
|
29
|
+
externalId: 'wallet-1',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
walletProviders: {
|
|
34
|
+
[WarpChainName.Ethereum]: {
|
|
35
|
+
[providerKey]: createRemoteWalletProvider(
|
|
36
|
+
{
|
|
37
|
+
baseUrl: 'https://signer.example',
|
|
38
|
+
providerName: providerKey,
|
|
39
|
+
serviceToken: process.env.SIGNER_SERVICE_TOKEN,
|
|
40
|
+
getAccessToken: async ({ walletId, chain, nonce }) => {
|
|
41
|
+
return await issueSignerToken({ walletId, chain, nonce })
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
providerKey
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Register the provider under the same key you store on the wallet config.
|
|
52
|
+
If you use a custom provider key, pass it as `providerName` or the helper fallback so generated/imported wallets persist the correct provider.
|
|
53
|
+
|
|
54
|
+
## Default Endpoints
|
|
55
|
+
|
|
56
|
+
- `POST /v1/wallets/generate`
|
|
57
|
+
- `POST /v1/wallets/import`
|
|
58
|
+
- `POST /v1/wallets/export`
|
|
59
|
+
- `POST /v1/sign/transaction`
|
|
60
|
+
- `POST /v1/sign/message`
|
|
61
|
+
|
|
62
|
+
Override with `endpoints` when your signer API differs.
|
|
63
|
+
|
|
64
|
+
## Advanced Customization
|
|
65
|
+
|
|
66
|
+
- `headers`: static request headers
|
|
67
|
+
- `getHeaders(context)`: per-operation dynamic headers
|
|
68
|
+
- `transformPayload(context, payload)`: mutate payloads before sending
|
|
69
|
+
|
|
70
|
+
These hooks allow integrating heterogeneous signer APIs without modifying SDK internals.
|
|
71
|
+
|
|
72
|
+
## Security Defaults
|
|
73
|
+
|
|
74
|
+
- HTTPS required by default (`allowInsecureHttp` only for explicit local/dev use)
|
|
75
|
+
- Relative endpoint paths only (prevents endpoint host override/SSRF)
|
|
76
|
+
- Strict token validation (non-empty service/access token)
|
|
77
|
+
- Request timeout (default `15000ms`)
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { WarpWalletProvider, WalletProvider, WarpClientConfig, WarpChainInfo, WarpAdapterGenericTransaction, WarpWalletDetails, WalletProviderFactory } from '@joai/warps';
|
|
2
|
+
|
|
3
|
+
declare const DEFAULT_REMOTE_WALLET_PROVIDER_NAME: WarpWalletProvider;
|
|
4
|
+
type RemoteWalletProviderEndpoints = {
|
|
5
|
+
generate: string;
|
|
6
|
+
import: string;
|
|
7
|
+
export: string;
|
|
8
|
+
signTransaction: string;
|
|
9
|
+
signMessage: string;
|
|
10
|
+
};
|
|
11
|
+
type RemoteWalletOperation = 'generate' | 'import' | 'export' | 'signTransaction' | 'signMessage';
|
|
12
|
+
type RemoteWalletAccessTokenParams = {
|
|
13
|
+
walletId: string;
|
|
14
|
+
chain: string;
|
|
15
|
+
nonce: string;
|
|
16
|
+
};
|
|
17
|
+
type RemoteWalletRequestContext = {
|
|
18
|
+
operation: RemoteWalletOperation;
|
|
19
|
+
chain: string;
|
|
20
|
+
walletId?: string;
|
|
21
|
+
nonce?: string;
|
|
22
|
+
};
|
|
23
|
+
type RemoteWalletProviderConfig = {
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
allowInsecureHttp?: boolean;
|
|
26
|
+
providerName?: WarpWalletProvider;
|
|
27
|
+
endpoints?: Partial<RemoteWalletProviderEndpoints>;
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
headers?: Record<string, string>;
|
|
30
|
+
getHeaders?: (context: RemoteWalletRequestContext) => Promise<Record<string, string> | undefined> | Record<string, string> | undefined;
|
|
31
|
+
transformPayload?: (context: RemoteWalletRequestContext, payload: Record<string, unknown>) => Promise<Record<string, unknown>> | Record<string, unknown>;
|
|
32
|
+
serviceToken?: string;
|
|
33
|
+
getServiceToken?: () => Promise<string> | string;
|
|
34
|
+
accessToken?: string;
|
|
35
|
+
getAccessToken?: (params: RemoteWalletAccessTokenParams) => Promise<string> | string;
|
|
36
|
+
};
|
|
37
|
+
type RemoteWalletLifecycleResponse = {
|
|
38
|
+
provider?: WarpWalletProvider;
|
|
39
|
+
walletId: string;
|
|
40
|
+
externalId?: string;
|
|
41
|
+
address: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type ResolvedRemoteWalletProviderConfig = RemoteWalletProviderConfig & {
|
|
45
|
+
providerName: WarpWalletProvider;
|
|
46
|
+
endpoints: RemoteWalletProviderEndpoints;
|
|
47
|
+
};
|
|
48
|
+
declare class RemoteWalletProvider implements WalletProvider {
|
|
49
|
+
private readonly config;
|
|
50
|
+
private readonly chain;
|
|
51
|
+
protected readonly remoteConfig: ResolvedRemoteWalletProviderConfig;
|
|
52
|
+
constructor(config: WarpClientConfig, chain: WarpChainInfo, remoteConfig: RemoteWalletProviderConfig, fallbackProviderName?: WarpWalletProvider);
|
|
53
|
+
getAddress(): Promise<string | null>;
|
|
54
|
+
getPublicKey(): Promise<string | null>;
|
|
55
|
+
signTransaction(tx: WarpAdapterGenericTransaction): Promise<WarpAdapterGenericTransaction>;
|
|
56
|
+
signMessage(message: string): Promise<string>;
|
|
57
|
+
importFromMnemonic(mnemonic: string): Promise<WarpWalletDetails>;
|
|
58
|
+
importFromPrivateKey(privateKey: string): Promise<WarpWalletDetails>;
|
|
59
|
+
export(): Promise<WarpWalletDetails>;
|
|
60
|
+
generate(): Promise<WarpWalletDetails>;
|
|
61
|
+
private requestLifecycleWallet;
|
|
62
|
+
private toWalletDetails;
|
|
63
|
+
private getWalletIdOrThrow;
|
|
64
|
+
private resolveAccessToken;
|
|
65
|
+
private getSignAuth;
|
|
66
|
+
private resolveServiceToken;
|
|
67
|
+
private toSerializableTransaction;
|
|
68
|
+
private request;
|
|
69
|
+
private requireToken;
|
|
70
|
+
private createContext;
|
|
71
|
+
private resolveHeaders;
|
|
72
|
+
private resolvePayload;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
declare const createRemoteWalletProvider: (remoteConfig: RemoteWalletProviderConfig, fallbackProviderName?: WarpWalletProvider) => WalletProviderFactory;
|
|
76
|
+
|
|
77
|
+
export { DEFAULT_REMOTE_WALLET_PROVIDER_NAME, type RemoteWalletAccessTokenParams, type RemoteWalletLifecycleResponse, type RemoteWalletOperation, RemoteWalletProvider, type RemoteWalletProviderConfig, type RemoteWalletProviderEndpoints, type RemoteWalletRequestContext, createRemoteWalletProvider };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { WarpWalletProvider, WalletProvider, WarpClientConfig, WarpChainInfo, WarpAdapterGenericTransaction, WarpWalletDetails, WalletProviderFactory } from '@joai/warps';
|
|
2
|
+
|
|
3
|
+
declare const DEFAULT_REMOTE_WALLET_PROVIDER_NAME: WarpWalletProvider;
|
|
4
|
+
type RemoteWalletProviderEndpoints = {
|
|
5
|
+
generate: string;
|
|
6
|
+
import: string;
|
|
7
|
+
export: string;
|
|
8
|
+
signTransaction: string;
|
|
9
|
+
signMessage: string;
|
|
10
|
+
};
|
|
11
|
+
type RemoteWalletOperation = 'generate' | 'import' | 'export' | 'signTransaction' | 'signMessage';
|
|
12
|
+
type RemoteWalletAccessTokenParams = {
|
|
13
|
+
walletId: string;
|
|
14
|
+
chain: string;
|
|
15
|
+
nonce: string;
|
|
16
|
+
};
|
|
17
|
+
type RemoteWalletRequestContext = {
|
|
18
|
+
operation: RemoteWalletOperation;
|
|
19
|
+
chain: string;
|
|
20
|
+
walletId?: string;
|
|
21
|
+
nonce?: string;
|
|
22
|
+
};
|
|
23
|
+
type RemoteWalletProviderConfig = {
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
allowInsecureHttp?: boolean;
|
|
26
|
+
providerName?: WarpWalletProvider;
|
|
27
|
+
endpoints?: Partial<RemoteWalletProviderEndpoints>;
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
headers?: Record<string, string>;
|
|
30
|
+
getHeaders?: (context: RemoteWalletRequestContext) => Promise<Record<string, string> | undefined> | Record<string, string> | undefined;
|
|
31
|
+
transformPayload?: (context: RemoteWalletRequestContext, payload: Record<string, unknown>) => Promise<Record<string, unknown>> | Record<string, unknown>;
|
|
32
|
+
serviceToken?: string;
|
|
33
|
+
getServiceToken?: () => Promise<string> | string;
|
|
34
|
+
accessToken?: string;
|
|
35
|
+
getAccessToken?: (params: RemoteWalletAccessTokenParams) => Promise<string> | string;
|
|
36
|
+
};
|
|
37
|
+
type RemoteWalletLifecycleResponse = {
|
|
38
|
+
provider?: WarpWalletProvider;
|
|
39
|
+
walletId: string;
|
|
40
|
+
externalId?: string;
|
|
41
|
+
address: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type ResolvedRemoteWalletProviderConfig = RemoteWalletProviderConfig & {
|
|
45
|
+
providerName: WarpWalletProvider;
|
|
46
|
+
endpoints: RemoteWalletProviderEndpoints;
|
|
47
|
+
};
|
|
48
|
+
declare class RemoteWalletProvider implements WalletProvider {
|
|
49
|
+
private readonly config;
|
|
50
|
+
private readonly chain;
|
|
51
|
+
protected readonly remoteConfig: ResolvedRemoteWalletProviderConfig;
|
|
52
|
+
constructor(config: WarpClientConfig, chain: WarpChainInfo, remoteConfig: RemoteWalletProviderConfig, fallbackProviderName?: WarpWalletProvider);
|
|
53
|
+
getAddress(): Promise<string | null>;
|
|
54
|
+
getPublicKey(): Promise<string | null>;
|
|
55
|
+
signTransaction(tx: WarpAdapterGenericTransaction): Promise<WarpAdapterGenericTransaction>;
|
|
56
|
+
signMessage(message: string): Promise<string>;
|
|
57
|
+
importFromMnemonic(mnemonic: string): Promise<WarpWalletDetails>;
|
|
58
|
+
importFromPrivateKey(privateKey: string): Promise<WarpWalletDetails>;
|
|
59
|
+
export(): Promise<WarpWalletDetails>;
|
|
60
|
+
generate(): Promise<WarpWalletDetails>;
|
|
61
|
+
private requestLifecycleWallet;
|
|
62
|
+
private toWalletDetails;
|
|
63
|
+
private getWalletIdOrThrow;
|
|
64
|
+
private resolveAccessToken;
|
|
65
|
+
private getSignAuth;
|
|
66
|
+
private resolveServiceToken;
|
|
67
|
+
private toSerializableTransaction;
|
|
68
|
+
private request;
|
|
69
|
+
private requireToken;
|
|
70
|
+
private createContext;
|
|
71
|
+
private resolveHeaders;
|
|
72
|
+
private resolvePayload;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
declare const createRemoteWalletProvider: (remoteConfig: RemoteWalletProviderConfig, fallbackProviderName?: WarpWalletProvider) => WalletProviderFactory;
|
|
76
|
+
|
|
77
|
+
export { DEFAULT_REMOTE_WALLET_PROVIDER_NAME, type RemoteWalletAccessTokenParams, type RemoteWalletLifecycleResponse, type RemoteWalletOperation, RemoteWalletProvider, type RemoteWalletProviderConfig, type RemoteWalletProviderEndpoints, type RemoteWalletRequestContext, createRemoteWalletProvider };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DEFAULT_REMOTE_WALLET_PROVIDER_NAME: () => DEFAULT_REMOTE_WALLET_PROVIDER_NAME,
|
|
24
|
+
RemoteWalletProvider: () => RemoteWalletProvider,
|
|
25
|
+
createRemoteWalletProvider: () => createRemoteWalletProvider
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/RemoteWalletProvider.ts
|
|
30
|
+
var import_warps = require("@joai/warps");
|
|
31
|
+
|
|
32
|
+
// src/types.ts
|
|
33
|
+
var DEFAULT_REMOTE_WALLET_PROVIDER_NAME = "remote";
|
|
34
|
+
|
|
35
|
+
// src/RemoteWalletProvider.ts
|
|
36
|
+
var defaultEndpoints = {
|
|
37
|
+
generate: "/v1/wallets/generate",
|
|
38
|
+
import: "/v1/wallets/import",
|
|
39
|
+
export: "/v1/wallets/export",
|
|
40
|
+
signTransaction: "/v1/sign/transaction",
|
|
41
|
+
signMessage: "/v1/sign/message"
|
|
42
|
+
};
|
|
43
|
+
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
44
|
+
var isStrictHex = (value) => /^[0-9a-f]+$/i.test(value) && value.length % 2 === 0;
|
|
45
|
+
var isLoopbackHost = (host) => ["localhost", "127.0.0.1", "::1"].includes(host.toLowerCase());
|
|
46
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
47
|
+
var normalizeAndValidateBaseUrl = (baseUrl, allowInsecureHttp) => {
|
|
48
|
+
const normalized = trimTrailingSlash(baseUrl.trim());
|
|
49
|
+
let parsedUrl;
|
|
50
|
+
try {
|
|
51
|
+
parsedUrl = new URL(normalized);
|
|
52
|
+
} catch {
|
|
53
|
+
throw new Error("RemoteWalletProvider: baseUrl must be an absolute URL");
|
|
54
|
+
}
|
|
55
|
+
const isHttps = parsedUrl.protocol.toLowerCase() === "https:";
|
|
56
|
+
if (!isHttps && !allowInsecureHttp && !isLoopbackHost(parsedUrl.hostname)) {
|
|
57
|
+
throw new Error("RemoteWalletProvider: baseUrl must use HTTPS unless allowInsecureHttp is explicitly enabled");
|
|
58
|
+
}
|
|
59
|
+
return normalized;
|
|
60
|
+
};
|
|
61
|
+
var normalizeEndpointPath = (path) => {
|
|
62
|
+
const normalized = path.trim();
|
|
63
|
+
if (!normalized) {
|
|
64
|
+
throw new Error("RemoteWalletProvider: endpoint path must be a non-empty string");
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
new URL(normalized);
|
|
68
|
+
throw new Error("RemoteWalletProvider: endpoint path must be relative");
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (error instanceof Error && error.message.includes("must be relative")) {
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
75
|
+
};
|
|
76
|
+
var normalizeEndpoints = (endpoints) => ({
|
|
77
|
+
generate: normalizeEndpointPath(endpoints.generate),
|
|
78
|
+
import: normalizeEndpointPath(endpoints.import),
|
|
79
|
+
export: normalizeEndpointPath(endpoints.export),
|
|
80
|
+
signTransaction: normalizeEndpointPath(endpoints.signTransaction),
|
|
81
|
+
signMessage: normalizeEndpointPath(endpoints.signMessage)
|
|
82
|
+
});
|
|
83
|
+
var withDefaults = (remoteConfig, fallbackProviderName) => {
|
|
84
|
+
const baseUrl = normalizeAndValidateBaseUrl(remoteConfig.baseUrl, remoteConfig.allowInsecureHttp);
|
|
85
|
+
const endpoints = normalizeEndpoints({
|
|
86
|
+
...defaultEndpoints,
|
|
87
|
+
...remoteConfig.endpoints || {}
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
...remoteConfig,
|
|
91
|
+
baseUrl,
|
|
92
|
+
providerName: remoteConfig.providerName ?? fallbackProviderName,
|
|
93
|
+
endpoints
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
var RemoteWalletProvider = class {
|
|
97
|
+
constructor(config, chain, remoteConfig, fallbackProviderName = DEFAULT_REMOTE_WALLET_PROVIDER_NAME) {
|
|
98
|
+
this.config = config;
|
|
99
|
+
this.chain = chain;
|
|
100
|
+
this.remoteConfig = withDefaults(remoteConfig, fallbackProviderName);
|
|
101
|
+
}
|
|
102
|
+
async getAddress() {
|
|
103
|
+
return (0, import_warps.getWarpWalletAddressFromConfig)(this.config, this.chain.name);
|
|
104
|
+
}
|
|
105
|
+
async getPublicKey() {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
async signTransaction(tx) {
|
|
109
|
+
const signAuth = await this.getSignAuth();
|
|
110
|
+
const transaction = this.toSerializableTransaction(tx);
|
|
111
|
+
const response = await this.request(
|
|
112
|
+
this.remoteConfig.endpoints.signTransaction,
|
|
113
|
+
{ walletId: signAuth.walletId, chain: this.chain.name, nonce: signAuth.nonce, transaction },
|
|
114
|
+
{
|
|
115
|
+
bearer: signAuth.token,
|
|
116
|
+
context: this.createContext("signTransaction", { walletId: signAuth.walletId, nonce: signAuth.nonce })
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
const txRecord = isRecord(tx) ? tx : {};
|
|
120
|
+
if (typeof response.transactionHash === "string" && response.transactionHash.trim() !== "") {
|
|
121
|
+
return { ...txRecord, transactionHash: response.transactionHash };
|
|
122
|
+
}
|
|
123
|
+
if (response.signedTransaction !== void 0 && response.signedTransaction !== null) {
|
|
124
|
+
if (typeof response.signedTransaction === "string") {
|
|
125
|
+
return { ...txRecord, signature: response.signedTransaction };
|
|
126
|
+
}
|
|
127
|
+
if (isRecord(response.signedTransaction)) {
|
|
128
|
+
return { ...txRecord, ...response.signedTransaction };
|
|
129
|
+
}
|
|
130
|
+
throw new Error("RemoteWalletProvider: Invalid signedTransaction format from signer service");
|
|
131
|
+
}
|
|
132
|
+
if (response.signature !== void 0 && response.signature !== null) {
|
|
133
|
+
const isMultiversxSignature = this.chain.name === "multiversx" || this.chain.name === "claws";
|
|
134
|
+
if (isMultiversxSignature) {
|
|
135
|
+
if (typeof response.signature !== "string" || !isStrictHex(response.signature)) {
|
|
136
|
+
throw new Error("RemoteWalletProvider: Invalid hex signature for multiversx transaction");
|
|
137
|
+
}
|
|
138
|
+
return { ...txRecord, signature: Buffer.from(response.signature, "hex") };
|
|
139
|
+
}
|
|
140
|
+
if (typeof response.signature !== "string" && !Array.isArray(response.signature)) {
|
|
141
|
+
throw new Error("RemoteWalletProvider: Invalid signature format from signer service");
|
|
142
|
+
}
|
|
143
|
+
return { ...txRecord, signature: response.signature };
|
|
144
|
+
}
|
|
145
|
+
throw new Error("RemoteWalletProvider: Invalid signTransaction response from signer service");
|
|
146
|
+
}
|
|
147
|
+
async signMessage(message) {
|
|
148
|
+
const signAuth = await this.getSignAuth();
|
|
149
|
+
const response = await this.request(
|
|
150
|
+
this.remoteConfig.endpoints.signMessage,
|
|
151
|
+
{ walletId: signAuth.walletId, chain: this.chain.name, nonce: signAuth.nonce, message },
|
|
152
|
+
{
|
|
153
|
+
bearer: signAuth.token,
|
|
154
|
+
context: this.createContext("signMessage", { walletId: signAuth.walletId, nonce: signAuth.nonce })
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
if (!response.signature) throw new Error("RemoteWalletProvider: Missing signature in signMessage response");
|
|
158
|
+
return response.signature;
|
|
159
|
+
}
|
|
160
|
+
async importFromMnemonic(mnemonic) {
|
|
161
|
+
void mnemonic;
|
|
162
|
+
throw new Error("RemoteWalletProvider: importFromMnemonic() is not supported. Use importFromPrivateKey or generate().");
|
|
163
|
+
}
|
|
164
|
+
async importFromPrivateKey(privateKey) {
|
|
165
|
+
const agentId = this.config.user?.id;
|
|
166
|
+
if (!agentId) throw new Error("RemoteWalletProvider: user.id is required for key import");
|
|
167
|
+
const serviceToken = await this.resolveServiceToken();
|
|
168
|
+
return await this.requestLifecycleWallet(this.remoteConfig.endpoints.import, { agentId, chain: this.chain.name, privateKey }, serviceToken, "import");
|
|
169
|
+
}
|
|
170
|
+
async export() {
|
|
171
|
+
const walletId = this.getWalletIdOrThrow();
|
|
172
|
+
const serviceToken = await this.resolveServiceToken();
|
|
173
|
+
const response = await this.request(
|
|
174
|
+
this.remoteConfig.endpoints.export,
|
|
175
|
+
{ walletId },
|
|
176
|
+
{
|
|
177
|
+
bearer: serviceToken,
|
|
178
|
+
context: this.createContext("export", { walletId })
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
return {
|
|
182
|
+
provider: response.provider ?? this.remoteConfig.providerName,
|
|
183
|
+
address: response.address,
|
|
184
|
+
privateKey: response.privateKey ?? null,
|
|
185
|
+
externalId: response.externalId ?? walletId
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async generate() {
|
|
189
|
+
const agentId = this.config.user?.id;
|
|
190
|
+
if (!agentId) throw new Error("RemoteWalletProvider: user.id is required for wallet generation");
|
|
191
|
+
const serviceToken = await this.resolveServiceToken();
|
|
192
|
+
return await this.requestLifecycleWallet(this.remoteConfig.endpoints.generate, { agentId, chain: this.chain.name }, serviceToken, "generate");
|
|
193
|
+
}
|
|
194
|
+
async requestLifecycleWallet(path, payload, token, operation) {
|
|
195
|
+
const response = await this.request(path, payload, {
|
|
196
|
+
bearer: token,
|
|
197
|
+
context: this.createContext(operation)
|
|
198
|
+
});
|
|
199
|
+
const walletDetails = this.toWalletDetails(response);
|
|
200
|
+
(0, import_warps.setWarpWalletInConfig)(this.config, this.chain.name, walletDetails);
|
|
201
|
+
return walletDetails;
|
|
202
|
+
}
|
|
203
|
+
toWalletDetails(response) {
|
|
204
|
+
return {
|
|
205
|
+
provider: response.provider ?? this.remoteConfig.providerName,
|
|
206
|
+
address: response.address,
|
|
207
|
+
externalId: response.externalId ?? response.walletId
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
getWalletIdOrThrow() {
|
|
211
|
+
const walletId = (0, import_warps.getWarpWalletExternalIdFromConfig)(this.config, this.chain.name);
|
|
212
|
+
if (!walletId) {
|
|
213
|
+
throw new Error(`RemoteWalletProvider: externalId(walletId) is required for chain ${this.chain.name}`);
|
|
214
|
+
}
|
|
215
|
+
return walletId;
|
|
216
|
+
}
|
|
217
|
+
async resolveAccessToken(walletId, nonce) {
|
|
218
|
+
if (this.remoteConfig.getAccessToken) {
|
|
219
|
+
const token = await this.remoteConfig.getAccessToken({ walletId, chain: this.chain.name, nonce });
|
|
220
|
+
return this.requireToken(token, "access token callback");
|
|
221
|
+
}
|
|
222
|
+
if (this.remoteConfig.accessToken) return this.requireToken(this.remoteConfig.accessToken, "access token");
|
|
223
|
+
throw new Error("RemoteWalletProvider: No access token provider configured");
|
|
224
|
+
}
|
|
225
|
+
async getSignAuth() {
|
|
226
|
+
const walletId = this.getWalletIdOrThrow();
|
|
227
|
+
const nonce = crypto.randomUUID();
|
|
228
|
+
const token = await this.resolveAccessToken(walletId, nonce);
|
|
229
|
+
return { walletId, nonce, token };
|
|
230
|
+
}
|
|
231
|
+
async resolveServiceToken() {
|
|
232
|
+
if (this.remoteConfig.getServiceToken) {
|
|
233
|
+
const token = await this.remoteConfig.getServiceToken();
|
|
234
|
+
return this.requireToken(token, "service token callback");
|
|
235
|
+
}
|
|
236
|
+
if (this.remoteConfig.serviceToken) return this.requireToken(this.remoteConfig.serviceToken, "service token");
|
|
237
|
+
throw new Error("RemoteWalletProvider: No service token configured for wallet lifecycle endpoints");
|
|
238
|
+
}
|
|
239
|
+
toSerializableTransaction(tx) {
|
|
240
|
+
if (tx && typeof tx === "object") {
|
|
241
|
+
if ("toPlainObject" in tx && typeof tx.toPlainObject === "function") {
|
|
242
|
+
return tx.toPlainObject();
|
|
243
|
+
}
|
|
244
|
+
if ("toJSON" in tx && typeof tx.toJSON === "function") {
|
|
245
|
+
return tx.toJSON();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return tx;
|
|
249
|
+
}
|
|
250
|
+
async request(path, payload, options) {
|
|
251
|
+
const controller = new AbortController();
|
|
252
|
+
const timeout = setTimeout(() => controller.abort(), this.remoteConfig.timeoutMs ?? 15e3);
|
|
253
|
+
const context = options.context;
|
|
254
|
+
try {
|
|
255
|
+
const resolvedPayload = await this.resolvePayload(context, payload);
|
|
256
|
+
const resolvedHeaders = await this.resolveHeaders(context, options.bearer);
|
|
257
|
+
const response = await fetch(`${this.remoteConfig.baseUrl}${path}`, {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: resolvedHeaders,
|
|
260
|
+
body: JSON.stringify(resolvedPayload, (_, value) => typeof value === "bigint" ? value.toString() : value),
|
|
261
|
+
signal: controller.signal
|
|
262
|
+
});
|
|
263
|
+
if (!response.ok) {
|
|
264
|
+
const text = await response.text();
|
|
265
|
+
throw new Error(`RemoteWalletProvider request failed (${response.status}): ${text}`);
|
|
266
|
+
}
|
|
267
|
+
return await response.json();
|
|
268
|
+
} finally {
|
|
269
|
+
clearTimeout(timeout);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
requireToken(token, source) {
|
|
273
|
+
if (typeof token !== "string" || token.trim() === "") {
|
|
274
|
+
throw new Error(`RemoteWalletProvider: ${source} returned an empty token`);
|
|
275
|
+
}
|
|
276
|
+
return token.trim();
|
|
277
|
+
}
|
|
278
|
+
createContext(operation, extra) {
|
|
279
|
+
return {
|
|
280
|
+
operation,
|
|
281
|
+
chain: this.chain.name,
|
|
282
|
+
...extra
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
async resolveHeaders(context, bearer) {
|
|
286
|
+
const headersFromConfig = this.remoteConfig.headers ?? {};
|
|
287
|
+
const dynamicHeaders = this.remoteConfig.getHeaders ? await this.remoteConfig.getHeaders(context) : {};
|
|
288
|
+
return {
|
|
289
|
+
"Content-Type": "application/json",
|
|
290
|
+
...headersFromConfig,
|
|
291
|
+
...dynamicHeaders ?? {},
|
|
292
|
+
...bearer ? { Authorization: `Bearer ${bearer}` } : {}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
async resolvePayload(context, payload) {
|
|
296
|
+
if (!this.remoteConfig.transformPayload) return payload;
|
|
297
|
+
return await this.remoteConfig.transformPayload(context, payload);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// src/helpers.ts
|
|
302
|
+
var createRemoteWalletProvider = (remoteConfig, fallbackProviderName = DEFAULT_REMOTE_WALLET_PROVIDER_NAME) => {
|
|
303
|
+
return (config, chain) => new RemoteWalletProvider(config, chain, remoteConfig, fallbackProviderName);
|
|
304
|
+
};
|
|
305
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
306
|
+
0 && (module.exports = {
|
|
307
|
+
DEFAULT_REMOTE_WALLET_PROVIDER_NAME,
|
|
308
|
+
RemoteWalletProvider,
|
|
309
|
+
createRemoteWalletProvider
|
|
310
|
+
});
|
|
311
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/RemoteWalletProvider.ts","../src/types.ts","../src/helpers.ts"],"sourcesContent":["export * from './RemoteWalletProvider'\nexport * from './helpers'\nexport * from './types'\n","import {\n getWarpWalletAddressFromConfig,\n getWarpWalletExternalIdFromConfig,\n setWarpWalletInConfig,\n WalletProvider,\n WarpAdapterGenericTransaction,\n WarpChainInfo,\n WarpClientConfig,\n WarpWalletDetails,\n WarpWalletProvider,\n} from '@joai/warps'\nimport {\n RemoteWalletOperation,\n RemoteWalletRequestContext,\n RemoteWalletLifecycleResponse,\n RemoteWalletProviderEndpoints,\n RemoteWalletProviderConfig,\n DEFAULT_REMOTE_WALLET_PROVIDER_NAME,\n} from './types'\n\nconst defaultEndpoints: RemoteWalletProviderEndpoints = {\n generate: '/v1/wallets/generate',\n import: '/v1/wallets/import',\n export: '/v1/wallets/export',\n signTransaction: '/v1/sign/transaction',\n signMessage: '/v1/sign/message',\n}\n\ntype ResolvedRemoteWalletProviderConfig = RemoteWalletProviderConfig & {\n providerName: WarpWalletProvider\n endpoints: RemoteWalletProviderEndpoints\n}\n\ntype SignTransactionResponse = {\n signedTransaction?: unknown\n signature?: unknown\n transactionHash?: unknown\n}\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, '')\nconst isStrictHex = (value: string): boolean => /^[0-9a-f]+$/i.test(value) && value.length % 2 === 0\nconst isLoopbackHost = (host: string): boolean => ['localhost', '127.0.0.1', '::1'].includes(host.toLowerCase())\nconst isRecord = (value: unknown): value is Record<string, unknown> => Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n\nconst normalizeAndValidateBaseUrl = (baseUrl: string, allowInsecureHttp?: boolean): string => {\n const normalized = trimTrailingSlash(baseUrl.trim())\n let parsedUrl: URL\n\n try {\n parsedUrl = new URL(normalized)\n } catch {\n throw new Error('RemoteWalletProvider: baseUrl must be an absolute URL')\n }\n\n const isHttps = parsedUrl.protocol.toLowerCase() === 'https:'\n if (!isHttps && !allowInsecureHttp && !isLoopbackHost(parsedUrl.hostname)) {\n throw new Error('RemoteWalletProvider: baseUrl must use HTTPS unless allowInsecureHttp is explicitly enabled')\n }\n\n return normalized\n}\n\nconst normalizeEndpointPath = (path: string): string => {\n const normalized = path.trim()\n if (!normalized) {\n throw new Error('RemoteWalletProvider: endpoint path must be a non-empty string')\n }\n\n try {\n new URL(normalized)\n throw new Error('RemoteWalletProvider: endpoint path must be relative')\n } catch (error) {\n if (error instanceof Error && error.message.includes('must be relative')) {\n throw error\n }\n }\n\n return normalized.startsWith('/') ? normalized : `/${normalized}`\n}\n\nconst normalizeEndpoints = (endpoints: RemoteWalletProviderEndpoints): RemoteWalletProviderEndpoints => ({\n generate: normalizeEndpointPath(endpoints.generate),\n import: normalizeEndpointPath(endpoints.import),\n export: normalizeEndpointPath(endpoints.export),\n signTransaction: normalizeEndpointPath(endpoints.signTransaction),\n signMessage: normalizeEndpointPath(endpoints.signMessage),\n})\n\nconst withDefaults = (\n remoteConfig: RemoteWalletProviderConfig,\n fallbackProviderName: WarpWalletProvider\n): ResolvedRemoteWalletProviderConfig => {\n const baseUrl = normalizeAndValidateBaseUrl(remoteConfig.baseUrl, remoteConfig.allowInsecureHttp)\n const endpoints = normalizeEndpoints({\n ...defaultEndpoints,\n ...(remoteConfig.endpoints || {}),\n })\n\n return {\n ...remoteConfig,\n baseUrl,\n providerName: remoteConfig.providerName ?? fallbackProviderName,\n endpoints,\n }\n}\n\nexport class RemoteWalletProvider implements WalletProvider {\n protected readonly remoteConfig: ResolvedRemoteWalletProviderConfig\n\n constructor(\n private readonly config: WarpClientConfig,\n private readonly chain: WarpChainInfo,\n remoteConfig: RemoteWalletProviderConfig,\n fallbackProviderName: WarpWalletProvider = DEFAULT_REMOTE_WALLET_PROVIDER_NAME\n ) {\n this.remoteConfig = withDefaults(remoteConfig, fallbackProviderName)\n }\n\n async getAddress(): Promise<string | null> {\n return getWarpWalletAddressFromConfig(this.config, this.chain.name)\n }\n\n async getPublicKey(): Promise<string | null> {\n return null\n }\n\n async signTransaction(tx: WarpAdapterGenericTransaction): Promise<WarpAdapterGenericTransaction> {\n const signAuth = await this.getSignAuth()\n const transaction = this.toSerializableTransaction(tx)\n\n const response = await this.request<SignTransactionResponse>(\n this.remoteConfig.endpoints.signTransaction,\n { walletId: signAuth.walletId, chain: this.chain.name, nonce: signAuth.nonce, transaction },\n {\n bearer: signAuth.token,\n context: this.createContext('signTransaction', { walletId: signAuth.walletId, nonce: signAuth.nonce }),\n }\n )\n const txRecord = isRecord(tx) ? tx : {}\n\n if (typeof response.transactionHash === 'string' && response.transactionHash.trim() !== '') {\n return { ...txRecord, transactionHash: response.transactionHash }\n }\n\n if (response.signedTransaction !== undefined && response.signedTransaction !== null) {\n if (typeof response.signedTransaction === 'string') {\n return { ...txRecord, signature: response.signedTransaction }\n }\n\n if (isRecord(response.signedTransaction)) {\n return { ...txRecord, ...response.signedTransaction }\n }\n\n throw new Error('RemoteWalletProvider: Invalid signedTransaction format from signer service')\n }\n\n if (response.signature !== undefined && response.signature !== null) {\n const isMultiversxSignature = this.chain.name === 'multiversx' || this.chain.name === 'claws'\n\n if (isMultiversxSignature) {\n if (typeof response.signature !== 'string' || !isStrictHex(response.signature)) {\n throw new Error('RemoteWalletProvider: Invalid hex signature for multiversx transaction')\n }\n return { ...txRecord, signature: Buffer.from(response.signature, 'hex') }\n }\n\n if (typeof response.signature !== 'string' && !Array.isArray(response.signature)) {\n throw new Error('RemoteWalletProvider: Invalid signature format from signer service')\n }\n\n return { ...txRecord, signature: response.signature }\n }\n\n throw new Error('RemoteWalletProvider: Invalid signTransaction response from signer service')\n }\n\n async signMessage(message: string): Promise<string> {\n const signAuth = await this.getSignAuth()\n\n const response = await this.request<{ signature: string }>(\n this.remoteConfig.endpoints.signMessage,\n { walletId: signAuth.walletId, chain: this.chain.name, nonce: signAuth.nonce, message },\n {\n bearer: signAuth.token,\n context: this.createContext('signMessage', { walletId: signAuth.walletId, nonce: signAuth.nonce }),\n }\n )\n\n if (!response.signature) throw new Error('RemoteWalletProvider: Missing signature in signMessage response')\n return response.signature\n }\n\n async importFromMnemonic(mnemonic: string): Promise<WarpWalletDetails> {\n void mnemonic\n throw new Error('RemoteWalletProvider: importFromMnemonic() is not supported. Use importFromPrivateKey or generate().')\n }\n\n async importFromPrivateKey(privateKey: string): Promise<WarpWalletDetails> {\n const agentId = this.config.user?.id\n if (!agentId) throw new Error('RemoteWalletProvider: user.id is required for key import')\n const serviceToken = await this.resolveServiceToken()\n\n return await this.requestLifecycleWallet(this.remoteConfig.endpoints.import, { agentId, chain: this.chain.name, privateKey }, serviceToken, 'import')\n }\n\n async export(): Promise<WarpWalletDetails> {\n const walletId = this.getWalletIdOrThrow()\n const serviceToken = await this.resolveServiceToken()\n\n const response = await this.request<{ provider?: WarpWalletProvider; address: string; privateKey?: string; externalId?: string }>(\n this.remoteConfig.endpoints.export,\n { walletId },\n {\n bearer: serviceToken,\n context: this.createContext('export', { walletId }),\n }\n )\n\n return {\n provider: response.provider ?? this.remoteConfig.providerName,\n address: response.address,\n privateKey: response.privateKey ?? null,\n externalId: response.externalId ?? walletId,\n }\n }\n\n async generate(): Promise<WarpWalletDetails> {\n const agentId = this.config.user?.id\n if (!agentId) throw new Error('RemoteWalletProvider: user.id is required for wallet generation')\n const serviceToken = await this.resolveServiceToken()\n\n return await this.requestLifecycleWallet(this.remoteConfig.endpoints.generate, { agentId, chain: this.chain.name }, serviceToken, 'generate')\n }\n\n private async requestLifecycleWallet(\n path: string,\n payload: Record<string, unknown>,\n token: string,\n operation: RemoteWalletOperation\n ): Promise<WarpWalletDetails> {\n const response = await this.request<RemoteWalletLifecycleResponse>(path, payload, {\n bearer: token,\n context: this.createContext(operation),\n })\n const walletDetails = this.toWalletDetails(response)\n setWarpWalletInConfig(this.config, this.chain.name, walletDetails)\n return walletDetails\n }\n\n private toWalletDetails(response: RemoteWalletLifecycleResponse): WarpWalletDetails {\n return {\n provider: response.provider ?? this.remoteConfig.providerName,\n address: response.address,\n externalId: response.externalId ?? response.walletId,\n }\n }\n\n private getWalletIdOrThrow(): string {\n const walletId = getWarpWalletExternalIdFromConfig(this.config, this.chain.name)\n if (!walletId) {\n throw new Error(`RemoteWalletProvider: externalId(walletId) is required for chain ${this.chain.name}`)\n }\n return walletId\n }\n\n private async resolveAccessToken(walletId: string, nonce: string): Promise<string> {\n if (this.remoteConfig.getAccessToken) {\n const token = await this.remoteConfig.getAccessToken({ walletId, chain: this.chain.name, nonce })\n return this.requireToken(token, 'access token callback')\n }\n if (this.remoteConfig.accessToken) return this.requireToken(this.remoteConfig.accessToken, 'access token')\n throw new Error('RemoteWalletProvider: No access token provider configured')\n }\n\n private async getSignAuth(): Promise<{ walletId: string; nonce: string; token: string }> {\n const walletId = this.getWalletIdOrThrow()\n const nonce = crypto.randomUUID()\n const token = await this.resolveAccessToken(walletId, nonce)\n return { walletId, nonce, token }\n }\n\n private async resolveServiceToken(): Promise<string> {\n if (this.remoteConfig.getServiceToken) {\n const token = await this.remoteConfig.getServiceToken()\n return this.requireToken(token, 'service token callback')\n }\n if (this.remoteConfig.serviceToken) return this.requireToken(this.remoteConfig.serviceToken, 'service token')\n throw new Error('RemoteWalletProvider: No service token configured for wallet lifecycle endpoints')\n }\n\n private toSerializableTransaction(tx: WarpAdapterGenericTransaction): unknown {\n if (tx && typeof tx === 'object') {\n if ('toPlainObject' in tx && typeof (tx as { toPlainObject?: () => unknown }).toPlainObject === 'function') {\n return (tx as { toPlainObject: () => unknown }).toPlainObject()\n }\n if ('toJSON' in tx && typeof (tx as { toJSON?: () => unknown }).toJSON === 'function') {\n return (tx as { toJSON: () => unknown }).toJSON()\n }\n }\n return tx\n }\n\n private async request<T>(\n path: string,\n payload: Record<string, unknown>,\n options: { bearer?: string; context: RemoteWalletRequestContext }\n ): Promise<T> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.remoteConfig.timeoutMs ?? 15_000)\n const context = options.context\n\n try {\n const resolvedPayload = await this.resolvePayload(context, payload)\n const resolvedHeaders = await this.resolveHeaders(context, options.bearer)\n\n const response = await fetch(`${this.remoteConfig.baseUrl}${path}`, {\n method: 'POST',\n headers: resolvedHeaders,\n body: JSON.stringify(resolvedPayload, (_, value: unknown) => (typeof value === 'bigint' ? value.toString() : value)),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const text = await response.text()\n throw new Error(`RemoteWalletProvider request failed (${response.status}): ${text}`)\n }\n\n return (await response.json()) as T\n } finally {\n clearTimeout(timeout)\n }\n }\n\n private requireToken(token: unknown, source: string): string {\n if (typeof token !== 'string' || token.trim() === '') {\n throw new Error(`RemoteWalletProvider: ${source} returned an empty token`)\n }\n return token.trim()\n }\n\n private createContext(operation: RemoteWalletOperation, extra?: Partial<RemoteWalletRequestContext>): RemoteWalletRequestContext {\n return {\n operation,\n chain: this.chain.name,\n ...extra,\n }\n }\n\n private async resolveHeaders(context: RemoteWalletRequestContext, bearer?: string): Promise<Record<string, string>> {\n const headersFromConfig = this.remoteConfig.headers ?? {}\n const dynamicHeaders = this.remoteConfig.getHeaders ? await this.remoteConfig.getHeaders(context) : {}\n\n return {\n 'Content-Type': 'application/json',\n ...headersFromConfig,\n ...(dynamicHeaders ?? {}),\n ...(bearer ? { Authorization: `Bearer ${bearer}` } : {}),\n }\n }\n\n private async resolvePayload(context: RemoteWalletRequestContext, payload: Record<string, unknown>): Promise<Record<string, unknown>> {\n if (!this.remoteConfig.transformPayload) return payload\n return await this.remoteConfig.transformPayload(context, payload)\n }\n}\n","import { WarpWalletProvider } from '@joai/warps'\n\nexport const DEFAULT_REMOTE_WALLET_PROVIDER_NAME: WarpWalletProvider = 'remote'\n\nexport type RemoteWalletProviderEndpoints = {\n generate: string\n import: string\n export: string\n signTransaction: string\n signMessage: string\n}\n\nexport type RemoteWalletOperation = 'generate' | 'import' | 'export' | 'signTransaction' | 'signMessage'\n\nexport type RemoteWalletAccessTokenParams = {\n walletId: string\n chain: string\n nonce: string\n}\n\nexport type RemoteWalletRequestContext = {\n operation: RemoteWalletOperation\n chain: string\n walletId?: string\n nonce?: string\n}\n\nexport type RemoteWalletProviderConfig = {\n baseUrl: string\n allowInsecureHttp?: boolean\n providerName?: WarpWalletProvider\n endpoints?: Partial<RemoteWalletProviderEndpoints>\n timeoutMs?: number\n headers?: Record<string, string>\n getHeaders?: (context: RemoteWalletRequestContext) => Promise<Record<string, string> | undefined> | Record<string, string> | undefined\n transformPayload?: (\n context: RemoteWalletRequestContext,\n payload: Record<string, unknown>\n ) => Promise<Record<string, unknown>> | Record<string, unknown>\n serviceToken?: string\n getServiceToken?: () => Promise<string> | string\n accessToken?: string\n getAccessToken?: (params: RemoteWalletAccessTokenParams) => Promise<string> | string\n}\n\nexport type RemoteWalletLifecycleResponse = {\n provider?: WarpWalletProvider\n walletId: string\n externalId?: string\n address: string\n}\n","import { WalletProviderFactory, WarpChainInfo, WarpClientConfig, WarpWalletProvider } from '@joai/warps'\nimport { RemoteWalletProvider } from './RemoteWalletProvider'\nimport { RemoteWalletProviderConfig, DEFAULT_REMOTE_WALLET_PROVIDER_NAME } from './types'\n\nexport const createRemoteWalletProvider = (\n remoteConfig: RemoteWalletProviderConfig,\n fallbackProviderName: WarpWalletProvider = DEFAULT_REMOTE_WALLET_PROVIDER_NAME\n): WalletProviderFactory => {\n return (config: WarpClientConfig, chain: WarpChainInfo) => new RemoteWalletProvider(config, chain, remoteConfig, fallbackProviderName)\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// src/RemoteWalletProvider.ts
|
|
2
|
+
import {
|
|
3
|
+
getWarpWalletAddressFromConfig,
|
|
4
|
+
getWarpWalletExternalIdFromConfig,
|
|
5
|
+
setWarpWalletInConfig
|
|
6
|
+
} from "@joai/warps";
|
|
7
|
+
|
|
8
|
+
// src/types.ts
|
|
9
|
+
var DEFAULT_REMOTE_WALLET_PROVIDER_NAME = "remote";
|
|
10
|
+
|
|
11
|
+
// src/RemoteWalletProvider.ts
|
|
12
|
+
var defaultEndpoints = {
|
|
13
|
+
generate: "/v1/wallets/generate",
|
|
14
|
+
import: "/v1/wallets/import",
|
|
15
|
+
export: "/v1/wallets/export",
|
|
16
|
+
signTransaction: "/v1/sign/transaction",
|
|
17
|
+
signMessage: "/v1/sign/message"
|
|
18
|
+
};
|
|
19
|
+
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
20
|
+
var isStrictHex = (value) => /^[0-9a-f]+$/i.test(value) && value.length % 2 === 0;
|
|
21
|
+
var isLoopbackHost = (host) => ["localhost", "127.0.0.1", "::1"].includes(host.toLowerCase());
|
|
22
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
23
|
+
var normalizeAndValidateBaseUrl = (baseUrl, allowInsecureHttp) => {
|
|
24
|
+
const normalized = trimTrailingSlash(baseUrl.trim());
|
|
25
|
+
let parsedUrl;
|
|
26
|
+
try {
|
|
27
|
+
parsedUrl = new URL(normalized);
|
|
28
|
+
} catch {
|
|
29
|
+
throw new Error("RemoteWalletProvider: baseUrl must be an absolute URL");
|
|
30
|
+
}
|
|
31
|
+
const isHttps = parsedUrl.protocol.toLowerCase() === "https:";
|
|
32
|
+
if (!isHttps && !allowInsecureHttp && !isLoopbackHost(parsedUrl.hostname)) {
|
|
33
|
+
throw new Error("RemoteWalletProvider: baseUrl must use HTTPS unless allowInsecureHttp is explicitly enabled");
|
|
34
|
+
}
|
|
35
|
+
return normalized;
|
|
36
|
+
};
|
|
37
|
+
var normalizeEndpointPath = (path) => {
|
|
38
|
+
const normalized = path.trim();
|
|
39
|
+
if (!normalized) {
|
|
40
|
+
throw new Error("RemoteWalletProvider: endpoint path must be a non-empty string");
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
new URL(normalized);
|
|
44
|
+
throw new Error("RemoteWalletProvider: endpoint path must be relative");
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error instanceof Error && error.message.includes("must be relative")) {
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
51
|
+
};
|
|
52
|
+
var normalizeEndpoints = (endpoints) => ({
|
|
53
|
+
generate: normalizeEndpointPath(endpoints.generate),
|
|
54
|
+
import: normalizeEndpointPath(endpoints.import),
|
|
55
|
+
export: normalizeEndpointPath(endpoints.export),
|
|
56
|
+
signTransaction: normalizeEndpointPath(endpoints.signTransaction),
|
|
57
|
+
signMessage: normalizeEndpointPath(endpoints.signMessage)
|
|
58
|
+
});
|
|
59
|
+
var withDefaults = (remoteConfig, fallbackProviderName) => {
|
|
60
|
+
const baseUrl = normalizeAndValidateBaseUrl(remoteConfig.baseUrl, remoteConfig.allowInsecureHttp);
|
|
61
|
+
const endpoints = normalizeEndpoints({
|
|
62
|
+
...defaultEndpoints,
|
|
63
|
+
...remoteConfig.endpoints || {}
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
...remoteConfig,
|
|
67
|
+
baseUrl,
|
|
68
|
+
providerName: remoteConfig.providerName ?? fallbackProviderName,
|
|
69
|
+
endpoints
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
var RemoteWalletProvider = class {
|
|
73
|
+
constructor(config, chain, remoteConfig, fallbackProviderName = DEFAULT_REMOTE_WALLET_PROVIDER_NAME) {
|
|
74
|
+
this.config = config;
|
|
75
|
+
this.chain = chain;
|
|
76
|
+
this.remoteConfig = withDefaults(remoteConfig, fallbackProviderName);
|
|
77
|
+
}
|
|
78
|
+
async getAddress() {
|
|
79
|
+
return getWarpWalletAddressFromConfig(this.config, this.chain.name);
|
|
80
|
+
}
|
|
81
|
+
async getPublicKey() {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
async signTransaction(tx) {
|
|
85
|
+
const signAuth = await this.getSignAuth();
|
|
86
|
+
const transaction = this.toSerializableTransaction(tx);
|
|
87
|
+
const response = await this.request(
|
|
88
|
+
this.remoteConfig.endpoints.signTransaction,
|
|
89
|
+
{ walletId: signAuth.walletId, chain: this.chain.name, nonce: signAuth.nonce, transaction },
|
|
90
|
+
{
|
|
91
|
+
bearer: signAuth.token,
|
|
92
|
+
context: this.createContext("signTransaction", { walletId: signAuth.walletId, nonce: signAuth.nonce })
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
const txRecord = isRecord(tx) ? tx : {};
|
|
96
|
+
if (typeof response.transactionHash === "string" && response.transactionHash.trim() !== "") {
|
|
97
|
+
return { ...txRecord, transactionHash: response.transactionHash };
|
|
98
|
+
}
|
|
99
|
+
if (response.signedTransaction !== void 0 && response.signedTransaction !== null) {
|
|
100
|
+
if (typeof response.signedTransaction === "string") {
|
|
101
|
+
return { ...txRecord, signature: response.signedTransaction };
|
|
102
|
+
}
|
|
103
|
+
if (isRecord(response.signedTransaction)) {
|
|
104
|
+
return { ...txRecord, ...response.signedTransaction };
|
|
105
|
+
}
|
|
106
|
+
throw new Error("RemoteWalletProvider: Invalid signedTransaction format from signer service");
|
|
107
|
+
}
|
|
108
|
+
if (response.signature !== void 0 && response.signature !== null) {
|
|
109
|
+
const isMultiversxSignature = this.chain.name === "multiversx" || this.chain.name === "claws";
|
|
110
|
+
if (isMultiversxSignature) {
|
|
111
|
+
if (typeof response.signature !== "string" || !isStrictHex(response.signature)) {
|
|
112
|
+
throw new Error("RemoteWalletProvider: Invalid hex signature for multiversx transaction");
|
|
113
|
+
}
|
|
114
|
+
return { ...txRecord, signature: Buffer.from(response.signature, "hex") };
|
|
115
|
+
}
|
|
116
|
+
if (typeof response.signature !== "string" && !Array.isArray(response.signature)) {
|
|
117
|
+
throw new Error("RemoteWalletProvider: Invalid signature format from signer service");
|
|
118
|
+
}
|
|
119
|
+
return { ...txRecord, signature: response.signature };
|
|
120
|
+
}
|
|
121
|
+
throw new Error("RemoteWalletProvider: Invalid signTransaction response from signer service");
|
|
122
|
+
}
|
|
123
|
+
async signMessage(message) {
|
|
124
|
+
const signAuth = await this.getSignAuth();
|
|
125
|
+
const response = await this.request(
|
|
126
|
+
this.remoteConfig.endpoints.signMessage,
|
|
127
|
+
{ walletId: signAuth.walletId, chain: this.chain.name, nonce: signAuth.nonce, message },
|
|
128
|
+
{
|
|
129
|
+
bearer: signAuth.token,
|
|
130
|
+
context: this.createContext("signMessage", { walletId: signAuth.walletId, nonce: signAuth.nonce })
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
if (!response.signature) throw new Error("RemoteWalletProvider: Missing signature in signMessage response");
|
|
134
|
+
return response.signature;
|
|
135
|
+
}
|
|
136
|
+
async importFromMnemonic(mnemonic) {
|
|
137
|
+
void mnemonic;
|
|
138
|
+
throw new Error("RemoteWalletProvider: importFromMnemonic() is not supported. Use importFromPrivateKey or generate().");
|
|
139
|
+
}
|
|
140
|
+
async importFromPrivateKey(privateKey) {
|
|
141
|
+
const agentId = this.config.user?.id;
|
|
142
|
+
if (!agentId) throw new Error("RemoteWalletProvider: user.id is required for key import");
|
|
143
|
+
const serviceToken = await this.resolveServiceToken();
|
|
144
|
+
return await this.requestLifecycleWallet(this.remoteConfig.endpoints.import, { agentId, chain: this.chain.name, privateKey }, serviceToken, "import");
|
|
145
|
+
}
|
|
146
|
+
async export() {
|
|
147
|
+
const walletId = this.getWalletIdOrThrow();
|
|
148
|
+
const serviceToken = await this.resolveServiceToken();
|
|
149
|
+
const response = await this.request(
|
|
150
|
+
this.remoteConfig.endpoints.export,
|
|
151
|
+
{ walletId },
|
|
152
|
+
{
|
|
153
|
+
bearer: serviceToken,
|
|
154
|
+
context: this.createContext("export", { walletId })
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
return {
|
|
158
|
+
provider: response.provider ?? this.remoteConfig.providerName,
|
|
159
|
+
address: response.address,
|
|
160
|
+
privateKey: response.privateKey ?? null,
|
|
161
|
+
externalId: response.externalId ?? walletId
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
async generate() {
|
|
165
|
+
const agentId = this.config.user?.id;
|
|
166
|
+
if (!agentId) throw new Error("RemoteWalletProvider: user.id is required for wallet generation");
|
|
167
|
+
const serviceToken = await this.resolveServiceToken();
|
|
168
|
+
return await this.requestLifecycleWallet(this.remoteConfig.endpoints.generate, { agentId, chain: this.chain.name }, serviceToken, "generate");
|
|
169
|
+
}
|
|
170
|
+
async requestLifecycleWallet(path, payload, token, operation) {
|
|
171
|
+
const response = await this.request(path, payload, {
|
|
172
|
+
bearer: token,
|
|
173
|
+
context: this.createContext(operation)
|
|
174
|
+
});
|
|
175
|
+
const walletDetails = this.toWalletDetails(response);
|
|
176
|
+
setWarpWalletInConfig(this.config, this.chain.name, walletDetails);
|
|
177
|
+
return walletDetails;
|
|
178
|
+
}
|
|
179
|
+
toWalletDetails(response) {
|
|
180
|
+
return {
|
|
181
|
+
provider: response.provider ?? this.remoteConfig.providerName,
|
|
182
|
+
address: response.address,
|
|
183
|
+
externalId: response.externalId ?? response.walletId
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
getWalletIdOrThrow() {
|
|
187
|
+
const walletId = getWarpWalletExternalIdFromConfig(this.config, this.chain.name);
|
|
188
|
+
if (!walletId) {
|
|
189
|
+
throw new Error(`RemoteWalletProvider: externalId(walletId) is required for chain ${this.chain.name}`);
|
|
190
|
+
}
|
|
191
|
+
return walletId;
|
|
192
|
+
}
|
|
193
|
+
async resolveAccessToken(walletId, nonce) {
|
|
194
|
+
if (this.remoteConfig.getAccessToken) {
|
|
195
|
+
const token = await this.remoteConfig.getAccessToken({ walletId, chain: this.chain.name, nonce });
|
|
196
|
+
return this.requireToken(token, "access token callback");
|
|
197
|
+
}
|
|
198
|
+
if (this.remoteConfig.accessToken) return this.requireToken(this.remoteConfig.accessToken, "access token");
|
|
199
|
+
throw new Error("RemoteWalletProvider: No access token provider configured");
|
|
200
|
+
}
|
|
201
|
+
async getSignAuth() {
|
|
202
|
+
const walletId = this.getWalletIdOrThrow();
|
|
203
|
+
const nonce = crypto.randomUUID();
|
|
204
|
+
const token = await this.resolveAccessToken(walletId, nonce);
|
|
205
|
+
return { walletId, nonce, token };
|
|
206
|
+
}
|
|
207
|
+
async resolveServiceToken() {
|
|
208
|
+
if (this.remoteConfig.getServiceToken) {
|
|
209
|
+
const token = await this.remoteConfig.getServiceToken();
|
|
210
|
+
return this.requireToken(token, "service token callback");
|
|
211
|
+
}
|
|
212
|
+
if (this.remoteConfig.serviceToken) return this.requireToken(this.remoteConfig.serviceToken, "service token");
|
|
213
|
+
throw new Error("RemoteWalletProvider: No service token configured for wallet lifecycle endpoints");
|
|
214
|
+
}
|
|
215
|
+
toSerializableTransaction(tx) {
|
|
216
|
+
if (tx && typeof tx === "object") {
|
|
217
|
+
if ("toPlainObject" in tx && typeof tx.toPlainObject === "function") {
|
|
218
|
+
return tx.toPlainObject();
|
|
219
|
+
}
|
|
220
|
+
if ("toJSON" in tx && typeof tx.toJSON === "function") {
|
|
221
|
+
return tx.toJSON();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return tx;
|
|
225
|
+
}
|
|
226
|
+
async request(path, payload, options) {
|
|
227
|
+
const controller = new AbortController();
|
|
228
|
+
const timeout = setTimeout(() => controller.abort(), this.remoteConfig.timeoutMs ?? 15e3);
|
|
229
|
+
const context = options.context;
|
|
230
|
+
try {
|
|
231
|
+
const resolvedPayload = await this.resolvePayload(context, payload);
|
|
232
|
+
const resolvedHeaders = await this.resolveHeaders(context, options.bearer);
|
|
233
|
+
const response = await fetch(`${this.remoteConfig.baseUrl}${path}`, {
|
|
234
|
+
method: "POST",
|
|
235
|
+
headers: resolvedHeaders,
|
|
236
|
+
body: JSON.stringify(resolvedPayload, (_, value) => typeof value === "bigint" ? value.toString() : value),
|
|
237
|
+
signal: controller.signal
|
|
238
|
+
});
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
const text = await response.text();
|
|
241
|
+
throw new Error(`RemoteWalletProvider request failed (${response.status}): ${text}`);
|
|
242
|
+
}
|
|
243
|
+
return await response.json();
|
|
244
|
+
} finally {
|
|
245
|
+
clearTimeout(timeout);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
requireToken(token, source) {
|
|
249
|
+
if (typeof token !== "string" || token.trim() === "") {
|
|
250
|
+
throw new Error(`RemoteWalletProvider: ${source} returned an empty token`);
|
|
251
|
+
}
|
|
252
|
+
return token.trim();
|
|
253
|
+
}
|
|
254
|
+
createContext(operation, extra) {
|
|
255
|
+
return {
|
|
256
|
+
operation,
|
|
257
|
+
chain: this.chain.name,
|
|
258
|
+
...extra
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async resolveHeaders(context, bearer) {
|
|
262
|
+
const headersFromConfig = this.remoteConfig.headers ?? {};
|
|
263
|
+
const dynamicHeaders = this.remoteConfig.getHeaders ? await this.remoteConfig.getHeaders(context) : {};
|
|
264
|
+
return {
|
|
265
|
+
"Content-Type": "application/json",
|
|
266
|
+
...headersFromConfig,
|
|
267
|
+
...dynamicHeaders ?? {},
|
|
268
|
+
...bearer ? { Authorization: `Bearer ${bearer}` } : {}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
async resolvePayload(context, payload) {
|
|
272
|
+
if (!this.remoteConfig.transformPayload) return payload;
|
|
273
|
+
return await this.remoteConfig.transformPayload(context, payload);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// src/helpers.ts
|
|
278
|
+
var createRemoteWalletProvider = (remoteConfig, fallbackProviderName = DEFAULT_REMOTE_WALLET_PROVIDER_NAME) => {
|
|
279
|
+
return (config, chain) => new RemoteWalletProvider(config, chain, remoteConfig, fallbackProviderName);
|
|
280
|
+
};
|
|
281
|
+
export {
|
|
282
|
+
DEFAULT_REMOTE_WALLET_PROVIDER_NAME,
|
|
283
|
+
RemoteWalletProvider,
|
|
284
|
+
createRemoteWalletProvider
|
|
285
|
+
};
|
|
286
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/RemoteWalletProvider.ts","../src/types.ts","../src/helpers.ts"],"sourcesContent":["import {\n getWarpWalletAddressFromConfig,\n getWarpWalletExternalIdFromConfig,\n setWarpWalletInConfig,\n WalletProvider,\n WarpAdapterGenericTransaction,\n WarpChainInfo,\n WarpClientConfig,\n WarpWalletDetails,\n WarpWalletProvider,\n} from '@joai/warps'\nimport {\n RemoteWalletOperation,\n RemoteWalletRequestContext,\n RemoteWalletLifecycleResponse,\n RemoteWalletProviderEndpoints,\n RemoteWalletProviderConfig,\n DEFAULT_REMOTE_WALLET_PROVIDER_NAME,\n} from './types'\n\nconst defaultEndpoints: RemoteWalletProviderEndpoints = {\n generate: '/v1/wallets/generate',\n import: '/v1/wallets/import',\n export: '/v1/wallets/export',\n signTransaction: '/v1/sign/transaction',\n signMessage: '/v1/sign/message',\n}\n\ntype ResolvedRemoteWalletProviderConfig = RemoteWalletProviderConfig & {\n providerName: WarpWalletProvider\n endpoints: RemoteWalletProviderEndpoints\n}\n\ntype SignTransactionResponse = {\n signedTransaction?: unknown\n signature?: unknown\n transactionHash?: unknown\n}\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, '')\nconst isStrictHex = (value: string): boolean => /^[0-9a-f]+$/i.test(value) && value.length % 2 === 0\nconst isLoopbackHost = (host: string): boolean => ['localhost', '127.0.0.1', '::1'].includes(host.toLowerCase())\nconst isRecord = (value: unknown): value is Record<string, unknown> => Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n\nconst normalizeAndValidateBaseUrl = (baseUrl: string, allowInsecureHttp?: boolean): string => {\n const normalized = trimTrailingSlash(baseUrl.trim())\n let parsedUrl: URL\n\n try {\n parsedUrl = new URL(normalized)\n } catch {\n throw new Error('RemoteWalletProvider: baseUrl must be an absolute URL')\n }\n\n const isHttps = parsedUrl.protocol.toLowerCase() === 'https:'\n if (!isHttps && !allowInsecureHttp && !isLoopbackHost(parsedUrl.hostname)) {\n throw new Error('RemoteWalletProvider: baseUrl must use HTTPS unless allowInsecureHttp is explicitly enabled')\n }\n\n return normalized\n}\n\nconst normalizeEndpointPath = (path: string): string => {\n const normalized = path.trim()\n if (!normalized) {\n throw new Error('RemoteWalletProvider: endpoint path must be a non-empty string')\n }\n\n try {\n new URL(normalized)\n throw new Error('RemoteWalletProvider: endpoint path must be relative')\n } catch (error) {\n if (error instanceof Error && error.message.includes('must be relative')) {\n throw error\n }\n }\n\n return normalized.startsWith('/') ? normalized : `/${normalized}`\n}\n\nconst normalizeEndpoints = (endpoints: RemoteWalletProviderEndpoints): RemoteWalletProviderEndpoints => ({\n generate: normalizeEndpointPath(endpoints.generate),\n import: normalizeEndpointPath(endpoints.import),\n export: normalizeEndpointPath(endpoints.export),\n signTransaction: normalizeEndpointPath(endpoints.signTransaction),\n signMessage: normalizeEndpointPath(endpoints.signMessage),\n})\n\nconst withDefaults = (\n remoteConfig: RemoteWalletProviderConfig,\n fallbackProviderName: WarpWalletProvider\n): ResolvedRemoteWalletProviderConfig => {\n const baseUrl = normalizeAndValidateBaseUrl(remoteConfig.baseUrl, remoteConfig.allowInsecureHttp)\n const endpoints = normalizeEndpoints({\n ...defaultEndpoints,\n ...(remoteConfig.endpoints || {}),\n })\n\n return {\n ...remoteConfig,\n baseUrl,\n providerName: remoteConfig.providerName ?? fallbackProviderName,\n endpoints,\n }\n}\n\nexport class RemoteWalletProvider implements WalletProvider {\n protected readonly remoteConfig: ResolvedRemoteWalletProviderConfig\n\n constructor(\n private readonly config: WarpClientConfig,\n private readonly chain: WarpChainInfo,\n remoteConfig: RemoteWalletProviderConfig,\n fallbackProviderName: WarpWalletProvider = DEFAULT_REMOTE_WALLET_PROVIDER_NAME\n ) {\n this.remoteConfig = withDefaults(remoteConfig, fallbackProviderName)\n }\n\n async getAddress(): Promise<string | null> {\n return getWarpWalletAddressFromConfig(this.config, this.chain.name)\n }\n\n async getPublicKey(): Promise<string | null> {\n return null\n }\n\n async signTransaction(tx: WarpAdapterGenericTransaction): Promise<WarpAdapterGenericTransaction> {\n const signAuth = await this.getSignAuth()\n const transaction = this.toSerializableTransaction(tx)\n\n const response = await this.request<SignTransactionResponse>(\n this.remoteConfig.endpoints.signTransaction,\n { walletId: signAuth.walletId, chain: this.chain.name, nonce: signAuth.nonce, transaction },\n {\n bearer: signAuth.token,\n context: this.createContext('signTransaction', { walletId: signAuth.walletId, nonce: signAuth.nonce }),\n }\n )\n const txRecord = isRecord(tx) ? tx : {}\n\n if (typeof response.transactionHash === 'string' && response.transactionHash.trim() !== '') {\n return { ...txRecord, transactionHash: response.transactionHash }\n }\n\n if (response.signedTransaction !== undefined && response.signedTransaction !== null) {\n if (typeof response.signedTransaction === 'string') {\n return { ...txRecord, signature: response.signedTransaction }\n }\n\n if (isRecord(response.signedTransaction)) {\n return { ...txRecord, ...response.signedTransaction }\n }\n\n throw new Error('RemoteWalletProvider: Invalid signedTransaction format from signer service')\n }\n\n if (response.signature !== undefined && response.signature !== null) {\n const isMultiversxSignature = this.chain.name === 'multiversx' || this.chain.name === 'claws'\n\n if (isMultiversxSignature) {\n if (typeof response.signature !== 'string' || !isStrictHex(response.signature)) {\n throw new Error('RemoteWalletProvider: Invalid hex signature for multiversx transaction')\n }\n return { ...txRecord, signature: Buffer.from(response.signature, 'hex') }\n }\n\n if (typeof response.signature !== 'string' && !Array.isArray(response.signature)) {\n throw new Error('RemoteWalletProvider: Invalid signature format from signer service')\n }\n\n return { ...txRecord, signature: response.signature }\n }\n\n throw new Error('RemoteWalletProvider: Invalid signTransaction response from signer service')\n }\n\n async signMessage(message: string): Promise<string> {\n const signAuth = await this.getSignAuth()\n\n const response = await this.request<{ signature: string }>(\n this.remoteConfig.endpoints.signMessage,\n { walletId: signAuth.walletId, chain: this.chain.name, nonce: signAuth.nonce, message },\n {\n bearer: signAuth.token,\n context: this.createContext('signMessage', { walletId: signAuth.walletId, nonce: signAuth.nonce }),\n }\n )\n\n if (!response.signature) throw new Error('RemoteWalletProvider: Missing signature in signMessage response')\n return response.signature\n }\n\n async importFromMnemonic(mnemonic: string): Promise<WarpWalletDetails> {\n void mnemonic\n throw new Error('RemoteWalletProvider: importFromMnemonic() is not supported. Use importFromPrivateKey or generate().')\n }\n\n async importFromPrivateKey(privateKey: string): Promise<WarpWalletDetails> {\n const agentId = this.config.user?.id\n if (!agentId) throw new Error('RemoteWalletProvider: user.id is required for key import')\n const serviceToken = await this.resolveServiceToken()\n\n return await this.requestLifecycleWallet(this.remoteConfig.endpoints.import, { agentId, chain: this.chain.name, privateKey }, serviceToken, 'import')\n }\n\n async export(): Promise<WarpWalletDetails> {\n const walletId = this.getWalletIdOrThrow()\n const serviceToken = await this.resolveServiceToken()\n\n const response = await this.request<{ provider?: WarpWalletProvider; address: string; privateKey?: string; externalId?: string }>(\n this.remoteConfig.endpoints.export,\n { walletId },\n {\n bearer: serviceToken,\n context: this.createContext('export', { walletId }),\n }\n )\n\n return {\n provider: response.provider ?? this.remoteConfig.providerName,\n address: response.address,\n privateKey: response.privateKey ?? null,\n externalId: response.externalId ?? walletId,\n }\n }\n\n async generate(): Promise<WarpWalletDetails> {\n const agentId = this.config.user?.id\n if (!agentId) throw new Error('RemoteWalletProvider: user.id is required for wallet generation')\n const serviceToken = await this.resolveServiceToken()\n\n return await this.requestLifecycleWallet(this.remoteConfig.endpoints.generate, { agentId, chain: this.chain.name }, serviceToken, 'generate')\n }\n\n private async requestLifecycleWallet(\n path: string,\n payload: Record<string, unknown>,\n token: string,\n operation: RemoteWalletOperation\n ): Promise<WarpWalletDetails> {\n const response = await this.request<RemoteWalletLifecycleResponse>(path, payload, {\n bearer: token,\n context: this.createContext(operation),\n })\n const walletDetails = this.toWalletDetails(response)\n setWarpWalletInConfig(this.config, this.chain.name, walletDetails)\n return walletDetails\n }\n\n private toWalletDetails(response: RemoteWalletLifecycleResponse): WarpWalletDetails {\n return {\n provider: response.provider ?? this.remoteConfig.providerName,\n address: response.address,\n externalId: response.externalId ?? response.walletId,\n }\n }\n\n private getWalletIdOrThrow(): string {\n const walletId = getWarpWalletExternalIdFromConfig(this.config, this.chain.name)\n if (!walletId) {\n throw new Error(`RemoteWalletProvider: externalId(walletId) is required for chain ${this.chain.name}`)\n }\n return walletId\n }\n\n private async resolveAccessToken(walletId: string, nonce: string): Promise<string> {\n if (this.remoteConfig.getAccessToken) {\n const token = await this.remoteConfig.getAccessToken({ walletId, chain: this.chain.name, nonce })\n return this.requireToken(token, 'access token callback')\n }\n if (this.remoteConfig.accessToken) return this.requireToken(this.remoteConfig.accessToken, 'access token')\n throw new Error('RemoteWalletProvider: No access token provider configured')\n }\n\n private async getSignAuth(): Promise<{ walletId: string; nonce: string; token: string }> {\n const walletId = this.getWalletIdOrThrow()\n const nonce = crypto.randomUUID()\n const token = await this.resolveAccessToken(walletId, nonce)\n return { walletId, nonce, token }\n }\n\n private async resolveServiceToken(): Promise<string> {\n if (this.remoteConfig.getServiceToken) {\n const token = await this.remoteConfig.getServiceToken()\n return this.requireToken(token, 'service token callback')\n }\n if (this.remoteConfig.serviceToken) return this.requireToken(this.remoteConfig.serviceToken, 'service token')\n throw new Error('RemoteWalletProvider: No service token configured for wallet lifecycle endpoints')\n }\n\n private toSerializableTransaction(tx: WarpAdapterGenericTransaction): unknown {\n if (tx && typeof tx === 'object') {\n if ('toPlainObject' in tx && typeof (tx as { toPlainObject?: () => unknown }).toPlainObject === 'function') {\n return (tx as { toPlainObject: () => unknown }).toPlainObject()\n }\n if ('toJSON' in tx && typeof (tx as { toJSON?: () => unknown }).toJSON === 'function') {\n return (tx as { toJSON: () => unknown }).toJSON()\n }\n }\n return tx\n }\n\n private async request<T>(\n path: string,\n payload: Record<string, unknown>,\n options: { bearer?: string; context: RemoteWalletRequestContext }\n ): Promise<T> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.remoteConfig.timeoutMs ?? 15_000)\n const context = options.context\n\n try {\n const resolvedPayload = await this.resolvePayload(context, payload)\n const resolvedHeaders = await this.resolveHeaders(context, options.bearer)\n\n const response = await fetch(`${this.remoteConfig.baseUrl}${path}`, {\n method: 'POST',\n headers: resolvedHeaders,\n body: JSON.stringify(resolvedPayload, (_, value: unknown) => (typeof value === 'bigint' ? value.toString() : value)),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const text = await response.text()\n throw new Error(`RemoteWalletProvider request failed (${response.status}): ${text}`)\n }\n\n return (await response.json()) as T\n } finally {\n clearTimeout(timeout)\n }\n }\n\n private requireToken(token: unknown, source: string): string {\n if (typeof token !== 'string' || token.trim() === '') {\n throw new Error(`RemoteWalletProvider: ${source} returned an empty token`)\n }\n return token.trim()\n }\n\n private createContext(operation: RemoteWalletOperation, extra?: Partial<RemoteWalletRequestContext>): RemoteWalletRequestContext {\n return {\n operation,\n chain: this.chain.name,\n ...extra,\n }\n }\n\n private async resolveHeaders(context: RemoteWalletRequestContext, bearer?: string): Promise<Record<string, string>> {\n const headersFromConfig = this.remoteConfig.headers ?? {}\n const dynamicHeaders = this.remoteConfig.getHeaders ? await this.remoteConfig.getHeaders(context) : {}\n\n return {\n 'Content-Type': 'application/json',\n ...headersFromConfig,\n ...(dynamicHeaders ?? {}),\n ...(bearer ? { Authorization: `Bearer ${bearer}` } : {}),\n }\n }\n\n private async resolvePayload(context: RemoteWalletRequestContext, payload: Record<string, unknown>): Promise<Record<string, unknown>> {\n if (!this.remoteConfig.transformPayload) return payload\n return await this.remoteConfig.transformPayload(context, payload)\n }\n}\n","import { WarpWalletProvider } from '@joai/warps'\n\nexport const DEFAULT_REMOTE_WALLET_PROVIDER_NAME: WarpWalletProvider = 'remote'\n\nexport type RemoteWalletProviderEndpoints = {\n generate: string\n import: string\n export: string\n signTransaction: string\n signMessage: string\n}\n\nexport type RemoteWalletOperation = 'generate' | 'import' | 'export' | 'signTransaction' | 'signMessage'\n\nexport type RemoteWalletAccessTokenParams = {\n walletId: string\n chain: string\n nonce: string\n}\n\nexport type RemoteWalletRequestContext = {\n operation: RemoteWalletOperation\n chain: string\n walletId?: string\n nonce?: string\n}\n\nexport type RemoteWalletProviderConfig = {\n baseUrl: string\n allowInsecureHttp?: boolean\n providerName?: WarpWalletProvider\n endpoints?: Partial<RemoteWalletProviderEndpoints>\n timeoutMs?: number\n headers?: Record<string, string>\n getHeaders?: (context: RemoteWalletRequestContext) => Promise<Record<string, string> | undefined> | Record<string, string> | undefined\n transformPayload?: (\n context: RemoteWalletRequestContext,\n payload: Record<string, unknown>\n ) => Promise<Record<string, unknown>> | Record<string, unknown>\n serviceToken?: string\n getServiceToken?: () => Promise<string> | string\n accessToken?: string\n getAccessToken?: (params: RemoteWalletAccessTokenParams) => Promise<string> | string\n}\n\nexport type RemoteWalletLifecycleResponse = {\n provider?: WarpWalletProvider\n walletId: string\n externalId?: string\n address: string\n}\n","import { WalletProviderFactory, WarpChainInfo, WarpClientConfig, WarpWalletProvider } from '@joai/warps'\nimport { RemoteWalletProvider } from './RemoteWalletProvider'\nimport { RemoteWalletProviderConfig, DEFAULT_REMOTE_WALLET_PROVIDER_NAME } from './types'\n\nexport const createRemoteWalletProvider = (\n remoteConfig: RemoteWalletProviderConfig,\n fallbackProviderName: WarpWalletProvider = DEFAULT_REMOTE_WALLET_PROVIDER_NAME\n): WalletProviderFactory => {\n return (config: WarpClientConfig, chain: WarpChainInfo) => new RemoteWalletProvider(config, chain, remoteConfig, fallbackProviderName)\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@joai/warps-wallet-remote",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Generic remote wallet provider for signer services",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"test": "jest --config jest.config.mjs",
|
|
18
|
+
"lint": "tsc --noEmit",
|
|
19
|
+
"preversion": "npm run lint && npm run build"
|
|
20
|
+
},
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/jest": "^30.0.0",
|
|
28
|
+
"jest": "^30.3.0",
|
|
29
|
+
"ts-jest": "^29.4.6",
|
|
30
|
+
"tsup": "^8.5.1",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@joai/warps": "^4.1.0"
|
|
38
|
+
}
|
|
39
|
+
}
|