@crossmint/client-sdk-smart-wallet 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +289 -0
- package/dist/index.d.ts +289 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
- package/src/ABI/ERC1155.json +325 -0
- package/src/ABI/ERC20.json +222 -0
- package/src/ABI/ERC721.json +320 -0
- package/src/SmartWalletSDK.test.ts +32 -0
- package/src/SmartWalletSDK.ts +75 -0
- package/src/api/APIErrorService.ts +72 -0
- package/src/api/BaseCrossmintService.ts +82 -0
- package/src/api/CrossmintWalletService.test.ts +42 -0
- package/src/api/CrossmintWalletService.ts +50 -0
- package/src/blockchain/BlockchainNetworks.ts +121 -0
- package/src/blockchain/token/index.ts +1 -0
- package/src/blockchain/transfer.ts +54 -0
- package/src/blockchain/wallets/EVMSmartWallet.ts +109 -0
- package/src/blockchain/wallets/clientDecorator.ts +127 -0
- package/src/blockchain/wallets/eoa.ts +49 -0
- package/src/blockchain/wallets/index.ts +1 -0
- package/src/blockchain/wallets/passkey.ts +117 -0
- package/src/blockchain/wallets/paymaster.ts +49 -0
- package/src/blockchain/wallets/service.ts +193 -0
- package/src/error/index.ts +148 -0
- package/src/error/processor.ts +36 -0
- package/src/index.ts +34 -0
- package/src/services/logging/BrowserLoggerInterface.ts +5 -0
- package/src/services/logging/ConsoleProvider.ts +24 -0
- package/src/services/logging/DatadogProvider.ts +39 -0
- package/src/services/logging/index.ts +16 -0
- package/src/types/API.ts +40 -0
- package/src/types/Config.ts +35 -0
- package/src/types/Tokens.ts +27 -0
- package/src/types/internal.ts +50 -0
- package/src/utils/blockchain.ts +15 -0
- package/src/utils/constants.ts +31 -0
- package/src/utils/environment.ts +3 -0
- package/src/utils/helpers.ts +15 -0
- package/src/utils/log.test.ts +76 -0
- package/src/utils/log.ts +157 -0
- package/src/utils/signer.ts +36 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const ZERO_DEV_TYPE = "ZeroDev";
|
|
2
|
+
export const CURRENT_VERSION = 0;
|
|
3
|
+
export const DATADOG_CLIENT_TOKEN = "pub035be8a594b35be1887b6ba76c4029ca";
|
|
4
|
+
export const CROSSMINT_DEV_URL = "http://localhost:3000/api";
|
|
5
|
+
export const CROSSMINT_STG_URL = "https://staging.crossmint.com/api";
|
|
6
|
+
export const CROSSMINT_PROD_URL = "https://www.crossmint.com/api";
|
|
7
|
+
export const ZD_ETHEREUM_PROJECT_ID = "9ee29857-8077-404b-9a9a-31eeea996a4a";
|
|
8
|
+
export const ZD_POLYGON_PROJECT_ID = "023d4a21-d801-4450-b629-24439ab1369d";
|
|
9
|
+
export const ZD_BSC_PROJECT_ID = "3d166617-da86-494b-9348-e8a13343bc04";
|
|
10
|
+
export const ZD_OPTIMISM_PROJECT_ID = "e9314f9e-a13d-414f-b965-c591a0248243";
|
|
11
|
+
export const ZD_ARBITRUM_PROJECT_ID = "1641cd99-c1ef-404a-9d26-a9dc67b1ba51";
|
|
12
|
+
export const ZD_GOERLI_PROJECT_ID = "3cfecfb6-9d7d-4ef6-acaa-ac8f79f6cd5a";
|
|
13
|
+
export const ZD_SEPOLIA_PROJECT_ID = "7ff22858-06f0-4f3a-8b46-5b41d8c75d0e";
|
|
14
|
+
export const ZD_AMOY_PROJECT_ID = "3b24773b-d91e-4c01-8ce5-04807463bbca";
|
|
15
|
+
export const ZD_ZKATANA_PROJECT_ID = "d54706a0-304b-419e-8a33-03c26ba3f0e9";
|
|
16
|
+
export const ZD_ZKYOTO_PROJECT_ID = "ce986d52-4f27-4a2b-b429-eb2322f15f32";
|
|
17
|
+
export const ZD_ASTAR_PROJECT_ID = "6204f336-643f-41af-94e1-f8d146c91675";
|
|
18
|
+
export const ZD_BASE_SEPOLIA_PROJECT_ID = "3eb830c5-f91b-48e0-bb7d-dc30103a60b2";
|
|
19
|
+
export const ZD_OPTIMISM_SEPOLIA_PROJECT_ID = "6188b92b-993e-4f39-be22-56e4806416a8";
|
|
20
|
+
export const ZD_ARBITRUM_SEPOLIA_PROJECT_ID = "8b8b6d1a-184c-4198-8f29-c07e63aad595";
|
|
21
|
+
export const ZD_BASE_PROJECT_ID = "5535aa3b-4f9c-45af-9c38-0072369564a3";
|
|
22
|
+
export const ZD_ARBITRUM_NOVA_PROJECT_ID = "b6a6db7c-65de-4f74-9d8f-f70d9c083531";
|
|
23
|
+
export const RELAY_API_KEY = "c8999b7e-8bb7-416d-a854-dac8632f9ee6_crossmint";
|
|
24
|
+
export const SCW_SERVICE = "SCW_SDK";
|
|
25
|
+
export const SDK_VERSION = "0.1.0";
|
|
26
|
+
export const API_VERSION = "2024-06-09";
|
|
27
|
+
export const BUNDLER_RPC = "https://rpc.zerodev.app/api/v2/bundler/";
|
|
28
|
+
export const PAYMASTER_RPC = "https://rpc.zerodev.app/api/v2/paymaster/";
|
|
29
|
+
export const PM_BASE_SEPOLIA_RPC =
|
|
30
|
+
"https://api.developer.coinbase.com/rpc/v1/base-sepolia/6BTAmOQZ0x1YWtI24hIKKqdHWmVP1UXf";
|
|
31
|
+
export const PM_BASE_RPC = "https://api.developer.coinbase.com/rpc/v1/base/6BTAmOQZ0x1YWtI24hIKKqdHWmVP1UXf";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function isLocalhost() {
|
|
2
|
+
if (process.env.NODE_ENV === "test") {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
return window.location.origin.includes("localhost");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isEmpty(str: string | undefined | null): str is undefined | null {
|
|
10
|
+
return !str || str.length === 0 || str.trim().length === 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function equalsIgnoreCase(a?: string, b?: string): boolean {
|
|
14
|
+
return a?.toLowerCase() === b?.toLowerCase();
|
|
15
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { logInfo } from "../services/logging";
|
|
2
|
+
import { LoggerWrapper } from "./log";
|
|
3
|
+
import * as logModule from "./log";
|
|
4
|
+
|
|
5
|
+
jest.mock("../services/logging");
|
|
6
|
+
|
|
7
|
+
describe("Log test", () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
jest.clearAllMocks();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe("LoggerWrapper", () => {
|
|
13
|
+
const addOne = (input: number): number => input + 1;
|
|
14
|
+
|
|
15
|
+
class BaseClass extends LoggerWrapper {
|
|
16
|
+
constructor() {
|
|
17
|
+
super("BaseClass");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
someMethod(input: number): any {
|
|
21
|
+
return addOne(input);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
it("should return the same value and log the input and output of myMethod", () => {
|
|
26
|
+
const base = new BaseClass();
|
|
27
|
+
const input = 2;
|
|
28
|
+
const result = base.someMethod(input);
|
|
29
|
+
|
|
30
|
+
expect(result).toEqual(addOne(input));
|
|
31
|
+
expect(logInfo).toHaveBeenCalledTimes(2);
|
|
32
|
+
|
|
33
|
+
const logInfoCalls = (logInfo as jest.MockedFunction<typeof logInfo>).mock.calls;
|
|
34
|
+
expect(logInfoCalls[0][0].includes("input")).toBeTruthy();
|
|
35
|
+
expect(logInfoCalls[0][0].includes(String(input))).toBeTruthy();
|
|
36
|
+
expect(logInfoCalls[1][0].includes("output")).toBeTruthy();
|
|
37
|
+
expect(logInfoCalls[1][0].includes(String(result))).toBeTruthy();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("logInputOutput", () => {
|
|
42
|
+
const addTwo = (input: number): number => input + 2;
|
|
43
|
+
|
|
44
|
+
it("should log the input and output of the function", () => {
|
|
45
|
+
const addTwoWithLogging = logModule.logInputOutput((arg: number) => addTwo(arg), "addTwo");
|
|
46
|
+
|
|
47
|
+
const input = 2;
|
|
48
|
+
const result = addTwoWithLogging(input);
|
|
49
|
+
|
|
50
|
+
expect(result).toEqual(addTwo(input));
|
|
51
|
+
expect(logInfo).toHaveBeenCalledTimes(2);
|
|
52
|
+
|
|
53
|
+
const logInfoCalls = (logInfo as jest.MockedFunction<typeof logInfo>).mock.calls;
|
|
54
|
+
expect(logInfoCalls[0][0].includes("input")).toBeTruthy();
|
|
55
|
+
expect(logInfoCalls[0][0].includes(String(input))).toBeTruthy();
|
|
56
|
+
expect(logInfoCalls[1][0].includes("output")).toBeTruthy();
|
|
57
|
+
expect(logInfoCalls[1][0].includes(String(result))).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("logPerformance", () => {
|
|
62
|
+
const addThree = async (input: number): Promise<number> => input + 3;
|
|
63
|
+
|
|
64
|
+
it("should log the time", async () => {
|
|
65
|
+
const input = 3;
|
|
66
|
+
|
|
67
|
+
const result = await logModule.logPerformance("addThreeString", () => addThree(input));
|
|
68
|
+
|
|
69
|
+
expect(result).toEqual(await addThree(input));
|
|
70
|
+
expect(logInfo).toHaveBeenCalledTimes(1);
|
|
71
|
+
|
|
72
|
+
const logInfoCalls = (logInfo as jest.MockedFunction<typeof logInfo>).mock.calls;
|
|
73
|
+
expect(logInfoCalls[0][0].includes("TIME")).toBeTruthy();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
package/src/utils/log.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
|
|
3
|
+
import { logError, logInfo } from "../services/logging";
|
|
4
|
+
import { SCW_SERVICE } from "./constants";
|
|
5
|
+
import { isLocalhost } from "./helpers";
|
|
6
|
+
|
|
7
|
+
export class LoggerWrapper {
|
|
8
|
+
constructor(className: string, private extraInfo = {}, private logIdempotencyKey = uuidv4()) {
|
|
9
|
+
return new Proxy(this, {
|
|
10
|
+
get: (target: any, propKey: PropertyKey, receiver: any) => {
|
|
11
|
+
const origMethod = target[propKey];
|
|
12
|
+
const identifierTag = `[${SCW_SERVICE} - ${className} - ${String(propKey)}]`;
|
|
13
|
+
|
|
14
|
+
if (typeof origMethod === "function") {
|
|
15
|
+
return (...args: any[]) => {
|
|
16
|
+
this.logInput(args, identifierTag);
|
|
17
|
+
|
|
18
|
+
const result = origMethod.apply(target, args);
|
|
19
|
+
if (result instanceof Promise) {
|
|
20
|
+
return result
|
|
21
|
+
.then((res: any) => {
|
|
22
|
+
this.logOutput(res, identifierTag);
|
|
23
|
+
return res;
|
|
24
|
+
})
|
|
25
|
+
.catch((err: any) => {
|
|
26
|
+
this.logError(err, identifierTag);
|
|
27
|
+
throw err;
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
this.logOutput(result, identifierTag);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return Reflect.get(target, propKey, receiver);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private logInput(args: object, identifierTag: string) {
|
|
41
|
+
logInfoIfNotInLocalhost(
|
|
42
|
+
`${identifierTag} input - ${beautify(args)} - extra_info - ${beautify(
|
|
43
|
+
this.extraInfo
|
|
44
|
+
)} - log_idempotency_key - ${this.logIdempotencyKey}`,
|
|
45
|
+
{ args, ...this.extraInfo, logIdempotencyKey: this.logIdempotencyKey }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private logOutput(res: object, identifierTag: string) {
|
|
50
|
+
logInfoIfNotInLocalhost(
|
|
51
|
+
`${identifierTag} output - ${beautify(res)} - extra_info - ${beautify(
|
|
52
|
+
this.extraInfo
|
|
53
|
+
)} - log_idempotency_key - ${this.logIdempotencyKey}`,
|
|
54
|
+
{
|
|
55
|
+
res,
|
|
56
|
+
...this.extraInfo,
|
|
57
|
+
logIdempotencyKey: this.logIdempotencyKey,
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private logError(err: object, identifierTag: string) {
|
|
63
|
+
logError(
|
|
64
|
+
`${identifierTag} threw_error - ${err} - extra_info - ${beautify(this.extraInfo)} - log_idempotency_key - ${
|
|
65
|
+
this.logIdempotencyKey
|
|
66
|
+
}`,
|
|
67
|
+
{ err, ...this.extraInfo }
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected logPerformance<T>(name: string, cb: () => Promise<T>) {
|
|
72
|
+
return logPerformance(name, cb, this.extraInfo);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function logPerformance<T>(name: string, cb: () => Promise<T>, extraInfo?: object) {
|
|
77
|
+
const start = new Date().getTime();
|
|
78
|
+
const result = await cb();
|
|
79
|
+
const durationInMs = new Date().getTime() - start;
|
|
80
|
+
const args = { durationInMs, ...extraInfo };
|
|
81
|
+
logInfoIfNotInLocalhost(`[${SCW_SERVICE} - ${name} - TIME] - ${beautify(args)}`, { args });
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function logInputOutput<T, A extends any[]>(fn: (...args: A) => T, functionName: string): (...args: A) => T {
|
|
86
|
+
return function (this: any, ...args: A): T {
|
|
87
|
+
const identifierTag = `[${SCW_SERVICE} - function: ${functionName}]`;
|
|
88
|
+
logInfoIfNotInLocalhost(`${identifierTag} input: ${beautify(args)}`, { args });
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const result = fn.apply(this, args);
|
|
92
|
+
if (result instanceof Promise) {
|
|
93
|
+
return result
|
|
94
|
+
.then((res) => {
|
|
95
|
+
logInfoIfNotInLocalhost(`${identifierTag} output: ${beautify(res)}`, { res });
|
|
96
|
+
return res;
|
|
97
|
+
})
|
|
98
|
+
.catch((err) => {
|
|
99
|
+
logError(`${identifierTag} threw_error: ${beautify(err)}`, { err });
|
|
100
|
+
throw err;
|
|
101
|
+
}) as T;
|
|
102
|
+
} else {
|
|
103
|
+
logInfoIfNotInLocalhost(`${identifierTag} output: ${beautify(result)}`, { res: result });
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
logError(`${identifierTag} threw_error: ${beautify(err)}`, { err });
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function beautify(json: any) {
|
|
114
|
+
try {
|
|
115
|
+
return json != null ? JSON.stringify(json, null, 2) : json;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
return stringifyAvoidingCircular(json);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function stringifyAvoidingCircular(json: any) {
|
|
122
|
+
// stringify an object, avoiding circular structures
|
|
123
|
+
// https://stackoverflow.com/a/31557814
|
|
124
|
+
const simpleObject: { [key: string]: any } = {};
|
|
125
|
+
for (const prop in json) {
|
|
126
|
+
if (!Object.prototype.hasOwnProperty.call(json, prop)) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (typeof json[prop] == "object") {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (typeof json[prop] == "function") {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
simpleObject[prop] = json[prop];
|
|
136
|
+
}
|
|
137
|
+
return JSON.stringify(simpleObject, null, 2); // returns cleaned up JSON
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function logInfoIfNotInLocalhost(message: string, context?: object) {
|
|
141
|
+
if (isLocalhost()) {
|
|
142
|
+
console.log(message);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
logInfo(message, context);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function errorToJSON(error: Error | unknown) {
|
|
149
|
+
const errorToLog = error instanceof Error ? error : { message: "Unknown error", name: "Unknown error" };
|
|
150
|
+
|
|
151
|
+
if (!(errorToLog instanceof Error) && (errorToLog as any).constructor?.name !== "SyntheticBaseEvent") {
|
|
152
|
+
logError("ERROR_TO_JSON_FAILED", { error: errorToLog });
|
|
153
|
+
throw new Error("[errorToJSON] err is not instanceof Error nor SyntheticBaseEvent");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return JSON.parse(JSON.stringify(errorToLog, Object.getOwnPropertyNames(errorToLog)));
|
|
157
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { providerToSmartAccountSigner } from "permissionless";
|
|
2
|
+
import type { SmartAccountSigner } from "permissionless/accounts";
|
|
3
|
+
import { Address, EIP1193Provider } from "viem";
|
|
4
|
+
|
|
5
|
+
import { EVMBlockchainIncludingTestnet } from "@crossmint/common-sdk-base";
|
|
6
|
+
|
|
7
|
+
import { SmartWalletSDKError } from "../error";
|
|
8
|
+
import { ViemAccount, WalletParams } from "../types/Config";
|
|
9
|
+
import { logInputOutput } from "./log";
|
|
10
|
+
|
|
11
|
+
type CreateOwnerSignerInput = {
|
|
12
|
+
chain: EVMBlockchainIncludingTestnet;
|
|
13
|
+
walletParams: WalletParams;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const createOwnerSigner = logInputOutput(
|
|
17
|
+
async ({ walletParams }: CreateOwnerSignerInput): Promise<SmartAccountSigner<"custom", Address>> => {
|
|
18
|
+
if (isEIP1193Provider(walletParams.signer)) {
|
|
19
|
+
return await providerToSmartAccountSigner(walletParams.signer);
|
|
20
|
+
} else if (isAccount(walletParams.signer)) {
|
|
21
|
+
return walletParams.signer.account;
|
|
22
|
+
} else {
|
|
23
|
+
const signer = walletParams.signer as any;
|
|
24
|
+
throw new SmartWalletSDKError(`The signer type ${signer.type} is not supported`);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"createOwnerSigner"
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
function isEIP1193Provider(signer: any): signer is EIP1193Provider {
|
|
31
|
+
return signer && typeof signer.request === "function";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isAccount(signer: any): signer is ViemAccount {
|
|
35
|
+
return signer && signer.type === "VIEM_ACCOUNT";
|
|
36
|
+
}
|