@safe-global/relay-kit 3.0.1 → 3.1.0-alpha.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/src/packs/safe-4337/Safe4337Pack.d.ts +11 -5
- package/dist/src/packs/safe-4337/Safe4337Pack.js +184 -108
- package/dist/src/packs/safe-4337/Safe4337Pack.js.map +1 -1
- package/dist/src/packs/safe-4337/Safe4337Pack.test.js +218 -29
- package/dist/src/packs/safe-4337/Safe4337Pack.test.js.map +1 -1
- package/dist/src/packs/safe-4337/SafeOperation.d.ts +3 -1
- package/dist/src/packs/safe-4337/SafeOperation.js +2 -1
- package/dist/src/packs/safe-4337/SafeOperation.js.map +1 -1
- package/dist/src/packs/safe-4337/SafeOperation.test.js +5 -0
- package/dist/src/packs/safe-4337/SafeOperation.test.js.map +1 -1
- package/dist/src/packs/safe-4337/constants.d.ts +2 -0
- package/dist/src/packs/safe-4337/constants.js +5 -2
- package/dist/src/packs/safe-4337/constants.js.map +1 -1
- package/dist/src/packs/safe-4337/estimators/PimlicoFeeEstimator.js +2 -2
- package/dist/src/packs/safe-4337/estimators/PimlicoFeeEstimator.js.map +1 -1
- package/dist/src/packs/safe-4337/testing-utils/fixtures.d.ts +36 -0
- package/dist/src/packs/safe-4337/testing-utils/fixtures.js +41 -5
- package/dist/src/packs/safe-4337/testing-utils/fixtures.js.map +1 -1
- package/dist/src/packs/safe-4337/testing-utils/helpers.js +0 -1
- package/dist/src/packs/safe-4337/testing-utils/helpers.js.map +1 -1
- package/dist/src/packs/safe-4337/types.d.ts +2 -4
- package/dist/src/packs/safe-4337/utils/entrypoint.d.ts +4 -0
- package/dist/src/packs/safe-4337/utils/entrypoint.js +23 -0
- package/dist/src/packs/safe-4337/utils/entrypoint.js.map +1 -0
- package/dist/src/packs/safe-4337/utils.d.ts +61 -5
- package/dist/src/packs/safe-4337/utils.js +131 -9
- package/dist/src/packs/safe-4337/utils.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SigningMethod } from '@safe-global/protocol-kit';
|
|
2
2
|
import { RelayKitBasePack } from '../../RelayKitBasePack';
|
|
3
|
+
import { SafeOperationResponse } from '@safe-global/safe-core-sdk-types';
|
|
3
4
|
import EthSafeOperation from './SafeOperation';
|
|
4
5
|
import { EstimateFeeProps, Safe4337CreateTransactionProps, Safe4337ExecutableProps, Safe4337InitOptions, Safe4337Options, UserOperationReceipt, UserOperationWithPayload } from './types';
|
|
5
6
|
/**
|
|
@@ -25,7 +26,7 @@ export declare class Safe4337Pack extends RelayKitBasePack<{
|
|
|
25
26
|
*
|
|
26
27
|
* @param {Safe4337Options} options - The initialization parameters.
|
|
27
28
|
*/
|
|
28
|
-
constructor({ protocolKit, bundlerClient,
|
|
29
|
+
constructor({ protocolKit, bundlerClient, bundlerUrl, paymasterOptions, entryPointAddress, safe4337ModuleAddress }: Safe4337Options);
|
|
29
30
|
/**
|
|
30
31
|
* Initializes a Safe4337Pack class.
|
|
31
32
|
* This method creates the protocolKit instance based on the input parameters.
|
|
@@ -57,18 +58,23 @@ export declare class Safe4337Pack extends RelayKitBasePack<{
|
|
|
57
58
|
/**
|
|
58
59
|
* Signs a safe operation.
|
|
59
60
|
*
|
|
60
|
-
* @param {EthSafeOperation} safeOperation - The SafeOperation to sign.
|
|
61
|
+
* @param {EthSafeOperation | SafeOperationResponse} safeOperation - The SafeOperation to sign. It can be:
|
|
62
|
+
* - A response from the API (Tx Service)
|
|
63
|
+
* - An instance of EthSafeOperation
|
|
61
64
|
* @param {SigningMethod} signingMethod - The signing method to use.
|
|
62
65
|
* @return {Promise<EthSafeOperation>} The Promise object will resolve to the signed SafeOperation.
|
|
63
66
|
*/
|
|
64
|
-
signSafeOperation(safeOperation: EthSafeOperation, signingMethod?: SigningMethod): Promise<EthSafeOperation>;
|
|
67
|
+
signSafeOperation(safeOperation: EthSafeOperation | SafeOperationResponse, signingMethod?: SigningMethod): Promise<EthSafeOperation>;
|
|
65
68
|
/**
|
|
66
69
|
* Executes the relay transaction.
|
|
67
70
|
*
|
|
68
|
-
* @param {
|
|
71
|
+
* @param {Safe4337ExecutableProps} props - The parameters for the transaction execution.
|
|
72
|
+
* @param {EthSafeOperation | SafeOperationResponse} props.executable - The SafeOperation to execute. It can be:
|
|
73
|
+
* - A response from the API (Tx Service)
|
|
74
|
+
* - An instance of EthSafeOperation
|
|
69
75
|
* @return {Promise<string>} The user operation hash.
|
|
70
76
|
*/
|
|
71
|
-
executeTransaction({ executable
|
|
77
|
+
executeTransaction({ executable }: Safe4337ExecutableProps): Promise<string>;
|
|
72
78
|
/**
|
|
73
79
|
* Return a UserOperation based on a hash (userOpHash) returned by eth_sendUserOperation
|
|
74
80
|
*
|
|
@@ -36,7 +36,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
36
36
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
38
|
};
|
|
39
|
-
var _Safe4337Pack_instances, _Safe4337Pack_BUNDLER_URL, _Safe4337Pack_ENTRYPOINT_ADDRESS, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, _Safe4337Pack_bundlerClient,
|
|
39
|
+
var _Safe4337Pack_instances, _Safe4337Pack_BUNDLER_URL, _Safe4337Pack_ENTRYPOINT_ADDRESS, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, _Safe4337Pack_bundlerClient, _Safe4337Pack_paymasterOptions, _Safe4337Pack_toSafeOperation, _Safe4337Pack_getSafeNonceFromEntrypoint, _Safe4337Pack_encodeExecuteUserOpCallData;
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
41
|
exports.Safe4337Pack = void 0;
|
|
42
42
|
const ethers_1 = require("ethers");
|
|
@@ -48,8 +48,20 @@ const safe_modules_deployments_1 = require("@safe-global/safe-modules-deployment
|
|
|
48
48
|
const SafeOperation_1 = __importDefault(require("./SafeOperation"));
|
|
49
49
|
const constants_1 = require("./constants");
|
|
50
50
|
const utils_1 = require("./utils");
|
|
51
|
+
const entrypoint_1 = require("./utils/entrypoint");
|
|
51
52
|
const PimlicoFeeEstimator_1 = require("./estimators/PimlicoFeeEstimator");
|
|
52
53
|
const MAX_ERC20_AMOUNT_TO_APPROVE = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn;
|
|
54
|
+
const EQ_OR_GT_1_4_1 = '>=1.4.1';
|
|
55
|
+
/**
|
|
56
|
+
Some of the contracts used in the PoC app are still experimental, and not included in
|
|
57
|
+
the production deployment packages, thus we need to hardcode their addresses here.
|
|
58
|
+
Deployment commit: https://github.com/safe-global/safe-modules/commit/3853f34f31837e0a0aee47a4452564278f8c62ba
|
|
59
|
+
*/
|
|
60
|
+
// FIXME: use the production deployment packages instead of a hardcoded address
|
|
61
|
+
const SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS = '0x608Cf2e3412c6BDA14E6D8A0a7D27c4240FeD6F1';
|
|
62
|
+
// FIXME: use the production deployment packages instead of a hardcoded address
|
|
63
|
+
// Sepolia only
|
|
64
|
+
const P256_VERIFIER_ADDRESS = '0xcA89CBa4813D5B40AeC6E57A30d0Eeb500d6531b'; // FCLP256Verifier
|
|
53
65
|
/**
|
|
54
66
|
* Safe4337Pack class that extends RelayKitBasePack.
|
|
55
67
|
* This class provides an implementation of the ERC-4337 that enables Safe accounts to wrk with UserOperations.
|
|
@@ -65,18 +77,16 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
65
77
|
*
|
|
66
78
|
* @param {Safe4337Options} options - The initialization parameters.
|
|
67
79
|
*/
|
|
68
|
-
constructor({ protocolKit, bundlerClient,
|
|
80
|
+
constructor({ protocolKit, bundlerClient, bundlerUrl, paymasterOptions, entryPointAddress, safe4337ModuleAddress }) {
|
|
69
81
|
super(protocolKit);
|
|
70
82
|
_Safe4337Pack_instances.add(this);
|
|
71
83
|
_Safe4337Pack_BUNDLER_URL.set(this, void 0);
|
|
72
84
|
_Safe4337Pack_ENTRYPOINT_ADDRESS.set(this, void 0);
|
|
73
85
|
_Safe4337Pack_SAFE_4337_MODULE_ADDRESS.set(this, '0x');
|
|
74
86
|
_Safe4337Pack_bundlerClient.set(this, void 0);
|
|
75
|
-
_Safe4337Pack_publicClient.set(this, void 0);
|
|
76
87
|
_Safe4337Pack_paymasterOptions.set(this, void 0);
|
|
77
88
|
__classPrivateFieldSet(this, _Safe4337Pack_BUNDLER_URL, bundlerUrl, "f");
|
|
78
89
|
__classPrivateFieldSet(this, _Safe4337Pack_bundlerClient, bundlerClient, "f");
|
|
79
|
-
__classPrivateFieldSet(this, _Safe4337Pack_publicClient, publicClient, "f");
|
|
80
90
|
__classPrivateFieldSet(this, _Safe4337Pack_paymasterOptions, paymasterOptions, "f");
|
|
81
91
|
__classPrivateFieldSet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, entryPointAddress, "f");
|
|
82
92
|
__classPrivateFieldSet(this, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, safe4337ModuleAddress, "f");
|
|
@@ -92,17 +102,20 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
92
102
|
* @return {Promise<Safe4337Pack>} The Promise object that will be resolved into an instance of Safe4337Pack.
|
|
93
103
|
*/
|
|
94
104
|
static async init(initOptions) {
|
|
95
|
-
const { provider, signer, options, bundlerUrl,
|
|
105
|
+
const { provider, signer, options, bundlerUrl, customContracts, paymasterOptions } = initOptions;
|
|
96
106
|
let protocolKit;
|
|
97
107
|
const bundlerClient = (0, utils_1.getEip4337BundlerProvider)(bundlerUrl);
|
|
98
|
-
const publicClient = (0, utils_1.getEip1193Provider)(rpcUrl);
|
|
99
108
|
const chainId = await bundlerClient.send(constants_1.RPC_4337_CALLS.CHAIN_ID, []);
|
|
100
109
|
let addModulesLibAddress = customContracts?.addModulesLibAddress;
|
|
101
110
|
const network = parseInt(chainId, 16).toString();
|
|
111
|
+
const safeModulesVersion = initOptions.safeModulesVersion || constants_1.DEFAULT_SAFE_MODULES_VERSION;
|
|
112
|
+
if ((0, satisfies_1.default)(safeModulesVersion, entrypoint_1.EQ_OR_GT_0_3_0)) {
|
|
113
|
+
throw new Error(`Incompatibility detected: Safe modules version ${safeModulesVersion} is not supported. The SDK can use 0.2.0 only.`);
|
|
114
|
+
}
|
|
102
115
|
if (!addModulesLibAddress) {
|
|
103
116
|
const addModulesDeployment = (0, safe_modules_deployments_1.getAddModulesLibDeployment)({
|
|
104
117
|
released: true,
|
|
105
|
-
version:
|
|
118
|
+
version: safeModulesVersion,
|
|
106
119
|
network
|
|
107
120
|
});
|
|
108
121
|
addModulesLibAddress = addModulesDeployment?.networkAddresses[network];
|
|
@@ -111,13 +124,13 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
111
124
|
if (!safe4337ModuleAddress) {
|
|
112
125
|
const safe4337ModuleDeployment = (0, safe_modules_deployments_1.getSafe4337ModuleDeployment)({
|
|
113
126
|
released: true,
|
|
114
|
-
version:
|
|
127
|
+
version: safeModulesVersion,
|
|
115
128
|
network
|
|
116
129
|
});
|
|
117
130
|
safe4337ModuleAddress = safe4337ModuleDeployment?.networkAddresses[network];
|
|
118
131
|
}
|
|
119
132
|
if (!addModulesLibAddress || !safe4337ModuleAddress) {
|
|
120
|
-
throw new Error(`Safe4337Module and/or AddModulesLib not available for chain ${network} and modules version ${
|
|
133
|
+
throw new Error(`Safe4337Module and/or AddModulesLib not available for chain ${network} and modules version ${safeModulesVersion}`);
|
|
121
134
|
}
|
|
122
135
|
// Existing Safe
|
|
123
136
|
if ('safeAddress' in options) {
|
|
@@ -127,7 +140,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
127
140
|
safeAddress: options.safeAddress
|
|
128
141
|
});
|
|
129
142
|
const safeVersion = await protocolKit.getContractVersion();
|
|
130
|
-
const isSafeVersion4337Compatible = (0, satisfies_1.default)(safeVersion,
|
|
143
|
+
const isSafeVersion4337Compatible = (0, satisfies_1.default)(safeVersion, EQ_OR_GT_1_4_1);
|
|
131
144
|
if (!isSafeVersion4337Compatible) {
|
|
132
145
|
throw new Error(`Incompatibility detected: The current Safe Account version (${safeVersion}) is not supported. EIP-4337 requires the Safe to use at least v1.4.1.`);
|
|
133
146
|
}
|
|
@@ -147,41 +160,74 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
147
160
|
if (!options.owners || !options.threshold) {
|
|
148
161
|
throw new Error('Owners and threshold are required to deploy a new Safe');
|
|
149
162
|
}
|
|
150
|
-
|
|
151
|
-
|
|
163
|
+
const safeVersion = options.safeVersion || constants_1.DEFAULT_SAFE_VERSION;
|
|
164
|
+
// we need to create a batch to setup the 4337 Safe Account
|
|
165
|
+
// first setup transaction: Enable 4337 module
|
|
166
|
+
const enable4337ModuleTransaction = {
|
|
167
|
+
to: addModulesLibAddress,
|
|
168
|
+
value: '0',
|
|
169
|
+
data: constants_1.INTERFACES.encodeFunctionData('enableModules', [[safe4337ModuleAddress]]),
|
|
170
|
+
operation: safe_core_sdk_types_1.OperationType.DelegateCall // DelegateCall required for enabling the 4337 module
|
|
171
|
+
};
|
|
172
|
+
const setupTransactions = [enable4337ModuleTransaction];
|
|
152
173
|
const { isSponsored, paymasterTokenAddress } = paymasterOptions || {};
|
|
153
174
|
const isApproveTransactionRequired = !!paymasterOptions && !isSponsored && !!paymasterTokenAddress;
|
|
154
175
|
if (isApproveTransactionRequired) {
|
|
155
176
|
const { paymasterAddress, amountToApprove = MAX_ERC20_AMOUNT_TO_APPROVE } = paymasterOptions;
|
|
156
|
-
|
|
157
|
-
to: addModulesLibAddress,
|
|
158
|
-
value: '0',
|
|
159
|
-
data: constants_1.INTERFACES.encodeFunctionData('enableModules', [[safe4337ModuleAddress]]),
|
|
160
|
-
operation: safe_core_sdk_types_1.OperationType.DelegateCall // DelegateCall required for enabling the 4337 module
|
|
161
|
-
};
|
|
177
|
+
// second transaction: approve ERC-20 paymaster token
|
|
162
178
|
const approveToPaymasterTransaction = {
|
|
163
179
|
to: paymasterTokenAddress,
|
|
164
180
|
data: constants_1.INTERFACES.encodeFunctionData('approve', [paymasterAddress, amountToApprove]),
|
|
165
181
|
value: '0',
|
|
166
182
|
operation: safe_core_sdk_types_1.OperationType.Call // Call for approve
|
|
167
183
|
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
184
|
+
setupTransactions.push(approveToPaymasterTransaction);
|
|
185
|
+
}
|
|
186
|
+
const safeProvider = await protocol_kit_1.SafeProvider.init(provider, signer, safeVersion);
|
|
187
|
+
// third transaction: passkey support via shared signer SafeWebAuthnSharedSigner
|
|
188
|
+
// see: https://github.com/safe-global/safe-modules/blob/main/modules/passkey/contracts/4337/experimental/README.md
|
|
189
|
+
const isPasskeySigner = await safeProvider.isPasskeySigner();
|
|
190
|
+
if (isPasskeySigner) {
|
|
191
|
+
const passkeySigner = (await safeProvider.getExternalSigner());
|
|
192
|
+
if (!options.owners.includes(SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS)) {
|
|
193
|
+
options.owners.push(SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS);
|
|
194
|
+
}
|
|
195
|
+
const passkeyOwnerConfiguration = {
|
|
196
|
+
...passkeySigner.coordinates,
|
|
197
|
+
verifiers: P256_VERIFIER_ADDRESS
|
|
198
|
+
};
|
|
199
|
+
const sharedSignerTransaction = {
|
|
200
|
+
to: SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS,
|
|
201
|
+
value: '0',
|
|
202
|
+
data: constants_1.INTERFACES.encodeFunctionData('configure', [passkeyOwnerConfiguration]),
|
|
203
|
+
operation: safe_core_sdk_types_1.OperationType.DelegateCall // DelegateCall required into the SafeWebAuthnSharedSigner instance in order for it to set its configuration.
|
|
204
|
+
};
|
|
205
|
+
setupTransactions.push(sharedSignerTransaction);
|
|
206
|
+
}
|
|
207
|
+
let deploymentTo;
|
|
208
|
+
let deploymentData;
|
|
209
|
+
const isBatch = setupTransactions.length > 1;
|
|
210
|
+
if (isBatch) {
|
|
172
211
|
const multiSendContract = await (0, protocol_kit_1.getMultiSendContract)({
|
|
173
|
-
safeProvider
|
|
174
|
-
safeVersion
|
|
212
|
+
safeProvider,
|
|
213
|
+
safeVersion
|
|
175
214
|
});
|
|
215
|
+
const batchData = constants_1.INTERFACES.encodeFunctionData('multiSend', [
|
|
216
|
+
(0, protocol_kit_1.encodeMultiSendData)(setupTransactions)
|
|
217
|
+
]);
|
|
176
218
|
deploymentTo = await multiSendContract.getAddress();
|
|
177
219
|
deploymentData = batchData;
|
|
178
220
|
}
|
|
221
|
+
else {
|
|
222
|
+
deploymentTo = enable4337ModuleTransaction.to;
|
|
223
|
+
deploymentData = enable4337ModuleTransaction.data;
|
|
224
|
+
}
|
|
179
225
|
protocolKit = await protocol_kit_1.default.init({
|
|
180
226
|
provider,
|
|
181
227
|
signer,
|
|
182
228
|
predictedSafe: {
|
|
183
229
|
safeDeploymentConfig: {
|
|
184
|
-
safeVersion
|
|
230
|
+
safeVersion,
|
|
185
231
|
saltNonce: options.saltNonce || undefined
|
|
186
232
|
},
|
|
187
233
|
safeAccountConfig: {
|
|
@@ -197,20 +243,32 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
197
243
|
}
|
|
198
244
|
});
|
|
199
245
|
}
|
|
200
|
-
let
|
|
201
|
-
if (
|
|
202
|
-
|
|
246
|
+
let selectedEntryPoint;
|
|
247
|
+
if (customContracts?.entryPointAddress) {
|
|
248
|
+
const requiredSafeModulesVersion = (0, entrypoint_1.entryPointToSafeModules)(customContracts?.entryPointAddress);
|
|
249
|
+
if (!(0, satisfies_1.default)(safeModulesVersion, requiredSafeModulesVersion))
|
|
250
|
+
throw new Error(`The selected entrypoint ${customContracts?.entryPointAddress} is not compatible with version ${safeModulesVersion} of Safe modules`);
|
|
251
|
+
selectedEntryPoint = customContracts?.entryPointAddress;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const supportedEntryPoints = await bundlerClient.send(constants_1.RPC_4337_CALLS.SUPPORTED_ENTRY_POINTS, []);
|
|
203
255
|
if (!supportedEntryPoints.length) {
|
|
204
256
|
throw new Error('No entrypoint provided or available through the bundler');
|
|
205
257
|
}
|
|
258
|
+
selectedEntryPoint = supportedEntryPoints.find((entryPoint) => {
|
|
259
|
+
const requiredSafeModulesVersion = (0, entrypoint_1.entryPointToSafeModules)(entryPoint);
|
|
260
|
+
return (0, satisfies_1.default)(safeModulesVersion, requiredSafeModulesVersion);
|
|
261
|
+
});
|
|
262
|
+
if (!selectedEntryPoint) {
|
|
263
|
+
throw new Error(`Incompatibility detected: None of the entrypoints provided by the bundler is compatible with the Safe modules version ${safeModulesVersion}`);
|
|
264
|
+
}
|
|
206
265
|
}
|
|
207
266
|
return new Safe4337Pack({
|
|
208
267
|
protocolKit,
|
|
209
268
|
bundlerClient,
|
|
210
|
-
publicClient,
|
|
211
269
|
paymasterOptions,
|
|
212
270
|
bundlerUrl,
|
|
213
|
-
entryPointAddress:
|
|
271
|
+
entryPointAddress: selectedEntryPoint,
|
|
214
272
|
safe4337ModuleAddress
|
|
215
273
|
});
|
|
216
274
|
}
|
|
@@ -223,6 +281,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
223
281
|
* @return {Promise<EthSafeOperation>} The Promise object that will be resolved into the gas estimation.
|
|
224
282
|
*/
|
|
225
283
|
async getEstimateFee({ safeOperation, feeEstimator = new PimlicoFeeEstimator_1.PimlicoFeeEstimator() }) {
|
|
284
|
+
const threshold = await this.protocolKit.getThreshold();
|
|
226
285
|
const setupEstimationData = await feeEstimator?.setupEstimation?.({
|
|
227
286
|
bundlerUrl: __classPrivateFieldGet(this, _Safe4337Pack_BUNDLER_URL, "f"),
|
|
228
287
|
entryPoint: __classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f"),
|
|
@@ -231,7 +290,10 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
231
290
|
if (setupEstimationData) {
|
|
232
291
|
safeOperation.addEstimations(setupEstimationData);
|
|
233
292
|
}
|
|
234
|
-
const estimateUserOperationGas = await __classPrivateFieldGet(this, _Safe4337Pack_bundlerClient, "f").send(constants_1.RPC_4337_CALLS.ESTIMATE_USER_OPERATION_GAS, [
|
|
293
|
+
const estimateUserOperationGas = await __classPrivateFieldGet(this, _Safe4337Pack_bundlerClient, "f").send(constants_1.RPC_4337_CALLS.ESTIMATE_USER_OPERATION_GAS, [
|
|
294
|
+
(0, utils_1.userOperationToHexValues)((0, utils_1.addDummySignature)(safeOperation.toUserOperation(), SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS, threshold)),
|
|
295
|
+
__classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f")
|
|
296
|
+
]);
|
|
235
297
|
if (estimateUserOperationGas) {
|
|
236
298
|
safeOperation.addEstimations({
|
|
237
299
|
preVerificationGas: BigInt(estimateUserOperationGas.preVerificationGas),
|
|
@@ -252,7 +314,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
252
314
|
throw new Error('No paymaster url provided for a sponsored transaction');
|
|
253
315
|
}
|
|
254
316
|
const paymasterEstimation = await feeEstimator?.getPaymasterEstimation?.({
|
|
255
|
-
userOperation: safeOperation.toUserOperation(),
|
|
317
|
+
userOperation: (0, utils_1.addDummySignature)(safeOperation.toUserOperation(), SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS, threshold),
|
|
256
318
|
paymasterUrl: __classPrivateFieldGet(this, _Safe4337Pack_paymasterOptions, "f").paymasterUrl,
|
|
257
319
|
entryPoint: __classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f"),
|
|
258
320
|
sponsorshipPolicyId: __classPrivateFieldGet(this, _Safe4337Pack_paymasterOptions, "f").sponsorshipPolicyId
|
|
@@ -274,7 +336,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
274
336
|
*/
|
|
275
337
|
async createTransaction({ transactions, options = {} }) {
|
|
276
338
|
const safeAddress = await this.protocolKit.getAddress();
|
|
277
|
-
const nonce = await __classPrivateFieldGet(this, _Safe4337Pack_instances, "m",
|
|
339
|
+
const nonce = await __classPrivateFieldGet(this, _Safe4337Pack_instances, "m", _Safe4337Pack_getSafeNonceFromEntrypoint).call(this, safeAddress);
|
|
278
340
|
const { amountToApprove, validUntil, validAfter, feeEstimator } = options;
|
|
279
341
|
if (amountToApprove) {
|
|
280
342
|
if (!__classPrivateFieldGet(this, _Safe4337Pack_paymasterOptions, "f") || !__classPrivateFieldGet(this, _Safe4337Pack_paymasterOptions, "f").paymasterTokenAddress) {
|
|
@@ -296,7 +358,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
296
358
|
? __classPrivateFieldGet(this, _Safe4337Pack_instances, "m", _Safe4337Pack_encodeExecuteUserOpCallData).call(this, {
|
|
297
359
|
to: multiSendAddress,
|
|
298
360
|
value: '0',
|
|
299
|
-
data:
|
|
361
|
+
data: (0, utils_1.encodeMultiSendCallData)(transactions),
|
|
300
362
|
operation: safe_core_sdk_types_1.OperationType.DelegateCall
|
|
301
363
|
})
|
|
302
364
|
: __classPrivateFieldGet(this, _Safe4337Pack_instances, "m", _Safe4337Pack_encodeExecuteUserOpCallData).call(this, transactions[0]);
|
|
@@ -319,6 +381,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
319
381
|
userOperation.initCode = await this.protocolKit.getInitCode();
|
|
320
382
|
}
|
|
321
383
|
const safeOperation = new SafeOperation_1.default(userOperation, {
|
|
384
|
+
moduleAddress: __classPrivateFieldGet(this, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, "f"),
|
|
322
385
|
entryPoint: __classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f"),
|
|
323
386
|
validUntil,
|
|
324
387
|
validAfter
|
|
@@ -331,37 +394,60 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
331
394
|
/**
|
|
332
395
|
* Signs a safe operation.
|
|
333
396
|
*
|
|
334
|
-
* @param {EthSafeOperation} safeOperation - The SafeOperation to sign.
|
|
397
|
+
* @param {EthSafeOperation | SafeOperationResponse} safeOperation - The SafeOperation to sign. It can be:
|
|
398
|
+
* - A response from the API (Tx Service)
|
|
399
|
+
* - An instance of EthSafeOperation
|
|
335
400
|
* @param {SigningMethod} signingMethod - The signing method to use.
|
|
336
401
|
* @return {Promise<EthSafeOperation>} The Promise object will resolve to the signed SafeOperation.
|
|
337
402
|
*/
|
|
338
403
|
async signSafeOperation(safeOperation, signingMethod = protocol_kit_1.SigningMethod.ETH_SIGN_TYPED_DATA_V4) {
|
|
404
|
+
let safeOp;
|
|
405
|
+
if ((0, safe_core_sdk_types_1.isSafeOperationResponse)(safeOperation)) {
|
|
406
|
+
safeOp = __classPrivateFieldGet(this, _Safe4337Pack_instances, "m", _Safe4337Pack_toSafeOperation).call(this, safeOperation);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
safeOp = safeOperation;
|
|
410
|
+
}
|
|
339
411
|
const owners = await this.protocolKit.getOwners();
|
|
340
|
-
const
|
|
412
|
+
const safeProvider = this.protocolKit.getSafeProvider();
|
|
413
|
+
const signerAddress = await safeProvider.getSignerAddress();
|
|
414
|
+
const chainId = await safeProvider.getChainId();
|
|
415
|
+
const isPasskeySigner = await safeProvider.isPasskeySigner();
|
|
341
416
|
if (!signerAddress) {
|
|
342
417
|
throw new Error('There is no signer address available to sign the SafeOperation');
|
|
343
418
|
}
|
|
344
419
|
const addressIsOwner = owners.some((owner) => signerAddress && owner.toLowerCase() === signerAddress.toLowerCase());
|
|
345
|
-
if (!addressIsOwner) {
|
|
420
|
+
if (!addressIsOwner && !isPasskeySigner) {
|
|
346
421
|
throw new Error('UserOperations can only be signed by Safe owners');
|
|
347
422
|
}
|
|
348
423
|
let signature;
|
|
349
|
-
if (
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
424
|
+
if (isPasskeySigner) {
|
|
425
|
+
const safeOpHash = (0, utils_1.calculateSafeUserOperationHash)(safeOp.data, chainId, __classPrivateFieldGet(this, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, "f"));
|
|
426
|
+
const passkeySignature = await this.protocolKit.signHash(safeOpHash);
|
|
427
|
+
// SafeWebAuthnSharedSigner signature
|
|
428
|
+
signature = new protocol_kit_1.EthSafeSignature(SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS, passkeySignature.data, true);
|
|
353
429
|
}
|
|
354
430
|
else {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
431
|
+
if (signingMethod in
|
|
432
|
+
[
|
|
433
|
+
protocol_kit_1.SigningMethod.ETH_SIGN_TYPED_DATA_V4,
|
|
434
|
+
protocol_kit_1.SigningMethod.ETH_SIGN_TYPED_DATA_V3,
|
|
435
|
+
protocol_kit_1.SigningMethod.ETH_SIGN_TYPED_DATA
|
|
436
|
+
]) {
|
|
437
|
+
signature = await (0, utils_1.signSafeOp)(safeOp.data, this.protocolKit.getSafeProvider(), __classPrivateFieldGet(this, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, "f"));
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
const safeOpHash = (0, utils_1.calculateSafeUserOperationHash)(safeOp.data, chainId, __classPrivateFieldGet(this, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, "f"));
|
|
441
|
+
signature = await this.protocolKit.signHash(safeOpHash);
|
|
442
|
+
}
|
|
358
443
|
}
|
|
359
|
-
const signedSafeOperation = new SafeOperation_1.default(
|
|
444
|
+
const signedSafeOperation = new SafeOperation_1.default(safeOp.toUserOperation(), {
|
|
445
|
+
moduleAddress: __classPrivateFieldGet(this, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, "f"),
|
|
360
446
|
entryPoint: __classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f"),
|
|
361
|
-
validUntil:
|
|
362
|
-
validAfter:
|
|
447
|
+
validUntil: safeOp.data.validUntil,
|
|
448
|
+
validAfter: safeOp.data.validAfter
|
|
363
449
|
});
|
|
364
|
-
|
|
450
|
+
safeOp.signatures.forEach((signature) => {
|
|
365
451
|
signedSafeOperation.addSignature(signature);
|
|
366
452
|
});
|
|
367
453
|
signedSafeOperation.addSignature(signature);
|
|
@@ -370,12 +456,25 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
370
456
|
/**
|
|
371
457
|
* Executes the relay transaction.
|
|
372
458
|
*
|
|
373
|
-
* @param {
|
|
459
|
+
* @param {Safe4337ExecutableProps} props - The parameters for the transaction execution.
|
|
460
|
+
* @param {EthSafeOperation | SafeOperationResponse} props.executable - The SafeOperation to execute. It can be:
|
|
461
|
+
* - A response from the API (Tx Service)
|
|
462
|
+
* - An instance of EthSafeOperation
|
|
374
463
|
* @return {Promise<string>} The user operation hash.
|
|
375
464
|
*/
|
|
376
|
-
async executeTransaction({ executable
|
|
465
|
+
async executeTransaction({ executable }) {
|
|
466
|
+
let safeOperation;
|
|
467
|
+
if ((0, safe_core_sdk_types_1.isSafeOperationResponse)(executable)) {
|
|
468
|
+
safeOperation = __classPrivateFieldGet(this, _Safe4337Pack_instances, "m", _Safe4337Pack_toSafeOperation).call(this, executable);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
safeOperation = executable;
|
|
472
|
+
}
|
|
377
473
|
const userOperation = safeOperation.toUserOperation();
|
|
378
|
-
return __classPrivateFieldGet(this,
|
|
474
|
+
return __classPrivateFieldGet(this, _Safe4337Pack_bundlerClient, "f").send(constants_1.RPC_4337_CALLS.SEND_USER_OPERATION, [
|
|
475
|
+
(0, utils_1.userOperationToHexValues)(userOperation),
|
|
476
|
+
__classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f")
|
|
477
|
+
]);
|
|
379
478
|
}
|
|
380
479
|
/**
|
|
381
480
|
* Return a UserOperation based on a hash (userOpHash) returned by eth_sendUserOperation
|
|
@@ -384,8 +483,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
384
483
|
* @returns {UserOperation} - null in case the UserOperation is not yet included in a block, or a full UserOperation, with the addition of entryPoint, blockNumber, blockHash and transactionHash
|
|
385
484
|
*/
|
|
386
485
|
async getUserOperationByHash(userOpHash) {
|
|
387
|
-
|
|
388
|
-
return userOperation;
|
|
486
|
+
return __classPrivateFieldGet(this, _Safe4337Pack_bundlerClient, "f").send(constants_1.RPC_4337_CALLS.GET_USER_OPERATION_BY_HASH, [userOpHash]);
|
|
389
487
|
}
|
|
390
488
|
/**
|
|
391
489
|
* Return a UserOperation receipt based on a hash (userOpHash) returned by eth_sendUserOperation
|
|
@@ -394,8 +492,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
394
492
|
* @returns {UserOperationReceipt} - null in case the UserOperation is not yet included in a block, or UserOperationReceipt object
|
|
395
493
|
*/
|
|
396
494
|
async getUserOperationReceipt(userOpHash) {
|
|
397
|
-
|
|
398
|
-
return userOperationReceipt;
|
|
495
|
+
return __classPrivateFieldGet(this, _Safe4337Pack_bundlerClient, "f").send(constants_1.RPC_4337_CALLS.GET_USER_OPERATION_RECEIPT, [userOpHash]);
|
|
399
496
|
}
|
|
400
497
|
/**
|
|
401
498
|
* Returns an array of the entryPoint addresses supported by the client.
|
|
@@ -404,8 +501,7 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
404
501
|
* @returns {string[]} - The supported entry points.
|
|
405
502
|
*/
|
|
406
503
|
async getSupportedEntryPoints() {
|
|
407
|
-
|
|
408
|
-
return supportedEntryPoints;
|
|
504
|
+
return __classPrivateFieldGet(this, _Safe4337Pack_bundlerClient, "f").send(constants_1.RPC_4337_CALLS.SUPPORTED_ENTRY_POINTS, []);
|
|
409
505
|
}
|
|
410
506
|
/**
|
|
411
507
|
* Returns EIP-155 Chain ID.
|
|
@@ -413,60 +509,44 @@ class Safe4337Pack extends RelayKitBasePack_1.RelayKitBasePack {
|
|
|
413
509
|
* @returns {string} - The chain id.
|
|
414
510
|
*/
|
|
415
511
|
async getChainId() {
|
|
416
|
-
|
|
417
|
-
return chainId;
|
|
512
|
+
return __classPrivateFieldGet(this, _Safe4337Pack_bundlerClient, "f").send(constants_1.RPC_4337_CALLS.CHAIN_ID, []);
|
|
418
513
|
}
|
|
419
514
|
}
|
|
420
515
|
exports.Safe4337Pack = Safe4337Pack;
|
|
421
|
-
_Safe4337Pack_BUNDLER_URL = new WeakMap(), _Safe4337Pack_ENTRYPOINT_ADDRESS = new WeakMap(), _Safe4337Pack_SAFE_4337_MODULE_ADDRESS = new WeakMap(), _Safe4337Pack_bundlerClient = new WeakMap(),
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
__classPrivateFieldGet(this,
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
* Signs typed data.
|
|
441
|
-
*
|
|
442
|
-
* @param {SafeUserOperation} safeUserOperation - Safe user operation to sign.
|
|
443
|
-
* @return {Promise<SafeSignature>} The SafeSignature object containing the data and the signatures.
|
|
444
|
-
*/
|
|
445
|
-
async function _Safe4337Pack_signTypedData(safeUserOperation) {
|
|
446
|
-
const safeProvider = this.protocolKit.getSafeProvider();
|
|
447
|
-
const signer = (await safeProvider.getExternalSigner());
|
|
448
|
-
const chainId = await safeProvider.getChainId();
|
|
449
|
-
const signerAddress = await signer.getAddress();
|
|
450
|
-
const signature = await signer.signTypedData({
|
|
451
|
-
chainId,
|
|
452
|
-
verifyingContract: __classPrivateFieldGet(this, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, "f")
|
|
453
|
-
}, constants_1.EIP712_SAFE_OPERATION_TYPE, {
|
|
454
|
-
...safeUserOperation,
|
|
455
|
-
nonce: ethers_1.ethers.toBeHex(safeUserOperation.nonce),
|
|
456
|
-
validAfter: ethers_1.ethers.toBeHex(safeUserOperation.validAfter),
|
|
457
|
-
validUntil: ethers_1.ethers.toBeHex(safeUserOperation.validUntil),
|
|
458
|
-
maxFeePerGas: ethers_1.ethers.toBeHex(safeUserOperation.maxFeePerGas),
|
|
459
|
-
maxPriorityFeePerGas: ethers_1.ethers.toBeHex(safeUserOperation.maxPriorityFeePerGas)
|
|
516
|
+
_Safe4337Pack_BUNDLER_URL = new WeakMap(), _Safe4337Pack_ENTRYPOINT_ADDRESS = new WeakMap(), _Safe4337Pack_SAFE_4337_MODULE_ADDRESS = new WeakMap(), _Safe4337Pack_bundlerClient = new WeakMap(), _Safe4337Pack_paymasterOptions = new WeakMap(), _Safe4337Pack_instances = new WeakSet(), _Safe4337Pack_toSafeOperation = function _Safe4337Pack_toSafeOperation(safeOperationResponse) {
|
|
517
|
+
const { validUntil, validAfter, userOperation } = safeOperationResponse;
|
|
518
|
+
const safeOperation = new SafeOperation_1.default({
|
|
519
|
+
sender: userOperation?.sender || '0x',
|
|
520
|
+
nonce: userOperation?.nonce?.toString() || '0',
|
|
521
|
+
initCode: userOperation?.initCode || '',
|
|
522
|
+
callData: userOperation?.callData || '',
|
|
523
|
+
callGasLimit: BigInt(userOperation?.callGasLimit || 0n),
|
|
524
|
+
verificationGasLimit: BigInt(userOperation?.verificationGasLimit || 0),
|
|
525
|
+
preVerificationGas: BigInt(userOperation?.preVerificationGas || 0),
|
|
526
|
+
maxFeePerGas: BigInt(userOperation?.maxFeePerGas || 0),
|
|
527
|
+
maxPriorityFeePerGas: BigInt(userOperation?.maxPriorityFeePerGas || 0),
|
|
528
|
+
paymasterAndData: userOperation?.paymasterData || '0x',
|
|
529
|
+
signature: userOperation?.signature || '0x'
|
|
530
|
+
}, {
|
|
531
|
+
moduleAddress: __classPrivateFieldGet(this, _Safe4337Pack_SAFE_4337_MODULE_ADDRESS, "f"),
|
|
532
|
+
entryPoint: userOperation?.entryPoint || __classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f"),
|
|
533
|
+
validAfter: validAfter ? new Date(validAfter).getTime() : undefined,
|
|
534
|
+
validUntil: validUntil ? new Date(validUntil).getTime() : undefined
|
|
460
535
|
});
|
|
461
|
-
|
|
462
|
-
|
|
536
|
+
if (safeOperationResponse.confirmations) {
|
|
537
|
+
safeOperationResponse.confirmations.forEach((confirmation) => {
|
|
538
|
+
safeOperation.addSignature(new protocol_kit_1.EthSafeSignature(confirmation.owner, confirmation.signature));
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
return safeOperation;
|
|
542
|
+
}, _Safe4337Pack_getSafeNonceFromEntrypoint =
|
|
463
543
|
/**
|
|
464
544
|
* Gets account nonce from the bundler.
|
|
465
545
|
*
|
|
466
|
-
* @param {string}
|
|
546
|
+
* @param {string} safeAddress - Account address for which the nonce is to be fetched.
|
|
467
547
|
* @returns {Promise<string>} The Promise object will resolve to the account nonce.
|
|
468
548
|
*/
|
|
469
|
-
async function
|
|
549
|
+
async function _Safe4337Pack_getSafeNonceFromEntrypoint(safeAddress) {
|
|
470
550
|
const abi = [
|
|
471
551
|
{
|
|
472
552
|
inputs: [
|
|
@@ -479,8 +559,8 @@ async function _Safe4337Pack_getAccountNonce(sender) {
|
|
|
479
559
|
type: 'function'
|
|
480
560
|
}
|
|
481
561
|
];
|
|
482
|
-
const contract = new ethers_1.ethers.Contract(__classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f") || '0x', abi,
|
|
483
|
-
const newNonce = await contract.getNonce(
|
|
562
|
+
const contract = new ethers_1.ethers.Contract(__classPrivateFieldGet(this, _Safe4337Pack_ENTRYPOINT_ADDRESS, "f") || '0x', abi, this.protocolKit.getSafeProvider().getExternalProvider());
|
|
563
|
+
const newNonce = await contract.getNonce(safeAddress, BigInt(0));
|
|
484
564
|
return newNonce.toString();
|
|
485
565
|
}, _Safe4337Pack_encodeExecuteUserOpCallData = function _Safe4337Pack_encodeExecuteUserOpCallData(transaction) {
|
|
486
566
|
return constants_1.INTERFACES.encodeFunctionData('executeUserOp', [
|
|
@@ -489,9 +569,5 @@ async function _Safe4337Pack_getAccountNonce(sender) {
|
|
|
489
569
|
transaction.data,
|
|
490
570
|
transaction.operation || safe_core_sdk_types_1.OperationType.Call
|
|
491
571
|
]);
|
|
492
|
-
}, _Safe4337Pack_encodeMultiSendCallData = function _Safe4337Pack_encodeMultiSendCallData(transactions) {
|
|
493
|
-
return constants_1.INTERFACES.encodeFunctionData('multiSend', [
|
|
494
|
-
(0, protocol_kit_1.encodeMultiSendData)(transactions.map((tx) => ({ ...tx, operation: tx.operation ?? safe_core_sdk_types_1.OperationType.Call })))
|
|
495
|
-
]);
|
|
496
572
|
};
|
|
497
573
|
//# sourceMappingURL=Safe4337Pack.js.map
|