@nevermined-io/core-kit 0.4.5 → 0.5.1
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/dist/Instantiable.abstract.js +15 -13
- package/dist/Instantiable.abstract.js.map +1 -0
- package/dist/artifacts/generated.d.ts +174 -40
- package/dist/artifacts/generated.d.ts.map +1 -1
- package/dist/artifacts/generated.js +22805 -7111
- package/dist/artifacts/generated.js.map +1 -0
- package/dist/contracts/AccessManager.js +12 -3
- package/dist/contracts/AccessManager.js.map +1 -0
- package/dist/contracts/AssetRegistry.d.ts +1 -0
- package/dist/contracts/AssetRegistry.d.ts.map +1 -1
- package/dist/contracts/AssetRegistry.js +134 -69
- package/dist/contracts/AssetRegistry.js.map +1 -0
- package/dist/contracts/ContractBase.d.ts.map +1 -1
- package/dist/contracts/ContractBase.js +173 -140
- package/dist/contracts/ContractBase.js.map +1 -0
- package/dist/contracts/ContractsApi.js +6 -54
- package/dist/contracts/ContractsApi.js.map +1 -0
- package/dist/contracts/CryptoTemplateBase.js +23 -11
- package/dist/contracts/CryptoTemplateBase.js.map +1 -0
- package/dist/contracts/FiatPaymentTemplate.js +9 -1
- package/dist/contracts/FiatPaymentTemplate.js.map +1 -0
- package/dist/contracts/FiatSettlementCondition.js +8 -1
- package/dist/contracts/FiatSettlementCondition.js.map +1 -0
- package/dist/contracts/FixedPaymentTemplate.js +42 -10
- package/dist/contracts/FixedPaymentTemplate.js.map +1 -0
- package/dist/contracts/NFT1155Base.js +48 -12
- package/dist/contracts/NFT1155Base.js.map +1 -0
- package/dist/contracts/NFT1155Credits.js +2 -0
- package/dist/contracts/NFT1155Credits.js.map +1 -0
- package/dist/contracts/NFT1155ExpirableCredits.js +26 -6
- package/dist/contracts/NFT1155ExpirableCredits.js.map +1 -0
- package/dist/contracts/NVMConfig.js +2 -0
- package/dist/contracts/NVMConfig.js.map +1 -0
- package/dist/contracts/PayAsYouGoTemplate.js +36 -10
- package/dist/contracts/PayAsYouGoTemplate.js.map +1 -0
- package/dist/contracts/ProtocolStandardFees.js +7 -1
- package/dist/contracts/ProtocolStandardFees.js.map +1 -0
- package/dist/contracts/Roles.js +2 -0
- package/dist/contracts/Roles.js.map +1 -0
- package/dist/contracts/index.js +2 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/errors/NeverminedErrors.js +27 -26
- package/dist/errors/NeverminedErrors.js.map +1 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/models/AgentX402AccessToken.js +78 -46
- package/dist/models/AgentX402AccessToken.js.map +1 -0
- package/dist/models/Logger.js +12 -12
- package/dist/models/Logger.js.map +1 -0
- package/dist/models/NeverminedOptions.d.ts +3 -3
- package/dist/models/NeverminedOptions.d.ts.map +1 -1
- package/dist/models/NeverminedOptions.js +3 -28
- package/dist/models/NeverminedOptions.js.map +1 -0
- package/dist/models/NvmApiKey.js +96 -147
- package/dist/models/NvmApiKey.js.map +1 -0
- package/dist/models/Transactions.js +3 -1
- package/dist/models/Transactions.js.map +1 -0
- package/dist/models/index.js +2 -0
- package/dist/models/index.js.map +1 -0
- package/dist/nevermined/Nevermined.js +20 -42
- package/dist/nevermined/Nevermined.js.map +1 -0
- package/dist/nevermined/api/PaymentsApi.js +38 -43
- package/dist/nevermined/api/PaymentsApi.js.map +1 -0
- package/dist/nevermined/api/ServicesApi.js +7 -15
- package/dist/nevermined/api/ServicesApi.js.map +1 -0
- package/dist/nevermined/api/UtilsApi.js +7 -19
- package/dist/nevermined/api/UtilsApi.js.map +1 -0
- package/dist/nevermined/index.js +2 -0
- package/dist/nevermined/index.js.map +1 -0
- package/dist/nevermined/utils/AgentUtils.js +25 -23
- package/dist/nevermined/utils/AgentUtils.js.map +1 -0
- package/dist/nevermined/utils/AnvilHelpers.js +34 -19
- package/dist/nevermined/utils/AnvilHelpers.js.map +1 -0
- package/dist/nevermined/utils/BlockchainViemUtils.d.ts +4 -4
- package/dist/nevermined/utils/BlockchainViemUtils.js +102 -108
- package/dist/nevermined/utils/BlockchainViemUtils.js.map +1 -0
- package/dist/nevermined/utils/JwtUtils.js +33 -28
- package/dist/nevermined/utils/JwtUtils.js.map +1 -0
- package/dist/nevermined/utils/SignatureUtils.js +23 -27
- package/dist/nevermined/utils/SignatureUtils.js.map +1 -0
- package/dist/nevermined/utils/WebServiceConnector.js +18 -20
- package/dist/nevermined/utils/WebServiceConnector.js.map +1 -0
- package/dist/nevermined/utils/ZeroDevPolicies.js +115 -75
- package/dist/nevermined/utils/ZeroDevPolicies.js.map +1 -0
- package/dist/nevermined/utils/index.js +2 -0
- package/dist/nevermined/utils/index.js.map +1 -0
- package/dist/services/Api.js +11 -5
- package/dist/services/Api.js.map +1 -0
- package/dist/services/Profiles.js +15 -20
- package/dist/services/Profiles.js.map +1 -0
- package/dist/services/index.js +2 -0
- package/dist/services/index.js.map +1 -0
- package/dist/utils/ConversionTypeHelpers.js +13 -6
- package/dist/utils/ConversionTypeHelpers.js.map +1 -0
- package/dist/utils/DeploymentInfo.js +12 -7
- package/dist/utils/DeploymentInfo.js.map +1 -0
- package/dist/utils/Network.js +27 -19
- package/dist/utils/Network.js.map +1 -0
- package/dist/utils/helpers.js +42 -31
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +17 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createKernelAccountClient, createZeroDevPaymasterClient } from '@zerodev/sdk';
|
|
2
|
-
import { estimateUserOperationFees, getBundlerRpcUrl, getPaymasterRpcUrl, isRelayerMode
|
|
3
|
-
import { BaseError, ContractFunctionRevertedError, encodeFunctionData, getContract, http, parseAbi, parseEventLogs
|
|
2
|
+
import { estimateUserOperationFees, getBundlerRpcUrl, getPaymasterRpcUrl, isRelayerMode } from '../nevermined/utils/BlockchainViemUtils.js';
|
|
3
|
+
import { BaseError, ContractFunctionRevertedError, encodeFunctionData, getContract, http, parseAbi, parseEventLogs } from 'viem';
|
|
4
4
|
import { ContractsError, ContractSimulationError } from '../errors/index.js';
|
|
5
5
|
import { Instantiable } from '../Instantiable.abstract.js';
|
|
6
6
|
import { getInputsOfFunctionFormatted, getTransactionReceipt } from '../nevermined/index.js';
|
|
7
|
+
import { getRandomBigInt } from '../utils/helpers.js';
|
|
7
8
|
import { getChain } from '../utils/Network.js';
|
|
8
9
|
/**
|
|
9
10
|
* When a testnet RPC (Base Sepolia) fails internally, some providers return
|
|
@@ -17,23 +18,26 @@ import { getChain } from '../utils/Network.js';
|
|
|
17
18
|
* chance to intercept a bogus estimate. We run the estimation ourselves
|
|
18
19
|
* here, sanity-check the result, and always pass an explicit gas value
|
|
19
20
|
* into `writeContract` so it doesn't re-query the misbehaving RPC.
|
|
20
|
-
*/
|
|
21
|
-
const
|
|
22
|
-
|
|
21
|
+
*/ const GAS_SANITY_THRESHOLD = 25_000_000n;
|
|
22
|
+
const GAS_FALLBACK_CAP = 5_000_000n;
|
|
23
|
+
// EntryPoint v0.7: ZeroDev's `toKernelPluginManager` rejects `customNonceKey > maxUint16`.
|
|
24
|
+
// 16 bits = 65 536 channels — collision probability for the SDK's expected concurrency
|
|
25
|
+
// (k=2–5 parallel sends from one SCA) is negligible (< 0.02 %). High-fan-out callers
|
|
26
|
+
// (k > 50) should consider a per-process monotonic counter instead. See issue #1579.
|
|
27
|
+
const ZERODEV_NONCE_KEY_BITS = 16;
|
|
28
|
+
// Client-side cap on `waitForUserOperationReceipt`. The userOp is already in the bundler
|
|
29
|
+
// mempool when this fires — a client-side timeout does NOT cancel it. Callers that catch
|
|
30
|
+
// the resulting `ContractsError` MUST poll `bundlerClient.getUserOperationReceipt(hash)`
|
|
31
|
+
// before retrying; otherwise a retry can produce a duplicate on-chain op.
|
|
32
|
+
const USEROP_RECEIPT_TIMEOUT_MS = 90_000;
|
|
23
33
|
function capForBogusEstimate(estimatedGas, logger) {
|
|
24
|
-
if (estimatedGas < GAS_SANITY_THRESHOLD)
|
|
25
|
-
|
|
26
|
-
logger.warn(`Estimated gas (${estimatedGas}) exceeds sanity threshold ` +
|
|
27
|
-
`(${GAS_SANITY_THRESHOLD}); overriding with ${GAS_FALLBACK_CAP} to bypass misbehaving RPC estimate.`);
|
|
34
|
+
if (estimatedGas < GAS_SANITY_THRESHOLD) return estimatedGas;
|
|
35
|
+
logger.warn(`Estimated gas (${estimatedGas}) exceeds sanity threshold ` + `(${GAS_SANITY_THRESHOLD}); overriding with ${GAS_FALLBACK_CAP} to bypass misbehaving RPC estimate.`);
|
|
28
36
|
return GAS_FALLBACK_CAP;
|
|
29
37
|
}
|
|
30
38
|
export class ContractBase extends Instantiable {
|
|
31
|
-
contractName
|
|
32
|
-
|
|
33
|
-
address;
|
|
34
|
-
_zeroDevPaymaster = null;
|
|
35
|
-
constructor(contractName, address) {
|
|
36
|
-
super();
|
|
39
|
+
constructor(contractName, address){
|
|
40
|
+
super(), this._zeroDevPaymaster = null;
|
|
37
41
|
this.contractName = contractName;
|
|
38
42
|
this.address = address;
|
|
39
43
|
}
|
|
@@ -43,21 +47,27 @@ export class ContractBase extends Instantiable {
|
|
|
43
47
|
this.contract = getContract({
|
|
44
48
|
address: contractConfig.address,
|
|
45
49
|
abi: contractConfig.abi,
|
|
46
|
-
client: {
|
|
50
|
+
client: {
|
|
51
|
+
public: this.publicClient,
|
|
52
|
+
wallet: this.walletClient
|
|
53
|
+
}
|
|
47
54
|
});
|
|
48
55
|
}
|
|
49
56
|
async call(functionName, args, from) {
|
|
50
57
|
try {
|
|
51
|
-
return
|
|
58
|
+
return await this.client.public.readContract({
|
|
52
59
|
address: this.address,
|
|
53
60
|
abi: this.contract.abi,
|
|
54
|
-
functionName: parseAbi([
|
|
61
|
+
functionName: parseAbi([
|
|
62
|
+
functionName
|
|
63
|
+
]),
|
|
55
64
|
args,
|
|
56
|
-
...
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
...from && {
|
|
66
|
+
account: from
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
//return await this.contract[functionSignature](...args, { from })
|
|
70
|
+
} catch (err) {
|
|
61
71
|
throw new ContractsError(`Calling method "${functionName}" on contract "${this.contractName}" failed. Args: ${args} - ${err}`);
|
|
62
72
|
}
|
|
63
73
|
}
|
|
@@ -66,16 +76,14 @@ export class ContractBase extends Instantiable {
|
|
|
66
76
|
if ('transactionHash' in txHash) {
|
|
67
77
|
// txHash is TransactionReceipt
|
|
68
78
|
return txHash;
|
|
69
|
-
}
|
|
70
|
-
else if ('userOpHash' in txHash) {
|
|
79
|
+
} else if ('userOpHash' in txHash) {
|
|
71
80
|
// txHash is UserOperationReceipt, fetch the actual receipt using the utility function with retry logic
|
|
72
81
|
const hash = txHash.receipt.transactionHash;
|
|
73
82
|
return await getTransactionReceipt({
|
|
74
83
|
txHash: hash,
|
|
75
|
-
publicClient: this.client.public
|
|
84
|
+
publicClient: this.client.public
|
|
76
85
|
});
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
86
|
+
} else {
|
|
79
87
|
throw new Error('Unknown transaction type');
|
|
80
88
|
}
|
|
81
89
|
}
|
|
@@ -84,23 +92,20 @@ export class ContractBase extends Instantiable {
|
|
|
84
92
|
abi: this.contract.abi,
|
|
85
93
|
logs: txReceipt.logs,
|
|
86
94
|
eventName,
|
|
87
|
-
strict: false
|
|
95
|
+
strict: false
|
|
88
96
|
});
|
|
89
97
|
}
|
|
90
98
|
async multicall(calls, from, params = {}) {
|
|
91
99
|
if (from.type === 'local') {
|
|
92
100
|
this.logger.debug(`Blockchain Multicall using Local account`);
|
|
93
101
|
return await this.localAccountMulticall(calls, from, params, params.progress);
|
|
94
|
-
}
|
|
95
|
-
else if (from.type === 'json-rpc') {
|
|
102
|
+
} else if (from.type === 'json-rpc') {
|
|
96
103
|
this.logger.debug(`Blockchain Multicall using JSON-RPC account`);
|
|
97
104
|
return await this.localAccountMulticall(calls, from, params, params.progress);
|
|
98
|
-
}
|
|
99
|
-
else if (from.type === 'smart') {
|
|
105
|
+
} else if (from.type === 'smart') {
|
|
100
106
|
this.logger.debug(`Blockchain Multicall using ZeroDev account`);
|
|
101
107
|
return await this.internalMulticallSmartAccount(calls, from, params, params.progress);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
108
|
+
} else {
|
|
104
109
|
throw new ContractsError(`Account not supported`);
|
|
105
110
|
}
|
|
106
111
|
}
|
|
@@ -108,16 +113,13 @@ export class ContractBase extends Instantiable {
|
|
|
108
113
|
if (from.type === 'local') {
|
|
109
114
|
this.logger.debug(`Simulating calls using Local account`);
|
|
110
115
|
return await this.localAccountSimulateMulticall(calls, from, params);
|
|
111
|
-
}
|
|
112
|
-
else if (from.type === 'json-rpc') {
|
|
116
|
+
} else if (from.type === 'json-rpc') {
|
|
113
117
|
this.logger.debug(`Simulating calls using JSON-RPC account`);
|
|
114
118
|
return await this.localAccountSimulateMulticall(calls, from, params);
|
|
115
|
-
}
|
|
116
|
-
else if (from.type === 'smart') {
|
|
119
|
+
} else if (from.type === 'smart') {
|
|
117
120
|
this.logger.debug(`Simulating calls using Smart Account account`);
|
|
118
121
|
return await this.internalSimulateMulticallSmartAccount(calls, from, params);
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
122
|
+
} else {
|
|
121
123
|
throw new ContractsError(`Account not supported`);
|
|
122
124
|
}
|
|
123
125
|
}
|
|
@@ -125,16 +127,13 @@ export class ContractBase extends Instantiable {
|
|
|
125
127
|
if (from.type === 'local') {
|
|
126
128
|
this.logger.debug(`Simulating calls using Local account`);
|
|
127
129
|
return await this.localAccountSimulate(functionName, from, args, params);
|
|
128
|
-
}
|
|
129
|
-
else if (from.type === 'json-rpc') {
|
|
130
|
+
} else if (from.type === 'json-rpc') {
|
|
130
131
|
this.logger.debug(`Simulating calls using JSON-RPC account`);
|
|
131
132
|
return await this.localAccountSimulate(functionName, from, args, params);
|
|
132
|
-
}
|
|
133
|
-
else if (from.type === 'smart') {
|
|
133
|
+
} else if (from.type === 'smart') {
|
|
134
134
|
this.logger.debug(`Simulating calls using Smart Account account`);
|
|
135
135
|
return await this.internalSimulateSmartAccount(functionName, from, args, params);
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
136
|
+
} else {
|
|
138
137
|
throw new ContractsError(`Account not supported`);
|
|
139
138
|
}
|
|
140
139
|
}
|
|
@@ -142,16 +141,13 @@ export class ContractBase extends Instantiable {
|
|
|
142
141
|
if (from.type === 'local') {
|
|
143
142
|
this.logger.debug(`Blockchain Send using Local account with functionName: ${functionName} and following args: ${args}`);
|
|
144
143
|
return await this.localAccountSend(functionName, from, args, params, params.progress);
|
|
145
|
-
}
|
|
146
|
-
else if (from.type === 'json-rpc') {
|
|
144
|
+
} else if (from.type === 'json-rpc') {
|
|
147
145
|
this.logger.debug(`Blockchain Send using JSON-RPC account to ${functionName}`);
|
|
148
146
|
return await this.localAccountSend(functionName, from, args, params, params.progress);
|
|
149
|
-
}
|
|
150
|
-
else if (from.type === 'smart') {
|
|
147
|
+
} else if (from.type === 'smart') {
|
|
151
148
|
this.logger.debug(`Blockchain Send using ZeroDev account`);
|
|
152
149
|
return await this.internalSendSmartAccount(functionName, from, args, params, params.progress);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
150
|
+
} else {
|
|
155
151
|
throw new ContractsError(`Account not supported`);
|
|
156
152
|
}
|
|
157
153
|
}
|
|
@@ -162,7 +158,7 @@ export class ContractBase extends Instantiable {
|
|
|
162
158
|
args: [],
|
|
163
159
|
method: '',
|
|
164
160
|
from: from,
|
|
165
|
-
contractAddress: this.address
|
|
161
|
+
contractAddress: this.address
|
|
166
162
|
});
|
|
167
163
|
}
|
|
168
164
|
const kernelClient = createKernelAccountClient({
|
|
@@ -172,26 +168,34 @@ export class ContractBase extends Instantiable {
|
|
|
172
168
|
client: this.client.public,
|
|
173
169
|
...this.getPaymasterConfig(),
|
|
174
170
|
userOperation: {
|
|
175
|
-
estimateFeesPerGas: async ({ bundlerClient })
|
|
171
|
+
estimateFeesPerGas: async ({ bundlerClient })=>{
|
|
176
172
|
return estimateUserOperationFees(bundlerClient, this.client.public);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
179
175
|
});
|
|
180
176
|
const encodedCalls = [];
|
|
181
|
-
calls.map((call)
|
|
177
|
+
calls.map((call)=>{
|
|
182
178
|
encodedCalls.push({
|
|
183
179
|
to: call.contractAddress || this.address,
|
|
184
180
|
value: txparams.value || 0n,
|
|
185
181
|
data: encodeFunctionData({
|
|
186
182
|
abi: call.abi,
|
|
187
183
|
functionName: call.functionName,
|
|
188
|
-
args: call.args
|
|
189
|
-
})
|
|
184
|
+
args: call.args
|
|
185
|
+
})
|
|
190
186
|
});
|
|
191
187
|
});
|
|
188
|
+
let userOpHash;
|
|
192
189
|
try {
|
|
193
|
-
|
|
190
|
+
// Random nonce key per send so concurrent userOps from the same SCA
|
|
191
|
+
// hit distinct EntryPoint nonce channels instead of colliding on the
|
|
192
|
+
// default `customNonceKey=0n`. See issue #1579.
|
|
193
|
+
const nonce = await from.getNonce({
|
|
194
|
+
key: getRandomBigInt(ZERODEV_NONCE_KEY_BITS)
|
|
195
|
+
});
|
|
196
|
+
userOpHash = await kernelClient.sendUserOperation({
|
|
194
197
|
callData: await kernelClient.account.encodeCalls(encodedCalls),
|
|
198
|
+
nonce
|
|
195
199
|
});
|
|
196
200
|
if (progress) {
|
|
197
201
|
progress({
|
|
@@ -199,31 +203,33 @@ export class ContractBase extends Instantiable {
|
|
|
199
203
|
args: [],
|
|
200
204
|
method: '',
|
|
201
205
|
from: from,
|
|
202
|
-
contractAddress: this.address
|
|
206
|
+
contractAddress: this.address
|
|
203
207
|
});
|
|
204
208
|
}
|
|
205
209
|
// Wait for the transaction to be mined
|
|
206
210
|
this.logger.debug(`Waiting for transaction to be mined... ${userOpHash}`);
|
|
207
|
-
const txReceipt = await kernelClient.waitForUserOperationReceipt({
|
|
211
|
+
const txReceipt = await kernelClient.waitForUserOperationReceipt({
|
|
212
|
+
hash: userOpHash,
|
|
213
|
+
timeout: USEROP_RECEIPT_TIMEOUT_MS
|
|
214
|
+
});
|
|
208
215
|
this.logger.debug(`Transaction mined - UserOperationHash: ${userOpHash}`);
|
|
209
216
|
this.logger.debug(`Transaction receipt - TransactionHash: ${txReceipt.receipt.transactionHash}`);
|
|
210
217
|
this.assertUserOpSucceeded(txReceipt, 'multicall', userOpHash);
|
|
211
218
|
return txReceipt.receipt;
|
|
212
|
-
}
|
|
213
|
-
catch (err) {
|
|
219
|
+
} catch (err) {
|
|
214
220
|
if (err instanceof BaseError) {
|
|
215
|
-
const revertError = err.walk((err)
|
|
221
|
+
const revertError = err.walk((err)=>err instanceof ContractFunctionRevertedError);
|
|
216
222
|
this.logger.error(`revertError: ${String(revertError)}`);
|
|
217
223
|
}
|
|
218
|
-
|
|
224
|
+
// Surface userOpHash so callers can poll for eventual inclusion before
|
|
225
|
+
// retrying — a client-side receipt-wait timeout does NOT cancel the userOp.
|
|
226
|
+
const hashSuffix = userOpHash ? ` userOpHash=${userOpHash}` : '';
|
|
227
|
+
throw new ContractsError(`Multicall failed:${hashSuffix} ${err}`);
|
|
219
228
|
}
|
|
220
229
|
}
|
|
221
230
|
assertUserOpSucceeded(txReceipt, method, userOpHash) {
|
|
222
|
-
if (txReceipt.success)
|
|
223
|
-
|
|
224
|
-
throw new ContractsError(`UserOp inner call reverted for "${method}" on "${this.contractName}". ` +
|
|
225
|
-
`userOpHash=${userOpHash} txHash=${txReceipt.receipt.transactionHash} ` +
|
|
226
|
-
`reason=${txReceipt.reason ?? 'unknown'}`);
|
|
231
|
+
if (txReceipt.success) return;
|
|
232
|
+
throw new ContractsError(`UserOp inner call reverted for "${method}" on "${this.contractName}". ` + `userOpHash=${userOpHash} txHash=${txReceipt.receipt.transactionHash} ` + `reason=${txReceipt.reason ?? 'unknown'}`);
|
|
227
233
|
}
|
|
228
234
|
async internalSendSmartAccount(name, from, args, txparams, progress) {
|
|
229
235
|
const functionInputs = getInputsOfFunctionFormatted(this.contract.abi, name, args);
|
|
@@ -237,7 +243,7 @@ export class ContractBase extends Instantiable {
|
|
|
237
243
|
value,
|
|
238
244
|
contractName: this.contractName,
|
|
239
245
|
contractAddress: this.address,
|
|
240
|
-
gasLimit
|
|
246
|
+
gasLimit
|
|
241
247
|
});
|
|
242
248
|
}
|
|
243
249
|
try {
|
|
@@ -247,12 +253,13 @@ export class ContractBase extends Instantiable {
|
|
|
247
253
|
functionName: name,
|
|
248
254
|
args,
|
|
249
255
|
account: from,
|
|
250
|
-
...
|
|
256
|
+
...txparams.value && {
|
|
257
|
+
value: txparams.value
|
|
258
|
+
}
|
|
251
259
|
});
|
|
252
|
-
}
|
|
253
|
-
catch (err) {
|
|
260
|
+
} catch (err) {
|
|
254
261
|
if (err instanceof BaseError) {
|
|
255
|
-
const revertError = err.walk((err)
|
|
262
|
+
const revertError = err.walk((err)=>err instanceof ContractFunctionRevertedError);
|
|
256
263
|
this.logger.error(`revertError: ${String(revertError)}`);
|
|
257
264
|
}
|
|
258
265
|
throw new ContractsError(`Calling method "${name}" on contract "${this.contractName}" failed. Args: ${args} - ${err}`);
|
|
@@ -264,27 +271,37 @@ export class ContractBase extends Instantiable {
|
|
|
264
271
|
client: this.client.public,
|
|
265
272
|
...this.getPaymasterConfig(),
|
|
266
273
|
userOperation: {
|
|
267
|
-
estimateFeesPerGas: async ({ bundlerClient })
|
|
274
|
+
estimateFeesPerGas: async ({ bundlerClient })=>{
|
|
268
275
|
return estimateUserOperationFees(bundlerClient, this.client.public);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
const data = encodeFunctionData({
|
|
280
|
+
abi: this.contract.abi,
|
|
281
|
+
functionName: name,
|
|
282
|
+
args
|
|
271
283
|
});
|
|
272
|
-
const data = encodeFunctionData({ abi: this.contract.abi, functionName: name, args });
|
|
273
284
|
let userOpHash;
|
|
274
285
|
try {
|
|
286
|
+
// Random nonce key per send so concurrent userOps from the same SCA
|
|
287
|
+
// hit distinct EntryPoint nonce channels instead of colliding on the
|
|
288
|
+
// default `customNonceKey=0n`. See issue #1579.
|
|
289
|
+
const nonce = await from.getNonce({
|
|
290
|
+
key: getRandomBigInt(ZERODEV_NONCE_KEY_BITS)
|
|
291
|
+
});
|
|
275
292
|
userOpHash = await kernelClient.sendUserOperation({
|
|
276
293
|
callData: await kernelClient.account.encodeCalls([
|
|
277
294
|
{
|
|
278
295
|
to: this.address,
|
|
279
296
|
value: txparams.value || 0n,
|
|
280
|
-
data
|
|
281
|
-
}
|
|
297
|
+
data
|
|
298
|
+
}
|
|
282
299
|
]),
|
|
300
|
+
nonce
|
|
283
301
|
});
|
|
284
|
-
}
|
|
285
|
-
catch (err) {
|
|
302
|
+
} catch (err) {
|
|
286
303
|
if (err instanceof BaseError) {
|
|
287
|
-
const revertError = err.walk((err)
|
|
304
|
+
const revertError = err.walk((err)=>err instanceof ContractFunctionRevertedError);
|
|
288
305
|
this.logger.error(`revertError: ${String(revertError)}`);
|
|
289
306
|
}
|
|
290
307
|
throw new ContractsError(`Calling method "${name}" on contract "${this.contractName}" failed. Args: ${args} - ${err}`);
|
|
@@ -299,10 +316,22 @@ export class ContractBase extends Instantiable {
|
|
|
299
316
|
value,
|
|
300
317
|
contractName: this.contractName,
|
|
301
318
|
contractAddress: this.address,
|
|
302
|
-
gasLimit
|
|
319
|
+
gasLimit
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
let txReceipt;
|
|
323
|
+
try {
|
|
324
|
+
txReceipt = await kernelClient.waitForUserOperationReceipt({
|
|
325
|
+
hash: userOpHash,
|
|
326
|
+
timeout: USEROP_RECEIPT_TIMEOUT_MS
|
|
303
327
|
});
|
|
328
|
+
} catch (err) {
|
|
329
|
+
// The userOp is already in the bundler mempool — a client-side timeout
|
|
330
|
+
// does NOT cancel it. Surface userOpHash so callers can poll for eventual
|
|
331
|
+
// inclusion before retrying, otherwise a retry can produce a duplicate
|
|
332
|
+
// on-chain op. Wrap symmetric to `internalMulticallSmartAccount`.
|
|
333
|
+
throw new ContractsError(`Calling method "${name}" on contract "${this.contractName}" timed out ` + `waiting for UserOp receipt. userOpHash=${userOpHash} — confirm on-chain ` + `inclusion before retrying. Underlying: ${err}`);
|
|
304
334
|
}
|
|
305
|
-
const txReceipt = await kernelClient.waitForUserOperationReceipt({ hash: userOpHash });
|
|
306
335
|
this.assertUserOpSucceeded(txReceipt, name, userOpHash);
|
|
307
336
|
if (progress) {
|
|
308
337
|
progress({
|
|
@@ -314,7 +343,7 @@ export class ContractBase extends Instantiable {
|
|
|
314
343
|
value,
|
|
315
344
|
contractName: this.contractName,
|
|
316
345
|
contractAddress: this.address,
|
|
317
|
-
gasLimit
|
|
346
|
+
gasLimit
|
|
318
347
|
});
|
|
319
348
|
}
|
|
320
349
|
return txReceipt;
|
|
@@ -327,11 +356,12 @@ export class ContractBase extends Instantiable {
|
|
|
327
356
|
functionName: name,
|
|
328
357
|
args,
|
|
329
358
|
account: from.address,
|
|
330
|
-
...
|
|
359
|
+
...txparams.value && {
|
|
360
|
+
value: txparams.value
|
|
361
|
+
}
|
|
331
362
|
});
|
|
332
363
|
return true;
|
|
333
|
-
}
|
|
334
|
-
catch (err) {
|
|
364
|
+
} catch (err) {
|
|
335
365
|
throw new ContractSimulationError(`Error Simulating contract call "${name}" on contract "${this.contractName}" failed. Args: ${args} - ${err}`);
|
|
336
366
|
}
|
|
337
367
|
}
|
|
@@ -343,10 +373,11 @@ export class ContractBase extends Instantiable {
|
|
|
343
373
|
functionName: name,
|
|
344
374
|
args,
|
|
345
375
|
account: from,
|
|
346
|
-
...
|
|
376
|
+
...txparams.value && {
|
|
377
|
+
value: txparams.value
|
|
378
|
+
}
|
|
347
379
|
});
|
|
348
|
-
}
|
|
349
|
-
catch (err) {
|
|
380
|
+
} catch (err) {
|
|
350
381
|
throw new ContractSimulationError(`Error Simulating contract call "${name}" on contract "${this.contractName}" failed. Args: ${args} - ${err}`);
|
|
351
382
|
}
|
|
352
383
|
return true;
|
|
@@ -384,7 +415,7 @@ export class ContractBase extends Instantiable {
|
|
|
384
415
|
value,
|
|
385
416
|
contractName,
|
|
386
417
|
contractAddress,
|
|
387
|
-
gasLimit
|
|
418
|
+
gasLimit
|
|
388
419
|
});
|
|
389
420
|
}
|
|
390
421
|
let txHash;
|
|
@@ -395,7 +426,9 @@ export class ContractBase extends Instantiable {
|
|
|
395
426
|
functionName: name,
|
|
396
427
|
args,
|
|
397
428
|
account: from,
|
|
398
|
-
...
|
|
429
|
+
...txparams.value && {
|
|
430
|
+
value: txparams.value
|
|
431
|
+
}
|
|
399
432
|
});
|
|
400
433
|
// Resolve gas ourselves so `writeContract` never falls back to its own
|
|
401
434
|
// `eth_estimateGas`. An explicit caller `gasLimit` always wins; otherwise
|
|
@@ -410,25 +443,24 @@ export class ContractBase extends Instantiable {
|
|
|
410
443
|
functionName: name,
|
|
411
444
|
args,
|
|
412
445
|
account: from,
|
|
413
|
-
...
|
|
446
|
+
...txparams.value && {
|
|
447
|
+
value: txparams.value
|
|
448
|
+
}
|
|
414
449
|
});
|
|
415
450
|
resolvedGas = capForBogusEstimate(estimate, this.logger);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
this.logger.warn(`estimateContractGas for "${name}" on "${contractName}" failed (${estErr}); ` +
|
|
419
|
-
`falling back to ${GAS_FALLBACK_CAP} to avoid a bogus RPC re-estimate in writeContract.`);
|
|
451
|
+
} catch (estErr) {
|
|
452
|
+
this.logger.warn(`estimateContractGas for "${name}" on "${contractName}" failed (${estErr}); ` + `falling back to ${GAS_FALLBACK_CAP} to avoid a bogus RPC re-estimate in writeContract.`);
|
|
420
453
|
resolvedGas = GAS_FALLBACK_CAP;
|
|
421
454
|
}
|
|
422
455
|
}
|
|
423
456
|
txHash = await this.client.wallet.writeContract({
|
|
424
457
|
...request,
|
|
425
458
|
account: from,
|
|
426
|
-
gas: resolvedGas
|
|
459
|
+
gas: resolvedGas
|
|
427
460
|
});
|
|
428
|
-
}
|
|
429
|
-
catch (err) {
|
|
461
|
+
} catch (err) {
|
|
430
462
|
if (err instanceof BaseError) {
|
|
431
|
-
const revertError = err.walk((err)
|
|
463
|
+
const revertError = err.walk((err)=>err instanceof ContractFunctionRevertedError);
|
|
432
464
|
this.logger.error(`revertError: ${String(revertError)}`);
|
|
433
465
|
}
|
|
434
466
|
throw new ContractsError(`Calling method "${name}" on contract "${contractName}" failed. Args: ${args} - ${err}`);
|
|
@@ -443,10 +475,13 @@ export class ContractBase extends Instantiable {
|
|
|
443
475
|
value,
|
|
444
476
|
contractName,
|
|
445
477
|
contractAddress,
|
|
446
|
-
gasLimit
|
|
478
|
+
gasLimit
|
|
447
479
|
});
|
|
448
480
|
}
|
|
449
|
-
const txReceipt = getTransactionReceipt({
|
|
481
|
+
const txReceipt = getTransactionReceipt({
|
|
482
|
+
txHash,
|
|
483
|
+
publicClient: this.client.public
|
|
484
|
+
});
|
|
450
485
|
if (progress) {
|
|
451
486
|
progress({
|
|
452
487
|
stage: 'receipt',
|
|
@@ -457,33 +492,30 @@ export class ContractBase extends Instantiable {
|
|
|
457
492
|
value,
|
|
458
493
|
contractName,
|
|
459
494
|
contractAddress,
|
|
460
|
-
gasLimit
|
|
495
|
+
gasLimit
|
|
461
496
|
});
|
|
462
497
|
}
|
|
463
498
|
return txReceipt;
|
|
464
499
|
}
|
|
465
500
|
async localAccountMulticall(calls, from, txparams, progress) {
|
|
466
501
|
const receipts = [];
|
|
467
|
-
for (const call of calls)
|
|
502
|
+
for (const call of calls){
|
|
468
503
|
receipts.push(await this.localAccountSend(call.functionName, from, call.args, txparams, progress, call.abi, call.contractAddress, call.contractName));
|
|
469
504
|
}
|
|
470
|
-
if (receipts.length > 0)
|
|
471
|
-
|
|
472
|
-
else
|
|
473
|
-
throw new ContractsError(`No receipts found for multicall`);
|
|
505
|
+
if (receipts.length > 0) return receipts.pop();
|
|
506
|
+
else throw new ContractsError(`No receipts found for multicall`);
|
|
474
507
|
}
|
|
475
508
|
async localAccountSimulateMulticall(calls, from, txparams) {
|
|
476
509
|
try {
|
|
477
510
|
const { results } = await this.client.public.simulateCalls({
|
|
478
511
|
calls,
|
|
479
|
-
account: from
|
|
512
|
+
account: from
|
|
480
513
|
});
|
|
481
514
|
this.logger.debug(`Simulated multicall - ${results}`);
|
|
482
515
|
return true;
|
|
483
|
-
}
|
|
484
|
-
catch (err) {
|
|
516
|
+
} catch (err) {
|
|
485
517
|
if (err instanceof BaseError) {
|
|
486
|
-
const revertError = err.walk((err)
|
|
518
|
+
const revertError = err.walk((err)=>err instanceof ContractFunctionRevertedError);
|
|
487
519
|
this.logger.error(`revertError: ${String(revertError)}`);
|
|
488
520
|
}
|
|
489
521
|
throw new ContractsError(`Error simulating multicall - ${err}`);
|
|
@@ -498,21 +530,21 @@ export class ContractBase extends Instantiable {
|
|
|
498
530
|
client: this.client.public,
|
|
499
531
|
...this.getPaymasterConfig(),
|
|
500
532
|
userOperation: {
|
|
501
|
-
estimateFeesPerGas: async ({ bundlerClient })
|
|
533
|
+
estimateFeesPerGas: async ({ bundlerClient })=>{
|
|
502
534
|
return estimateUserOperationFees(bundlerClient, this.client.public);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
505
537
|
});
|
|
506
538
|
const encodedCalls = [];
|
|
507
|
-
for (const call of calls)
|
|
539
|
+
for (const call of calls){
|
|
508
540
|
encodedCalls.push({
|
|
509
541
|
to: call.to,
|
|
510
542
|
value: call.value || 0n,
|
|
511
543
|
data: encodeFunctionData({
|
|
512
544
|
abi: call.abi,
|
|
513
545
|
functionName: call.functionName,
|
|
514
|
-
args: call.args
|
|
515
|
-
})
|
|
546
|
+
args: call.args
|
|
547
|
+
})
|
|
516
548
|
});
|
|
517
549
|
}
|
|
518
550
|
// DONT DELETE THE COMMENTED LINES - ARE NECESSARY FOR F***ING ZERODEV DEBUGGING
|
|
@@ -529,14 +561,13 @@ export class ContractBase extends Instantiable {
|
|
|
529
561
|
// this.logger.info(`Call ${index}: to=${call.to}, value=${call.value}, data=${call.data.slice(0, 10)}...`)
|
|
530
562
|
// })
|
|
531
563
|
await kernelClient.prepareUserOperation({
|
|
532
|
-
callData: await kernelClient.account.encodeCalls(encodedCalls)
|
|
564
|
+
callData: await kernelClient.account.encodeCalls(encodedCalls)
|
|
533
565
|
});
|
|
534
566
|
return true;
|
|
535
|
-
}
|
|
536
|
-
catch (err) {
|
|
567
|
+
} catch (err) {
|
|
537
568
|
this.logger.error(`Error details: ${JSON.stringify(err, null, 2)}`);
|
|
538
569
|
if (err instanceof BaseError) {
|
|
539
|
-
const revertError = err.walk((e)
|
|
570
|
+
const revertError = err.walk((e)=>e instanceof ContractFunctionRevertedError);
|
|
540
571
|
if (revertError) {
|
|
541
572
|
this.logger.error(`Revert error: ${String(revertError)}`);
|
|
542
573
|
}
|
|
@@ -548,26 +579,26 @@ export class ContractBase extends Instantiable {
|
|
|
548
579
|
if (!this._zeroDevPaymaster) {
|
|
549
580
|
this._zeroDevPaymaster = createZeroDevPaymasterClient({
|
|
550
581
|
chain: getChain(this.config.chainId),
|
|
551
|
-
transport: http(this.getPaymasterRPC())
|
|
582
|
+
transport: http(this.getPaymasterRPC())
|
|
552
583
|
});
|
|
553
584
|
}
|
|
554
585
|
return this._zeroDevPaymaster;
|
|
555
586
|
}
|
|
556
587
|
/**
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
if (isRelayerMode())
|
|
565
|
-
return {};
|
|
588
|
+
* Builds the `paymaster` partial for `createKernelAccountClient`. In relayer
|
|
589
|
+
* mode (e.g. local `ultra-relay` for tests) returns an empty object so the
|
|
590
|
+
* kernel client submits UserOps without paymaster fields and the bundler
|
|
591
|
+
* pays gas via its executor key. In production returns the ZeroDev paymaster
|
|
592
|
+
* sponsorship callback.
|
|
593
|
+
*/ getPaymasterConfig() {
|
|
594
|
+
if (isRelayerMode()) return {};
|
|
566
595
|
const zerodevPaymaster = this.getOrCreatePaymaster();
|
|
567
596
|
return {
|
|
568
597
|
paymaster: {
|
|
569
|
-
getPaymasterData: (userOperation)
|
|
570
|
-
|
|
598
|
+
getPaymasterData: (userOperation)=>zerodevPaymaster.sponsorUserOperation({
|
|
599
|
+
userOperation
|
|
600
|
+
})
|
|
601
|
+
}
|
|
571
602
|
};
|
|
572
603
|
}
|
|
573
604
|
getBundlerRPC() {
|
|
@@ -592,3 +623,5 @@ export class ContractBase extends Instantiable {
|
|
|
592
623
|
return getPaymasterRpcUrl(projectId, this.config.chainId);
|
|
593
624
|
}
|
|
594
625
|
}
|
|
626
|
+
|
|
627
|
+
//# sourceMappingURL=ContractBase.js.map
|