@keeperhub/wallet 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/cli.cjs +69 -13
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +69 -13
- package/dist/cli.js.map +1 -1
- package/dist/hook-entrypoint.cjs +23 -0
- package/dist/hook-entrypoint.cjs.map +1 -1
- package/dist/hook-entrypoint.js +23 -0
- package/dist/hook-entrypoint.js.map +1 -1
- package/dist/index.cjs +127 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -22
- package/dist/index.d.ts +75 -22
- package/dist/index.js +125 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,9 @@ declare class KeeperHubError extends Error {
|
|
|
33
33
|
readonly code: string;
|
|
34
34
|
constructor(code: string, message: string);
|
|
35
35
|
}
|
|
36
|
+
/** Protocol preference for a single pay() or fetch() call. "auto" preserves
|
|
37
|
+
* the x402-first default when both challenges are offered. */
|
|
38
|
+
type PaymentHint = "x402" | "mpp" | "auto";
|
|
36
39
|
declare class WalletConfigMissingError extends Error {
|
|
37
40
|
constructor();
|
|
38
41
|
}
|
|
@@ -259,6 +262,25 @@ type MppChallenge = {
|
|
|
259
262
|
};
|
|
260
263
|
declare function parseMppChallenge(response: Response): MppChallenge | null;
|
|
261
264
|
|
|
265
|
+
type X402Challenge = {
|
|
266
|
+
x402Version: 2;
|
|
267
|
+
accepts: Array<{
|
|
268
|
+
scheme: "exact";
|
|
269
|
+
network: string;
|
|
270
|
+
asset: string;
|
|
271
|
+
amount: string;
|
|
272
|
+
payTo: string;
|
|
273
|
+
maxTimeoutSeconds: number;
|
|
274
|
+
extra: Record<string, unknown>;
|
|
275
|
+
}>;
|
|
276
|
+
resource: {
|
|
277
|
+
url: string;
|
|
278
|
+
description: string;
|
|
279
|
+
mimeType: string;
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
declare function parseX402Challenge(response: Response): Promise<X402Challenge | null>;
|
|
283
|
+
|
|
262
284
|
type PaySignerOptions = {
|
|
263
285
|
/** Override wallet loader (primarily for tests). */
|
|
264
286
|
walletLoader?: () => Promise<WalletConfig>;
|
|
@@ -296,6 +318,18 @@ type PayRetryOptions = {
|
|
|
296
318
|
headers?: RequestInit["headers"];
|
|
297
319
|
/** HTTP method for the retry. Defaults to "POST". */
|
|
298
320
|
method?: string;
|
|
321
|
+
/**
|
|
322
|
+
* Per-call protocol preference. "x402" forces Base USDC; "mpp" forces Tempo
|
|
323
|
+
* USDC.e; "auto" (default, also the behaviour when omitted) uses x402 when
|
|
324
|
+
* offered, MPP otherwise. Throws KeeperHubError("X402_NOT_OFFERED") or
|
|
325
|
+
* KeeperHubError("MPP_NOT_OFFERED") when the requested protocol is absent
|
|
326
|
+
* from the challenge (KEEP-361).
|
|
327
|
+
*/
|
|
328
|
+
paymentHint?: PaymentHint;
|
|
329
|
+
};
|
|
330
|
+
/** RequestInit extended with paymentHint for per-call protocol selection. */
|
|
331
|
+
type FetchInit = RequestInit & {
|
|
332
|
+
paymentHint?: PaymentHint;
|
|
299
333
|
};
|
|
300
334
|
type PaymentSigner = {
|
|
301
335
|
/**
|
|
@@ -315,12 +349,36 @@ type PaymentSigner = {
|
|
|
315
349
|
* `pay()` with `init.body` + `init.headers` so the retry carries the
|
|
316
350
|
* original payload. Returns whatever the retry (or first response, if not
|
|
317
351
|
* 402) returns. No-op for non-402 responses.
|
|
352
|
+
*
|
|
353
|
+
* Pass `init.paymentHint` to force a specific payment protocol for this
|
|
354
|
+
* call. Omitting it is equivalent to `paymentHint: "auto"` (x402-first).
|
|
318
355
|
*/
|
|
319
|
-
fetch: (input: string | URL, init?:
|
|
356
|
+
fetch: (input: string | URL, init?: FetchInit) => Promise<Response>;
|
|
320
357
|
};
|
|
358
|
+
/**
|
|
359
|
+
* Pure function that decides which payment protocol to use given challenge
|
|
360
|
+
* availability and caller's hint. Exported for unit testing.
|
|
361
|
+
*
|
|
362
|
+
* Returns "x402" or "mpp" to direct the caller to the appropriate path,
|
|
363
|
+
* or null when hint is "auto" and no challenge is present (pay() then
|
|
364
|
+
* returns the original 402 response unchanged). Throws KeeperHubError with
|
|
365
|
+
* a specific code when the requested protocol is unavailable (KEEP-361).
|
|
366
|
+
*/
|
|
367
|
+
declare function selectProtocol(x402: X402Challenge | null, mpp: MppChallenge | null, hint: PaymentHint | undefined): "x402" | "mpp" | null;
|
|
321
368
|
declare function createPaymentSigner(opts?: PaySignerOptions): PaymentSigner;
|
|
322
369
|
declare const paymentSigner: PaymentSigner;
|
|
323
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Pick the hook command to write into settings.json.
|
|
373
|
+
*
|
|
374
|
+
* Returns the bare bin name if it resolves on PATH (global install or a
|
|
375
|
+
* dev-time `npm link`), otherwise a version-pinned `npx` invocation that
|
|
376
|
+
* pulls the installer's own version of `@keeperhub/wallet` on demand.
|
|
377
|
+
* Override-able via the env var `KEEPERHUB_WALLET_HOOK_COMMAND` for
|
|
378
|
+
* test fixtures and unusual deployments (env input is trusted — it is
|
|
379
|
+
* written verbatim into settings.json and executed by the user's shell).
|
|
380
|
+
*/
|
|
381
|
+
declare function resolveHookCommand(): string;
|
|
324
382
|
type InstallResult = {
|
|
325
383
|
skillWrites: Array<{
|
|
326
384
|
agent: string;
|
|
@@ -337,31 +395,26 @@ type InstallOptions = {
|
|
|
337
395
|
homeOverride?: string;
|
|
338
396
|
skillSourcePath?: string;
|
|
339
397
|
onNotice?: (msg: string) => void;
|
|
398
|
+
/**
|
|
399
|
+
* Hook command to write into settings.json (and reference in stderr
|
|
400
|
+
* notices for non-Claude agents). Defaults to {@link resolveHookCommand}.
|
|
401
|
+
* Override for tests, monorepo setups, or unusual deployments.
|
|
402
|
+
*/
|
|
403
|
+
hookCommand?: string;
|
|
404
|
+
};
|
|
405
|
+
type RegisterClaudeCodeHookOptions = {
|
|
406
|
+
/**
|
|
407
|
+
* Hook command to write. Defaults to {@link resolveHookCommand}. Tests
|
|
408
|
+
* pass a deterministic value to keep assertions stable across host
|
|
409
|
+
* environments (CI may or may not have the bin on PATH).
|
|
410
|
+
*/
|
|
411
|
+
hookCommand?: string;
|
|
340
412
|
};
|
|
341
|
-
declare function registerClaudeCodeHook(settingsPath: string): Promise<void>;
|
|
413
|
+
declare function registerClaudeCodeHook(settingsPath: string, options?: RegisterClaudeCodeHookOptions): Promise<void>;
|
|
342
414
|
declare function installSkill(options?: InstallOptions): Promise<InstallResult>;
|
|
343
415
|
|
|
344
416
|
declare function readWalletConfig(): Promise<WalletConfig>;
|
|
345
417
|
declare function writeWalletConfig(config: WalletConfig): Promise<void>;
|
|
346
418
|
declare function getWalletConfigPath(): string;
|
|
347
419
|
|
|
348
|
-
type X402Challenge
|
|
349
|
-
x402Version: 2;
|
|
350
|
-
accepts: Array<{
|
|
351
|
-
scheme: "exact";
|
|
352
|
-
network: string;
|
|
353
|
-
asset: string;
|
|
354
|
-
amount: string;
|
|
355
|
-
payTo: string;
|
|
356
|
-
maxTimeoutSeconds: number;
|
|
357
|
-
extra: Record<string, unknown>;
|
|
358
|
-
}>;
|
|
359
|
-
resource: {
|
|
360
|
-
url: string;
|
|
361
|
-
description: string;
|
|
362
|
-
mimeType: string;
|
|
363
|
-
};
|
|
364
|
-
};
|
|
365
|
-
declare function parseX402Challenge(response: Response): Promise<X402Challenge | null>;
|
|
366
|
-
|
|
367
|
-
export { type AgentTarget, type AskTierResponse, BASE_USDC, type BalanceSnapshot, type CheckBalanceOptions, type ClientOptions, type CreateHookOptions, DEFAULT_SAFETY_CONFIG, type FundInstructions, type HmacHeaders, type HookDecision, type InstallOptions, type InstallResult, KeeperHubClient, KeeperHubError, type MppChallenge, type PaymentSigner, type SafetyConfig, TEMPO_USDC_E, type WalletConfig, WalletConfigMissingError, type X402Challenge, buildHmacHeaders, checkBalance, computeSignature, createPaymentSigner, createPreToolUseHook, detectAgents, fund, getSafetyConfigPath, getWalletConfigPath, installSkill, loadSafetyConfig, parseMppChallenge, parseX402Challenge, paymentSigner, readWalletConfig, registerClaudeCodeHook, tempo, validateAndMerge, writeWalletConfig };
|
|
420
|
+
export { type AgentTarget, type AskTierResponse, BASE_USDC, type BalanceSnapshot, type CheckBalanceOptions, type ClientOptions, type CreateHookOptions, DEFAULT_SAFETY_CONFIG, type FetchInit, type FundInstructions, type HmacHeaders, type HookDecision, type InstallOptions, type InstallResult, KeeperHubClient, KeeperHubError, type MppChallenge, type PayRetryOptions, type PaymentHint, type PaymentSigner, type RegisterClaudeCodeHookOptions, type SafetyConfig, TEMPO_USDC_E, type WalletConfig, WalletConfigMissingError, type X402Challenge, buildHmacHeaders, checkBalance, computeSignature, createPaymentSigner, createPreToolUseHook, detectAgents, fund, getSafetyConfigPath, getWalletConfigPath, installSkill, loadSafetyConfig, parseMppChallenge, parseX402Challenge, paymentSigner, readWalletConfig, registerClaudeCodeHook, resolveHookCommand, selectProtocol, tempo, validateAndMerge, writeWalletConfig };
|
package/dist/index.js
CHANGED
|
@@ -147,15 +147,69 @@ function fund(walletAddress) {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
// src/skill-install.ts
|
|
150
|
+
import { execFileSync } from "child_process";
|
|
150
151
|
import { chmod, copyFile, mkdir, readFile, writeFile } from "fs/promises";
|
|
152
|
+
import { readFileSync } from "fs";
|
|
151
153
|
import { dirname as dirname2, join as join2 } from "path";
|
|
152
154
|
import { fileURLToPath } from "url";
|
|
153
|
-
var
|
|
154
|
-
var
|
|
155
|
-
|
|
155
|
+
var HOOK_BIN = "keeperhub-wallet-hook";
|
|
156
|
+
var HOOK_COMMAND_BARE = HOOK_BIN;
|
|
157
|
+
var PACKAGE_NAME = "@keeperhub/wallet";
|
|
158
|
+
function readPackageVersion() {
|
|
159
|
+
try {
|
|
160
|
+
const here = dirname2(fileURLToPath(import.meta.url));
|
|
161
|
+
const pkgPath = join2(here, "..", "package.json");
|
|
162
|
+
const raw = readFileSync(pkgPath, "utf-8");
|
|
163
|
+
const parsed = JSON.parse(raw);
|
|
164
|
+
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
165
|
+
return parsed.version;
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
return "latest";
|
|
170
|
+
}
|
|
171
|
+
function buildNpxCommand(version) {
|
|
172
|
+
return `npx -y -p ${PACKAGE_NAME}@${version} ${HOOK_BIN}`;
|
|
173
|
+
}
|
|
174
|
+
var KEEPERHUB_HOOK_MARKER = HOOK_BIN;
|
|
175
|
+
function filterKeeperhubHooksFromEntry(entry) {
|
|
176
|
+
if (typeof entry !== "object" || entry === null) {
|
|
177
|
+
return entry;
|
|
178
|
+
}
|
|
179
|
+
const candidate = entry;
|
|
180
|
+
if (!Array.isArray(candidate.hooks)) {
|
|
181
|
+
return entry;
|
|
182
|
+
}
|
|
183
|
+
const survivors = candidate.hooks.filter((h) => {
|
|
184
|
+
const cmd = h?.command;
|
|
185
|
+
return !(typeof cmd === "string" && cmd.includes(KEEPERHUB_HOOK_MARKER));
|
|
186
|
+
});
|
|
187
|
+
if (survivors.length === candidate.hooks.length) {
|
|
188
|
+
return entry;
|
|
189
|
+
}
|
|
190
|
+
if (survivors.length === 0) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
return { ...candidate, hooks: survivors };
|
|
194
|
+
}
|
|
195
|
+
function resolveHookCommand() {
|
|
196
|
+
const envOverride = process.env.KEEPERHUB_WALLET_HOOK_COMMAND;
|
|
197
|
+
if (envOverride && envOverride.length > 0) {
|
|
198
|
+
return envOverride;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
execFileSync("/bin/sh", ["-c", `command -v ${HOOK_BIN}`], {
|
|
202
|
+
stdio: "ignore"
|
|
203
|
+
});
|
|
204
|
+
return HOOK_COMMAND_BARE;
|
|
205
|
+
} catch {
|
|
206
|
+
return buildNpxCommand(readPackageVersion());
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function buildKeeperhubEntry(command) {
|
|
156
210
|
return {
|
|
157
211
|
matcher: "*",
|
|
158
|
-
hooks: [{ type: "command", command
|
|
212
|
+
hooks: [{ type: "command", command }]
|
|
159
213
|
};
|
|
160
214
|
}
|
|
161
215
|
function resolveDefaultSkillSource() {
|
|
@@ -166,7 +220,8 @@ function defaultNotice(msg) {
|
|
|
166
220
|
process.stderr.write(`${msg}
|
|
167
221
|
`);
|
|
168
222
|
}
|
|
169
|
-
async function registerClaudeCodeHook(settingsPath) {
|
|
223
|
+
async function registerClaudeCodeHook(settingsPath, options = {}) {
|
|
224
|
+
const command = options.hookCommand ?? resolveHookCommand();
|
|
170
225
|
let raw = null;
|
|
171
226
|
try {
|
|
172
227
|
raw = await readFile(settingsPath, "utf-8");
|
|
@@ -189,12 +244,12 @@ async function registerClaudeCodeHook(settingsPath) {
|
|
|
189
244
|
const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
|
|
190
245
|
const filtered = [];
|
|
191
246
|
for (const entry of existingPreToolUse) {
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
filtered.push(
|
|
247
|
+
const survivor = filterKeeperhubHooksFromEntry(entry);
|
|
248
|
+
if (survivor !== null) {
|
|
249
|
+
filtered.push(survivor);
|
|
195
250
|
}
|
|
196
251
|
}
|
|
197
|
-
filtered.push(buildKeeperhubEntry());
|
|
252
|
+
filtered.push(buildKeeperhubEntry(command));
|
|
198
253
|
hooks.PreToolUse = filtered;
|
|
199
254
|
config.hooks = hooks;
|
|
200
255
|
await mkdir(dirname2(settingsPath), { recursive: true, mode: 448 });
|
|
@@ -210,26 +265,27 @@ async function writeSkillToAgent(agent, skillSource) {
|
|
|
210
265
|
await chmod(target, 420);
|
|
211
266
|
return { agent: agent.agent, path: target, status: "written" };
|
|
212
267
|
}
|
|
213
|
-
function buildNoticeMessage(agent) {
|
|
214
|
-
return `${agent.agent} does not support auto-registered PreToolUse hooks; run \`${
|
|
268
|
+
function buildNoticeMessage(agent, command) {
|
|
269
|
+
return `${agent.agent} does not support auto-registered PreToolUse hooks; run \`${command}\` on every tool use via ${agent.agent}'s settings file at ${agent.settingsFile}`;
|
|
215
270
|
}
|
|
216
271
|
async function installSkill(options = {}) {
|
|
217
272
|
const agents = detectAgents(options.homeOverride);
|
|
218
273
|
const skillSource = options.skillSourcePath ?? resolveDefaultSkillSource();
|
|
219
274
|
const onNotice = options.onNotice ?? defaultNotice;
|
|
275
|
+
const hookCommand = options.hookCommand ?? resolveHookCommand();
|
|
220
276
|
const skillWrites = [];
|
|
221
277
|
const hookRegistrations = [];
|
|
222
278
|
for (const agent of agents) {
|
|
223
279
|
const write = await writeSkillToAgent(agent, skillSource);
|
|
224
280
|
skillWrites.push(write);
|
|
225
281
|
if (agent.hookSupport === "claude-code") {
|
|
226
|
-
await registerClaudeCodeHook(agent.settingsFile);
|
|
282
|
+
await registerClaudeCodeHook(agent.settingsFile, { hookCommand });
|
|
227
283
|
hookRegistrations.push({
|
|
228
284
|
agent: agent.agent,
|
|
229
285
|
status: "registered"
|
|
230
286
|
});
|
|
231
287
|
} else {
|
|
232
|
-
const message = buildNoticeMessage(agent);
|
|
288
|
+
const message = buildNoticeMessage(agent, hookCommand);
|
|
233
289
|
hookRegistrations.push({
|
|
234
290
|
agent: agent.agent,
|
|
235
291
|
status: "notice",
|
|
@@ -637,6 +693,26 @@ var DEFAULT_TOOL_RE = /keeperhub|wallet|sign/i;
|
|
|
637
693
|
function defaultToolMatcher(name) {
|
|
638
694
|
return DEFAULT_TOOL_RE.test(name);
|
|
639
695
|
}
|
|
696
|
+
function hasPaymentShape(input) {
|
|
697
|
+
const ti = input.tool_input ?? {};
|
|
698
|
+
const challenge = ti.paymentChallenge;
|
|
699
|
+
if (challenge !== void 0 && challenge !== null) {
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
if (ti.amount !== void 0 && ti.amount !== null) {
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
if (ti.unit !== void 0 && ti.unit !== null) {
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
for (const field of ["to", "contract", "assetAddress"]) {
|
|
709
|
+
const v = ti[field];
|
|
710
|
+
if (typeof v === "string" && ADDRESS_RE.test(v)) {
|
|
711
|
+
return true;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return false;
|
|
715
|
+
}
|
|
640
716
|
function extractAmountMicroUsdc(input) {
|
|
641
717
|
const ti = input.tool_input ?? {};
|
|
642
718
|
const challenge = ti.paymentChallenge ?? {};
|
|
@@ -686,6 +762,9 @@ async function createPreToolUseHook(options = {}) {
|
|
|
686
762
|
if (!(typeof hookInput.tool_name === "string" && toolMatcher(hookInput.tool_name))) {
|
|
687
763
|
return { decision: "allow" };
|
|
688
764
|
}
|
|
765
|
+
if (!hasPaymentShape(hookInput)) {
|
|
766
|
+
return { decision: "allow" };
|
|
767
|
+
}
|
|
689
768
|
const contractAddr = extractContractAddress(hookInput);
|
|
690
769
|
const amountMicro = extractAmountMicroUsdc(hookInput);
|
|
691
770
|
if (contractAddr && !safety.allowlisted_contracts.includes(contractAddr)) {
|
|
@@ -818,6 +897,30 @@ var NONCE_BYTES = 32;
|
|
|
818
897
|
async function sleep(ms) {
|
|
819
898
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
820
899
|
}
|
|
900
|
+
function selectProtocol(x402, mpp, hint) {
|
|
901
|
+
const h = hint ?? "auto";
|
|
902
|
+
if (h === "x402") {
|
|
903
|
+
if (!x402) {
|
|
904
|
+
throw new KeeperHubError(
|
|
905
|
+
"X402_NOT_OFFERED",
|
|
906
|
+
"x402 is not offered by this endpoint"
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
return "x402";
|
|
910
|
+
}
|
|
911
|
+
if (h === "mpp") {
|
|
912
|
+
if (!mpp) {
|
|
913
|
+
throw new KeeperHubError(
|
|
914
|
+
"MPP_NOT_OFFERED",
|
|
915
|
+
"mpp is not offered by this endpoint"
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
return "mpp";
|
|
919
|
+
}
|
|
920
|
+
if (x402) return "x402";
|
|
921
|
+
if (mpp) return "mpp";
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
821
924
|
function createPaymentSigner(opts = {}) {
|
|
822
925
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
823
926
|
const walletLoader = opts.walletLoader ?? readWalletConfig;
|
|
@@ -960,12 +1063,13 @@ function createPaymentSigner(opts = {}) {
|
|
|
960
1063
|
return response;
|
|
961
1064
|
}
|
|
962
1065
|
const wallet = await walletLoader();
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
}
|
|
966
|
-
if (x402) {
|
|
1066
|
+
const protocol = selectProtocol(x402, mpp, options?.paymentHint);
|
|
1067
|
+
if (protocol === "x402") {
|
|
967
1068
|
return payViaX402(response, x402, wallet, options);
|
|
968
1069
|
}
|
|
1070
|
+
if (protocol === "mpp") {
|
|
1071
|
+
return payViaMpp(response, mpp, wallet, options);
|
|
1072
|
+
}
|
|
969
1073
|
return response;
|
|
970
1074
|
}
|
|
971
1075
|
return {
|
|
@@ -978,7 +1082,8 @@ function createPaymentSigner(opts = {}) {
|
|
|
978
1082
|
return pay(first, {
|
|
979
1083
|
body: init?.body ?? void 0,
|
|
980
1084
|
headers: init?.headers,
|
|
981
|
-
method: init?.method
|
|
1085
|
+
method: init?.method,
|
|
1086
|
+
paymentHint: init?.paymentHint
|
|
982
1087
|
});
|
|
983
1088
|
}
|
|
984
1089
|
};
|
|
@@ -1008,8 +1113,10 @@ export {
|
|
|
1008
1113
|
paymentSigner,
|
|
1009
1114
|
readWalletConfig,
|
|
1010
1115
|
registerClaudeCodeHook,
|
|
1116
|
+
resolveHookCommand,
|
|
1011
1117
|
runCli,
|
|
1012
1118
|
runHookCli,
|
|
1119
|
+
selectProtocol,
|
|
1013
1120
|
tempo,
|
|
1014
1121
|
validateAndMerge,
|
|
1015
1122
|
writeWalletConfig
|