@0xsequence/account 2.3.16 → 2.3.18
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/0xsequence-account.cjs.d.ts +2 -2
- package/dist/0xsequence-account.cjs.js +15 -6
- package/package.json +11 -11
- package/dist/0xsequence-account.cjs.dev.js +0 -1105
- package/dist/0xsequence-account.cjs.prod.js +0 -1105
- package/dist/0xsequence-account.esm.js +0 -1100
- package/dist/declarations/src/account.d.ts +0 -178
- package/dist/declarations/src/index.d.ts +0 -1
- package/dist/declarations/src/signer.d.ts +0 -58
|
@@ -1,1100 +0,0 @@
|
|
|
1
|
-
import { walletContracts } from '@0xsequence/abi';
|
|
2
|
-
import { commons, universal } from '@0xsequence/core';
|
|
3
|
-
import { defaults, migrator, version } from '@0xsequence/migration';
|
|
4
|
-
import { ChainId } from '@0xsequence/network';
|
|
5
|
-
import { proto, isRelayer, RpcRelayer } from '@0xsequence/relayer';
|
|
6
|
-
import { toHexString, getFetchRequest, encodeTypedDataDigest } from '@0xsequence/utils';
|
|
7
|
-
import { Wallet } from '@0xsequence/wallet';
|
|
8
|
-
import { ethers, MessagePrefix } from 'ethers';
|
|
9
|
-
|
|
10
|
-
function _extends() {
|
|
11
|
-
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
12
|
-
for (var e = 1; e < arguments.length; e++) {
|
|
13
|
-
var t = arguments[e];
|
|
14
|
-
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
15
|
-
}
|
|
16
|
-
return n;
|
|
17
|
-
}, _extends.apply(null, arguments);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function encodeGasRefundTransaction(option) {
|
|
21
|
-
if (!option) return [];
|
|
22
|
-
const value = BigInt(option.value);
|
|
23
|
-
switch (option.token.type) {
|
|
24
|
-
case proto.FeeTokenType.UNKNOWN:
|
|
25
|
-
return [{
|
|
26
|
-
delegateCall: false,
|
|
27
|
-
revertOnError: true,
|
|
28
|
-
gasLimit: option.gasLimit,
|
|
29
|
-
to: option.to,
|
|
30
|
-
value: toHexString(value),
|
|
31
|
-
data: '0x'
|
|
32
|
-
}];
|
|
33
|
-
case proto.FeeTokenType.ERC20_TOKEN:
|
|
34
|
-
if (!option.token.contractAddress) {
|
|
35
|
-
throw new Error(`No contract address for ERC-20 fee option`);
|
|
36
|
-
}
|
|
37
|
-
return [{
|
|
38
|
-
delegateCall: false,
|
|
39
|
-
revertOnError: true,
|
|
40
|
-
gasLimit: option.gasLimit,
|
|
41
|
-
to: option.token.contractAddress,
|
|
42
|
-
value: 0,
|
|
43
|
-
data: new ethers.Interface([{
|
|
44
|
-
constant: false,
|
|
45
|
-
inputs: [{
|
|
46
|
-
type: 'address'
|
|
47
|
-
}, {
|
|
48
|
-
type: 'uint256'
|
|
49
|
-
}],
|
|
50
|
-
name: 'transfer',
|
|
51
|
-
outputs: [],
|
|
52
|
-
type: 'function'
|
|
53
|
-
}]).encodeFunctionData('transfer', [option.to, toHexString(value)])
|
|
54
|
-
}];
|
|
55
|
-
default:
|
|
56
|
-
throw new Error(`Unhandled fee token type ${option.token.type}`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
class AccountSigner {
|
|
60
|
-
constructor(account, chainId, options) {
|
|
61
|
-
this.account = account;
|
|
62
|
-
this.chainId = chainId;
|
|
63
|
-
this.options = options;
|
|
64
|
-
}
|
|
65
|
-
get provider() {
|
|
66
|
-
return this.account.providerFor(this.chainId);
|
|
67
|
-
}
|
|
68
|
-
async getAddress() {
|
|
69
|
-
return this.account.address;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Signs a message.
|
|
74
|
-
*
|
|
75
|
-
* This method will sign the message using the account associated with this signer
|
|
76
|
-
* and the specified chain ID. The message is already being prefixed with the EIP-191 prefix.
|
|
77
|
-
*
|
|
78
|
-
* @param message - The message to sign. Can be a string or BytesLike.
|
|
79
|
-
* @returns A Promise that resolves to the signature as a hexadecimal string
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* ```typescript
|
|
83
|
-
* const signer = account.getSigner(chainId)
|
|
84
|
-
*
|
|
85
|
-
* const message = "Hello, Sequence!";
|
|
86
|
-
* const signature = await signer.signMessage(message);
|
|
87
|
-
* console.log(signature);
|
|
88
|
-
* // => "0x123abc..." (hexadecimal signature)
|
|
89
|
-
*/
|
|
90
|
-
signMessage(message) {
|
|
91
|
-
var _this$options$cantVal, _this$options;
|
|
92
|
-
return this.account.signMessage(message, this.chainId, (_this$options$cantVal = (_this$options = this.options) == null ? void 0 : _this$options.cantValidateBehavior) != null ? _this$options$cantVal : 'throw');
|
|
93
|
-
}
|
|
94
|
-
signTypedData(domain, types, value) {
|
|
95
|
-
var _this$options$cantVal2, _this$options2;
|
|
96
|
-
return this.account.signTypedData(domain, types, value, this.chainId, (_this$options$cantVal2 = (_this$options2 = this.options) == null ? void 0 : _this$options2.cantValidateBehavior) != null ? _this$options$cantVal2 : 'throw');
|
|
97
|
-
}
|
|
98
|
-
async defaultSelectFee(_txs, options) {
|
|
99
|
-
// If no options, return undefined
|
|
100
|
-
if (options.length === 0) return undefined;
|
|
101
|
-
|
|
102
|
-
// If there are multiple options, try them one by one
|
|
103
|
-
// until we find one that satisfies the balance requirement
|
|
104
|
-
const balanceOfAbi = [{
|
|
105
|
-
constant: true,
|
|
106
|
-
inputs: [{
|
|
107
|
-
type: 'address'
|
|
108
|
-
}],
|
|
109
|
-
name: 'balanceOf',
|
|
110
|
-
outputs: [{
|
|
111
|
-
type: 'uint256'
|
|
112
|
-
}],
|
|
113
|
-
type: 'function'
|
|
114
|
-
}];
|
|
115
|
-
for (const option of options) {
|
|
116
|
-
if (option.token.type === proto.FeeTokenType.UNKNOWN) {
|
|
117
|
-
// Native token
|
|
118
|
-
const balance = await this.getBalance();
|
|
119
|
-
if (balance >= BigInt(option.value)) {
|
|
120
|
-
return option;
|
|
121
|
-
}
|
|
122
|
-
} else if (option.token.contractAddress && option.token.type === proto.FeeTokenType.ERC20_TOKEN) {
|
|
123
|
-
// ERC20 token
|
|
124
|
-
const token = new ethers.Contract(option.token.contractAddress, balanceOfAbi, this.provider);
|
|
125
|
-
const balance = await token.balanceOf(this.account.address);
|
|
126
|
-
if (balance >= BigInt(option.value)) {
|
|
127
|
-
return option;
|
|
128
|
-
}
|
|
129
|
-
} else ;
|
|
130
|
-
}
|
|
131
|
-
throw new Error('No fee option available - not enough balance');
|
|
132
|
-
}
|
|
133
|
-
async sendTransaction(txs, options) {
|
|
134
|
-
var _this$options$stubSig, _this$options3, _this$options$selectF, _this$options4, _this$options5;
|
|
135
|
-
const prepare = await this.account.prepareTransactions({
|
|
136
|
-
txs,
|
|
137
|
-
chainId: this.chainId,
|
|
138
|
-
stubSignatureOverrides: (_this$options$stubSig = (_this$options3 = this.options) == null ? void 0 : _this$options3.stubSignatureOverrides) != null ? _this$options$stubSig : new Map(),
|
|
139
|
-
simulateForFeeOptions: options == null ? void 0 : options.simulateForFeeOptions
|
|
140
|
-
});
|
|
141
|
-
const selectMethod = (_this$options$selectF = (_this$options4 = this.options) == null ? void 0 : _this$options4.selectFee) != null ? _this$options$selectF : this.defaultSelectFee.bind(this);
|
|
142
|
-
const feeOption = await selectMethod(txs, prepare.feeOptions);
|
|
143
|
-
const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
|
|
144
|
-
return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, ((_this$options5 = this.options) == null ? void 0 : _this$options5.nonceSpace) !== undefined ? {
|
|
145
|
-
nonceSpace: this.options.nonceSpace
|
|
146
|
-
} : undefined); // Will always have a transaction response
|
|
147
|
-
}
|
|
148
|
-
getBalance(blockTag) {
|
|
149
|
-
return this.provider.getBalance(this.account.address, blockTag);
|
|
150
|
-
}
|
|
151
|
-
call(transaction, blockTag) {
|
|
152
|
-
return this.provider.call(_extends({}, transaction, {
|
|
153
|
-
blockTag
|
|
154
|
-
}));
|
|
155
|
-
}
|
|
156
|
-
async resolveName(name) {
|
|
157
|
-
const res = await this.provider.resolveName(name);
|
|
158
|
-
if (!res) throw new Error(`Could not resolve name ${name}`);
|
|
159
|
-
return res;
|
|
160
|
-
}
|
|
161
|
-
connect(_provider) {
|
|
162
|
-
throw new Error('Method not implemented.');
|
|
163
|
-
}
|
|
164
|
-
signTransaction(transaction) {
|
|
165
|
-
throw new Error('Method not implemented.');
|
|
166
|
-
}
|
|
167
|
-
getTransactionCount(blockTag) {
|
|
168
|
-
throw new Error('Method not implemented.');
|
|
169
|
-
}
|
|
170
|
-
estimateGas(transaction) {
|
|
171
|
-
throw new Error('Method not implemented.');
|
|
172
|
-
}
|
|
173
|
-
getChainId() {
|
|
174
|
-
return Promise.resolve(Number(this.chainId));
|
|
175
|
-
}
|
|
176
|
-
getGasPrice() {
|
|
177
|
-
throw new Error('Method not implemented.');
|
|
178
|
-
}
|
|
179
|
-
getFeeData() {
|
|
180
|
-
throw new Error('Method not implemented.');
|
|
181
|
-
}
|
|
182
|
-
getNonce(blockTag) {
|
|
183
|
-
throw new Error('Method not implemented.');
|
|
184
|
-
}
|
|
185
|
-
populateCall(tx) {
|
|
186
|
-
throw new Error('Method not implemented.');
|
|
187
|
-
}
|
|
188
|
-
checkTransaction(transaction) {
|
|
189
|
-
throw new Error('Method not implemented.');
|
|
190
|
-
}
|
|
191
|
-
async populateTransaction(tx) {
|
|
192
|
-
throw new Error('Method not implemented.');
|
|
193
|
-
}
|
|
194
|
-
_checkProvider(operation) {
|
|
195
|
-
throw new Error('Method not implemented.');
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
class Chain0Reader {
|
|
200
|
-
async isDeployed(_wallet) {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
async implementation(_wallet) {
|
|
204
|
-
return undefined;
|
|
205
|
-
}
|
|
206
|
-
async imageHash(_wallet) {
|
|
207
|
-
return undefined;
|
|
208
|
-
}
|
|
209
|
-
async nonce(_wallet, _space) {
|
|
210
|
-
return 0n;
|
|
211
|
-
}
|
|
212
|
-
async isValidSignature(_wallet, _digest, _signature) {
|
|
213
|
-
throw new Error('Method not supported.');
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
class Account {
|
|
217
|
-
constructor(options) {
|
|
218
|
-
this.address = void 0;
|
|
219
|
-
this.networks = void 0;
|
|
220
|
-
this.tracker = void 0;
|
|
221
|
-
this.contexts = void 0;
|
|
222
|
-
this.migrator = void 0;
|
|
223
|
-
this.migrations = void 0;
|
|
224
|
-
this.orchestrator = void 0;
|
|
225
|
-
this.jwt = void 0;
|
|
226
|
-
this.projectAccessKey = void 0;
|
|
227
|
-
this.address = ethers.getAddress(options.address);
|
|
228
|
-
this.contexts = options.contexts;
|
|
229
|
-
this.tracker = options.tracker;
|
|
230
|
-
this.networks = options.networks;
|
|
231
|
-
this.orchestrator = options.orchestrator;
|
|
232
|
-
this.jwt = options.jwt;
|
|
233
|
-
this.projectAccessKey = options.projectAccessKey;
|
|
234
|
-
this.migrations = options.migrations || defaults.DefaultMigrations;
|
|
235
|
-
this.migrator = new migrator.Migrator(options.tracker, this.migrations, this.contexts);
|
|
236
|
-
}
|
|
237
|
-
getSigner(chainId, options) {
|
|
238
|
-
return new AccountSigner(this, chainId, options);
|
|
239
|
-
}
|
|
240
|
-
static async new(options) {
|
|
241
|
-
var _options$migrations;
|
|
242
|
-
const mig = new migrator.Migrator(options.tracker, (_options$migrations = options.migrations) != null ? _options$migrations : defaults.DefaultMigrations, options.contexts);
|
|
243
|
-
const lastMigration = mig.lastMigration();
|
|
244
|
-
const lastCoder = lastMigration.configCoder;
|
|
245
|
-
const config = lastCoder.fromSimple(options.config);
|
|
246
|
-
const imageHash = lastCoder.imageHashOf(config);
|
|
247
|
-
const context = options.contexts[lastMigration.version];
|
|
248
|
-
const address = commons.context.addressOf(context, imageHash);
|
|
249
|
-
await options.tracker.saveCounterfactualWallet({
|
|
250
|
-
config,
|
|
251
|
-
context: Object.values(options.contexts)
|
|
252
|
-
});
|
|
253
|
-
return new Account({
|
|
254
|
-
address,
|
|
255
|
-
tracker: options.tracker,
|
|
256
|
-
contexts: options.contexts,
|
|
257
|
-
networks: options.networks,
|
|
258
|
-
orchestrator: options.orchestrator,
|
|
259
|
-
migrations: options.migrations,
|
|
260
|
-
projectAccessKey: options.projectAccessKey
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
getAddress() {
|
|
264
|
-
return Promise.resolve(this.address);
|
|
265
|
-
}
|
|
266
|
-
get version() {
|
|
267
|
-
return this.migrator.lastMigration().version;
|
|
268
|
-
}
|
|
269
|
-
get coders() {
|
|
270
|
-
const lastMigration = this.migrator.lastMigration();
|
|
271
|
-
return {
|
|
272
|
-
signature: lastMigration.signatureCoder,
|
|
273
|
-
config: lastMigration.configCoder
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
network(chainId) {
|
|
277
|
-
const tcid = BigInt(chainId);
|
|
278
|
-
const found = this.networks.find(n => tcid === BigInt(n.chainId));
|
|
279
|
-
if (!found) throw new Error(`Network not found for chainId ${chainId}`);
|
|
280
|
-
return found;
|
|
281
|
-
}
|
|
282
|
-
providerFor(chainId) {
|
|
283
|
-
const found = this.network(chainId);
|
|
284
|
-
if (!found.provider && !found.rpcUrl) {
|
|
285
|
-
throw new Error(`Provider not found for chainId ${chainId}`);
|
|
286
|
-
}
|
|
287
|
-
const network = new ethers.Network(found.name, found.chainId);
|
|
288
|
-
return found.provider || new ethers.JsonRpcProvider(getFetchRequest(found.rpcUrl, this.projectAccessKey, this.jwt), network, {
|
|
289
|
-
staticNetwork: network
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
reader(chainId) {
|
|
293
|
-
if (BigInt(chainId) === 0n) {
|
|
294
|
-
return new Chain0Reader();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// TODO: Networks should be able to provide a reader directly
|
|
298
|
-
// and we should default to the on-chain reader
|
|
299
|
-
return new commons.reader.OnChainReader(this.providerFor(chainId));
|
|
300
|
-
}
|
|
301
|
-
relayer(chainId) {
|
|
302
|
-
const found = this.network(chainId);
|
|
303
|
-
if (!found.relayer) throw new Error(`Relayer not found for chainId ${chainId}`);
|
|
304
|
-
if (isRelayer(found.relayer)) return found.relayer;
|
|
305
|
-
return new RpcRelayer(_extends({}, found.relayer, {
|
|
306
|
-
projectAccessKey: this.projectAccessKey,
|
|
307
|
-
jwtAuth: this.jwt
|
|
308
|
-
}));
|
|
309
|
-
}
|
|
310
|
-
setOrchestrator(orchestrator) {
|
|
311
|
-
this.orchestrator = orchestrator;
|
|
312
|
-
}
|
|
313
|
-
setJwt(jwt) {
|
|
314
|
-
this.jwt = jwt;
|
|
315
|
-
}
|
|
316
|
-
contextFor(version) {
|
|
317
|
-
const ctx = this.contexts[version];
|
|
318
|
-
if (!ctx) throw new Error(`Context not found for version ${version}`);
|
|
319
|
-
return ctx;
|
|
320
|
-
}
|
|
321
|
-
walletForStatus(chainId, status) {
|
|
322
|
-
const coder = universal.coderFor(status.version);
|
|
323
|
-
return this.walletFor(chainId, this.contextFor(status.version), status.config, coder);
|
|
324
|
-
}
|
|
325
|
-
walletFor(chainId, context, config, coders) {
|
|
326
|
-
const isNetworkZero = BigInt(chainId) === 0n;
|
|
327
|
-
return new Wallet({
|
|
328
|
-
config,
|
|
329
|
-
context,
|
|
330
|
-
chainId,
|
|
331
|
-
coders,
|
|
332
|
-
relayer: isNetworkZero ? undefined : this.relayer(chainId),
|
|
333
|
-
address: this.address,
|
|
334
|
-
orchestrator: this.orchestrator,
|
|
335
|
-
reader: this.reader(chainId)
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Get the status of the account on a given network
|
|
340
|
-
// this does the following process:
|
|
341
|
-
// 1. Get the current on-chain status of the wallet (version + imageHash)
|
|
342
|
-
// 2. Get any pending migrations that have been signed by the wallet
|
|
343
|
-
// 3. Get any pending configuration updates that have been signed by the wallet
|
|
344
|
-
// 4. Fetch reverse lookups for both on-chain and pending configurations
|
|
345
|
-
async status(chainId, longestPath = false) {
|
|
346
|
-
var _this = this;
|
|
347
|
-
const isDeployedPromise = this.reader(chainId).isDeployed(this.address);
|
|
348
|
-
const counterfactualImageHashPromise = this.tracker.imageHashOfCounterfactualWallet({
|
|
349
|
-
wallet: this.address
|
|
350
|
-
}).then(r => {
|
|
351
|
-
if (!r) throw new Error(`Counterfactual imageHash not found for wallet ${this.address}`);
|
|
352
|
-
return r;
|
|
353
|
-
});
|
|
354
|
-
const counterFactualVersionPromise = counterfactualImageHashPromise.then(r => {
|
|
355
|
-
return version.counterfactualVersion(this.address, r.imageHash, Object.values(this.contexts));
|
|
356
|
-
});
|
|
357
|
-
const onChainVersionPromise = async function () {
|
|
358
|
-
const isDeployed = await isDeployedPromise;
|
|
359
|
-
if (!isDeployed) return counterFactualVersionPromise;
|
|
360
|
-
const implementation = await _this.reader(chainId).implementation(_this.address);
|
|
361
|
-
if (!implementation) throw new Error(`Implementation not found for wallet ${_this.address}`);
|
|
362
|
-
const versions = Object.values(_this.contexts);
|
|
363
|
-
for (let i = 0; i < versions.length; i++) {
|
|
364
|
-
if (versions[i].mainModule === implementation || versions[i].mainModuleUpgradable === implementation) {
|
|
365
|
-
return versions[i].version;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
throw new Error(`Version not found for implementation ${implementation}`);
|
|
369
|
-
}();
|
|
370
|
-
const onChainImageHashPromise = async function () {
|
|
371
|
-
const deployedImageHash = await _this.reader(chainId).imageHash(_this.address);
|
|
372
|
-
if (deployedImageHash) return deployedImageHash;
|
|
373
|
-
const counterfactualImageHash = await counterfactualImageHashPromise;
|
|
374
|
-
if (counterfactualImageHash) return counterfactualImageHash.imageHash;
|
|
375
|
-
throw new Error(`On-chain imageHash not found for wallet ${_this.address}`);
|
|
376
|
-
}();
|
|
377
|
-
const onChainConfigPromise = async function () {
|
|
378
|
-
const onChainImageHash = await onChainImageHashPromise;
|
|
379
|
-
const onChainConfig = await _this.tracker.configOfImageHash({
|
|
380
|
-
imageHash: onChainImageHash
|
|
381
|
-
});
|
|
382
|
-
if (onChainConfig) return onChainConfig;
|
|
383
|
-
throw new Error(`On-chain config not found for imageHash ${onChainImageHash}`);
|
|
384
|
-
}();
|
|
385
|
-
const onChainVersion = await onChainVersionPromise;
|
|
386
|
-
const onChainImageHash = await onChainImageHashPromise;
|
|
387
|
-
let fromImageHash = onChainImageHash;
|
|
388
|
-
let lastVersion = onChainVersion;
|
|
389
|
-
let signedMigrations = [];
|
|
390
|
-
if (onChainVersion !== this.version) {
|
|
391
|
-
// We either need to use the presigned configuration updates, or we haven't performed
|
|
392
|
-
// any updates yet, so we can only use the on-chain imageHash as-is
|
|
393
|
-
const presignedMigrate = await this.migrator.getAllMigratePresignedTransaction({
|
|
394
|
-
address: this.address,
|
|
395
|
-
fromImageHash: onChainImageHash,
|
|
396
|
-
fromVersion: onChainVersion,
|
|
397
|
-
chainId
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
// The migrator returns the original version and imageHash
|
|
401
|
-
// if no presigned migration is found, so no need to check here
|
|
402
|
-
fromImageHash = presignedMigrate.lastImageHash;
|
|
403
|
-
lastVersion = presignedMigrate.lastVersion;
|
|
404
|
-
signedMigrations = presignedMigrate.signedMigrations;
|
|
405
|
-
}
|
|
406
|
-
const presigned = await this.tracker.loadPresignedConfiguration({
|
|
407
|
-
wallet: this.address,
|
|
408
|
-
fromImageHash: fromImageHash,
|
|
409
|
-
longestPath
|
|
410
|
-
});
|
|
411
|
-
const imageHash = presigned && presigned.length > 0 ? presigned[presigned.length - 1].nextImageHash : fromImageHash;
|
|
412
|
-
const config = await this.tracker.configOfImageHash({
|
|
413
|
-
imageHash
|
|
414
|
-
});
|
|
415
|
-
if (!config) {
|
|
416
|
-
throw new Error(`Config not found for imageHash ${imageHash}`);
|
|
417
|
-
}
|
|
418
|
-
const isDeployed = await isDeployedPromise;
|
|
419
|
-
const counterfactualImageHash = await counterfactualImageHashPromise;
|
|
420
|
-
const checkpoint = universal.coderFor(lastVersion).config.checkpointOf(config);
|
|
421
|
-
return {
|
|
422
|
-
original: _extends({}, counterfactualImageHash, {
|
|
423
|
-
version: await counterFactualVersionPromise
|
|
424
|
-
}),
|
|
425
|
-
onChain: {
|
|
426
|
-
imageHash: onChainImageHash,
|
|
427
|
-
config: await onChainConfigPromise,
|
|
428
|
-
version: onChainVersion,
|
|
429
|
-
deployed: isDeployed
|
|
430
|
-
},
|
|
431
|
-
fullyMigrated: lastVersion === this.version,
|
|
432
|
-
signedMigrations,
|
|
433
|
-
version: lastVersion,
|
|
434
|
-
presignedConfigurations: presigned,
|
|
435
|
-
imageHash,
|
|
436
|
-
config,
|
|
437
|
-
checkpoint,
|
|
438
|
-
canOnchainValidate: onChainVersion === this.version && isDeployed
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
mustBeFullyMigrated(status) {
|
|
442
|
-
if (!status.fullyMigrated) {
|
|
443
|
-
throw new Error(`Wallet ${this.address} is not fully migrated`);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
async predecorateSignedTransactions(status, chainId) {
|
|
447
|
-
// Request signed predecorate transactions from child wallets
|
|
448
|
-
const bundles = await this.orchestrator.predecorateSignedTransactions({
|
|
449
|
-
chainId
|
|
450
|
-
});
|
|
451
|
-
// Get signed predecorate transaction
|
|
452
|
-
const predecorated = await this.predecorateTransactions([], status, chainId);
|
|
453
|
-
if (commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
454
|
-
// Sign it
|
|
455
|
-
bundles.push(await this.signTransactions(predecorated, chainId));
|
|
456
|
-
}
|
|
457
|
-
return bundles;
|
|
458
|
-
}
|
|
459
|
-
async predecorateTransactions(txs, status, chainId) {
|
|
460
|
-
txs = Array.isArray(txs) ? txs : [txs];
|
|
461
|
-
// if onchain wallet config is not up to date
|
|
462
|
-
// then we should append an extra transaction that updates it
|
|
463
|
-
// to the latest "lazy" state
|
|
464
|
-
if (status.onChain.imageHash !== status.imageHash) {
|
|
465
|
-
const wallet = this.walletForStatus(chainId, status);
|
|
466
|
-
const updateConfig = await wallet.buildUpdateConfigurationTransaction(status.config);
|
|
467
|
-
txs = [...txs, ...updateConfig.transactions];
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// On immutable chains, we add the WalletProxyHook
|
|
471
|
-
const {
|
|
472
|
-
proxyImplementationHook
|
|
473
|
-
} = this.contexts[status.config.version];
|
|
474
|
-
if (proxyImplementationHook && (chainId === ChainId.IMMUTABLE_ZKEVM || chainId === ChainId.IMMUTABLE_ZKEVM_TESTNET)) {
|
|
475
|
-
const provider = this.providerFor(chainId);
|
|
476
|
-
if (provider) {
|
|
477
|
-
const hook = new ethers.Contract(this.address, walletContracts.walletProxyHook.abi, provider);
|
|
478
|
-
let implementation;
|
|
479
|
-
try {
|
|
480
|
-
implementation = await hook.PROXY_getImplementation();
|
|
481
|
-
} catch (e) {
|
|
482
|
-
// Handle below
|
|
483
|
-
console.log('Error getting implementation address', e);
|
|
484
|
-
}
|
|
485
|
-
if (!implementation || implementation === ethers.ZeroAddress) {
|
|
486
|
-
console.log('Adding wallet proxy hook');
|
|
487
|
-
const hooksInterface = new ethers.Interface(walletContracts.moduleHooks.abi);
|
|
488
|
-
const tx = {
|
|
489
|
-
to: this.address,
|
|
490
|
-
data: hooksInterface.encodeFunctionData(hooksInterface.getFunction('addHook'), ['0x90611127', proxyImplementationHook]),
|
|
491
|
-
gasLimit: 50000,
|
|
492
|
-
// Expected ~28k gas. Buffer added
|
|
493
|
-
delegateCall: false,
|
|
494
|
-
revertOnError: false,
|
|
495
|
-
value: 0
|
|
496
|
-
};
|
|
497
|
-
txs = [tx, ...txs];
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
return txs;
|
|
502
|
-
}
|
|
503
|
-
async decorateTransactions(bundles, status, chainId) {
|
|
504
|
-
var _chainId, _bundles$;
|
|
505
|
-
if (!Array.isArray(bundles)) {
|
|
506
|
-
// Recurse with array
|
|
507
|
-
return this.decorateTransactions([bundles], status, chainId);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Default to chainId of first bundle when not supplied
|
|
511
|
-
chainId = (_chainId = chainId) != null ? _chainId : bundles[0].chainId;
|
|
512
|
-
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
513
|
-
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0;
|
|
514
|
-
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
515
|
-
return bundles[0];
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// Intent defaults to first bundle when no bootstrap transaction
|
|
519
|
-
const {
|
|
520
|
-
entrypoint
|
|
521
|
-
} = hasBootstrapTxs ? bootstrapBundle : bundles[0];
|
|
522
|
-
const decoratedBundle = {
|
|
523
|
-
entrypoint,
|
|
524
|
-
chainId,
|
|
525
|
-
// Intent of the first bundle is used
|
|
526
|
-
intent: (_bundles$ = bundles[0]) == null ? void 0 : _bundles$.intent,
|
|
527
|
-
transactions: [...bootstrapBundle.transactions, ...bundles.map(bundle => ({
|
|
528
|
-
to: bundle.entrypoint,
|
|
529
|
-
data: commons.transaction.encodeBundleExecData(bundle),
|
|
530
|
-
gasLimit: 0,
|
|
531
|
-
delegateCall: false,
|
|
532
|
-
revertOnError: true,
|
|
533
|
-
value: 0
|
|
534
|
-
}))]
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
// Re-compute the meta-transaction id to use the guest module subdigest
|
|
538
|
-
if (!status.onChain.deployed) {
|
|
539
|
-
const id = commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, decoratedBundle.transactions);
|
|
540
|
-
if (decoratedBundle.intent === undefined) {
|
|
541
|
-
decoratedBundle.intent = {
|
|
542
|
-
id,
|
|
543
|
-
wallet: this.address
|
|
544
|
-
};
|
|
545
|
-
} else {
|
|
546
|
-
decoratedBundle.intent.id = id;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
return decoratedBundle;
|
|
550
|
-
}
|
|
551
|
-
async decorateSignature(signature, status) {
|
|
552
|
-
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
553
|
-
return signature;
|
|
554
|
-
}
|
|
555
|
-
const coder = this.coders.signature;
|
|
556
|
-
const chain = status.presignedConfigurations.map(c => c.signature);
|
|
557
|
-
const chainedSignature = coder.chainSignatures(signature, chain);
|
|
558
|
-
return coder.trim(chainedSignature);
|
|
559
|
-
}
|
|
560
|
-
async publishWitnessFor(signers, chainId = 0) {
|
|
561
|
-
const digest = ethers.id(`This is a Sequence account woo! ${Date.now()}`);
|
|
562
|
-
const status = await this.status(chainId);
|
|
563
|
-
const allOfAll = this.coders.config.fromSimple({
|
|
564
|
-
threshold: signers.length,
|
|
565
|
-
checkpoint: 0,
|
|
566
|
-
signers: signers.map(s => ({
|
|
567
|
-
address: s,
|
|
568
|
-
weight: 1
|
|
569
|
-
}))
|
|
570
|
-
});
|
|
571
|
-
const wallet = this.walletFor(chainId, status.original.context, allOfAll, this.coders);
|
|
572
|
-
const signature = await wallet.signDigest(digest);
|
|
573
|
-
const decoded = this.coders.signature.decode(signature);
|
|
574
|
-
const signatures = this.coders.signature.signaturesOfDecoded(decoded);
|
|
575
|
-
if (signatures.length === 0) {
|
|
576
|
-
throw new Error('No signatures found');
|
|
577
|
-
}
|
|
578
|
-
return this.tracker.saveWitnesses({
|
|
579
|
-
wallet: this.address,
|
|
580
|
-
digest,
|
|
581
|
-
chainId,
|
|
582
|
-
signatures
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
async publishWitness() {
|
|
586
|
-
const digest = ethers.id(`This is a Sequence account woo! ${Date.now()}`);
|
|
587
|
-
const signature = await this.signDigest(digest, 0, false);
|
|
588
|
-
const decoded = this.coders.signature.decode(signature);
|
|
589
|
-
const signatures = this.coders.signature.signaturesOfDecoded(decoded);
|
|
590
|
-
return this.tracker.saveWitnesses({
|
|
591
|
-
wallet: this.address,
|
|
592
|
-
digest,
|
|
593
|
-
chainId: 0,
|
|
594
|
-
signatures
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore', metadata) {
|
|
598
|
-
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
599
|
-
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
600
|
-
|
|
601
|
-
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
602
|
-
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
603
|
-
// the behaviour of being migrated on all chains
|
|
604
|
-
const chainRef = BigInt(chainId) === 0n ? this.networks[0].chainId : chainId;
|
|
605
|
-
const status = await this.status(chainRef);
|
|
606
|
-
this.mustBeFullyMigrated(status);
|
|
607
|
-
|
|
608
|
-
// Check if we can validate onchain and what to do if we can't
|
|
609
|
-
// revert early, since there is no point in signing a digest now
|
|
610
|
-
if (!status.canOnchainValidate && cantValidateBehavior === 'throw') {
|
|
611
|
-
throw new Error('Wallet cannot validate onchain');
|
|
612
|
-
}
|
|
613
|
-
const wallet = this.walletForStatus(chainId, status);
|
|
614
|
-
const signature = await wallet.signDigest(digest, metadata);
|
|
615
|
-
const decorated = decorate ? this.decorateSignature(signature, status) : signature;
|
|
616
|
-
|
|
617
|
-
// If the wallet can't validate onchain then we
|
|
618
|
-
// need to prefix the decorated signature with all deployments and migrations
|
|
619
|
-
// aka doing a bootstrap using EIP-6492
|
|
620
|
-
if (!status.canOnchainValidate) {
|
|
621
|
-
switch (cantValidateBehavior) {
|
|
622
|
-
// NOTICE: We covered this case before signing the digest
|
|
623
|
-
// case 'throw':
|
|
624
|
-
// throw new Error('Wallet cannot validate on-chain')
|
|
625
|
-
case 'ignore':
|
|
626
|
-
return decorated;
|
|
627
|
-
case 'eip6492':
|
|
628
|
-
return this.buildEIP6492Signature(await decorated, status, chainId);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
return decorated;
|
|
632
|
-
}
|
|
633
|
-
buildOnChainSignature(digest) {
|
|
634
|
-
const subdigest = commons.signature.subdigestOf({
|
|
635
|
-
digest: ethers.hexlify(digest),
|
|
636
|
-
chainId: 0,
|
|
637
|
-
address: this.address
|
|
638
|
-
});
|
|
639
|
-
const hexSubdigest = ethers.hexlify(subdigest);
|
|
640
|
-
const config = this.coders.config.fromSimple({
|
|
641
|
-
// Threshold *only* needs to be > 0, this is not a magic number
|
|
642
|
-
// we only use 2 ** 15 because it may lead to lower gas costs in some chains
|
|
643
|
-
threshold: 32768,
|
|
644
|
-
checkpoint: 0,
|
|
645
|
-
signers: [],
|
|
646
|
-
subdigests: [hexSubdigest]
|
|
647
|
-
});
|
|
648
|
-
const walletInterface = new ethers.Interface(walletContracts.mainModule.abi);
|
|
649
|
-
const bundle = {
|
|
650
|
-
entrypoint: this.address,
|
|
651
|
-
transactions: [{
|
|
652
|
-
to: this.address,
|
|
653
|
-
data: walletInterface.encodeFunctionData(
|
|
654
|
-
// *NEVER* use updateImageHash here, as it would effectively destroy the wallet
|
|
655
|
-
// setExtraImageHash sets an additional imageHash, without changing the current one
|
|
656
|
-
'setExtraImageHash', [this.coders.config.imageHashOf(config),
|
|
657
|
-
// 2 ** 255 instead of max uint256, to have more zeros in the calldata
|
|
658
|
-
'57896044618658097711785492504343953926634992332820282019728792003956564819968']),
|
|
659
|
-
// Conservative gas limit, used because the current relayer
|
|
660
|
-
// has trouble estimating gas for this transaction
|
|
661
|
-
gasLimit: 250000
|
|
662
|
-
}]
|
|
663
|
-
};
|
|
664
|
-
|
|
665
|
-
// Fire and forget request to save the config
|
|
666
|
-
this.tracker.saveWalletConfig({
|
|
667
|
-
config
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
// Encode a signature proof for the given subdigest
|
|
671
|
-
// use `chainId = 0` to make it simpler, as this signature is only a proof
|
|
672
|
-
const signature = this.coders.signature.encodeSigners(config, new Map(), [hexSubdigest], 0).encoded;
|
|
673
|
-
return {
|
|
674
|
-
bundle,
|
|
675
|
-
signature
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
async buildEIP6492Signature(signature, status, chainId) {
|
|
679
|
-
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
680
|
-
if (bootstrapBundle.transactions.length === 0) {
|
|
681
|
-
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions');
|
|
682
|
-
}
|
|
683
|
-
const encoded = ethers.AbiCoder.defaultAbiCoder().encode(['address', 'bytes', 'bytes'], [bootstrapBundle.entrypoint, commons.transaction.encodeBundleExecData(bootstrapBundle), signature]);
|
|
684
|
-
return ethers.solidityPacked(['bytes', 'bytes32'], [encoded, commons.EIP6492.EIP_6492_SUFFIX]);
|
|
685
|
-
}
|
|
686
|
-
async editConfig(changes) {
|
|
687
|
-
const currentConfig = await this.status(0).then(s => s.config);
|
|
688
|
-
const newConfig = this.coders.config.editConfig(currentConfig, _extends({}, changes, {
|
|
689
|
-
checkpoint: this.coders.config.checkpointOf(currentConfig) + 1n
|
|
690
|
-
}));
|
|
691
|
-
return this.updateConfig(newConfig);
|
|
692
|
-
}
|
|
693
|
-
async updateConfig(config) {
|
|
694
|
-
// config should be for the current version of the wallet
|
|
695
|
-
if (!this.coders.config.isWalletConfig(config)) {
|
|
696
|
-
throw new Error(`Invalid config for wallet ${this.address}`);
|
|
697
|
-
}
|
|
698
|
-
const nextImageHash = this.coders.config.imageHashOf(config);
|
|
699
|
-
|
|
700
|
-
// sign an update config struct
|
|
701
|
-
const updateStruct = this.coders.signature.hashSetImageHash(nextImageHash);
|
|
702
|
-
|
|
703
|
-
// sign the update struct, using chain id 0
|
|
704
|
-
const signature = await this.signDigest(updateStruct, 0, false);
|
|
705
|
-
|
|
706
|
-
// save the presigned transaction to the sessions tracker
|
|
707
|
-
await this.tracker.savePresignedConfiguration({
|
|
708
|
-
wallet: this.address,
|
|
709
|
-
nextConfig: config,
|
|
710
|
-
signature,
|
|
711
|
-
referenceChainId: 1
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
// safety check, tracker should have a reverse lookup for the imageHash
|
|
715
|
-
// outside of the local cache
|
|
716
|
-
const reverseConfig = await this.tracker.configOfImageHash({
|
|
717
|
-
imageHash: nextImageHash,
|
|
718
|
-
noCache: true
|
|
719
|
-
});
|
|
720
|
-
if (!reverseConfig || this.coders.config.imageHashOf(reverseConfig) !== nextImageHash) {
|
|
721
|
-
throw Error(`Reverse lookup failed for imageHash ${nextImageHash}`);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
/**
|
|
726
|
-
* This method is used to bootstrap the wallet on a given chain.
|
|
727
|
-
* this deploys the wallets and executes all the necessary transactions
|
|
728
|
-
* for that wallet to start working with the given version.
|
|
729
|
-
*
|
|
730
|
-
* This usually involves: (a) deploying the wallet, (b) executing migrations
|
|
731
|
-
*
|
|
732
|
-
* Notice: It should NOT explicitly include chained signatures. Unless internally used
|
|
733
|
-
* by any of the migrations.
|
|
734
|
-
*
|
|
735
|
-
*/
|
|
736
|
-
async buildBootstrapTransactions(status, chainId) {
|
|
737
|
-
var _bundle$transactions;
|
|
738
|
-
const bundle = await this.orchestrator.buildDeployTransaction({
|
|
739
|
-
chainId
|
|
740
|
-
});
|
|
741
|
-
const transactions = (_bundle$transactions = bundle == null ? void 0 : bundle.transactions) != null ? _bundle$transactions : [];
|
|
742
|
-
|
|
743
|
-
// Add wallet deployment if needed
|
|
744
|
-
if (!status.onChain.deployed) {
|
|
745
|
-
let gasLimit;
|
|
746
|
-
switch (BigInt(chainId)) {
|
|
747
|
-
case BigInt(ChainId.SKALE_NEBULA):
|
|
748
|
-
gasLimit = 10000000n;
|
|
749
|
-
break;
|
|
750
|
-
case BigInt(ChainId.SOMNIA_TESTNET):
|
|
751
|
-
gasLimit = 10000000n;
|
|
752
|
-
break;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
// Wallet deployment will vary depending on the version
|
|
756
|
-
// so we need to use the context to get the correct deployment
|
|
757
|
-
const deployTransaction = Wallet.buildDeployTransaction(status.original.context, status.original.imageHash, gasLimit);
|
|
758
|
-
transactions.push(...deployTransaction.transactions);
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
// Get pending migrations
|
|
762
|
-
transactions.push(...status.signedMigrations.map(m => ({
|
|
763
|
-
to: m.tx.entrypoint,
|
|
764
|
-
data: commons.transaction.encodeBundleExecData(m.tx),
|
|
765
|
-
value: 0,
|
|
766
|
-
gasLimit: 0,
|
|
767
|
-
revertOnError: true,
|
|
768
|
-
delegateCall: false
|
|
769
|
-
})));
|
|
770
|
-
|
|
771
|
-
// Build the transaction intent, if the transaction has migrations
|
|
772
|
-
// then we should use one of the intents of the migrations (anyone will do)
|
|
773
|
-
// if it doesn't, then the only intent we could use if the GuestModule one
|
|
774
|
-
// ... but this may fail if the relayer uses a different GuestModule
|
|
775
|
-
const id = status.signedMigrations.length > 0 ? status.signedMigrations[0].tx.intent.id : commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, transactions);
|
|
776
|
-
|
|
777
|
-
// Everything is encoded as a bundle
|
|
778
|
-
// using the GuestModule of the account version
|
|
779
|
-
const {
|
|
780
|
-
guestModule
|
|
781
|
-
} = this.contextFor(status.version);
|
|
782
|
-
return {
|
|
783
|
-
entrypoint: guestModule,
|
|
784
|
-
transactions,
|
|
785
|
-
chainId,
|
|
786
|
-
intent: {
|
|
787
|
-
id,
|
|
788
|
-
wallet: this.address
|
|
789
|
-
}
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
async bootstrapTransactions(chainId, prestatus) {
|
|
793
|
-
const status = prestatus || (await this.status(chainId));
|
|
794
|
-
return this.buildBootstrapTransactions(status, chainId);
|
|
795
|
-
}
|
|
796
|
-
async doBootstrap(chainId, feeQuote, prestatus) {
|
|
797
|
-
const bootstrapTxs = await this.bootstrapTransactions(chainId, prestatus);
|
|
798
|
-
return this.relayer(chainId).relay(_extends({}, bootstrapTxs, {
|
|
799
|
-
chainId
|
|
800
|
-
}), feeQuote);
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
/**
|
|
804
|
-
* Signs a message.
|
|
805
|
-
*
|
|
806
|
-
* This method will sign the message using the account associated with this signer
|
|
807
|
-
* and the specified chain ID. If the message is already prefixed with the EIP-191
|
|
808
|
-
* prefix, it will be hashed directly. Otherwise, it will be prefixed before hashing.
|
|
809
|
-
*
|
|
810
|
-
* @param message - The message to sign. Can be a string or BytesLike.
|
|
811
|
-
* @param chainId - The chain ID to use for signing
|
|
812
|
-
* @param cantValidateBehavior - Behavior when the wallet cannot validate on-chain
|
|
813
|
-
* @returns A Promise that resolves to the signature as a hexadecimal string
|
|
814
|
-
*/
|
|
815
|
-
signMessage(message, chainId, cantValidateBehavior = 'ignore') {
|
|
816
|
-
const messageHex = ethers.hexlify(message);
|
|
817
|
-
const prefixHex = ethers.hexlify(ethers.toUtf8Bytes(MessagePrefix));
|
|
818
|
-
let digest;
|
|
819
|
-
|
|
820
|
-
// We check if the message is already prefixed with EIP-191
|
|
821
|
-
// This will avoid breaking changes for codebases where the message is already prefixed
|
|
822
|
-
if (messageHex.substring(2).startsWith(prefixHex.substring(2))) {
|
|
823
|
-
digest = ethers.keccak256(message);
|
|
824
|
-
} else {
|
|
825
|
-
digest = ethers.hashMessage(message);
|
|
826
|
-
}
|
|
827
|
-
return this.signDigest(digest, chainId, true, cantValidateBehavior);
|
|
828
|
-
}
|
|
829
|
-
async signTransactions(txs, chainId, pstatus, options) {
|
|
830
|
-
const status = pstatus || (await this.status(chainId));
|
|
831
|
-
this.mustBeFullyMigrated(status);
|
|
832
|
-
const wallet = this.walletForStatus(chainId, status);
|
|
833
|
-
const metadata = {
|
|
834
|
-
address: this.address,
|
|
835
|
-
digest: '',
|
|
836
|
-
// Set in wallet.signTransactions
|
|
837
|
-
chainId,
|
|
838
|
-
config: {
|
|
839
|
-
version: this.version
|
|
840
|
-
},
|
|
841
|
-
decorate: true,
|
|
842
|
-
cantValidateBehavior: 'ignore'
|
|
843
|
-
};
|
|
844
|
-
const nonceOptions = options != null && options.serial ? {
|
|
845
|
-
serial: true
|
|
846
|
-
} : (options == null ? void 0 : options.nonceSpace) !== undefined ? {
|
|
847
|
-
space: options.nonceSpace
|
|
848
|
-
} : undefined;
|
|
849
|
-
const signed = await wallet.signTransactions(txs, nonceOptions, metadata);
|
|
850
|
-
return _extends({}, signed, {
|
|
851
|
-
signature: await this.decorateSignature(signed.signature, status)
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
async signMigrations(chainId, editConfig) {
|
|
855
|
-
const status = await this.status(chainId);
|
|
856
|
-
if (status.fullyMigrated) return false;
|
|
857
|
-
const wallet = this.walletForStatus(chainId, status);
|
|
858
|
-
const nextConfig = editConfig(wallet.config);
|
|
859
|
-
const signed = await this.migrator.signNextMigration(this.address, status.version, wallet, nextConfig);
|
|
860
|
-
if (!signed) return false;
|
|
861
|
-
|
|
862
|
-
// Make sure the tracker has a copy of the config
|
|
863
|
-
// before attempting to save the migration
|
|
864
|
-
// otherwise if this second step fails the tracker could end up
|
|
865
|
-
// with a migration to an unknown config
|
|
866
|
-
await this.tracker.saveWalletConfig({
|
|
867
|
-
config: nextConfig
|
|
868
|
-
});
|
|
869
|
-
const nextCoder = universal.coderFor(nextConfig.version).config;
|
|
870
|
-
const nextImageHash = nextCoder.imageHashOf(nextConfig);
|
|
871
|
-
const reverseConfig = await this.tracker.configOfImageHash({
|
|
872
|
-
imageHash: nextImageHash,
|
|
873
|
-
noCache: true
|
|
874
|
-
});
|
|
875
|
-
if (!reverseConfig || nextCoder.imageHashOf(reverseConfig) !== nextImageHash) {
|
|
876
|
-
throw Error(`Reverse lookup failed for imageHash ${nextImageHash}`);
|
|
877
|
-
}
|
|
878
|
-
await this.tracker.saveMigration(this.address, signed, this.contexts);
|
|
879
|
-
return true;
|
|
880
|
-
}
|
|
881
|
-
async signAllMigrations(editConfig) {
|
|
882
|
-
var _this2 = this;
|
|
883
|
-
const failedChains = [];
|
|
884
|
-
const signedMigrations = await Promise.all(this.networks.map(async function (n) {
|
|
885
|
-
try {
|
|
886
|
-
// Signing migrations for each chain
|
|
887
|
-
return await _this2.signMigrations(n.chainId, editConfig);
|
|
888
|
-
} catch (error) {
|
|
889
|
-
console.warn(`Failed to sign migrations for chain ${n.chainId}`, error);
|
|
890
|
-
|
|
891
|
-
// Adding failed chainId to the failedChains array
|
|
892
|
-
failedChains.push(n.chainId);
|
|
893
|
-
// Using null as a placeholder for failed chains
|
|
894
|
-
return null;
|
|
895
|
-
}
|
|
896
|
-
}));
|
|
897
|
-
|
|
898
|
-
// Filter out null values to get only the successful signed migrations
|
|
899
|
-
const successfulSignedMigrations = signedMigrations.filter(migration => migration !== null);
|
|
900
|
-
return {
|
|
901
|
-
signedMigrations: successfulSignedMigrations,
|
|
902
|
-
failedChains
|
|
903
|
-
};
|
|
904
|
-
}
|
|
905
|
-
async isMigratedAllChains() {
|
|
906
|
-
var _this3 = this;
|
|
907
|
-
const failedChains = [];
|
|
908
|
-
const statuses = await Promise.all(this.networks.map(async function (n) {
|
|
909
|
-
try {
|
|
910
|
-
return await _this3.status(n.chainId);
|
|
911
|
-
} catch (error) {
|
|
912
|
-
failedChains.push(n.chainId);
|
|
913
|
-
console.warn(`Failed to get status for chain ${n.chainId}`, error);
|
|
914
|
-
|
|
915
|
-
// default to true for failed chains
|
|
916
|
-
return {
|
|
917
|
-
fullyMigrated: true
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
}));
|
|
921
|
-
const migratedAllChains = statuses.every(s => s.fullyMigrated);
|
|
922
|
-
return {
|
|
923
|
-
migratedAllChains,
|
|
924
|
-
failedChains
|
|
925
|
-
};
|
|
926
|
-
}
|
|
927
|
-
async sendSignedTransactions(signedBundle, chainId, quote, pstatus, callback, projectAccessKey) {
|
|
928
|
-
if (!Array.isArray(signedBundle)) {
|
|
929
|
-
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback, projectAccessKey);
|
|
930
|
-
}
|
|
931
|
-
const status = pstatus || (await this.status(chainId));
|
|
932
|
-
this.mustBeFullyMigrated(status);
|
|
933
|
-
const decoratedBundle = await this.decorateTransactions(signedBundle, status, chainId);
|
|
934
|
-
callback == null || callback(decoratedBundle);
|
|
935
|
-
return this.relayer(chainId).relay(decoratedBundle, quote, undefined, projectAccessKey);
|
|
936
|
-
}
|
|
937
|
-
async fillGasLimits(txs, chainId, status) {
|
|
938
|
-
const wallet = this.walletForStatus(chainId, status || (await this.status(chainId)));
|
|
939
|
-
return wallet.fillGasLimits(txs);
|
|
940
|
-
}
|
|
941
|
-
async gasRefundQuotes(txs, chainId, stubSignatureOverrides, status, options) {
|
|
942
|
-
const wstatus = status || (await this.status(chainId));
|
|
943
|
-
const wallet = this.walletForStatus(chainId, wstatus);
|
|
944
|
-
const predecorated = await this.predecorateTransactions(txs, wstatus, chainId);
|
|
945
|
-
const transactions = commons.transaction.fromTransactionish(this.address, predecorated);
|
|
946
|
-
|
|
947
|
-
// We can't sign the transactions (because we don't want to bother the user)
|
|
948
|
-
// so we use the latest configuration to build a "stub" signature, the relayer
|
|
949
|
-
// knows to ignore the wallet signatures
|
|
950
|
-
const stubSignature = wallet.coders.config.buildStubSignature(wallet.config, stubSignatureOverrides);
|
|
951
|
-
|
|
952
|
-
// Now we can decorate the transactions as always, but we need to manually build the signed bundle
|
|
953
|
-
const intentId = ethers.hexlify(ethers.randomBytes(32));
|
|
954
|
-
const signedBundle = {
|
|
955
|
-
chainId,
|
|
956
|
-
intent: {
|
|
957
|
-
id: intentId,
|
|
958
|
-
wallet: this.address
|
|
959
|
-
},
|
|
960
|
-
signature: stubSignature,
|
|
961
|
-
transactions,
|
|
962
|
-
entrypoint: this.address,
|
|
963
|
-
nonce: 0 // The relayer also ignored the nonce
|
|
964
|
-
};
|
|
965
|
-
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus);
|
|
966
|
-
const data = commons.transaction.encodeBundleExecData(decoratedBundle);
|
|
967
|
-
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options);
|
|
968
|
-
return _extends({}, res, {
|
|
969
|
-
decorated: decoratedBundle
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
async prepareTransactions(args) {
|
|
973
|
-
const status = await this.status(args.chainId);
|
|
974
|
-
const transactions = await this.fillGasLimits(args.txs, args.chainId, status);
|
|
975
|
-
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status, {
|
|
976
|
-
simulate: args.simulateForFeeOptions,
|
|
977
|
-
projectAccessKey: args.projectAccessKey
|
|
978
|
-
});
|
|
979
|
-
const flatDecorated = commons.transaction.unwind(this.address, gasRefundQuote.decorated.transactions);
|
|
980
|
-
return {
|
|
981
|
-
transactions,
|
|
982
|
-
flatDecorated,
|
|
983
|
-
feeOptions: gasRefundQuote.options,
|
|
984
|
-
feeQuote: gasRefundQuote.quote
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
|
|
988
|
-
const status = await this.status(chainId);
|
|
989
|
-
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
|
|
990
|
-
const hasTxs = commons.transaction.fromTransactionish(this.address, predecorated).length > 0;
|
|
991
|
-
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined;
|
|
992
|
-
const childBundles = await this.orchestrator.predecorateSignedTransactions({
|
|
993
|
-
chainId
|
|
994
|
-
});
|
|
995
|
-
const bundles = [];
|
|
996
|
-
if (signed !== undefined && signed.transactions.length > 0) {
|
|
997
|
-
bundles.push(signed);
|
|
998
|
-
}
|
|
999
|
-
bundles.push(...childBundles.filter(b => b.transactions.length > 0));
|
|
1000
|
-
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback, options == null ? void 0 : options.projectAccessKey);
|
|
1001
|
-
}
|
|
1002
|
-
async signTypedData(domain, types, message, chainId, cantValidateBehavior = 'ignore') {
|
|
1003
|
-
const digest = encodeTypedDataDigest({
|
|
1004
|
-
domain,
|
|
1005
|
-
types,
|
|
1006
|
-
message
|
|
1007
|
-
});
|
|
1008
|
-
return this.signDigest(digest, chainId, true, cantValidateBehavior);
|
|
1009
|
-
}
|
|
1010
|
-
async getSigners() {
|
|
1011
|
-
var _this4 = this;
|
|
1012
|
-
const last = ts => ts.length ? ts[ts.length - 1] : undefined;
|
|
1013
|
-
return (await Promise.all(this.networks.map(async function ({
|
|
1014
|
-
chainId,
|
|
1015
|
-
name
|
|
1016
|
-
}) {
|
|
1017
|
-
try {
|
|
1018
|
-
var _last;
|
|
1019
|
-
const status = await _this4.status(chainId);
|
|
1020
|
-
let latestImageHash = (_last = last(status.presignedConfigurations)) == null ? void 0 : _last.nextImageHash;
|
|
1021
|
-
if (!latestImageHash) {
|
|
1022
|
-
if (status.onChain.version !== status.version) {
|
|
1023
|
-
const migration = last(status.signedMigrations);
|
|
1024
|
-
if (migration) {
|
|
1025
|
-
const {
|
|
1026
|
-
toVersion,
|
|
1027
|
-
toConfig
|
|
1028
|
-
} = migration;
|
|
1029
|
-
const _coder = universal.genericCoderFor(toVersion);
|
|
1030
|
-
latestImageHash = _coder.config.imageHashOf(toConfig);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
if (!latestImageHash) {
|
|
1035
|
-
latestImageHash = status.onChain.imageHash;
|
|
1036
|
-
}
|
|
1037
|
-
const latestConfig = await _this4.tracker.configOfImageHash({
|
|
1038
|
-
imageHash: latestImageHash
|
|
1039
|
-
});
|
|
1040
|
-
if (!latestConfig) {
|
|
1041
|
-
throw new Error(`unable to find config for image hash ${latestImageHash}`);
|
|
1042
|
-
}
|
|
1043
|
-
const coder = universal.genericCoderFor(latestConfig.version);
|
|
1044
|
-
const signers = coder.config.signersOf(latestConfig);
|
|
1045
|
-
return signers.map(signer => _extends({}, signer, {
|
|
1046
|
-
network: chainId
|
|
1047
|
-
}));
|
|
1048
|
-
} catch (error) {
|
|
1049
|
-
console.warn(`unable to get signers on network ${chainId} ${name}`, error);
|
|
1050
|
-
return [];
|
|
1051
|
-
}
|
|
1052
|
-
}))).flat();
|
|
1053
|
-
}
|
|
1054
|
-
async getAllSigners() {
|
|
1055
|
-
var _this5 = this;
|
|
1056
|
-
const allSigners = [];
|
|
1057
|
-
|
|
1058
|
-
// We need to get the signers for each status
|
|
1059
|
-
await Promise.all(this.networks.map(async function (network) {
|
|
1060
|
-
const chainId = network.chainId;
|
|
1061
|
-
|
|
1062
|
-
// Getting the status with `longestPath` set to true will give us all the possible configurations
|
|
1063
|
-
// between the current onChain config and the latest config, including the ones "flagged for removal"
|
|
1064
|
-
const status = await _this5.status(chainId, true);
|
|
1065
|
-
const fullChain = [status.onChain.imageHash, ...(status.onChain.version !== status.version ? status.signedMigrations.map(m => universal.coderFor(m.toVersion).config.imageHashOf(m.toConfig)) : []), ...status.presignedConfigurations.map(update => update.nextImageHash)];
|
|
1066
|
-
return Promise.all(fullChain.map(async function (nextImageHash, iconf) {
|
|
1067
|
-
const isLast = iconf === fullChain.length - 1;
|
|
1068
|
-
const config = await _this5.tracker.configOfImageHash({
|
|
1069
|
-
imageHash: nextImageHash
|
|
1070
|
-
});
|
|
1071
|
-
if (!config) {
|
|
1072
|
-
console.warn(`AllSigners may be incomplete, config not found for imageHash ${nextImageHash}`);
|
|
1073
|
-
return;
|
|
1074
|
-
}
|
|
1075
|
-
const coder = universal.genericCoderFor(config.version);
|
|
1076
|
-
const signers = coder.config.signersOf(config);
|
|
1077
|
-
signers.forEach(signer => {
|
|
1078
|
-
const exists = allSigners.find(s => s.address === signer.address && s.network === chainId);
|
|
1079
|
-
if (exists && isLast && exists.flaggedForRemoval) {
|
|
1080
|
-
exists.flaggedForRemoval = false;
|
|
1081
|
-
return;
|
|
1082
|
-
}
|
|
1083
|
-
if (exists) return;
|
|
1084
|
-
allSigners.push({
|
|
1085
|
-
address: signer.address,
|
|
1086
|
-
weight: signer.weight,
|
|
1087
|
-
network: chainId,
|
|
1088
|
-
flaggedForRemoval: !isLast
|
|
1089
|
-
});
|
|
1090
|
-
});
|
|
1091
|
-
}));
|
|
1092
|
-
}));
|
|
1093
|
-
return allSigners;
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
function isAccount(value) {
|
|
1097
|
-
return value instanceof Account;
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
export { Account, isAccount };
|