@0xsequence/account 1.2.9 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/0xsequence-account.cjs.dev.js +94 -29
- package/dist/0xsequence-account.cjs.prod.js +94 -29
- package/dist/0xsequence-account.esm.js +95 -30
- package/dist/declarations/src/account.d.ts +9 -5
- package/package.json +10 -10
- package/src/account.ts +115 -41
- package/src/orchestrator/wrapper.ts +85 -0
- package/src/signer.ts +1 -1
|
@@ -140,8 +140,9 @@ class AccountSigner {
|
|
|
140
140
|
const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
|
|
141
141
|
return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
|
|
142
142
|
nonceSpace: this.options.nonceSpace
|
|
143
|
-
} : undefined);
|
|
143
|
+
} : undefined); // Will always have a transaction response
|
|
144
144
|
}
|
|
145
|
+
|
|
145
146
|
getBalance(blockTag) {
|
|
146
147
|
return this.provider.getBalance(this.account.address, blockTag);
|
|
147
148
|
}
|
|
@@ -209,6 +210,7 @@ class Account {
|
|
|
209
210
|
this.tracker = options.tracker;
|
|
210
211
|
this.networks = options.networks;
|
|
211
212
|
this.orchestrator = options.orchestrator;
|
|
213
|
+
this.jwt = options.jwt;
|
|
212
214
|
this.migrations = options.migrations || migration.defaults.DefaultMigrations;
|
|
213
215
|
this.migrator = new migration.migrator.Migrator(options.tracker, this.migrations, this.contexts);
|
|
214
216
|
}
|
|
@@ -259,7 +261,7 @@ class Account {
|
|
|
259
261
|
providerFor(chainId) {
|
|
260
262
|
const found = this.network(chainId);
|
|
261
263
|
if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`);
|
|
262
|
-
return found.provider || new ethers.ethers.providers.StaticJsonRpcProvider(utils.
|
|
264
|
+
return found.provider || new ethers.ethers.providers.StaticJsonRpcProvider(utils.getEthersConnectionInfo(found.rpcUrl, undefined, this.jwt), {
|
|
263
265
|
name: '',
|
|
264
266
|
chainId: ethers.ethers.BigNumber.from(chainId).toNumber()
|
|
265
267
|
});
|
|
@@ -275,11 +277,16 @@ class Account {
|
|
|
275
277
|
const found = this.network(chainId);
|
|
276
278
|
if (!found.relayer) throw new Error(`Relayer not found for chainId ${chainId}`);
|
|
277
279
|
if (relayer.isRelayer(found.relayer)) return found.relayer;
|
|
278
|
-
return new relayer.RpcRelayer(found.relayer
|
|
280
|
+
return new relayer.RpcRelayer(_extends({}, found.relayer, {
|
|
281
|
+
jwtAuth: this.jwt
|
|
282
|
+
}));
|
|
279
283
|
}
|
|
280
284
|
setOrchestrator(orchestrator) {
|
|
281
285
|
this.orchestrator = orchestrator;
|
|
282
286
|
}
|
|
287
|
+
setJwt(jwt) {
|
|
288
|
+
this.jwt = jwt;
|
|
289
|
+
}
|
|
283
290
|
contextFor(version) {
|
|
284
291
|
const ctx = this.contexts[version];
|
|
285
292
|
if (!ctx) throw new Error(`Context not found for version ${version}`);
|
|
@@ -410,6 +417,19 @@ class Account {
|
|
|
410
417
|
throw new Error(`Wallet ${this.address} is not fully migrated`);
|
|
411
418
|
}
|
|
412
419
|
}
|
|
420
|
+
async predecorateSignedTransactions(status, chainId) {
|
|
421
|
+
// Request signed predecorate transactions from child wallets
|
|
422
|
+
const bundles = await this.orchestrator.predecorateSignedTransactions({
|
|
423
|
+
chainId
|
|
424
|
+
});
|
|
425
|
+
// Get signed predecorate transaction
|
|
426
|
+
const predecorated = await this.predecorateTransactions([], status, chainId);
|
|
427
|
+
if (core.commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
428
|
+
// Sign it
|
|
429
|
+
bundles.push(await this.signTransactions(predecorated, chainId));
|
|
430
|
+
}
|
|
431
|
+
return bundles;
|
|
432
|
+
}
|
|
413
433
|
async predecorateTransactions(txs, status, chainId) {
|
|
414
434
|
// if onchain wallet config is not up to date
|
|
415
435
|
// then we should append an extra transaction that updates it
|
|
@@ -421,34 +441,49 @@ class Account {
|
|
|
421
441
|
}
|
|
422
442
|
return txs;
|
|
423
443
|
}
|
|
424
|
-
decorateTransactions(
|
|
425
|
-
|
|
426
|
-
if (
|
|
427
|
-
|
|
444
|
+
async decorateTransactions(bundles, status, chainId) {
|
|
445
|
+
var _chainId;
|
|
446
|
+
if (!Array.isArray(bundles)) {
|
|
447
|
+
// Recurse with array
|
|
448
|
+
return this.decorateTransactions([bundles], status, chainId);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Default to chainId of first bundle when not supplied
|
|
452
|
+
chainId = (_chainId = chainId) != null ? _chainId : bundles[0].chainId;
|
|
453
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
454
|
+
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0;
|
|
455
|
+
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
456
|
+
return bundles[0];
|
|
428
457
|
}
|
|
458
|
+
|
|
459
|
+
// Intent defaults to first bundle when no bootstrap transaction
|
|
460
|
+
const {
|
|
461
|
+
entrypoint
|
|
462
|
+
} = hasBootstrapTxs ? bootstrapBundle : bundles[0];
|
|
429
463
|
const decoratedBundle = {
|
|
430
|
-
entrypoint
|
|
431
|
-
chainId
|
|
432
|
-
|
|
433
|
-
|
|
464
|
+
entrypoint,
|
|
465
|
+
chainId,
|
|
466
|
+
// Intent of the first bundle is used
|
|
467
|
+
intent: bundles[0].intent,
|
|
468
|
+
transactions: [...bootstrapBundle.transactions, ...bundles.map(bundle => ({
|
|
434
469
|
to: bundle.entrypoint,
|
|
435
470
|
data: core.commons.transaction.encodeBundleExecData(bundle),
|
|
436
471
|
gasLimit: 0,
|
|
437
472
|
delegateCall: false,
|
|
438
473
|
revertOnError: true,
|
|
439
474
|
value: 0
|
|
440
|
-
}]
|
|
475
|
+
}))]
|
|
441
476
|
};
|
|
442
477
|
|
|
443
478
|
// Re-compute the meta-transaction id to use the guest module subdigest
|
|
444
479
|
if (!status.onChain.deployed) {
|
|
445
|
-
decoratedBundle.intent.id = core.commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule,
|
|
480
|
+
decoratedBundle.intent.id = core.commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, decoratedBundle.transactions);
|
|
446
481
|
}
|
|
447
482
|
return decoratedBundle;
|
|
448
483
|
}
|
|
449
|
-
decorateSignature(signature, status) {
|
|
484
|
+
async decorateSignature(signature, status) {
|
|
450
485
|
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
451
|
-
return
|
|
486
|
+
return signature;
|
|
452
487
|
}
|
|
453
488
|
const coder = this.coders.signature;
|
|
454
489
|
const chain = status.presignedConfigurations.map(c => c.signature);
|
|
@@ -467,14 +502,13 @@ class Account {
|
|
|
467
502
|
signatures
|
|
468
503
|
});
|
|
469
504
|
}
|
|
470
|
-
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore') {
|
|
505
|
+
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore', metadata) {
|
|
471
506
|
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
472
507
|
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
473
508
|
|
|
474
509
|
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
475
510
|
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
476
511
|
// the behaviour of being migrated on all chains
|
|
477
|
-
|
|
478
512
|
const chainRef = ethers.ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId;
|
|
479
513
|
const status = await this.status(chainRef);
|
|
480
514
|
this.mustBeFullyMigrated(status);
|
|
@@ -485,7 +519,7 @@ class Account {
|
|
|
485
519
|
throw new Error('Wallet cannot validate onchain');
|
|
486
520
|
}
|
|
487
521
|
const wallet = this.walletForStatus(chainId, status);
|
|
488
|
-
const signature = await wallet.signDigest(digest);
|
|
522
|
+
const signature = await wallet.signDigest(digest, metadata);
|
|
489
523
|
const decorated = decorate ? this.decorateSignature(signature, status) : signature;
|
|
490
524
|
|
|
491
525
|
// If the wallet can't validate onchain then we
|
|
@@ -504,8 +538,8 @@ class Account {
|
|
|
504
538
|
}
|
|
505
539
|
return decorated;
|
|
506
540
|
}
|
|
507
|
-
buildEIP6492Signature(signature, status, chainId) {
|
|
508
|
-
const bootstrapBundle = this.buildBootstrapTransactions(status, chainId);
|
|
541
|
+
async buildEIP6492Signature(signature, status, chainId) {
|
|
542
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
509
543
|
if (bootstrapBundle.transactions.length === 0) {
|
|
510
544
|
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions');
|
|
511
545
|
}
|
|
@@ -561,8 +595,12 @@ class Account {
|
|
|
561
595
|
* by any of the migrations.
|
|
562
596
|
*
|
|
563
597
|
*/
|
|
564
|
-
buildBootstrapTransactions(status, chainId) {
|
|
565
|
-
|
|
598
|
+
async buildBootstrapTransactions(status, chainId) {
|
|
599
|
+
var _bundle$transactions;
|
|
600
|
+
const bundle = await this.orchestrator.buildDeployTransaction({
|
|
601
|
+
chainId
|
|
602
|
+
});
|
|
603
|
+
const transactions = (_bundle$transactions = bundle == null ? void 0 : bundle.transactions) != null ? _bundle$transactions : [];
|
|
566
604
|
|
|
567
605
|
// Add wallet deployment if needed
|
|
568
606
|
if (!status.onChain.deployed) {
|
|
@@ -571,6 +609,7 @@ class Account {
|
|
|
571
609
|
const deployTransaction = wallet.Wallet.buildDeployTransaction(status.original.context, status.original.imageHash);
|
|
572
610
|
transactions.push(...deployTransaction.transactions);
|
|
573
611
|
}
|
|
612
|
+
transactions.length;
|
|
574
613
|
|
|
575
614
|
// Get pending migrations
|
|
576
615
|
transactions.push(...status.signedMigrations.map(m => ({
|
|
@@ -620,9 +659,20 @@ class Account {
|
|
|
620
659
|
const status = pstatus || (await this.status(chainId));
|
|
621
660
|
this.mustBeFullyMigrated(status);
|
|
622
661
|
const wallet = this.walletForStatus(chainId, status);
|
|
662
|
+
const metadata = {
|
|
663
|
+
address: this.address,
|
|
664
|
+
digest: '',
|
|
665
|
+
// Set in wallet.signTransactions
|
|
666
|
+
chainId,
|
|
667
|
+
config: {
|
|
668
|
+
version: this.version
|
|
669
|
+
},
|
|
670
|
+
decorate: true,
|
|
671
|
+
cantValidateBehavior: 'ignore'
|
|
672
|
+
};
|
|
623
673
|
const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
|
|
624
674
|
space: options == null ? void 0 : options.nonceSpace
|
|
625
|
-
});
|
|
675
|
+
}, metadata);
|
|
626
676
|
return _extends({}, signed, {
|
|
627
677
|
signature: await this.decorateSignature(signed.signature, status)
|
|
628
678
|
});
|
|
@@ -701,9 +751,12 @@ class Account {
|
|
|
701
751
|
};
|
|
702
752
|
}
|
|
703
753
|
async sendSignedTransactions(signedBundle, chainId, quote, pstatus, callback) {
|
|
704
|
-
|
|
754
|
+
if (!Array.isArray(signedBundle)) {
|
|
755
|
+
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback);
|
|
756
|
+
}
|
|
757
|
+
const status = pstatus || (await this.status(signedBundle[0].chainId));
|
|
705
758
|
this.mustBeFullyMigrated(status);
|
|
706
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, status);
|
|
759
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, status);
|
|
707
760
|
callback == null ? void 0 : callback(decoratedBundle);
|
|
708
761
|
return this.relayer(chainId).relay(decoratedBundle, quote);
|
|
709
762
|
}
|
|
@@ -736,7 +789,7 @@ class Account {
|
|
|
736
789
|
nonce: 0 // The relayer also ignored the nonce
|
|
737
790
|
};
|
|
738
791
|
|
|
739
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, wstatus);
|
|
792
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus);
|
|
740
793
|
const data = core.commons.transaction.encodeBundleExecData(decoratedBundle);
|
|
741
794
|
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options);
|
|
742
795
|
return _extends({}, res, {
|
|
@@ -760,9 +813,21 @@ class Account {
|
|
|
760
813
|
async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
|
|
761
814
|
const status = await this.status(chainId);
|
|
762
815
|
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
816
|
+
const hasTxs = core.commons.transaction.fromTransactionish(this.address, predecorated).length > 0;
|
|
817
|
+
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined;
|
|
818
|
+
const childBundles = await this.orchestrator.predecorateSignedTransactions({
|
|
819
|
+
chainId
|
|
820
|
+
});
|
|
821
|
+
const bundles = [];
|
|
822
|
+
if (signed !== undefined && signed.transactions.length > 0) {
|
|
823
|
+
bundles.push(signed);
|
|
824
|
+
}
|
|
825
|
+
bundles.push(...childBundles.filter(b => b.transactions.length > 0));
|
|
826
|
+
if (bundles.length === 0) {
|
|
827
|
+
// Nothing to send?
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback);
|
|
766
831
|
}
|
|
767
832
|
async signTypedData(domain, types, message, chainId, cantValidateBehavior = 'ignore') {
|
|
768
833
|
const digest = utils.encodeTypedDataDigest({
|
|
@@ -140,8 +140,9 @@ class AccountSigner {
|
|
|
140
140
|
const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
|
|
141
141
|
return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
|
|
142
142
|
nonceSpace: this.options.nonceSpace
|
|
143
|
-
} : undefined);
|
|
143
|
+
} : undefined); // Will always have a transaction response
|
|
144
144
|
}
|
|
145
|
+
|
|
145
146
|
getBalance(blockTag) {
|
|
146
147
|
return this.provider.getBalance(this.account.address, blockTag);
|
|
147
148
|
}
|
|
@@ -209,6 +210,7 @@ class Account {
|
|
|
209
210
|
this.tracker = options.tracker;
|
|
210
211
|
this.networks = options.networks;
|
|
211
212
|
this.orchestrator = options.orchestrator;
|
|
213
|
+
this.jwt = options.jwt;
|
|
212
214
|
this.migrations = options.migrations || migration.defaults.DefaultMigrations;
|
|
213
215
|
this.migrator = new migration.migrator.Migrator(options.tracker, this.migrations, this.contexts);
|
|
214
216
|
}
|
|
@@ -259,7 +261,7 @@ class Account {
|
|
|
259
261
|
providerFor(chainId) {
|
|
260
262
|
const found = this.network(chainId);
|
|
261
263
|
if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`);
|
|
262
|
-
return found.provider || new ethers.ethers.providers.StaticJsonRpcProvider(utils.
|
|
264
|
+
return found.provider || new ethers.ethers.providers.StaticJsonRpcProvider(utils.getEthersConnectionInfo(found.rpcUrl, undefined, this.jwt), {
|
|
263
265
|
name: '',
|
|
264
266
|
chainId: ethers.ethers.BigNumber.from(chainId).toNumber()
|
|
265
267
|
});
|
|
@@ -275,11 +277,16 @@ class Account {
|
|
|
275
277
|
const found = this.network(chainId);
|
|
276
278
|
if (!found.relayer) throw new Error(`Relayer not found for chainId ${chainId}`);
|
|
277
279
|
if (relayer.isRelayer(found.relayer)) return found.relayer;
|
|
278
|
-
return new relayer.RpcRelayer(found.relayer
|
|
280
|
+
return new relayer.RpcRelayer(_extends({}, found.relayer, {
|
|
281
|
+
jwtAuth: this.jwt
|
|
282
|
+
}));
|
|
279
283
|
}
|
|
280
284
|
setOrchestrator(orchestrator) {
|
|
281
285
|
this.orchestrator = orchestrator;
|
|
282
286
|
}
|
|
287
|
+
setJwt(jwt) {
|
|
288
|
+
this.jwt = jwt;
|
|
289
|
+
}
|
|
283
290
|
contextFor(version) {
|
|
284
291
|
const ctx = this.contexts[version];
|
|
285
292
|
if (!ctx) throw new Error(`Context not found for version ${version}`);
|
|
@@ -410,6 +417,19 @@ class Account {
|
|
|
410
417
|
throw new Error(`Wallet ${this.address} is not fully migrated`);
|
|
411
418
|
}
|
|
412
419
|
}
|
|
420
|
+
async predecorateSignedTransactions(status, chainId) {
|
|
421
|
+
// Request signed predecorate transactions from child wallets
|
|
422
|
+
const bundles = await this.orchestrator.predecorateSignedTransactions({
|
|
423
|
+
chainId
|
|
424
|
+
});
|
|
425
|
+
// Get signed predecorate transaction
|
|
426
|
+
const predecorated = await this.predecorateTransactions([], status, chainId);
|
|
427
|
+
if (core.commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
428
|
+
// Sign it
|
|
429
|
+
bundles.push(await this.signTransactions(predecorated, chainId));
|
|
430
|
+
}
|
|
431
|
+
return bundles;
|
|
432
|
+
}
|
|
413
433
|
async predecorateTransactions(txs, status, chainId) {
|
|
414
434
|
// if onchain wallet config is not up to date
|
|
415
435
|
// then we should append an extra transaction that updates it
|
|
@@ -421,34 +441,49 @@ class Account {
|
|
|
421
441
|
}
|
|
422
442
|
return txs;
|
|
423
443
|
}
|
|
424
|
-
decorateTransactions(
|
|
425
|
-
|
|
426
|
-
if (
|
|
427
|
-
|
|
444
|
+
async decorateTransactions(bundles, status, chainId) {
|
|
445
|
+
var _chainId;
|
|
446
|
+
if (!Array.isArray(bundles)) {
|
|
447
|
+
// Recurse with array
|
|
448
|
+
return this.decorateTransactions([bundles], status, chainId);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Default to chainId of first bundle when not supplied
|
|
452
|
+
chainId = (_chainId = chainId) != null ? _chainId : bundles[0].chainId;
|
|
453
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
454
|
+
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0;
|
|
455
|
+
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
456
|
+
return bundles[0];
|
|
428
457
|
}
|
|
458
|
+
|
|
459
|
+
// Intent defaults to first bundle when no bootstrap transaction
|
|
460
|
+
const {
|
|
461
|
+
entrypoint
|
|
462
|
+
} = hasBootstrapTxs ? bootstrapBundle : bundles[0];
|
|
429
463
|
const decoratedBundle = {
|
|
430
|
-
entrypoint
|
|
431
|
-
chainId
|
|
432
|
-
|
|
433
|
-
|
|
464
|
+
entrypoint,
|
|
465
|
+
chainId,
|
|
466
|
+
// Intent of the first bundle is used
|
|
467
|
+
intent: bundles[0].intent,
|
|
468
|
+
transactions: [...bootstrapBundle.transactions, ...bundles.map(bundle => ({
|
|
434
469
|
to: bundle.entrypoint,
|
|
435
470
|
data: core.commons.transaction.encodeBundleExecData(bundle),
|
|
436
471
|
gasLimit: 0,
|
|
437
472
|
delegateCall: false,
|
|
438
473
|
revertOnError: true,
|
|
439
474
|
value: 0
|
|
440
|
-
}]
|
|
475
|
+
}))]
|
|
441
476
|
};
|
|
442
477
|
|
|
443
478
|
// Re-compute the meta-transaction id to use the guest module subdigest
|
|
444
479
|
if (!status.onChain.deployed) {
|
|
445
|
-
decoratedBundle.intent.id = core.commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule,
|
|
480
|
+
decoratedBundle.intent.id = core.commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, decoratedBundle.transactions);
|
|
446
481
|
}
|
|
447
482
|
return decoratedBundle;
|
|
448
483
|
}
|
|
449
|
-
decorateSignature(signature, status) {
|
|
484
|
+
async decorateSignature(signature, status) {
|
|
450
485
|
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
451
|
-
return
|
|
486
|
+
return signature;
|
|
452
487
|
}
|
|
453
488
|
const coder = this.coders.signature;
|
|
454
489
|
const chain = status.presignedConfigurations.map(c => c.signature);
|
|
@@ -467,14 +502,13 @@ class Account {
|
|
|
467
502
|
signatures
|
|
468
503
|
});
|
|
469
504
|
}
|
|
470
|
-
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore') {
|
|
505
|
+
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore', metadata) {
|
|
471
506
|
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
472
507
|
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
473
508
|
|
|
474
509
|
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
475
510
|
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
476
511
|
// the behaviour of being migrated on all chains
|
|
477
|
-
|
|
478
512
|
const chainRef = ethers.ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId;
|
|
479
513
|
const status = await this.status(chainRef);
|
|
480
514
|
this.mustBeFullyMigrated(status);
|
|
@@ -485,7 +519,7 @@ class Account {
|
|
|
485
519
|
throw new Error('Wallet cannot validate onchain');
|
|
486
520
|
}
|
|
487
521
|
const wallet = this.walletForStatus(chainId, status);
|
|
488
|
-
const signature = await wallet.signDigest(digest);
|
|
522
|
+
const signature = await wallet.signDigest(digest, metadata);
|
|
489
523
|
const decorated = decorate ? this.decorateSignature(signature, status) : signature;
|
|
490
524
|
|
|
491
525
|
// If the wallet can't validate onchain then we
|
|
@@ -504,8 +538,8 @@ class Account {
|
|
|
504
538
|
}
|
|
505
539
|
return decorated;
|
|
506
540
|
}
|
|
507
|
-
buildEIP6492Signature(signature, status, chainId) {
|
|
508
|
-
const bootstrapBundle = this.buildBootstrapTransactions(status, chainId);
|
|
541
|
+
async buildEIP6492Signature(signature, status, chainId) {
|
|
542
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
509
543
|
if (bootstrapBundle.transactions.length === 0) {
|
|
510
544
|
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions');
|
|
511
545
|
}
|
|
@@ -561,8 +595,12 @@ class Account {
|
|
|
561
595
|
* by any of the migrations.
|
|
562
596
|
*
|
|
563
597
|
*/
|
|
564
|
-
buildBootstrapTransactions(status, chainId) {
|
|
565
|
-
|
|
598
|
+
async buildBootstrapTransactions(status, chainId) {
|
|
599
|
+
var _bundle$transactions;
|
|
600
|
+
const bundle = await this.orchestrator.buildDeployTransaction({
|
|
601
|
+
chainId
|
|
602
|
+
});
|
|
603
|
+
const transactions = (_bundle$transactions = bundle == null ? void 0 : bundle.transactions) != null ? _bundle$transactions : [];
|
|
566
604
|
|
|
567
605
|
// Add wallet deployment if needed
|
|
568
606
|
if (!status.onChain.deployed) {
|
|
@@ -571,6 +609,7 @@ class Account {
|
|
|
571
609
|
const deployTransaction = wallet.Wallet.buildDeployTransaction(status.original.context, status.original.imageHash);
|
|
572
610
|
transactions.push(...deployTransaction.transactions);
|
|
573
611
|
}
|
|
612
|
+
transactions.length;
|
|
574
613
|
|
|
575
614
|
// Get pending migrations
|
|
576
615
|
transactions.push(...status.signedMigrations.map(m => ({
|
|
@@ -620,9 +659,20 @@ class Account {
|
|
|
620
659
|
const status = pstatus || (await this.status(chainId));
|
|
621
660
|
this.mustBeFullyMigrated(status);
|
|
622
661
|
const wallet = this.walletForStatus(chainId, status);
|
|
662
|
+
const metadata = {
|
|
663
|
+
address: this.address,
|
|
664
|
+
digest: '',
|
|
665
|
+
// Set in wallet.signTransactions
|
|
666
|
+
chainId,
|
|
667
|
+
config: {
|
|
668
|
+
version: this.version
|
|
669
|
+
},
|
|
670
|
+
decorate: true,
|
|
671
|
+
cantValidateBehavior: 'ignore'
|
|
672
|
+
};
|
|
623
673
|
const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
|
|
624
674
|
space: options == null ? void 0 : options.nonceSpace
|
|
625
|
-
});
|
|
675
|
+
}, metadata);
|
|
626
676
|
return _extends({}, signed, {
|
|
627
677
|
signature: await this.decorateSignature(signed.signature, status)
|
|
628
678
|
});
|
|
@@ -701,9 +751,12 @@ class Account {
|
|
|
701
751
|
};
|
|
702
752
|
}
|
|
703
753
|
async sendSignedTransactions(signedBundle, chainId, quote, pstatus, callback) {
|
|
704
|
-
|
|
754
|
+
if (!Array.isArray(signedBundle)) {
|
|
755
|
+
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback);
|
|
756
|
+
}
|
|
757
|
+
const status = pstatus || (await this.status(signedBundle[0].chainId));
|
|
705
758
|
this.mustBeFullyMigrated(status);
|
|
706
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, status);
|
|
759
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, status);
|
|
707
760
|
callback == null ? void 0 : callback(decoratedBundle);
|
|
708
761
|
return this.relayer(chainId).relay(decoratedBundle, quote);
|
|
709
762
|
}
|
|
@@ -736,7 +789,7 @@ class Account {
|
|
|
736
789
|
nonce: 0 // The relayer also ignored the nonce
|
|
737
790
|
};
|
|
738
791
|
|
|
739
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, wstatus);
|
|
792
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus);
|
|
740
793
|
const data = core.commons.transaction.encodeBundleExecData(decoratedBundle);
|
|
741
794
|
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options);
|
|
742
795
|
return _extends({}, res, {
|
|
@@ -760,9 +813,21 @@ class Account {
|
|
|
760
813
|
async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
|
|
761
814
|
const status = await this.status(chainId);
|
|
762
815
|
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
816
|
+
const hasTxs = core.commons.transaction.fromTransactionish(this.address, predecorated).length > 0;
|
|
817
|
+
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined;
|
|
818
|
+
const childBundles = await this.orchestrator.predecorateSignedTransactions({
|
|
819
|
+
chainId
|
|
820
|
+
});
|
|
821
|
+
const bundles = [];
|
|
822
|
+
if (signed !== undefined && signed.transactions.length > 0) {
|
|
823
|
+
bundles.push(signed);
|
|
824
|
+
}
|
|
825
|
+
bundles.push(...childBundles.filter(b => b.transactions.length > 0));
|
|
826
|
+
if (bundles.length === 0) {
|
|
827
|
+
// Nothing to send?
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback);
|
|
766
831
|
}
|
|
767
832
|
async signTypedData(domain, types, message, chainId, cantValidateBehavior = 'ignore') {
|
|
768
833
|
const digest = utils.encodeTypedDataDigest({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { commons, universal } from '@0xsequence/core';
|
|
2
2
|
import { defaults, migrator, version } from '@0xsequence/migration';
|
|
3
3
|
import { proto, isRelayer, RpcRelayer } from '@0xsequence/relayer';
|
|
4
|
-
import {
|
|
4
|
+
import { getEthersConnectionInfo, encodeTypedDataDigest } from '@0xsequence/utils';
|
|
5
5
|
import { Wallet } from '@0xsequence/wallet';
|
|
6
6
|
import { ethers } from 'ethers';
|
|
7
7
|
|
|
@@ -136,8 +136,9 @@ class AccountSigner {
|
|
|
136
136
|
const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
|
|
137
137
|
return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
|
|
138
138
|
nonceSpace: this.options.nonceSpace
|
|
139
|
-
} : undefined);
|
|
139
|
+
} : undefined); // Will always have a transaction response
|
|
140
140
|
}
|
|
141
|
+
|
|
141
142
|
getBalance(blockTag) {
|
|
142
143
|
return this.provider.getBalance(this.account.address, blockTag);
|
|
143
144
|
}
|
|
@@ -205,6 +206,7 @@ class Account {
|
|
|
205
206
|
this.tracker = options.tracker;
|
|
206
207
|
this.networks = options.networks;
|
|
207
208
|
this.orchestrator = options.orchestrator;
|
|
209
|
+
this.jwt = options.jwt;
|
|
208
210
|
this.migrations = options.migrations || defaults.DefaultMigrations;
|
|
209
211
|
this.migrator = new migrator.Migrator(options.tracker, this.migrations, this.contexts);
|
|
210
212
|
}
|
|
@@ -255,7 +257,7 @@ class Account {
|
|
|
255
257
|
providerFor(chainId) {
|
|
256
258
|
const found = this.network(chainId);
|
|
257
259
|
if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`);
|
|
258
|
-
return found.provider || new ethers.providers.StaticJsonRpcProvider(
|
|
260
|
+
return found.provider || new ethers.providers.StaticJsonRpcProvider(getEthersConnectionInfo(found.rpcUrl, undefined, this.jwt), {
|
|
259
261
|
name: '',
|
|
260
262
|
chainId: ethers.BigNumber.from(chainId).toNumber()
|
|
261
263
|
});
|
|
@@ -271,11 +273,16 @@ class Account {
|
|
|
271
273
|
const found = this.network(chainId);
|
|
272
274
|
if (!found.relayer) throw new Error(`Relayer not found for chainId ${chainId}`);
|
|
273
275
|
if (isRelayer(found.relayer)) return found.relayer;
|
|
274
|
-
return new RpcRelayer(found.relayer
|
|
276
|
+
return new RpcRelayer(_extends({}, found.relayer, {
|
|
277
|
+
jwtAuth: this.jwt
|
|
278
|
+
}));
|
|
275
279
|
}
|
|
276
280
|
setOrchestrator(orchestrator) {
|
|
277
281
|
this.orchestrator = orchestrator;
|
|
278
282
|
}
|
|
283
|
+
setJwt(jwt) {
|
|
284
|
+
this.jwt = jwt;
|
|
285
|
+
}
|
|
279
286
|
contextFor(version) {
|
|
280
287
|
const ctx = this.contexts[version];
|
|
281
288
|
if (!ctx) throw new Error(`Context not found for version ${version}`);
|
|
@@ -406,6 +413,19 @@ class Account {
|
|
|
406
413
|
throw new Error(`Wallet ${this.address} is not fully migrated`);
|
|
407
414
|
}
|
|
408
415
|
}
|
|
416
|
+
async predecorateSignedTransactions(status, chainId) {
|
|
417
|
+
// Request signed predecorate transactions from child wallets
|
|
418
|
+
const bundles = await this.orchestrator.predecorateSignedTransactions({
|
|
419
|
+
chainId
|
|
420
|
+
});
|
|
421
|
+
// Get signed predecorate transaction
|
|
422
|
+
const predecorated = await this.predecorateTransactions([], status, chainId);
|
|
423
|
+
if (commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
424
|
+
// Sign it
|
|
425
|
+
bundles.push(await this.signTransactions(predecorated, chainId));
|
|
426
|
+
}
|
|
427
|
+
return bundles;
|
|
428
|
+
}
|
|
409
429
|
async predecorateTransactions(txs, status, chainId) {
|
|
410
430
|
// if onchain wallet config is not up to date
|
|
411
431
|
// then we should append an extra transaction that updates it
|
|
@@ -417,34 +437,49 @@ class Account {
|
|
|
417
437
|
}
|
|
418
438
|
return txs;
|
|
419
439
|
}
|
|
420
|
-
decorateTransactions(
|
|
421
|
-
|
|
422
|
-
if (
|
|
423
|
-
|
|
440
|
+
async decorateTransactions(bundles, status, chainId) {
|
|
441
|
+
var _chainId;
|
|
442
|
+
if (!Array.isArray(bundles)) {
|
|
443
|
+
// Recurse with array
|
|
444
|
+
return this.decorateTransactions([bundles], status, chainId);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Default to chainId of first bundle when not supplied
|
|
448
|
+
chainId = (_chainId = chainId) != null ? _chainId : bundles[0].chainId;
|
|
449
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
450
|
+
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0;
|
|
451
|
+
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
452
|
+
return bundles[0];
|
|
424
453
|
}
|
|
454
|
+
|
|
455
|
+
// Intent defaults to first bundle when no bootstrap transaction
|
|
456
|
+
const {
|
|
457
|
+
entrypoint
|
|
458
|
+
} = hasBootstrapTxs ? bootstrapBundle : bundles[0];
|
|
425
459
|
const decoratedBundle = {
|
|
426
|
-
entrypoint
|
|
427
|
-
chainId
|
|
428
|
-
|
|
429
|
-
|
|
460
|
+
entrypoint,
|
|
461
|
+
chainId,
|
|
462
|
+
// Intent of the first bundle is used
|
|
463
|
+
intent: bundles[0].intent,
|
|
464
|
+
transactions: [...bootstrapBundle.transactions, ...bundles.map(bundle => ({
|
|
430
465
|
to: bundle.entrypoint,
|
|
431
466
|
data: commons.transaction.encodeBundleExecData(bundle),
|
|
432
467
|
gasLimit: 0,
|
|
433
468
|
delegateCall: false,
|
|
434
469
|
revertOnError: true,
|
|
435
470
|
value: 0
|
|
436
|
-
}]
|
|
471
|
+
}))]
|
|
437
472
|
};
|
|
438
473
|
|
|
439
474
|
// Re-compute the meta-transaction id to use the guest module subdigest
|
|
440
475
|
if (!status.onChain.deployed) {
|
|
441
|
-
decoratedBundle.intent.id = commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule,
|
|
476
|
+
decoratedBundle.intent.id = commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, decoratedBundle.transactions);
|
|
442
477
|
}
|
|
443
478
|
return decoratedBundle;
|
|
444
479
|
}
|
|
445
|
-
decorateSignature(signature, status) {
|
|
480
|
+
async decorateSignature(signature, status) {
|
|
446
481
|
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
447
|
-
return
|
|
482
|
+
return signature;
|
|
448
483
|
}
|
|
449
484
|
const coder = this.coders.signature;
|
|
450
485
|
const chain = status.presignedConfigurations.map(c => c.signature);
|
|
@@ -463,14 +498,13 @@ class Account {
|
|
|
463
498
|
signatures
|
|
464
499
|
});
|
|
465
500
|
}
|
|
466
|
-
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore') {
|
|
501
|
+
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore', metadata) {
|
|
467
502
|
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
468
503
|
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
469
504
|
|
|
470
505
|
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
471
506
|
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
472
507
|
// the behaviour of being migrated on all chains
|
|
473
|
-
|
|
474
508
|
const chainRef = ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId;
|
|
475
509
|
const status = await this.status(chainRef);
|
|
476
510
|
this.mustBeFullyMigrated(status);
|
|
@@ -481,7 +515,7 @@ class Account {
|
|
|
481
515
|
throw new Error('Wallet cannot validate onchain');
|
|
482
516
|
}
|
|
483
517
|
const wallet = this.walletForStatus(chainId, status);
|
|
484
|
-
const signature = await wallet.signDigest(digest);
|
|
518
|
+
const signature = await wallet.signDigest(digest, metadata);
|
|
485
519
|
const decorated = decorate ? this.decorateSignature(signature, status) : signature;
|
|
486
520
|
|
|
487
521
|
// If the wallet can't validate onchain then we
|
|
@@ -500,8 +534,8 @@ class Account {
|
|
|
500
534
|
}
|
|
501
535
|
return decorated;
|
|
502
536
|
}
|
|
503
|
-
buildEIP6492Signature(signature, status, chainId) {
|
|
504
|
-
const bootstrapBundle = this.buildBootstrapTransactions(status, chainId);
|
|
537
|
+
async buildEIP6492Signature(signature, status, chainId) {
|
|
538
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
505
539
|
if (bootstrapBundle.transactions.length === 0) {
|
|
506
540
|
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions');
|
|
507
541
|
}
|
|
@@ -557,8 +591,12 @@ class Account {
|
|
|
557
591
|
* by any of the migrations.
|
|
558
592
|
*
|
|
559
593
|
*/
|
|
560
|
-
buildBootstrapTransactions(status, chainId) {
|
|
561
|
-
|
|
594
|
+
async buildBootstrapTransactions(status, chainId) {
|
|
595
|
+
var _bundle$transactions;
|
|
596
|
+
const bundle = await this.orchestrator.buildDeployTransaction({
|
|
597
|
+
chainId
|
|
598
|
+
});
|
|
599
|
+
const transactions = (_bundle$transactions = bundle == null ? void 0 : bundle.transactions) != null ? _bundle$transactions : [];
|
|
562
600
|
|
|
563
601
|
// Add wallet deployment if needed
|
|
564
602
|
if (!status.onChain.deployed) {
|
|
@@ -567,6 +605,7 @@ class Account {
|
|
|
567
605
|
const deployTransaction = Wallet.buildDeployTransaction(status.original.context, status.original.imageHash);
|
|
568
606
|
transactions.push(...deployTransaction.transactions);
|
|
569
607
|
}
|
|
608
|
+
transactions.length;
|
|
570
609
|
|
|
571
610
|
// Get pending migrations
|
|
572
611
|
transactions.push(...status.signedMigrations.map(m => ({
|
|
@@ -616,9 +655,20 @@ class Account {
|
|
|
616
655
|
const status = pstatus || (await this.status(chainId));
|
|
617
656
|
this.mustBeFullyMigrated(status);
|
|
618
657
|
const wallet = this.walletForStatus(chainId, status);
|
|
658
|
+
const metadata = {
|
|
659
|
+
address: this.address,
|
|
660
|
+
digest: '',
|
|
661
|
+
// Set in wallet.signTransactions
|
|
662
|
+
chainId,
|
|
663
|
+
config: {
|
|
664
|
+
version: this.version
|
|
665
|
+
},
|
|
666
|
+
decorate: true,
|
|
667
|
+
cantValidateBehavior: 'ignore'
|
|
668
|
+
};
|
|
619
669
|
const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
|
|
620
670
|
space: options == null ? void 0 : options.nonceSpace
|
|
621
|
-
});
|
|
671
|
+
}, metadata);
|
|
622
672
|
return _extends({}, signed, {
|
|
623
673
|
signature: await this.decorateSignature(signed.signature, status)
|
|
624
674
|
});
|
|
@@ -697,9 +747,12 @@ class Account {
|
|
|
697
747
|
};
|
|
698
748
|
}
|
|
699
749
|
async sendSignedTransactions(signedBundle, chainId, quote, pstatus, callback) {
|
|
700
|
-
|
|
750
|
+
if (!Array.isArray(signedBundle)) {
|
|
751
|
+
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback);
|
|
752
|
+
}
|
|
753
|
+
const status = pstatus || (await this.status(signedBundle[0].chainId));
|
|
701
754
|
this.mustBeFullyMigrated(status);
|
|
702
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, status);
|
|
755
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, status);
|
|
703
756
|
callback == null ? void 0 : callback(decoratedBundle);
|
|
704
757
|
return this.relayer(chainId).relay(decoratedBundle, quote);
|
|
705
758
|
}
|
|
@@ -732,7 +785,7 @@ class Account {
|
|
|
732
785
|
nonce: 0 // The relayer also ignored the nonce
|
|
733
786
|
};
|
|
734
787
|
|
|
735
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, wstatus);
|
|
788
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus);
|
|
736
789
|
const data = commons.transaction.encodeBundleExecData(decoratedBundle);
|
|
737
790
|
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options);
|
|
738
791
|
return _extends({}, res, {
|
|
@@ -756,9 +809,21 @@ class Account {
|
|
|
756
809
|
async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
|
|
757
810
|
const status = await this.status(chainId);
|
|
758
811
|
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
812
|
+
const hasTxs = commons.transaction.fromTransactionish(this.address, predecorated).length > 0;
|
|
813
|
+
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined;
|
|
814
|
+
const childBundles = await this.orchestrator.predecorateSignedTransactions({
|
|
815
|
+
chainId
|
|
816
|
+
});
|
|
817
|
+
const bundles = [];
|
|
818
|
+
if (signed !== undefined && signed.transactions.length > 0) {
|
|
819
|
+
bundles.push(signed);
|
|
820
|
+
}
|
|
821
|
+
bundles.push(...childBundles.filter(b => b.transactions.length > 0));
|
|
822
|
+
if (bundles.length === 0) {
|
|
823
|
+
// Nothing to send?
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback);
|
|
762
827
|
}
|
|
763
828
|
async signTypedData(domain, types, message, chainId, cantValidateBehavior = 'ignore') {
|
|
764
829
|
const digest = encodeTypedDataDigest({
|
|
@@ -35,6 +35,7 @@ export type AccountOptions = {
|
|
|
35
35
|
migrations?: migrator.Migrations;
|
|
36
36
|
orchestrator: Orchestrator;
|
|
37
37
|
networks: NetworkConfig[];
|
|
38
|
+
jwt?: string;
|
|
38
39
|
};
|
|
39
40
|
export interface PreparedTransactions {
|
|
40
41
|
transactions: commons.transaction.SimulatedTransaction[];
|
|
@@ -50,6 +51,7 @@ export declare class Account {
|
|
|
50
51
|
readonly migrator: migrator.Migrator;
|
|
51
52
|
readonly migrations: migrator.Migrations;
|
|
52
53
|
private orchestrator;
|
|
54
|
+
private jwt?;
|
|
53
55
|
constructor(options: AccountOptions);
|
|
54
56
|
getSigner(chainId: ChainId, options?: AccountSignerOptions): AccountSigner;
|
|
55
57
|
static new(options: {
|
|
@@ -71,16 +73,18 @@ export declare class Account {
|
|
|
71
73
|
reader(chainId: ethers.BigNumberish): commons.reader.Reader;
|
|
72
74
|
relayer(chainId: ethers.BigNumberish): Relayer;
|
|
73
75
|
setOrchestrator(orchestrator: Orchestrator): void;
|
|
76
|
+
setJwt(jwt: string): void;
|
|
74
77
|
contextFor(version: number): commons.context.WalletContext;
|
|
75
78
|
walletForStatus(chainId: ethers.BigNumberish, status: Pick<AccountStatus, 'version'> & Pick<AccountStatus, 'config'>): Wallet;
|
|
76
79
|
walletFor(chainId: ethers.BigNumberish, context: commons.context.WalletContext, config: commons.config.Config, coders: typeof this.coders): Wallet;
|
|
77
80
|
status(chainId: ethers.BigNumberish, longestPath?: boolean): Promise<AccountStatus>;
|
|
78
81
|
private mustBeFullyMigrated;
|
|
82
|
+
predecorateSignedTransactions(status: AccountStatus, chainId: ethers.BigNumberish): Promise<commons.transaction.SignedTransactionBundle[]>;
|
|
79
83
|
predecorateTransactions(txs: commons.transaction.Transactionish, status: AccountStatus, chainId: ethers.BigNumberish): Promise<commons.transaction.Transactionish>;
|
|
80
|
-
decorateTransactions(
|
|
84
|
+
decorateTransactions(bundles: commons.transaction.IntendedTransactionBundle | commons.transaction.IntendedTransactionBundle[], status: AccountStatus, chainId?: ethers.BigNumberish): Promise<commons.transaction.IntendedTransactionBundle>;
|
|
81
85
|
decorateSignature<T extends ethers.BytesLike>(signature: T, status: Partial<Pick<AccountStatus, 'presignedConfigurations'>>): Promise<T | string>;
|
|
82
86
|
publishWitness(): Promise<void>;
|
|
83
|
-
signDigest(digest: ethers.BytesLike, chainId: ethers.BigNumberish, decorate?: boolean, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw'): Promise<string>;
|
|
87
|
+
signDigest(digest: ethers.BytesLike, chainId: ethers.BigNumberish, decorate?: boolean, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw', metadata?: object): Promise<string>;
|
|
84
88
|
private buildEIP6492Signature;
|
|
85
89
|
editConfig(changes: {
|
|
86
90
|
add?: commons.config.SimpleSigner[];
|
|
@@ -99,7 +103,7 @@ export declare class Account {
|
|
|
99
103
|
* by any of the migrations.
|
|
100
104
|
*
|
|
101
105
|
*/
|
|
102
|
-
buildBootstrapTransactions(status: AccountStatus, chainId: ethers.BigNumberish): commons.transaction.IntendedTransactionBundle
|
|
106
|
+
buildBootstrapTransactions(status: AccountStatus, chainId: ethers.BigNumberish): Promise<commons.transaction.IntendedTransactionBundle>;
|
|
103
107
|
bootstrapTransactions(chainId: ethers.BigNumberish, prestatus?: AccountStatus): Promise<Omit<commons.transaction.IntendedTransactionBundle, 'chainId'>>;
|
|
104
108
|
doBootstrap(chainId: ethers.BigNumberish, feeQuote?: FeeQuote, prestatus?: AccountStatus): Promise<commons.transaction.TransactionResponse<any>>;
|
|
105
109
|
signMessage(message: ethers.BytesLike, chainId: ethers.BigNumberish, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw'): Promise<string>;
|
|
@@ -115,7 +119,7 @@ export declare class Account {
|
|
|
115
119
|
migratedAllChains: boolean;
|
|
116
120
|
failedChains: number[];
|
|
117
121
|
}>;
|
|
118
|
-
sendSignedTransactions(signedBundle: commons.transaction.IntendedTransactionBundle, chainId: ethers.BigNumberish, quote?: FeeQuote, pstatus?: AccountStatus, callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void): Promise<ethers.providers.TransactionResponse>;
|
|
122
|
+
sendSignedTransactions(signedBundle: commons.transaction.IntendedTransactionBundle | commons.transaction.IntendedTransactionBundle[], chainId: ethers.BigNumberish, quote?: FeeQuote, pstatus?: AccountStatus, callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void): Promise<ethers.providers.TransactionResponse>;
|
|
119
123
|
fillGasLimits(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, status?: AccountStatus): Promise<commons.transaction.SimulatedTransaction[]>;
|
|
120
124
|
gasRefundQuotes(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, stubSignatureOverrides: Map<string, string>, status?: AccountStatus, options?: {
|
|
121
125
|
simulate?: boolean;
|
|
@@ -132,7 +136,7 @@ export declare class Account {
|
|
|
132
136
|
}): Promise<PreparedTransactions>;
|
|
133
137
|
sendTransaction(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, quote?: FeeQuote, skipPreDecorate?: boolean, callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void, options?: {
|
|
134
138
|
nonceSpace?: ethers.BigNumberish;
|
|
135
|
-
}): Promise<ethers.providers.TransactionResponse>;
|
|
139
|
+
}): Promise<ethers.providers.TransactionResponse | undefined>;
|
|
136
140
|
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, message: Record<string, any>, chainId: ethers.BigNumberish, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw'): Promise<string>;
|
|
137
141
|
getAllSigners(): Promise<{
|
|
138
142
|
address: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xsequence/account",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "tools for migrating sequence wallets to new versions",
|
|
5
5
|
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/account",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -10,19 +10,19 @@
|
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"ethers": "^5.5.2",
|
|
13
|
-
"@0xsequence/
|
|
14
|
-
"@0xsequence/
|
|
15
|
-
"@0xsequence/network": "1.
|
|
16
|
-
"@0xsequence/relayer": "1.
|
|
17
|
-
"@0xsequence/sessions": "1.
|
|
18
|
-
"@0xsequence/utils": "1.
|
|
19
|
-
"@0xsequence/wallet": "1.
|
|
13
|
+
"@0xsequence/core": "1.4.0",
|
|
14
|
+
"@0xsequence/migration": "1.4.0",
|
|
15
|
+
"@0xsequence/network": "1.4.0",
|
|
16
|
+
"@0xsequence/relayer": "1.4.0",
|
|
17
|
+
"@0xsequence/sessions": "1.4.0",
|
|
18
|
+
"@0xsequence/utils": "1.4.0",
|
|
19
|
+
"@0xsequence/wallet": "1.4.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
|
23
23
|
"nyc": "^15.1.0",
|
|
24
|
-
"@0xsequence/signhub": "1.
|
|
25
|
-
"@0xsequence/tests": "1.
|
|
24
|
+
"@0xsequence/signhub": "1.4.0",
|
|
25
|
+
"@0xsequence/tests": "1.4.0"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"src",
|
package/src/account.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { commons, universal } from '@0xsequence/core'
|
|
2
|
+
import { WalletSignRequestMetadata } from '@0xsequence/core/src/commons'
|
|
2
3
|
import { migrator, defaults, version } from '@0xsequence/migration'
|
|
3
4
|
import { ChainId, NetworkConfig } from '@0xsequence/network'
|
|
4
5
|
import { FeeOption, FeeQuote, isRelayer, Relayer, RpcRelayer } from '@0xsequence/relayer'
|
|
5
6
|
import { tracker } from '@0xsequence/sessions'
|
|
6
7
|
import { Orchestrator } from '@0xsequence/signhub'
|
|
7
|
-
import { encodeTypedDataDigest,
|
|
8
|
+
import { encodeTypedDataDigest, getEthersConnectionInfo } from '@0xsequence/utils'
|
|
8
9
|
import { Wallet } from '@0xsequence/wallet'
|
|
9
10
|
import { ethers, TypedDataDomain, TypedDataField } from 'ethers'
|
|
10
11
|
import { AccountSigner, AccountSignerOptions } from './signer'
|
|
@@ -52,6 +53,9 @@ export type AccountOptions = {
|
|
|
52
53
|
|
|
53
54
|
// Networks information and providers
|
|
54
55
|
networks: NetworkConfig[]
|
|
56
|
+
|
|
57
|
+
// Jwt
|
|
58
|
+
jwt?: string
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
export interface PreparedTransactions {
|
|
@@ -95,6 +99,8 @@ export class Account {
|
|
|
95
99
|
|
|
96
100
|
private orchestrator: Orchestrator
|
|
97
101
|
|
|
102
|
+
private jwt?: string
|
|
103
|
+
|
|
98
104
|
constructor(options: AccountOptions) {
|
|
99
105
|
this.address = ethers.utils.getAddress(options.address)
|
|
100
106
|
|
|
@@ -102,6 +108,7 @@ export class Account {
|
|
|
102
108
|
this.tracker = options.tracker
|
|
103
109
|
this.networks = options.networks
|
|
104
110
|
this.orchestrator = options.orchestrator
|
|
111
|
+
this.jwt = options.jwt
|
|
105
112
|
|
|
106
113
|
this.migrations = options.migrations || defaults.DefaultMigrations
|
|
107
114
|
this.migrator = new migrator.Migrator(options.tracker, this.migrations, this.contexts)
|
|
@@ -173,7 +180,7 @@ export class Account {
|
|
|
173
180
|
if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`)
|
|
174
181
|
return (
|
|
175
182
|
found.provider ||
|
|
176
|
-
new ethers.providers.StaticJsonRpcProvider(
|
|
183
|
+
new ethers.providers.StaticJsonRpcProvider(getEthersConnectionInfo(found.rpcUrl, undefined, this.jwt), {
|
|
177
184
|
name: '',
|
|
178
185
|
chainId: ethers.BigNumber.from(chainId).toNumber()
|
|
179
186
|
})
|
|
@@ -192,13 +199,17 @@ export class Account {
|
|
|
192
199
|
const found = this.network(chainId)
|
|
193
200
|
if (!found.relayer) throw new Error(`Relayer not found for chainId ${chainId}`)
|
|
194
201
|
if (isRelayer(found.relayer)) return found.relayer
|
|
195
|
-
return new RpcRelayer(found.relayer)
|
|
202
|
+
return new RpcRelayer({ ...found.relayer, jwtAuth: this.jwt })
|
|
196
203
|
}
|
|
197
204
|
|
|
198
205
|
setOrchestrator(orchestrator: Orchestrator) {
|
|
199
206
|
this.orchestrator = orchestrator
|
|
200
207
|
}
|
|
201
208
|
|
|
209
|
+
setJwt(jwt: string) {
|
|
210
|
+
this.jwt = jwt
|
|
211
|
+
}
|
|
212
|
+
|
|
202
213
|
contextFor(version: number): commons.context.WalletContext {
|
|
203
214
|
const ctx = this.contexts[version]
|
|
204
215
|
if (!ctx) throw new Error(`Context not found for version ${version}`)
|
|
@@ -352,6 +363,21 @@ export class Account {
|
|
|
352
363
|
}
|
|
353
364
|
}
|
|
354
365
|
|
|
366
|
+
async predecorateSignedTransactions(
|
|
367
|
+
status: AccountStatus,
|
|
368
|
+
chainId: ethers.BigNumberish
|
|
369
|
+
): Promise<commons.transaction.SignedTransactionBundle[]> {
|
|
370
|
+
// Request signed predecorate transactions from child wallets
|
|
371
|
+
const bundles = await this.orchestrator.predecorateSignedTransactions({ chainId })
|
|
372
|
+
// Get signed predecorate transaction
|
|
373
|
+
const predecorated = await this.predecorateTransactions([], status, chainId)
|
|
374
|
+
if (commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
375
|
+
// Sign it
|
|
376
|
+
bundles.push(await this.signTransactions(predecorated, chainId))
|
|
377
|
+
}
|
|
378
|
+
return bundles
|
|
379
|
+
}
|
|
380
|
+
|
|
355
381
|
async predecorateTransactions(
|
|
356
382
|
txs: commons.transaction.Transactionish,
|
|
357
383
|
status: AccountStatus,
|
|
@@ -369,29 +395,46 @@ export class Account {
|
|
|
369
395
|
return txs
|
|
370
396
|
}
|
|
371
397
|
|
|
372
|
-
decorateTransactions(
|
|
373
|
-
|
|
374
|
-
status: AccountStatus
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (
|
|
378
|
-
|
|
398
|
+
async decorateTransactions(
|
|
399
|
+
bundles: commons.transaction.IntendedTransactionBundle | commons.transaction.IntendedTransactionBundle[],
|
|
400
|
+
status: AccountStatus,
|
|
401
|
+
chainId?: ethers.BigNumberish
|
|
402
|
+
): Promise<commons.transaction.IntendedTransactionBundle> {
|
|
403
|
+
if (!Array.isArray(bundles)) {
|
|
404
|
+
// Recurse with array
|
|
405
|
+
return this.decorateTransactions([bundles], status, chainId)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Default to chainId of first bundle when not supplied
|
|
409
|
+
chainId = chainId ?? bundles[0].chainId
|
|
410
|
+
|
|
411
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId)
|
|
412
|
+
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0
|
|
413
|
+
|
|
414
|
+
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
415
|
+
return bundles[0]
|
|
379
416
|
}
|
|
380
417
|
|
|
418
|
+
// Intent defaults to first bundle when no bootstrap transaction
|
|
419
|
+
const { entrypoint } = hasBootstrapTxs ? bootstrapBundle : bundles[0]
|
|
420
|
+
|
|
381
421
|
const decoratedBundle = {
|
|
382
|
-
entrypoint
|
|
383
|
-
chainId
|
|
384
|
-
|
|
422
|
+
entrypoint,
|
|
423
|
+
chainId,
|
|
424
|
+
// Intent of the first bundle is used
|
|
425
|
+
intent: bundles[0].intent,
|
|
385
426
|
transactions: [
|
|
386
427
|
...bootstrapBundle.transactions,
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
428
|
+
...bundles.map(
|
|
429
|
+
(bundle): commons.transaction.Transaction => ({
|
|
430
|
+
to: bundle.entrypoint,
|
|
431
|
+
data: commons.transaction.encodeBundleExecData(bundle),
|
|
432
|
+
gasLimit: 0,
|
|
433
|
+
delegateCall: false,
|
|
434
|
+
revertOnError: true,
|
|
435
|
+
value: 0
|
|
436
|
+
})
|
|
437
|
+
)
|
|
395
438
|
]
|
|
396
439
|
}
|
|
397
440
|
|
|
@@ -399,7 +442,7 @@ export class Account {
|
|
|
399
442
|
if (!status.onChain.deployed) {
|
|
400
443
|
decoratedBundle.intent.id = commons.transaction.subdigestOfGuestModuleTransactions(
|
|
401
444
|
this.contexts[this.version].guestModule,
|
|
402
|
-
|
|
445
|
+
chainId,
|
|
403
446
|
decoratedBundle.transactions
|
|
404
447
|
)
|
|
405
448
|
}
|
|
@@ -407,12 +450,12 @@ export class Account {
|
|
|
407
450
|
return decoratedBundle
|
|
408
451
|
}
|
|
409
452
|
|
|
410
|
-
decorateSignature<T extends ethers.BytesLike>(
|
|
453
|
+
async decorateSignature<T extends ethers.BytesLike>(
|
|
411
454
|
signature: T,
|
|
412
455
|
status: Partial<Pick<AccountStatus, 'presignedConfigurations'>>
|
|
413
456
|
): Promise<T | string> {
|
|
414
457
|
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
415
|
-
return
|
|
458
|
+
return signature
|
|
416
459
|
}
|
|
417
460
|
|
|
418
461
|
const coder = this.coders.signature
|
|
@@ -434,7 +477,8 @@ export class Account {
|
|
|
434
477
|
digest: ethers.BytesLike,
|
|
435
478
|
chainId: ethers.BigNumberish,
|
|
436
479
|
decorate: boolean = true,
|
|
437
|
-
cantValidateBehavior: 'ignore' | 'eip6492' | 'throw' = 'ignore'
|
|
480
|
+
cantValidateBehavior: 'ignore' | 'eip6492' | 'throw' = 'ignore',
|
|
481
|
+
metadata?: object
|
|
438
482
|
): Promise<string> {
|
|
439
483
|
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
440
484
|
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
@@ -442,10 +486,8 @@ export class Account {
|
|
|
442
486
|
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
443
487
|
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
444
488
|
// the behaviour of being migrated on all chains
|
|
445
|
-
|
|
446
489
|
const chainRef = ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId
|
|
447
490
|
const status = await this.status(chainRef)
|
|
448
|
-
|
|
449
491
|
this.mustBeFullyMigrated(status)
|
|
450
492
|
|
|
451
493
|
// Check if we can validate onchain and what to do if we can't
|
|
@@ -455,7 +497,7 @@ export class Account {
|
|
|
455
497
|
}
|
|
456
498
|
|
|
457
499
|
const wallet = this.walletForStatus(chainId, status)
|
|
458
|
-
const signature = await wallet.signDigest(digest)
|
|
500
|
+
const signature = await wallet.signDigest(digest, metadata)
|
|
459
501
|
|
|
460
502
|
const decorated = decorate ? this.decorateSignature(signature, status) : signature
|
|
461
503
|
|
|
@@ -478,8 +520,8 @@ export class Account {
|
|
|
478
520
|
return decorated
|
|
479
521
|
}
|
|
480
522
|
|
|
481
|
-
private buildEIP6492Signature(signature: string, status: AccountStatus, chainId: ethers.BigNumberish): string {
|
|
482
|
-
const bootstrapBundle = this.buildBootstrapTransactions(status, chainId)
|
|
523
|
+
private async buildEIP6492Signature(signature: string, status: AccountStatus, chainId: ethers.BigNumberish): Promise<string> {
|
|
524
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId)
|
|
483
525
|
if (bootstrapBundle.transactions.length === 0) {
|
|
484
526
|
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions')
|
|
485
527
|
}
|
|
@@ -550,8 +592,12 @@ export class Account {
|
|
|
550
592
|
* by any of the migrations.
|
|
551
593
|
*
|
|
552
594
|
*/
|
|
553
|
-
buildBootstrapTransactions(
|
|
554
|
-
|
|
595
|
+
async buildBootstrapTransactions(
|
|
596
|
+
status: AccountStatus,
|
|
597
|
+
chainId: ethers.BigNumberish
|
|
598
|
+
): Promise<commons.transaction.IntendedTransactionBundle> {
|
|
599
|
+
const bundle = await this.orchestrator.buildDeployTransaction({ chainId })
|
|
600
|
+
const transactions: commons.transaction.Transaction[] = bundle?.transactions ?? []
|
|
555
601
|
|
|
556
602
|
// Add wallet deployment if needed
|
|
557
603
|
if (!status.onChain.deployed) {
|
|
@@ -561,6 +607,7 @@ export class Account {
|
|
|
561
607
|
|
|
562
608
|
transactions.push(...deployTransaction.transactions)
|
|
563
609
|
}
|
|
610
|
+
const len = transactions.length
|
|
564
611
|
|
|
565
612
|
// Get pending migrations
|
|
566
613
|
transactions.push(
|
|
@@ -622,7 +669,17 @@ export class Account {
|
|
|
622
669
|
this.mustBeFullyMigrated(status)
|
|
623
670
|
|
|
624
671
|
const wallet = this.walletForStatus(chainId, status)
|
|
625
|
-
|
|
672
|
+
|
|
673
|
+
const metadata: WalletSignRequestMetadata = {
|
|
674
|
+
address: this.address,
|
|
675
|
+
digest: '', // Set in wallet.signTransactions
|
|
676
|
+
chainId,
|
|
677
|
+
config: { version: this.version },
|
|
678
|
+
decorate: true,
|
|
679
|
+
cantValidateBehavior: 'ignore'
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const signed = await wallet.signTransactions(txs, options?.nonceSpace && { space: options?.nonceSpace }, metadata)
|
|
626
683
|
|
|
627
684
|
return {
|
|
628
685
|
...signed,
|
|
@@ -707,16 +764,19 @@ export class Account {
|
|
|
707
764
|
}
|
|
708
765
|
|
|
709
766
|
async sendSignedTransactions(
|
|
710
|
-
signedBundle: commons.transaction.IntendedTransactionBundle,
|
|
767
|
+
signedBundle: commons.transaction.IntendedTransactionBundle | commons.transaction.IntendedTransactionBundle[],
|
|
711
768
|
chainId: ethers.BigNumberish,
|
|
712
769
|
quote?: FeeQuote,
|
|
713
770
|
pstatus?: AccountStatus,
|
|
714
771
|
callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void
|
|
715
772
|
): Promise<ethers.providers.TransactionResponse> {
|
|
716
|
-
|
|
773
|
+
if (!Array.isArray(signedBundle)) {
|
|
774
|
+
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback)
|
|
775
|
+
}
|
|
776
|
+
const status = pstatus || (await this.status(signedBundle[0].chainId))
|
|
717
777
|
this.mustBeFullyMigrated(status)
|
|
718
778
|
|
|
719
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, status)
|
|
779
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, status)
|
|
720
780
|
callback?.(decoratedBundle)
|
|
721
781
|
|
|
722
782
|
return this.relayer(chainId).relay(decoratedBundle, quote)
|
|
@@ -769,7 +829,7 @@ export class Account {
|
|
|
769
829
|
nonce: 0 // The relayer also ignored the nonce
|
|
770
830
|
}
|
|
771
831
|
|
|
772
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, wstatus)
|
|
832
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus)
|
|
773
833
|
const data = commons.transaction.encodeBundleExecData(decoratedBundle)
|
|
774
834
|
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options)
|
|
775
835
|
return { ...res, decorated: decoratedBundle }
|
|
@@ -806,13 +866,27 @@ export class Account {
|
|
|
806
866
|
options?: {
|
|
807
867
|
nonceSpace?: ethers.BigNumberish
|
|
808
868
|
}
|
|
809
|
-
): Promise<ethers.providers.TransactionResponse> {
|
|
869
|
+
): Promise<ethers.providers.TransactionResponse | undefined> {
|
|
810
870
|
const status = await this.status(chainId)
|
|
811
871
|
|
|
812
872
|
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId)
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
873
|
+
const hasTxs = commons.transaction.fromTransactionish(this.address, predecorated).length > 0
|
|
874
|
+
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined
|
|
875
|
+
|
|
876
|
+
const childBundles = await this.orchestrator.predecorateSignedTransactions({ chainId })
|
|
877
|
+
|
|
878
|
+
const bundles: commons.transaction.SignedTransactionBundle[] = []
|
|
879
|
+
if (signed !== undefined && signed.transactions.length > 0) {
|
|
880
|
+
bundles.push(signed)
|
|
881
|
+
}
|
|
882
|
+
bundles.push(...childBundles.filter(b => b.transactions.length > 0))
|
|
883
|
+
|
|
884
|
+
if (bundles.length === 0) {
|
|
885
|
+
// Nothing to send?
|
|
886
|
+
return
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback)
|
|
816
890
|
}
|
|
817
891
|
|
|
818
892
|
async signTypedData(
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { commons } from '@0xsequence/core'
|
|
2
|
+
import { signers, Status } from '@0xsequence/signhub'
|
|
3
|
+
import { ethers } from 'ethers'
|
|
4
|
+
import { Account } from '../account'
|
|
5
|
+
|
|
6
|
+
export type MetadataWithChainId = {
|
|
7
|
+
chainId: ethers.BigNumberish
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Implements a wrapper for using Sequence accounts as nested signers in the signhub orchestrator.
|
|
11
|
+
export class AccountOrchestratorWrapper implements signers.SapientSigner {
|
|
12
|
+
constructor(public account: Account) {}
|
|
13
|
+
|
|
14
|
+
async getAddress(): Promise<string> {
|
|
15
|
+
return this.account.address
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getChainIdFromMetadata(metadata: object): ethers.BigNumber {
|
|
19
|
+
try {
|
|
20
|
+
const { chainId } = metadata as MetadataWithChainId
|
|
21
|
+
return ethers.BigNumber.from(chainId)
|
|
22
|
+
} catch (err) {
|
|
23
|
+
// Invalid metadata object
|
|
24
|
+
throw new Error('AccountOrchestratorWrapper only supports metadata with chain id')
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async buildDeployTransaction(metadata: object): Promise<commons.transaction.TransactionBundle | undefined> {
|
|
29
|
+
const chainId = this.getChainIdFromMetadata(metadata)
|
|
30
|
+
const status = await this.account.status(chainId)
|
|
31
|
+
return this.account.buildBootstrapTransactions(status, chainId)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async predecorateSignedTransactions(metadata: object): Promise<commons.transaction.SignedTransactionBundle[]> {
|
|
35
|
+
const chainId = this.getChainIdFromMetadata(metadata)
|
|
36
|
+
const status = await this.account.status(chainId)
|
|
37
|
+
return this.account.predecorateSignedTransactions(status, chainId)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async decorateTransactions(
|
|
41
|
+
bundle: commons.transaction.IntendedTransactionBundle,
|
|
42
|
+
metadata: object
|
|
43
|
+
): Promise<commons.transaction.IntendedTransactionBundle> {
|
|
44
|
+
const chainId = this.getChainIdFromMetadata(metadata)
|
|
45
|
+
const status = await this.account.status(chainId)
|
|
46
|
+
return this.account.decorateTransactions(bundle, status)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async requestSignature(
|
|
50
|
+
_id: string,
|
|
51
|
+
message: ethers.utils.BytesLike,
|
|
52
|
+
metadata: object,
|
|
53
|
+
callbacks: {
|
|
54
|
+
onSignature: (signature: ethers.utils.BytesLike) => void
|
|
55
|
+
onRejection: (error: string) => void
|
|
56
|
+
onStatus: (situation: string) => void
|
|
57
|
+
}
|
|
58
|
+
): Promise<boolean> {
|
|
59
|
+
if (!commons.isWalletSignRequestMetadata(metadata)) {
|
|
60
|
+
throw new Error('AccountOrchestratorWrapper only supports wallet metadata requests')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { chainId, decorate } = metadata
|
|
64
|
+
// EIP-6492 not supported on nested signatures
|
|
65
|
+
// Default to throw instead of ignore. Ignoring should be explicit
|
|
66
|
+
const cantValidateBehavior = metadata.cantValidateBehavior ?? 'throw'
|
|
67
|
+
|
|
68
|
+
// For Sequence nested signatures we must use `signDigest` and not `signMessage`
|
|
69
|
+
// otherwise the account will hash the digest and the signature will be invalid.
|
|
70
|
+
try {
|
|
71
|
+
callbacks.onSignature(await this.account.signDigest(message, chainId, decorate, cantValidateBehavior, metadata))
|
|
72
|
+
} catch (err) {
|
|
73
|
+
callbacks.onRejection('Unable to sign account')
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
notifyStatusChange(_i: string, _s: Status, _m: object): void {}
|
|
81
|
+
|
|
82
|
+
suffix(): ethers.utils.BytesLike {
|
|
83
|
+
return [3]
|
|
84
|
+
}
|
|
85
|
+
}
|
package/src/signer.ts
CHANGED
|
@@ -157,7 +157,7 @@ export class AccountSigner implements ethers.Signer {
|
|
|
157
157
|
nonceSpace: this.options.nonceSpace
|
|
158
158
|
}
|
|
159
159
|
: undefined
|
|
160
|
-
)
|
|
160
|
+
) as Promise<ethers.providers.TransactionResponse> // Will always have a transaction response
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
getBalance(blockTag?: ethers.providers.BlockTag | undefined): Promise<ethers.BigNumber> {
|