@zoralabs/coins-sdk 0.5.2 → 0.7.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/CHANGELOG.md +21 -0
- package/dist/actions/createCoin.d.ts +92 -7
- package/dist/actions/createCoin.d.ts.map +1 -1
- package/dist/actions/tradeCoin.d.ts +27 -2
- package/dist/actions/tradeCoin.d.ts.map +1 -1
- package/dist/actions/updateCoinURI.d.ts +35 -1
- package/dist/actions/updateCoinURI.d.ts.map +1 -1
- package/dist/actions/updatePayoutRecipient.d.ts +41 -1
- package/dist/actions/updatePayoutRecipient.d.ts.map +1 -1
- package/dist/api/api-raw.d.ts +1 -0
- package/dist/api/api-raw.d.ts.map +1 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/queries.d.ts +9 -1
- package/dist/api/queries.d.ts.map +1 -1
- package/dist/api/social.d.ts +8 -0
- package/dist/api/social.d.ts.map +1 -0
- package/dist/client/sdk.gen.d.ts +144 -1
- package/dist/client/sdk.gen.d.ts.map +1 -1
- package/dist/client/types.gen.d.ts +411 -1
- package/dist/client/types.gen.d.ts.map +1 -1
- package/dist/index.cjs +724 -250
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +701 -227
- package/dist/index.js.map +1 -1
- package/dist/metadata/validateMetadataURIContent.d.ts.map +1 -1
- package/dist/utils/calls.d.ts +61 -0
- package/dist/utils/calls.d.ts.map +1 -0
- package/dist/utils/userOperation.d.ts +44 -0
- package/dist/utils/userOperation.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/actions/createCoin.ts +314 -60
- package/src/actions/tradeCoin.ts +237 -72
- package/src/actions/updateCoinURI.ts +84 -6
- package/src/actions/updatePayoutRecipient.ts +92 -5
- package/src/api/api-raw.test.ts +61 -0
- package/src/api/api-raw.ts +9 -0
- package/src/api/index.ts +4 -0
- package/src/api/queries.ts +36 -0
- package/src/api/social.ts +23 -0
- package/src/client/sdk.gen.ts +48 -0
- package/src/client/types.gen.ts +421 -1
- package/src/index.ts +45 -3
- package/src/metadata/validateMetadataURIContent.ts +17 -2
- package/src/utils/calls.ts +129 -0
- package/src/utils/userOperation.test.ts +84 -0
- package/src/utils/userOperation.ts +124 -0
package/src/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export {
|
|
2
2
|
createCoin,
|
|
3
|
+
createCoinSmartWallet,
|
|
3
4
|
createCoinCall,
|
|
5
|
+
validateCreateCoinCalls,
|
|
6
|
+
validateCreateCoinSmartWalletCalls,
|
|
4
7
|
getCoinCreateFromLogs,
|
|
5
8
|
CreateConstants,
|
|
6
9
|
} from "./actions/createCoin";
|
|
@@ -12,18 +15,53 @@ export type {
|
|
|
12
15
|
ContentCoinCurrency,
|
|
13
16
|
} from "./actions/createCoin";
|
|
14
17
|
|
|
15
|
-
export {
|
|
18
|
+
export {
|
|
19
|
+
updateCoinURI,
|
|
20
|
+
updateCoinURISmartWallet,
|
|
21
|
+
updateCoinURICall,
|
|
22
|
+
validateUpdateCoinURI,
|
|
23
|
+
} from "./actions/updateCoinURI";
|
|
16
24
|
export type { UpdateCoinURIArgs } from "./actions/updateCoinURI";
|
|
17
25
|
|
|
18
26
|
export {
|
|
19
27
|
updatePayoutRecipient,
|
|
28
|
+
updatePayoutRecipientSmartWallet,
|
|
20
29
|
updatePayoutRecipientCall,
|
|
30
|
+
validateUpdatePayoutRecipient,
|
|
21
31
|
} from "./actions/updatePayoutRecipient";
|
|
22
32
|
export type { UpdatePayoutRecipientArgs } from "./actions/updatePayoutRecipient";
|
|
23
33
|
|
|
24
|
-
export {
|
|
34
|
+
export {
|
|
35
|
+
tradeCoin,
|
|
36
|
+
tradeCoinSmartWallet,
|
|
37
|
+
createTradeCall,
|
|
38
|
+
createQuote,
|
|
39
|
+
validateTradeParameters,
|
|
40
|
+
} from "./actions/tradeCoin";
|
|
25
41
|
export type { TradeParameters } from "./actions/tradeCoin";
|
|
26
42
|
|
|
43
|
+
// Normalized call types + user-operation adapter
|
|
44
|
+
export {
|
|
45
|
+
toGenericCall,
|
|
46
|
+
toUserOperationCalls,
|
|
47
|
+
isContractCall,
|
|
48
|
+
isSendCall,
|
|
49
|
+
} from "./utils/calls";
|
|
50
|
+
export type {
|
|
51
|
+
GenericCall,
|
|
52
|
+
UserOperationCall,
|
|
53
|
+
ContractCall,
|
|
54
|
+
SendCall,
|
|
55
|
+
} from "./utils/calls";
|
|
56
|
+
|
|
57
|
+
// User Operation Utils
|
|
58
|
+
export {
|
|
59
|
+
prepareUserOperation,
|
|
60
|
+
submitUserOperation,
|
|
61
|
+
CoinbaseGasError,
|
|
62
|
+
} from "./utils/userOperation";
|
|
63
|
+
export type { PreparedUserOperation } from "./utils/userOperation";
|
|
64
|
+
|
|
27
65
|
// API Read Actions
|
|
28
66
|
export * from "./api/queries";
|
|
29
67
|
export type * from "./api/queries";
|
|
@@ -32,11 +70,15 @@ export type * from "./api/queries";
|
|
|
32
70
|
export * from "./api/explore";
|
|
33
71
|
export type * from "./api/explore";
|
|
34
72
|
|
|
73
|
+
// API Social Actions
|
|
74
|
+
export * from "./api/social";
|
|
75
|
+
export type * from "./api/social";
|
|
76
|
+
|
|
35
77
|
// API Key Setter
|
|
36
78
|
export { setApiKey } from "./api/api-key";
|
|
37
79
|
|
|
38
80
|
// Raw API helpers
|
|
39
|
-
export { apiGet, apiPost, setApiBaseUrl } from "./api/api-raw";
|
|
81
|
+
export { apiGet, apiPost, apiUrl, setApiBaseUrl } from "./api/api-raw";
|
|
40
82
|
|
|
41
83
|
// Metadata Validation Utils
|
|
42
84
|
export * from "./metadata";
|
|
@@ -10,11 +10,25 @@ import { validateMetadataJSON } from "./validateMetadataJSON";
|
|
|
10
10
|
export async function validateMetadataURIContent(
|
|
11
11
|
metadataURI: ValidMetadataURI,
|
|
12
12
|
) {
|
|
13
|
+
let response: Response;
|
|
13
14
|
const cleanedURI = cleanAndValidateMetadataURI(metadataURI);
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
response = await fetch(cleanedURI);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
// handle actual fetch failures (i.e. network errors)
|
|
20
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Metadata fetch failed for URL '${cleanedURI}': ${errorMessage}`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
if (!response.ok) {
|
|
16
|
-
throw new Error(
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Metadata fetch failed for URL '${cleanedURI}': ${response.statusText ? `${response.statusText} (HTTP ${response.status})` : `HTTP ${response.status}`}`,
|
|
29
|
+
);
|
|
17
30
|
}
|
|
31
|
+
|
|
18
32
|
if (
|
|
19
33
|
!["application/json", "text/plain"].includes(
|
|
20
34
|
response.headers.get("content-type") ?? "",
|
|
@@ -22,6 +36,7 @@ export async function validateMetadataURIContent(
|
|
|
22
36
|
) {
|
|
23
37
|
throw new Error("Metadata is not a valid JSON or plain text response type");
|
|
24
38
|
}
|
|
39
|
+
|
|
25
40
|
const metadataJson = await response.json();
|
|
26
41
|
return validateMetadataJSON(metadataJson);
|
|
27
42
|
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Abi,
|
|
3
|
+
type Address,
|
|
4
|
+
concatHex,
|
|
5
|
+
type ContractFunctionArgs,
|
|
6
|
+
type ContractFunctionName,
|
|
7
|
+
type ContractFunctionParameters,
|
|
8
|
+
encodeFunctionData,
|
|
9
|
+
type Hex,
|
|
10
|
+
} from "viem";
|
|
11
|
+
|
|
12
|
+
const EMPTY_HEX: Hex = "0x";
|
|
13
|
+
|
|
14
|
+
type WritableMutability = "payable" | "nonpayable";
|
|
15
|
+
|
|
16
|
+
type WritableFunction<abi extends Abi | readonly unknown[]> =
|
|
17
|
+
ContractFunctionName<abi, WritableMutability>;
|
|
18
|
+
|
|
19
|
+
type WritableArgs<
|
|
20
|
+
abi extends Abi | readonly unknown[],
|
|
21
|
+
functionName extends WritableFunction<abi>,
|
|
22
|
+
> = ContractFunctionArgs<abi, WritableMutability, functionName>;
|
|
23
|
+
|
|
24
|
+
export type ContractCall<
|
|
25
|
+
abi extends Abi | readonly unknown[] = readonly unknown[],
|
|
26
|
+
fn extends WritableFunction<abi> = WritableFunction<abi>,
|
|
27
|
+
args extends WritableArgs<abi, fn> = WritableArgs<abi, fn>,
|
|
28
|
+
> = ContractFunctionParameters<abi, WritableMutability, fn, args> & {
|
|
29
|
+
/** Optional ETH value to send with the call. */
|
|
30
|
+
value?: bigint;
|
|
31
|
+
/**
|
|
32
|
+
* Optional calldata appended after the encoded function data, e.g. for
|
|
33
|
+
* attribution. Mirrors viem's `dataSuffix`; concatenated by {@link toGenericCall}.
|
|
34
|
+
*/
|
|
35
|
+
dataSuffix?: Hex;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const isContractCall = (
|
|
39
|
+
call: ContractCall | SendCall,
|
|
40
|
+
): call is ContractCall => {
|
|
41
|
+
return (
|
|
42
|
+
(call as ContractCall).address !== undefined &&
|
|
43
|
+
(call as ContractCall).abi !== undefined &&
|
|
44
|
+
(call as ContractCall).functionName !== undefined
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type SendCall = {
|
|
49
|
+
to: Address;
|
|
50
|
+
value?: bigint;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const isSendCall = (call: ContractCall | SendCall): call is SendCall => {
|
|
54
|
+
return !isContractCall(call) && (call as SendCall).to !== undefined;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A normalized, fully-encoded contract call.
|
|
59
|
+
*
|
|
60
|
+
* This is the canonical call shape emitted by the action `createAndValidate*Calls`
|
|
61
|
+
* builders. It intentionally matches the encoded-call form accepted by both
|
|
62
|
+
* `walletClient.sendTransaction` (EOA execution) and viem's bundler client
|
|
63
|
+
* `prepareUserOperation` / `sendUserOperation` (smart wallet / user operation
|
|
64
|
+
* execution), so a single call list can drive either flow.
|
|
65
|
+
*/
|
|
66
|
+
export type GenericCall = {
|
|
67
|
+
to: Address;
|
|
68
|
+
data: Hex;
|
|
69
|
+
value: bigint;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The encoded-call shape accepted by viem's bundler client `calls` parameter
|
|
74
|
+
* (`prepareUserOperation` / `sendUserOperation`).
|
|
75
|
+
*
|
|
76
|
+
* `data` and `value` are optional on viem's side; we always populate them from a
|
|
77
|
+
* {@link GenericCall}.
|
|
78
|
+
*/
|
|
79
|
+
export type UserOperationCall = {
|
|
80
|
+
to: Address;
|
|
81
|
+
data?: Hex;
|
|
82
|
+
value?: bigint;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Converts a contract call or send call to a generic call.
|
|
87
|
+
*/
|
|
88
|
+
export function toGenericCall(call: ContractCall | SendCall): GenericCall {
|
|
89
|
+
// convert a simple send call to a user operation call
|
|
90
|
+
if (isSendCall(call)) {
|
|
91
|
+
return {
|
|
92
|
+
to: call.to,
|
|
93
|
+
value: call.value ?? 0n,
|
|
94
|
+
data: EMPTY_HEX,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// convert a contract call to a user operation call
|
|
99
|
+
// if the call has a data suffix, we need to manually concatenate it with the call data
|
|
100
|
+
// it is used to add the attribution to the call data
|
|
101
|
+
const { dataSuffix } = call;
|
|
102
|
+
const callData = encodeFunctionData(call);
|
|
103
|
+
const data = dataSuffix ? concatHex([callData, dataSuffix]) : callData;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
to: call.address,
|
|
107
|
+
value: call.value ?? 0n,
|
|
108
|
+
data,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Adapts a list of {@link GenericCall} into the call shape expected by viem's
|
|
114
|
+
* bundler client.
|
|
115
|
+
*
|
|
116
|
+
* A {@link GenericCall} is already fully encoded calldata, so this is a thin
|
|
117
|
+
* adapter rather than an encoder. It exists as an explicit seam: it owns any
|
|
118
|
+
* future divergence between our `GenericCall` shape and viem's user-operation
|
|
119
|
+
* call type, and keeps the conversion point obvious at call sites.
|
|
120
|
+
*/
|
|
121
|
+
export function toUserOperationCalls(
|
|
122
|
+
calls: GenericCall[],
|
|
123
|
+
): UserOperationCall[] {
|
|
124
|
+
return calls.map((call) => ({
|
|
125
|
+
to: call.to,
|
|
126
|
+
data: call.data,
|
|
127
|
+
value: call.value,
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { CoinbaseGasError } from "./userOperation";
|
|
3
|
+
|
|
4
|
+
// The constructor only reads `details`, `cause`, and `message`, but the param
|
|
5
|
+
// is typed with the full viem bundler-error shape, so fill the rest with stubs.
|
|
6
|
+
const makeBundlerError = (
|
|
7
|
+
overrides: Partial<{ details: string; cause: unknown; message: string }> = {},
|
|
8
|
+
) => ({
|
|
9
|
+
stack: "stack",
|
|
10
|
+
message: overrides.message ?? "bundler error message",
|
|
11
|
+
cause: "cause" in overrides ? overrides.cause : { code: -32000 },
|
|
12
|
+
details: overrides.details ?? "",
|
|
13
|
+
docsPath: "",
|
|
14
|
+
shortMessage: "",
|
|
15
|
+
version: "viem@2.x",
|
|
16
|
+
name: "UserOperationExecutionError",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const precheck = (balance: string, required: string) =>
|
|
20
|
+
`precheck failed: sender balance and deposit together is ${balance} but must be at least ${required} to pay for this operation`;
|
|
21
|
+
|
|
22
|
+
describe("CoinbaseGasError", () => {
|
|
23
|
+
it("formats both balance and required when present", () => {
|
|
24
|
+
// 1001597376034823 wei = 0.001001597376034823 ETH
|
|
25
|
+
// 10704882000000000 wei = 0.010704882 ETH
|
|
26
|
+
const error = new CoinbaseGasError(
|
|
27
|
+
makeBundlerError({
|
|
28
|
+
details: precheck("1001597376034823", "10704882000000000"),
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(error.message).toBe(
|
|
33
|
+
"Insufficient balance. You need at least 0.010704882 ETH to pay for this operation, but you only have 0.001001597376034823 ETH.",
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("falls back to required-only when balance is missing", () => {
|
|
38
|
+
const error = new CoinbaseGasError(
|
|
39
|
+
makeBundlerError({ details: precheck("", "10704882000000000") }),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
expect(error.message).toBe(
|
|
43
|
+
"Insufficient balance. Make sure you have at least 0.010704882 ETH in your wallet.",
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("falls back to generic message when only balance is present", () => {
|
|
48
|
+
const error = new CoinbaseGasError(
|
|
49
|
+
makeBundlerError({ details: precheck("1001597376034823", "") }),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(error.message).toBe(
|
|
53
|
+
"Insufficient balance. Make sure you have enough ETH to pay for this operation.",
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("falls back to generic message when both amounts are missing", () => {
|
|
58
|
+
const error = new CoinbaseGasError(
|
|
59
|
+
makeBundlerError({ details: precheck("", "") }),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
expect(error.message).toBe(
|
|
63
|
+
"Insufficient balance. Make sure you have enough ETH to pay for this operation.",
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("uses the raw details when the precheck pattern does not match", () => {
|
|
68
|
+
const details = "AA13 initCode failed or OOG";
|
|
69
|
+
const error = new CoinbaseGasError(makeBundlerError({ details }));
|
|
70
|
+
|
|
71
|
+
expect(error.message).toBe(details);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("propagates cause and details onto the instance", () => {
|
|
75
|
+
const cause = { code: -32000 };
|
|
76
|
+
const details = precheck("1001597376034823", "10704882000000000");
|
|
77
|
+
const error = new CoinbaseGasError(makeBundlerError({ details, cause }));
|
|
78
|
+
|
|
79
|
+
expect(error).toBeInstanceOf(Error);
|
|
80
|
+
expect(error).toBeInstanceOf(CoinbaseGasError);
|
|
81
|
+
expect(error.cause).toBe(cause);
|
|
82
|
+
expect(error.details).toBe(details);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { formatEther, Hex } from "viem";
|
|
2
|
+
import type {
|
|
3
|
+
BundlerClient,
|
|
4
|
+
SmartAccount,
|
|
5
|
+
UserOperation,
|
|
6
|
+
UserOperationReceipt,
|
|
7
|
+
} from "viem/account-abstraction";
|
|
8
|
+
import { UserOperationCall } from "./calls";
|
|
9
|
+
|
|
10
|
+
// Coinbase Smart Wallet uses ERC-4337 entry point 0.6.
|
|
11
|
+
export type PreparedUserOperation = UserOperation<"0.6">;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Prepares a user operation from a list of contract calls.
|
|
15
|
+
* Returns a fully-populated UserOperation (gas estimated, nonce filled) with a
|
|
16
|
+
* stub signature from gas estimation. Must be re-signed before submitting.
|
|
17
|
+
*/
|
|
18
|
+
export const prepareUserOperation = async ({
|
|
19
|
+
bundlerClient,
|
|
20
|
+
account,
|
|
21
|
+
calls,
|
|
22
|
+
}: {
|
|
23
|
+
bundlerClient: BundlerClient;
|
|
24
|
+
account: SmartAccount;
|
|
25
|
+
calls: readonly UserOperationCall[];
|
|
26
|
+
}): Promise<PreparedUserOperation> => {
|
|
27
|
+
const prepared = await bundlerClient.prepareUserOperation({
|
|
28
|
+
account,
|
|
29
|
+
calls,
|
|
30
|
+
});
|
|
31
|
+
return prepared as PreparedUserOperation;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Signs and submits a prepared user operation, then waits for the receipt.
|
|
36
|
+
*
|
|
37
|
+
* The prepared op carries a stub signature from gas estimation, so we re-sign
|
|
38
|
+
* here before sending. Otherwise viem's sendUserOperation would forward the
|
|
39
|
+
* stub and the bundler would reject it as invalid.
|
|
40
|
+
*/
|
|
41
|
+
export const submitUserOperation = async ({
|
|
42
|
+
bundlerClient,
|
|
43
|
+
account,
|
|
44
|
+
userOperation,
|
|
45
|
+
}: {
|
|
46
|
+
bundlerClient: BundlerClient;
|
|
47
|
+
account: SmartAccount;
|
|
48
|
+
userOperation: PreparedUserOperation;
|
|
49
|
+
}): Promise<UserOperationReceipt> => {
|
|
50
|
+
let hash: Hex;
|
|
51
|
+
const signature = await account.signUserOperation(userOperation);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
hash = await bundlerClient.sendUserOperation({
|
|
55
|
+
account,
|
|
56
|
+
...userOperation,
|
|
57
|
+
signature,
|
|
58
|
+
});
|
|
59
|
+
} catch (error) {
|
|
60
|
+
// handle gas errors to provide better user feedback
|
|
61
|
+
if (isGasError(error)) {
|
|
62
|
+
throw new CoinbaseGasError(error);
|
|
63
|
+
}
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return bundlerClient.waitForUserOperationReceipt({ hash });
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
type CoinbaseBundlerError = {
|
|
71
|
+
stack: string;
|
|
72
|
+
message: string;
|
|
73
|
+
cause: unknown;
|
|
74
|
+
details: string;
|
|
75
|
+
docsPath: string;
|
|
76
|
+
shortMessage: string;
|
|
77
|
+
version: string;
|
|
78
|
+
name: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export class CoinbaseGasError extends Error {
|
|
82
|
+
cause: unknown;
|
|
83
|
+
details: string;
|
|
84
|
+
required?: bigint;
|
|
85
|
+
available?: bigint;
|
|
86
|
+
constructor(error: CoinbaseBundlerError) {
|
|
87
|
+
let message: string;
|
|
88
|
+
let available: bigint | undefined;
|
|
89
|
+
let required: bigint | undefined;
|
|
90
|
+
|
|
91
|
+
const match = error.details.match(
|
|
92
|
+
/precheck failed: sender balance and deposit together is (\d+)? but must be at least (\d+)? to pay for this operation/,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (match) {
|
|
96
|
+
available = match[1] ? BigInt(match[1]) : undefined;
|
|
97
|
+
required = match[2] ? BigInt(match[2]) : undefined;
|
|
98
|
+
|
|
99
|
+
if (available !== undefined && required !== undefined) {
|
|
100
|
+
message = `Insufficient balance. You need at least ${formatEther(required)} ETH to pay for this operation, but you only have ${formatEther(available)} ETH.`;
|
|
101
|
+
} else if (required !== undefined) {
|
|
102
|
+
message = `Insufficient balance. Make sure you have at least ${formatEther(required)} ETH in your wallet.`;
|
|
103
|
+
} else {
|
|
104
|
+
message = `Insufficient balance. Make sure you have enough ETH to pay for this operation.`;
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
message = error.details ?? error.message;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
super(message);
|
|
111
|
+
this.cause = error.cause;
|
|
112
|
+
this.details = error.details;
|
|
113
|
+
this.available = available;
|
|
114
|
+
this.required = required;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isGasError(error: unknown): error is CoinbaseBundlerError {
|
|
119
|
+
return (
|
|
120
|
+
(error as CoinbaseBundlerError).details?.startsWith(
|
|
121
|
+
"precheck failed: sender balance and deposit together is",
|
|
122
|
+
) ?? false
|
|
123
|
+
);
|
|
124
|
+
}
|