@0xsequence/account 1.2.8 → 1.3.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 -32
- package/dist/0xsequence-account.cjs.prod.js +94 -32
- package/dist/0xsequence-account.esm.js +94 -32
- package/dist/declarations/src/account.d.ts +10 -6
- package/dist/declarations/src/signer.d.ts +3 -1
- package/package.json +10 -10
- package/src/account.ts +111 -41
- package/src/orchestrator/wrapper.ts +85 -0
- package/src/signer.ts +7 -3
|
@@ -126,21 +126,23 @@ class AccountSigner {
|
|
|
126
126
|
}
|
|
127
127
|
throw new Error('No fee option available - not enough balance');
|
|
128
128
|
}
|
|
129
|
-
async sendTransaction(txsPromise) {
|
|
129
|
+
async sendTransaction(txsPromise, options) {
|
|
130
130
|
var _this$options$stubSig, _this$options2, _this$options$selectF, _this$options3, _this$options4;
|
|
131
131
|
const txs = isDeferrable(txsPromise) ? await ethers.ethers.utils.resolveProperties(txsPromise) : txsPromise;
|
|
132
132
|
const prepare = await this.account.prepareTransactions({
|
|
133
133
|
txs,
|
|
134
134
|
chainId: this.chainId,
|
|
135
|
-
stubSignatureOverrides: (_this$options$stubSig = (_this$options2 = this.options) == null ? void 0 : _this$options2.stubSignatureOverrides) != null ? _this$options$stubSig : new Map()
|
|
135
|
+
stubSignatureOverrides: (_this$options$stubSig = (_this$options2 = this.options) == null ? void 0 : _this$options2.stubSignatureOverrides) != null ? _this$options$stubSig : new Map(),
|
|
136
|
+
simulateForFeeOptions: options == null ? void 0 : options.simulateForFeeOptions
|
|
136
137
|
});
|
|
137
138
|
const selectMethod = (_this$options$selectF = (_this$options3 = this.options) == null ? void 0 : _this$options3.selectFee) != null ? _this$options$selectF : this.defaultSelectFee.bind(this);
|
|
138
139
|
const feeOption = await selectMethod(txs, prepare.feeOptions);
|
|
139
140
|
const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
|
|
140
141
|
return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
|
|
141
142
|
nonceSpace: this.options.nonceSpace
|
|
142
|
-
} : undefined);
|
|
143
|
+
} : undefined); // Will always have a transaction response
|
|
143
144
|
}
|
|
145
|
+
|
|
144
146
|
getBalance(blockTag) {
|
|
145
147
|
return this.provider.getBalance(this.account.address, blockTag);
|
|
146
148
|
}
|
|
@@ -409,6 +411,19 @@ class Account {
|
|
|
409
411
|
throw new Error(`Wallet ${this.address} is not fully migrated`);
|
|
410
412
|
}
|
|
411
413
|
}
|
|
414
|
+
async predecorateSignedTransactions(status, chainId) {
|
|
415
|
+
// Request signed predecorate transactions from child wallets
|
|
416
|
+
const bundles = await this.orchestrator.predecorateSignedTransactions({
|
|
417
|
+
chainId
|
|
418
|
+
});
|
|
419
|
+
// Get signed predecorate transaction
|
|
420
|
+
const predecorated = await this.predecorateTransactions([], status, chainId);
|
|
421
|
+
if (core.commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
422
|
+
// Sign it
|
|
423
|
+
bundles.push(await this.signTransactions(predecorated, chainId));
|
|
424
|
+
}
|
|
425
|
+
return bundles;
|
|
426
|
+
}
|
|
412
427
|
async predecorateTransactions(txs, status, chainId) {
|
|
413
428
|
// if onchain wallet config is not up to date
|
|
414
429
|
// then we should append an extra transaction that updates it
|
|
@@ -420,34 +435,49 @@ class Account {
|
|
|
420
435
|
}
|
|
421
436
|
return txs;
|
|
422
437
|
}
|
|
423
|
-
decorateTransactions(
|
|
424
|
-
|
|
425
|
-
if (
|
|
426
|
-
|
|
438
|
+
async decorateTransactions(bundles, status, chainId) {
|
|
439
|
+
var _chainId;
|
|
440
|
+
if (!Array.isArray(bundles)) {
|
|
441
|
+
// Recurse with array
|
|
442
|
+
return this.decorateTransactions([bundles], status, chainId);
|
|
427
443
|
}
|
|
444
|
+
|
|
445
|
+
// Default to chainId of first bundle when not supplied
|
|
446
|
+
chainId = (_chainId = chainId) != null ? _chainId : bundles[0].chainId;
|
|
447
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
448
|
+
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0;
|
|
449
|
+
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
450
|
+
return bundles[0];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Intent defaults to first bundle when no bootstrap transaction
|
|
454
|
+
const {
|
|
455
|
+
entrypoint
|
|
456
|
+
} = hasBootstrapTxs ? bootstrapBundle : bundles[0];
|
|
428
457
|
const decoratedBundle = {
|
|
429
|
-
entrypoint
|
|
430
|
-
chainId
|
|
431
|
-
|
|
432
|
-
|
|
458
|
+
entrypoint,
|
|
459
|
+
chainId,
|
|
460
|
+
// Intent of the first bundle is used
|
|
461
|
+
intent: bundles[0].intent,
|
|
462
|
+
transactions: [...bootstrapBundle.transactions, ...bundles.map(bundle => ({
|
|
433
463
|
to: bundle.entrypoint,
|
|
434
464
|
data: core.commons.transaction.encodeBundleExecData(bundle),
|
|
435
465
|
gasLimit: 0,
|
|
436
466
|
delegateCall: false,
|
|
437
467
|
revertOnError: true,
|
|
438
468
|
value: 0
|
|
439
|
-
}]
|
|
469
|
+
}))]
|
|
440
470
|
};
|
|
441
471
|
|
|
442
472
|
// Re-compute the meta-transaction id to use the guest module subdigest
|
|
443
473
|
if (!status.onChain.deployed) {
|
|
444
|
-
decoratedBundle.intent.id = core.commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule,
|
|
474
|
+
decoratedBundle.intent.id = core.commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, decoratedBundle.transactions);
|
|
445
475
|
}
|
|
446
476
|
return decoratedBundle;
|
|
447
477
|
}
|
|
448
|
-
decorateSignature(signature, status) {
|
|
478
|
+
async decorateSignature(signature, status) {
|
|
449
479
|
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
450
|
-
return
|
|
480
|
+
return signature;
|
|
451
481
|
}
|
|
452
482
|
const coder = this.coders.signature;
|
|
453
483
|
const chain = status.presignedConfigurations.map(c => c.signature);
|
|
@@ -466,14 +496,13 @@ class Account {
|
|
|
466
496
|
signatures
|
|
467
497
|
});
|
|
468
498
|
}
|
|
469
|
-
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore') {
|
|
499
|
+
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore', metadata) {
|
|
470
500
|
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
471
501
|
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
472
502
|
|
|
473
503
|
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
474
504
|
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
475
505
|
// the behaviour of being migrated on all chains
|
|
476
|
-
|
|
477
506
|
const chainRef = ethers.ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId;
|
|
478
507
|
const status = await this.status(chainRef);
|
|
479
508
|
this.mustBeFullyMigrated(status);
|
|
@@ -484,7 +513,7 @@ class Account {
|
|
|
484
513
|
throw new Error('Wallet cannot validate onchain');
|
|
485
514
|
}
|
|
486
515
|
const wallet = this.walletForStatus(chainId, status);
|
|
487
|
-
const signature = await wallet.signDigest(digest);
|
|
516
|
+
const signature = await wallet.signDigest(digest, metadata);
|
|
488
517
|
const decorated = decorate ? this.decorateSignature(signature, status) : signature;
|
|
489
518
|
|
|
490
519
|
// If the wallet can't validate onchain then we
|
|
@@ -503,8 +532,8 @@ class Account {
|
|
|
503
532
|
}
|
|
504
533
|
return decorated;
|
|
505
534
|
}
|
|
506
|
-
buildEIP6492Signature(signature, status, chainId) {
|
|
507
|
-
const bootstrapBundle = this.buildBootstrapTransactions(status, chainId);
|
|
535
|
+
async buildEIP6492Signature(signature, status, chainId) {
|
|
536
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
508
537
|
if (bootstrapBundle.transactions.length === 0) {
|
|
509
538
|
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions');
|
|
510
539
|
}
|
|
@@ -560,8 +589,12 @@ class Account {
|
|
|
560
589
|
* by any of the migrations.
|
|
561
590
|
*
|
|
562
591
|
*/
|
|
563
|
-
buildBootstrapTransactions(status, chainId) {
|
|
564
|
-
|
|
592
|
+
async buildBootstrapTransactions(status, chainId) {
|
|
593
|
+
var _bundle$transactions;
|
|
594
|
+
const bundle = await this.orchestrator.buildDeployTransaction({
|
|
595
|
+
chainId
|
|
596
|
+
});
|
|
597
|
+
const transactions = (_bundle$transactions = bundle == null ? void 0 : bundle.transactions) != null ? _bundle$transactions : [];
|
|
565
598
|
|
|
566
599
|
// Add wallet deployment if needed
|
|
567
600
|
if (!status.onChain.deployed) {
|
|
@@ -570,6 +603,7 @@ class Account {
|
|
|
570
603
|
const deployTransaction = wallet.Wallet.buildDeployTransaction(status.original.context, status.original.imageHash);
|
|
571
604
|
transactions.push(...deployTransaction.transactions);
|
|
572
605
|
}
|
|
606
|
+
transactions.length;
|
|
573
607
|
|
|
574
608
|
// Get pending migrations
|
|
575
609
|
transactions.push(...status.signedMigrations.map(m => ({
|
|
@@ -619,9 +653,20 @@ class Account {
|
|
|
619
653
|
const status = pstatus || (await this.status(chainId));
|
|
620
654
|
this.mustBeFullyMigrated(status);
|
|
621
655
|
const wallet = this.walletForStatus(chainId, status);
|
|
656
|
+
const metadata = {
|
|
657
|
+
address: this.address,
|
|
658
|
+
digest: '',
|
|
659
|
+
// Set in wallet.signTransactions
|
|
660
|
+
chainId,
|
|
661
|
+
config: {
|
|
662
|
+
version: this.version
|
|
663
|
+
},
|
|
664
|
+
decorate: true,
|
|
665
|
+
cantValidateBehavior: 'ignore'
|
|
666
|
+
};
|
|
622
667
|
const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
|
|
623
668
|
space: options == null ? void 0 : options.nonceSpace
|
|
624
|
-
});
|
|
669
|
+
}, metadata);
|
|
625
670
|
return _extends({}, signed, {
|
|
626
671
|
signature: await this.decorateSignature(signed.signature, status)
|
|
627
672
|
});
|
|
@@ -700,9 +745,12 @@ class Account {
|
|
|
700
745
|
};
|
|
701
746
|
}
|
|
702
747
|
async sendSignedTransactions(signedBundle, chainId, quote, pstatus, callback) {
|
|
703
|
-
|
|
748
|
+
if (!Array.isArray(signedBundle)) {
|
|
749
|
+
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback);
|
|
750
|
+
}
|
|
751
|
+
const status = pstatus || (await this.status(signedBundle[0].chainId));
|
|
704
752
|
this.mustBeFullyMigrated(status);
|
|
705
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, status);
|
|
753
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, status);
|
|
706
754
|
callback == null ? void 0 : callback(decoratedBundle);
|
|
707
755
|
return this.relayer(chainId).relay(decoratedBundle, quote);
|
|
708
756
|
}
|
|
@@ -710,7 +758,7 @@ class Account {
|
|
|
710
758
|
const wallet = this.walletForStatus(chainId, status || (await this.status(chainId)));
|
|
711
759
|
return wallet.fillGasLimits(txs);
|
|
712
760
|
}
|
|
713
|
-
async gasRefundQuotes(txs, chainId, stubSignatureOverrides, status) {
|
|
761
|
+
async gasRefundQuotes(txs, chainId, stubSignatureOverrides, status, options) {
|
|
714
762
|
const wstatus = status || (await this.status(chainId));
|
|
715
763
|
const wallet = this.walletForStatus(chainId, wstatus);
|
|
716
764
|
const predecorated = await this.predecorateTransactions(txs, wstatus, chainId);
|
|
@@ -735,9 +783,9 @@ class Account {
|
|
|
735
783
|
nonce: 0 // The relayer also ignored the nonce
|
|
736
784
|
};
|
|
737
785
|
|
|
738
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, wstatus);
|
|
786
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus);
|
|
739
787
|
const data = core.commons.transaction.encodeBundleExecData(decoratedBundle);
|
|
740
|
-
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data);
|
|
788
|
+
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options);
|
|
741
789
|
return _extends({}, res, {
|
|
742
790
|
decorated: decoratedBundle
|
|
743
791
|
});
|
|
@@ -745,7 +793,9 @@ class Account {
|
|
|
745
793
|
async prepareTransactions(args) {
|
|
746
794
|
const status = await this.status(args.chainId);
|
|
747
795
|
const transactions = await this.fillGasLimits(args.txs, args.chainId, status);
|
|
748
|
-
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status
|
|
796
|
+
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status, {
|
|
797
|
+
simulate: args.simulateForFeeOptions
|
|
798
|
+
});
|
|
749
799
|
const flatDecorated = core.commons.transaction.unwind(this.address, gasRefundQuote.decorated.transactions);
|
|
750
800
|
return {
|
|
751
801
|
transactions,
|
|
@@ -757,9 +807,21 @@ class Account {
|
|
|
757
807
|
async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
|
|
758
808
|
const status = await this.status(chainId);
|
|
759
809
|
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
810
|
+
const hasTxs = core.commons.transaction.fromTransactionish(this.address, predecorated).length > 0;
|
|
811
|
+
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined;
|
|
812
|
+
const childBundles = await this.orchestrator.predecorateSignedTransactions({
|
|
813
|
+
chainId
|
|
814
|
+
});
|
|
815
|
+
const bundles = [];
|
|
816
|
+
if (signed !== undefined && signed.transactions.length > 0) {
|
|
817
|
+
bundles.push(signed);
|
|
818
|
+
}
|
|
819
|
+
bundles.push(...childBundles.filter(b => b.transactions.length > 0));
|
|
820
|
+
if (bundles.length === 0) {
|
|
821
|
+
// Nothing to send?
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback);
|
|
763
825
|
}
|
|
764
826
|
async signTypedData(domain, types, message, chainId, cantValidateBehavior = 'ignore') {
|
|
765
827
|
const digest = utils.encodeTypedDataDigest({
|
|
@@ -126,21 +126,23 @@ class AccountSigner {
|
|
|
126
126
|
}
|
|
127
127
|
throw new Error('No fee option available - not enough balance');
|
|
128
128
|
}
|
|
129
|
-
async sendTransaction(txsPromise) {
|
|
129
|
+
async sendTransaction(txsPromise, options) {
|
|
130
130
|
var _this$options$stubSig, _this$options2, _this$options$selectF, _this$options3, _this$options4;
|
|
131
131
|
const txs = isDeferrable(txsPromise) ? await ethers.ethers.utils.resolveProperties(txsPromise) : txsPromise;
|
|
132
132
|
const prepare = await this.account.prepareTransactions({
|
|
133
133
|
txs,
|
|
134
134
|
chainId: this.chainId,
|
|
135
|
-
stubSignatureOverrides: (_this$options$stubSig = (_this$options2 = this.options) == null ? void 0 : _this$options2.stubSignatureOverrides) != null ? _this$options$stubSig : new Map()
|
|
135
|
+
stubSignatureOverrides: (_this$options$stubSig = (_this$options2 = this.options) == null ? void 0 : _this$options2.stubSignatureOverrides) != null ? _this$options$stubSig : new Map(),
|
|
136
|
+
simulateForFeeOptions: options == null ? void 0 : options.simulateForFeeOptions
|
|
136
137
|
});
|
|
137
138
|
const selectMethod = (_this$options$selectF = (_this$options3 = this.options) == null ? void 0 : _this$options3.selectFee) != null ? _this$options$selectF : this.defaultSelectFee.bind(this);
|
|
138
139
|
const feeOption = await selectMethod(txs, prepare.feeOptions);
|
|
139
140
|
const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
|
|
140
141
|
return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
|
|
141
142
|
nonceSpace: this.options.nonceSpace
|
|
142
|
-
} : undefined);
|
|
143
|
+
} : undefined); // Will always have a transaction response
|
|
143
144
|
}
|
|
145
|
+
|
|
144
146
|
getBalance(blockTag) {
|
|
145
147
|
return this.provider.getBalance(this.account.address, blockTag);
|
|
146
148
|
}
|
|
@@ -409,6 +411,19 @@ class Account {
|
|
|
409
411
|
throw new Error(`Wallet ${this.address} is not fully migrated`);
|
|
410
412
|
}
|
|
411
413
|
}
|
|
414
|
+
async predecorateSignedTransactions(status, chainId) {
|
|
415
|
+
// Request signed predecorate transactions from child wallets
|
|
416
|
+
const bundles = await this.orchestrator.predecorateSignedTransactions({
|
|
417
|
+
chainId
|
|
418
|
+
});
|
|
419
|
+
// Get signed predecorate transaction
|
|
420
|
+
const predecorated = await this.predecorateTransactions([], status, chainId);
|
|
421
|
+
if (core.commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
422
|
+
// Sign it
|
|
423
|
+
bundles.push(await this.signTransactions(predecorated, chainId));
|
|
424
|
+
}
|
|
425
|
+
return bundles;
|
|
426
|
+
}
|
|
412
427
|
async predecorateTransactions(txs, status, chainId) {
|
|
413
428
|
// if onchain wallet config is not up to date
|
|
414
429
|
// then we should append an extra transaction that updates it
|
|
@@ -420,34 +435,49 @@ class Account {
|
|
|
420
435
|
}
|
|
421
436
|
return txs;
|
|
422
437
|
}
|
|
423
|
-
decorateTransactions(
|
|
424
|
-
|
|
425
|
-
if (
|
|
426
|
-
|
|
438
|
+
async decorateTransactions(bundles, status, chainId) {
|
|
439
|
+
var _chainId;
|
|
440
|
+
if (!Array.isArray(bundles)) {
|
|
441
|
+
// Recurse with array
|
|
442
|
+
return this.decorateTransactions([bundles], status, chainId);
|
|
427
443
|
}
|
|
444
|
+
|
|
445
|
+
// Default to chainId of first bundle when not supplied
|
|
446
|
+
chainId = (_chainId = chainId) != null ? _chainId : bundles[0].chainId;
|
|
447
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
448
|
+
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0;
|
|
449
|
+
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
450
|
+
return bundles[0];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Intent defaults to first bundle when no bootstrap transaction
|
|
454
|
+
const {
|
|
455
|
+
entrypoint
|
|
456
|
+
} = hasBootstrapTxs ? bootstrapBundle : bundles[0];
|
|
428
457
|
const decoratedBundle = {
|
|
429
|
-
entrypoint
|
|
430
|
-
chainId
|
|
431
|
-
|
|
432
|
-
|
|
458
|
+
entrypoint,
|
|
459
|
+
chainId,
|
|
460
|
+
// Intent of the first bundle is used
|
|
461
|
+
intent: bundles[0].intent,
|
|
462
|
+
transactions: [...bootstrapBundle.transactions, ...bundles.map(bundle => ({
|
|
433
463
|
to: bundle.entrypoint,
|
|
434
464
|
data: core.commons.transaction.encodeBundleExecData(bundle),
|
|
435
465
|
gasLimit: 0,
|
|
436
466
|
delegateCall: false,
|
|
437
467
|
revertOnError: true,
|
|
438
468
|
value: 0
|
|
439
|
-
}]
|
|
469
|
+
}))]
|
|
440
470
|
};
|
|
441
471
|
|
|
442
472
|
// Re-compute the meta-transaction id to use the guest module subdigest
|
|
443
473
|
if (!status.onChain.deployed) {
|
|
444
|
-
decoratedBundle.intent.id = core.commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule,
|
|
474
|
+
decoratedBundle.intent.id = core.commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, decoratedBundle.transactions);
|
|
445
475
|
}
|
|
446
476
|
return decoratedBundle;
|
|
447
477
|
}
|
|
448
|
-
decorateSignature(signature, status) {
|
|
478
|
+
async decorateSignature(signature, status) {
|
|
449
479
|
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
450
|
-
return
|
|
480
|
+
return signature;
|
|
451
481
|
}
|
|
452
482
|
const coder = this.coders.signature;
|
|
453
483
|
const chain = status.presignedConfigurations.map(c => c.signature);
|
|
@@ -466,14 +496,13 @@ class Account {
|
|
|
466
496
|
signatures
|
|
467
497
|
});
|
|
468
498
|
}
|
|
469
|
-
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore') {
|
|
499
|
+
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore', metadata) {
|
|
470
500
|
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
471
501
|
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
472
502
|
|
|
473
503
|
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
474
504
|
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
475
505
|
// the behaviour of being migrated on all chains
|
|
476
|
-
|
|
477
506
|
const chainRef = ethers.ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId;
|
|
478
507
|
const status = await this.status(chainRef);
|
|
479
508
|
this.mustBeFullyMigrated(status);
|
|
@@ -484,7 +513,7 @@ class Account {
|
|
|
484
513
|
throw new Error('Wallet cannot validate onchain');
|
|
485
514
|
}
|
|
486
515
|
const wallet = this.walletForStatus(chainId, status);
|
|
487
|
-
const signature = await wallet.signDigest(digest);
|
|
516
|
+
const signature = await wallet.signDigest(digest, metadata);
|
|
488
517
|
const decorated = decorate ? this.decorateSignature(signature, status) : signature;
|
|
489
518
|
|
|
490
519
|
// If the wallet can't validate onchain then we
|
|
@@ -503,8 +532,8 @@ class Account {
|
|
|
503
532
|
}
|
|
504
533
|
return decorated;
|
|
505
534
|
}
|
|
506
|
-
buildEIP6492Signature(signature, status, chainId) {
|
|
507
|
-
const bootstrapBundle = this.buildBootstrapTransactions(status, chainId);
|
|
535
|
+
async buildEIP6492Signature(signature, status, chainId) {
|
|
536
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
508
537
|
if (bootstrapBundle.transactions.length === 0) {
|
|
509
538
|
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions');
|
|
510
539
|
}
|
|
@@ -560,8 +589,12 @@ class Account {
|
|
|
560
589
|
* by any of the migrations.
|
|
561
590
|
*
|
|
562
591
|
*/
|
|
563
|
-
buildBootstrapTransactions(status, chainId) {
|
|
564
|
-
|
|
592
|
+
async buildBootstrapTransactions(status, chainId) {
|
|
593
|
+
var _bundle$transactions;
|
|
594
|
+
const bundle = await this.orchestrator.buildDeployTransaction({
|
|
595
|
+
chainId
|
|
596
|
+
});
|
|
597
|
+
const transactions = (_bundle$transactions = bundle == null ? void 0 : bundle.transactions) != null ? _bundle$transactions : [];
|
|
565
598
|
|
|
566
599
|
// Add wallet deployment if needed
|
|
567
600
|
if (!status.onChain.deployed) {
|
|
@@ -570,6 +603,7 @@ class Account {
|
|
|
570
603
|
const deployTransaction = wallet.Wallet.buildDeployTransaction(status.original.context, status.original.imageHash);
|
|
571
604
|
transactions.push(...deployTransaction.transactions);
|
|
572
605
|
}
|
|
606
|
+
transactions.length;
|
|
573
607
|
|
|
574
608
|
// Get pending migrations
|
|
575
609
|
transactions.push(...status.signedMigrations.map(m => ({
|
|
@@ -619,9 +653,20 @@ class Account {
|
|
|
619
653
|
const status = pstatus || (await this.status(chainId));
|
|
620
654
|
this.mustBeFullyMigrated(status);
|
|
621
655
|
const wallet = this.walletForStatus(chainId, status);
|
|
656
|
+
const metadata = {
|
|
657
|
+
address: this.address,
|
|
658
|
+
digest: '',
|
|
659
|
+
// Set in wallet.signTransactions
|
|
660
|
+
chainId,
|
|
661
|
+
config: {
|
|
662
|
+
version: this.version
|
|
663
|
+
},
|
|
664
|
+
decorate: true,
|
|
665
|
+
cantValidateBehavior: 'ignore'
|
|
666
|
+
};
|
|
622
667
|
const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
|
|
623
668
|
space: options == null ? void 0 : options.nonceSpace
|
|
624
|
-
});
|
|
669
|
+
}, metadata);
|
|
625
670
|
return _extends({}, signed, {
|
|
626
671
|
signature: await this.decorateSignature(signed.signature, status)
|
|
627
672
|
});
|
|
@@ -700,9 +745,12 @@ class Account {
|
|
|
700
745
|
};
|
|
701
746
|
}
|
|
702
747
|
async sendSignedTransactions(signedBundle, chainId, quote, pstatus, callback) {
|
|
703
|
-
|
|
748
|
+
if (!Array.isArray(signedBundle)) {
|
|
749
|
+
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback);
|
|
750
|
+
}
|
|
751
|
+
const status = pstatus || (await this.status(signedBundle[0].chainId));
|
|
704
752
|
this.mustBeFullyMigrated(status);
|
|
705
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, status);
|
|
753
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, status);
|
|
706
754
|
callback == null ? void 0 : callback(decoratedBundle);
|
|
707
755
|
return this.relayer(chainId).relay(decoratedBundle, quote);
|
|
708
756
|
}
|
|
@@ -710,7 +758,7 @@ class Account {
|
|
|
710
758
|
const wallet = this.walletForStatus(chainId, status || (await this.status(chainId)));
|
|
711
759
|
return wallet.fillGasLimits(txs);
|
|
712
760
|
}
|
|
713
|
-
async gasRefundQuotes(txs, chainId, stubSignatureOverrides, status) {
|
|
761
|
+
async gasRefundQuotes(txs, chainId, stubSignatureOverrides, status, options) {
|
|
714
762
|
const wstatus = status || (await this.status(chainId));
|
|
715
763
|
const wallet = this.walletForStatus(chainId, wstatus);
|
|
716
764
|
const predecorated = await this.predecorateTransactions(txs, wstatus, chainId);
|
|
@@ -735,9 +783,9 @@ class Account {
|
|
|
735
783
|
nonce: 0 // The relayer also ignored the nonce
|
|
736
784
|
};
|
|
737
785
|
|
|
738
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, wstatus);
|
|
786
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus);
|
|
739
787
|
const data = core.commons.transaction.encodeBundleExecData(decoratedBundle);
|
|
740
|
-
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data);
|
|
788
|
+
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options);
|
|
741
789
|
return _extends({}, res, {
|
|
742
790
|
decorated: decoratedBundle
|
|
743
791
|
});
|
|
@@ -745,7 +793,9 @@ class Account {
|
|
|
745
793
|
async prepareTransactions(args) {
|
|
746
794
|
const status = await this.status(args.chainId);
|
|
747
795
|
const transactions = await this.fillGasLimits(args.txs, args.chainId, status);
|
|
748
|
-
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status
|
|
796
|
+
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status, {
|
|
797
|
+
simulate: args.simulateForFeeOptions
|
|
798
|
+
});
|
|
749
799
|
const flatDecorated = core.commons.transaction.unwind(this.address, gasRefundQuote.decorated.transactions);
|
|
750
800
|
return {
|
|
751
801
|
transactions,
|
|
@@ -757,9 +807,21 @@ class Account {
|
|
|
757
807
|
async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
|
|
758
808
|
const status = await this.status(chainId);
|
|
759
809
|
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
810
|
+
const hasTxs = core.commons.transaction.fromTransactionish(this.address, predecorated).length > 0;
|
|
811
|
+
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined;
|
|
812
|
+
const childBundles = await this.orchestrator.predecorateSignedTransactions({
|
|
813
|
+
chainId
|
|
814
|
+
});
|
|
815
|
+
const bundles = [];
|
|
816
|
+
if (signed !== undefined && signed.transactions.length > 0) {
|
|
817
|
+
bundles.push(signed);
|
|
818
|
+
}
|
|
819
|
+
bundles.push(...childBundles.filter(b => b.transactions.length > 0));
|
|
820
|
+
if (bundles.length === 0) {
|
|
821
|
+
// Nothing to send?
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback);
|
|
763
825
|
}
|
|
764
826
|
async signTypedData(domain, types, message, chainId, cantValidateBehavior = 'ignore') {
|
|
765
827
|
const digest = utils.encodeTypedDataDigest({
|
|
@@ -122,21 +122,23 @@ class AccountSigner {
|
|
|
122
122
|
}
|
|
123
123
|
throw new Error('No fee option available - not enough balance');
|
|
124
124
|
}
|
|
125
|
-
async sendTransaction(txsPromise) {
|
|
125
|
+
async sendTransaction(txsPromise, options) {
|
|
126
126
|
var _this$options$stubSig, _this$options2, _this$options$selectF, _this$options3, _this$options4;
|
|
127
127
|
const txs = isDeferrable(txsPromise) ? await ethers.utils.resolveProperties(txsPromise) : txsPromise;
|
|
128
128
|
const prepare = await this.account.prepareTransactions({
|
|
129
129
|
txs,
|
|
130
130
|
chainId: this.chainId,
|
|
131
|
-
stubSignatureOverrides: (_this$options$stubSig = (_this$options2 = this.options) == null ? void 0 : _this$options2.stubSignatureOverrides) != null ? _this$options$stubSig : new Map()
|
|
131
|
+
stubSignatureOverrides: (_this$options$stubSig = (_this$options2 = this.options) == null ? void 0 : _this$options2.stubSignatureOverrides) != null ? _this$options$stubSig : new Map(),
|
|
132
|
+
simulateForFeeOptions: options == null ? void 0 : options.simulateForFeeOptions
|
|
132
133
|
});
|
|
133
134
|
const selectMethod = (_this$options$selectF = (_this$options3 = this.options) == null ? void 0 : _this$options3.selectFee) != null ? _this$options$selectF : this.defaultSelectFee.bind(this);
|
|
134
135
|
const feeOption = await selectMethod(txs, prepare.feeOptions);
|
|
135
136
|
const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
|
|
136
137
|
return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
|
|
137
138
|
nonceSpace: this.options.nonceSpace
|
|
138
|
-
} : undefined);
|
|
139
|
+
} : undefined); // Will always have a transaction response
|
|
139
140
|
}
|
|
141
|
+
|
|
140
142
|
getBalance(blockTag) {
|
|
141
143
|
return this.provider.getBalance(this.account.address, blockTag);
|
|
142
144
|
}
|
|
@@ -405,6 +407,19 @@ class Account {
|
|
|
405
407
|
throw new Error(`Wallet ${this.address} is not fully migrated`);
|
|
406
408
|
}
|
|
407
409
|
}
|
|
410
|
+
async predecorateSignedTransactions(status, chainId) {
|
|
411
|
+
// Request signed predecorate transactions from child wallets
|
|
412
|
+
const bundles = await this.orchestrator.predecorateSignedTransactions({
|
|
413
|
+
chainId
|
|
414
|
+
});
|
|
415
|
+
// Get signed predecorate transaction
|
|
416
|
+
const predecorated = await this.predecorateTransactions([], status, chainId);
|
|
417
|
+
if (commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
418
|
+
// Sign it
|
|
419
|
+
bundles.push(await this.signTransactions(predecorated, chainId));
|
|
420
|
+
}
|
|
421
|
+
return bundles;
|
|
422
|
+
}
|
|
408
423
|
async predecorateTransactions(txs, status, chainId) {
|
|
409
424
|
// if onchain wallet config is not up to date
|
|
410
425
|
// then we should append an extra transaction that updates it
|
|
@@ -416,34 +431,49 @@ class Account {
|
|
|
416
431
|
}
|
|
417
432
|
return txs;
|
|
418
433
|
}
|
|
419
|
-
decorateTransactions(
|
|
420
|
-
|
|
421
|
-
if (
|
|
422
|
-
|
|
434
|
+
async decorateTransactions(bundles, status, chainId) {
|
|
435
|
+
var _chainId;
|
|
436
|
+
if (!Array.isArray(bundles)) {
|
|
437
|
+
// Recurse with array
|
|
438
|
+
return this.decorateTransactions([bundles], status, chainId);
|
|
423
439
|
}
|
|
440
|
+
|
|
441
|
+
// Default to chainId of first bundle when not supplied
|
|
442
|
+
chainId = (_chainId = chainId) != null ? _chainId : bundles[0].chainId;
|
|
443
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
444
|
+
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0;
|
|
445
|
+
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
446
|
+
return bundles[0];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Intent defaults to first bundle when no bootstrap transaction
|
|
450
|
+
const {
|
|
451
|
+
entrypoint
|
|
452
|
+
} = hasBootstrapTxs ? bootstrapBundle : bundles[0];
|
|
424
453
|
const decoratedBundle = {
|
|
425
|
-
entrypoint
|
|
426
|
-
chainId
|
|
427
|
-
|
|
428
|
-
|
|
454
|
+
entrypoint,
|
|
455
|
+
chainId,
|
|
456
|
+
// Intent of the first bundle is used
|
|
457
|
+
intent: bundles[0].intent,
|
|
458
|
+
transactions: [...bootstrapBundle.transactions, ...bundles.map(bundle => ({
|
|
429
459
|
to: bundle.entrypoint,
|
|
430
460
|
data: commons.transaction.encodeBundleExecData(bundle),
|
|
431
461
|
gasLimit: 0,
|
|
432
462
|
delegateCall: false,
|
|
433
463
|
revertOnError: true,
|
|
434
464
|
value: 0
|
|
435
|
-
}]
|
|
465
|
+
}))]
|
|
436
466
|
};
|
|
437
467
|
|
|
438
468
|
// Re-compute the meta-transaction id to use the guest module subdigest
|
|
439
469
|
if (!status.onChain.deployed) {
|
|
440
|
-
decoratedBundle.intent.id = commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule,
|
|
470
|
+
decoratedBundle.intent.id = commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, decoratedBundle.transactions);
|
|
441
471
|
}
|
|
442
472
|
return decoratedBundle;
|
|
443
473
|
}
|
|
444
|
-
decorateSignature(signature, status) {
|
|
474
|
+
async decorateSignature(signature, status) {
|
|
445
475
|
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
446
|
-
return
|
|
476
|
+
return signature;
|
|
447
477
|
}
|
|
448
478
|
const coder = this.coders.signature;
|
|
449
479
|
const chain = status.presignedConfigurations.map(c => c.signature);
|
|
@@ -462,14 +492,13 @@ class Account {
|
|
|
462
492
|
signatures
|
|
463
493
|
});
|
|
464
494
|
}
|
|
465
|
-
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore') {
|
|
495
|
+
async signDigest(digest, chainId, decorate = true, cantValidateBehavior = 'ignore', metadata) {
|
|
466
496
|
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
467
497
|
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
468
498
|
|
|
469
499
|
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
470
500
|
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
471
501
|
// the behaviour of being migrated on all chains
|
|
472
|
-
|
|
473
502
|
const chainRef = ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId;
|
|
474
503
|
const status = await this.status(chainRef);
|
|
475
504
|
this.mustBeFullyMigrated(status);
|
|
@@ -480,7 +509,7 @@ class Account {
|
|
|
480
509
|
throw new Error('Wallet cannot validate onchain');
|
|
481
510
|
}
|
|
482
511
|
const wallet = this.walletForStatus(chainId, status);
|
|
483
|
-
const signature = await wallet.signDigest(digest);
|
|
512
|
+
const signature = await wallet.signDigest(digest, metadata);
|
|
484
513
|
const decorated = decorate ? this.decorateSignature(signature, status) : signature;
|
|
485
514
|
|
|
486
515
|
// If the wallet can't validate onchain then we
|
|
@@ -499,8 +528,8 @@ class Account {
|
|
|
499
528
|
}
|
|
500
529
|
return decorated;
|
|
501
530
|
}
|
|
502
|
-
buildEIP6492Signature(signature, status, chainId) {
|
|
503
|
-
const bootstrapBundle = this.buildBootstrapTransactions(status, chainId);
|
|
531
|
+
async buildEIP6492Signature(signature, status, chainId) {
|
|
532
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId);
|
|
504
533
|
if (bootstrapBundle.transactions.length === 0) {
|
|
505
534
|
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions');
|
|
506
535
|
}
|
|
@@ -556,8 +585,12 @@ class Account {
|
|
|
556
585
|
* by any of the migrations.
|
|
557
586
|
*
|
|
558
587
|
*/
|
|
559
|
-
buildBootstrapTransactions(status, chainId) {
|
|
560
|
-
|
|
588
|
+
async buildBootstrapTransactions(status, chainId) {
|
|
589
|
+
var _bundle$transactions;
|
|
590
|
+
const bundle = await this.orchestrator.buildDeployTransaction({
|
|
591
|
+
chainId
|
|
592
|
+
});
|
|
593
|
+
const transactions = (_bundle$transactions = bundle == null ? void 0 : bundle.transactions) != null ? _bundle$transactions : [];
|
|
561
594
|
|
|
562
595
|
// Add wallet deployment if needed
|
|
563
596
|
if (!status.onChain.deployed) {
|
|
@@ -566,6 +599,7 @@ class Account {
|
|
|
566
599
|
const deployTransaction = Wallet.buildDeployTransaction(status.original.context, status.original.imageHash);
|
|
567
600
|
transactions.push(...deployTransaction.transactions);
|
|
568
601
|
}
|
|
602
|
+
transactions.length;
|
|
569
603
|
|
|
570
604
|
// Get pending migrations
|
|
571
605
|
transactions.push(...status.signedMigrations.map(m => ({
|
|
@@ -615,9 +649,20 @@ class Account {
|
|
|
615
649
|
const status = pstatus || (await this.status(chainId));
|
|
616
650
|
this.mustBeFullyMigrated(status);
|
|
617
651
|
const wallet = this.walletForStatus(chainId, status);
|
|
652
|
+
const metadata = {
|
|
653
|
+
address: this.address,
|
|
654
|
+
digest: '',
|
|
655
|
+
// Set in wallet.signTransactions
|
|
656
|
+
chainId,
|
|
657
|
+
config: {
|
|
658
|
+
version: this.version
|
|
659
|
+
},
|
|
660
|
+
decorate: true,
|
|
661
|
+
cantValidateBehavior: 'ignore'
|
|
662
|
+
};
|
|
618
663
|
const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
|
|
619
664
|
space: options == null ? void 0 : options.nonceSpace
|
|
620
|
-
});
|
|
665
|
+
}, metadata);
|
|
621
666
|
return _extends({}, signed, {
|
|
622
667
|
signature: await this.decorateSignature(signed.signature, status)
|
|
623
668
|
});
|
|
@@ -696,9 +741,12 @@ class Account {
|
|
|
696
741
|
};
|
|
697
742
|
}
|
|
698
743
|
async sendSignedTransactions(signedBundle, chainId, quote, pstatus, callback) {
|
|
699
|
-
|
|
744
|
+
if (!Array.isArray(signedBundle)) {
|
|
745
|
+
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback);
|
|
746
|
+
}
|
|
747
|
+
const status = pstatus || (await this.status(signedBundle[0].chainId));
|
|
700
748
|
this.mustBeFullyMigrated(status);
|
|
701
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, status);
|
|
749
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, status);
|
|
702
750
|
callback == null ? void 0 : callback(decoratedBundle);
|
|
703
751
|
return this.relayer(chainId).relay(decoratedBundle, quote);
|
|
704
752
|
}
|
|
@@ -706,7 +754,7 @@ class Account {
|
|
|
706
754
|
const wallet = this.walletForStatus(chainId, status || (await this.status(chainId)));
|
|
707
755
|
return wallet.fillGasLimits(txs);
|
|
708
756
|
}
|
|
709
|
-
async gasRefundQuotes(txs, chainId, stubSignatureOverrides, status) {
|
|
757
|
+
async gasRefundQuotes(txs, chainId, stubSignatureOverrides, status, options) {
|
|
710
758
|
const wstatus = status || (await this.status(chainId));
|
|
711
759
|
const wallet = this.walletForStatus(chainId, wstatus);
|
|
712
760
|
const predecorated = await this.predecorateTransactions(txs, wstatus, chainId);
|
|
@@ -731,9 +779,9 @@ class Account {
|
|
|
731
779
|
nonce: 0 // The relayer also ignored the nonce
|
|
732
780
|
};
|
|
733
781
|
|
|
734
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, wstatus);
|
|
782
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus);
|
|
735
783
|
const data = commons.transaction.encodeBundleExecData(decoratedBundle);
|
|
736
|
-
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data);
|
|
784
|
+
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options);
|
|
737
785
|
return _extends({}, res, {
|
|
738
786
|
decorated: decoratedBundle
|
|
739
787
|
});
|
|
@@ -741,7 +789,9 @@ class Account {
|
|
|
741
789
|
async prepareTransactions(args) {
|
|
742
790
|
const status = await this.status(args.chainId);
|
|
743
791
|
const transactions = await this.fillGasLimits(args.txs, args.chainId, status);
|
|
744
|
-
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status
|
|
792
|
+
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status, {
|
|
793
|
+
simulate: args.simulateForFeeOptions
|
|
794
|
+
});
|
|
745
795
|
const flatDecorated = commons.transaction.unwind(this.address, gasRefundQuote.decorated.transactions);
|
|
746
796
|
return {
|
|
747
797
|
transactions,
|
|
@@ -753,9 +803,21 @@ class Account {
|
|
|
753
803
|
async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
|
|
754
804
|
const status = await this.status(chainId);
|
|
755
805
|
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
806
|
+
const hasTxs = commons.transaction.fromTransactionish(this.address, predecorated).length > 0;
|
|
807
|
+
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined;
|
|
808
|
+
const childBundles = await this.orchestrator.predecorateSignedTransactions({
|
|
809
|
+
chainId
|
|
810
|
+
});
|
|
811
|
+
const bundles = [];
|
|
812
|
+
if (signed !== undefined && signed.transactions.length > 0) {
|
|
813
|
+
bundles.push(signed);
|
|
814
|
+
}
|
|
815
|
+
bundles.push(...childBundles.filter(b => b.transactions.length > 0));
|
|
816
|
+
if (bundles.length === 0) {
|
|
817
|
+
// Nothing to send?
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback);
|
|
759
821
|
}
|
|
760
822
|
async signTypedData(domain, types, message, chainId, cantValidateBehavior = 'ignore') {
|
|
761
823
|
const digest = encodeTypedDataDigest({
|
|
@@ -76,11 +76,12 @@ export declare class Account {
|
|
|
76
76
|
walletFor(chainId: ethers.BigNumberish, context: commons.context.WalletContext, config: commons.config.Config, coders: typeof this.coders): Wallet;
|
|
77
77
|
status(chainId: ethers.BigNumberish, longestPath?: boolean): Promise<AccountStatus>;
|
|
78
78
|
private mustBeFullyMigrated;
|
|
79
|
+
predecorateSignedTransactions(status: AccountStatus, chainId: ethers.BigNumberish): Promise<commons.transaction.SignedTransactionBundle[]>;
|
|
79
80
|
predecorateTransactions(txs: commons.transaction.Transactionish, status: AccountStatus, chainId: ethers.BigNumberish): Promise<commons.transaction.Transactionish>;
|
|
80
|
-
decorateTransactions(
|
|
81
|
+
decorateTransactions(bundles: commons.transaction.IntendedTransactionBundle | commons.transaction.IntendedTransactionBundle[], status: AccountStatus, chainId?: ethers.BigNumberish): Promise<commons.transaction.IntendedTransactionBundle>;
|
|
81
82
|
decorateSignature<T extends ethers.BytesLike>(signature: T, status: Partial<Pick<AccountStatus, 'presignedConfigurations'>>): Promise<T | string>;
|
|
82
83
|
publishWitness(): Promise<void>;
|
|
83
|
-
signDigest(digest: ethers.BytesLike, chainId: ethers.BigNumberish, decorate?: boolean, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw'): Promise<string>;
|
|
84
|
+
signDigest(digest: ethers.BytesLike, chainId: ethers.BigNumberish, decorate?: boolean, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw', metadata?: object): Promise<string>;
|
|
84
85
|
private buildEIP6492Signature;
|
|
85
86
|
editConfig(changes: {
|
|
86
87
|
add?: commons.config.SimpleSigner[];
|
|
@@ -99,7 +100,7 @@ export declare class Account {
|
|
|
99
100
|
* by any of the migrations.
|
|
100
101
|
*
|
|
101
102
|
*/
|
|
102
|
-
buildBootstrapTransactions(status: AccountStatus, chainId: ethers.BigNumberish): commons.transaction.IntendedTransactionBundle
|
|
103
|
+
buildBootstrapTransactions(status: AccountStatus, chainId: ethers.BigNumberish): Promise<commons.transaction.IntendedTransactionBundle>;
|
|
103
104
|
bootstrapTransactions(chainId: ethers.BigNumberish, prestatus?: AccountStatus): Promise<Omit<commons.transaction.IntendedTransactionBundle, 'chainId'>>;
|
|
104
105
|
doBootstrap(chainId: ethers.BigNumberish, feeQuote?: FeeQuote, prestatus?: AccountStatus): Promise<commons.transaction.TransactionResponse<any>>;
|
|
105
106
|
signMessage(message: ethers.BytesLike, chainId: ethers.BigNumberish, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw'): Promise<string>;
|
|
@@ -115,9 +116,11 @@ export declare class Account {
|
|
|
115
116
|
migratedAllChains: boolean;
|
|
116
117
|
failedChains: number[];
|
|
117
118
|
}>;
|
|
118
|
-
sendSignedTransactions(signedBundle: commons.transaction.IntendedTransactionBundle, chainId: ethers.BigNumberish, quote?: FeeQuote, pstatus?: AccountStatus, callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void): Promise<ethers.providers.TransactionResponse>;
|
|
119
|
+
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
120
|
fillGasLimits(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, status?: AccountStatus): Promise<commons.transaction.SimulatedTransaction[]>;
|
|
120
|
-
gasRefundQuotes(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, stubSignatureOverrides: Map<string, string>, status?: AccountStatus
|
|
121
|
+
gasRefundQuotes(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, stubSignatureOverrides: Map<string, string>, status?: AccountStatus, options?: {
|
|
122
|
+
simulate?: boolean;
|
|
123
|
+
}): Promise<{
|
|
121
124
|
options: FeeOption[];
|
|
122
125
|
quote?: FeeQuote;
|
|
123
126
|
decorated: commons.transaction.IntendedTransactionBundle;
|
|
@@ -126,10 +129,11 @@ export declare class Account {
|
|
|
126
129
|
txs: commons.transaction.Transactionish;
|
|
127
130
|
chainId: ethers.BigNumberish;
|
|
128
131
|
stubSignatureOverrides: Map<string, string>;
|
|
132
|
+
simulateForFeeOptions?: boolean;
|
|
129
133
|
}): Promise<PreparedTransactions>;
|
|
130
134
|
sendTransaction(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, quote?: FeeQuote, skipPreDecorate?: boolean, callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void, options?: {
|
|
131
135
|
nonceSpace?: ethers.BigNumberish;
|
|
132
|
-
}): Promise<ethers.providers.TransactionResponse>;
|
|
136
|
+
}): Promise<ethers.providers.TransactionResponse | undefined>;
|
|
133
137
|
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, message: Record<string, any>, chainId: ethers.BigNumberish, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw'): Promise<string>;
|
|
134
138
|
getAllSigners(): Promise<{
|
|
135
139
|
address: string;
|
|
@@ -19,7 +19,9 @@ export declare class AccountSigner implements ethers.Signer {
|
|
|
19
19
|
getAddress(): Promise<string>;
|
|
20
20
|
signMessage(message: string | ethers.utils.Bytes): Promise<string>;
|
|
21
21
|
private defaultSelectFee;
|
|
22
|
-
sendTransaction(txsPromise: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish
|
|
22
|
+
sendTransaction(txsPromise: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish, options?: {
|
|
23
|
+
simulateForFeeOptions?: boolean;
|
|
24
|
+
}): Promise<ethers.providers.TransactionResponse>;
|
|
23
25
|
getBalance(blockTag?: ethers.providers.BlockTag | undefined): Promise<ethers.BigNumber>;
|
|
24
26
|
call(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>, blockTag?: ethers.providers.BlockTag | undefined): Promise<string>;
|
|
25
27
|
resolveName(name: string): Promise<string>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xsequence/account",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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/core": "1.
|
|
14
|
-
"@0xsequence/migration": "1.
|
|
15
|
-
"@0xsequence/
|
|
16
|
-
"@0xsequence/relayer": "1.
|
|
17
|
-
"@0xsequence/utils": "1.
|
|
18
|
-
"@0xsequence/
|
|
19
|
-
"@0xsequence/wallet": "1.
|
|
13
|
+
"@0xsequence/core": "1.3.0",
|
|
14
|
+
"@0xsequence/migration": "1.3.0",
|
|
15
|
+
"@0xsequence/network": "1.3.0",
|
|
16
|
+
"@0xsequence/relayer": "1.3.0",
|
|
17
|
+
"@0xsequence/utils": "1.3.0",
|
|
18
|
+
"@0xsequence/sessions": "1.3.0",
|
|
19
|
+
"@0xsequence/wallet": "1.3.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.3.0",
|
|
25
|
+
"@0xsequence/tests": "1.3.0"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"src",
|
package/src/account.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
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'
|
|
@@ -352,6 +353,21 @@ export class Account {
|
|
|
352
353
|
}
|
|
353
354
|
}
|
|
354
355
|
|
|
356
|
+
async predecorateSignedTransactions(
|
|
357
|
+
status: AccountStatus,
|
|
358
|
+
chainId: ethers.BigNumberish
|
|
359
|
+
): Promise<commons.transaction.SignedTransactionBundle[]> {
|
|
360
|
+
// Request signed predecorate transactions from child wallets
|
|
361
|
+
const bundles = await this.orchestrator.predecorateSignedTransactions({ chainId })
|
|
362
|
+
// Get signed predecorate transaction
|
|
363
|
+
const predecorated = await this.predecorateTransactions([], status, chainId)
|
|
364
|
+
if (commons.transaction.fromTransactionish(this.address, predecorated).length > 0) {
|
|
365
|
+
// Sign it
|
|
366
|
+
bundles.push(await this.signTransactions(predecorated, chainId))
|
|
367
|
+
}
|
|
368
|
+
return bundles
|
|
369
|
+
}
|
|
370
|
+
|
|
355
371
|
async predecorateTransactions(
|
|
356
372
|
txs: commons.transaction.Transactionish,
|
|
357
373
|
status: AccountStatus,
|
|
@@ -369,29 +385,46 @@ export class Account {
|
|
|
369
385
|
return txs
|
|
370
386
|
}
|
|
371
387
|
|
|
372
|
-
decorateTransactions(
|
|
373
|
-
|
|
374
|
-
status: AccountStatus
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (
|
|
378
|
-
|
|
388
|
+
async decorateTransactions(
|
|
389
|
+
bundles: commons.transaction.IntendedTransactionBundle | commons.transaction.IntendedTransactionBundle[],
|
|
390
|
+
status: AccountStatus,
|
|
391
|
+
chainId?: ethers.BigNumberish
|
|
392
|
+
): Promise<commons.transaction.IntendedTransactionBundle> {
|
|
393
|
+
if (!Array.isArray(bundles)) {
|
|
394
|
+
// Recurse with array
|
|
395
|
+
return this.decorateTransactions([bundles], status, chainId)
|
|
379
396
|
}
|
|
380
397
|
|
|
398
|
+
// Default to chainId of first bundle when not supplied
|
|
399
|
+
chainId = chainId ?? bundles[0].chainId
|
|
400
|
+
|
|
401
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId)
|
|
402
|
+
const hasBootstrapTxs = bootstrapBundle.transactions.length > 0
|
|
403
|
+
|
|
404
|
+
if (!hasBootstrapTxs && bundles.length === 1) {
|
|
405
|
+
return bundles[0]
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Intent defaults to first bundle when no bootstrap transaction
|
|
409
|
+
const { entrypoint } = hasBootstrapTxs ? bootstrapBundle : bundles[0]
|
|
410
|
+
|
|
381
411
|
const decoratedBundle = {
|
|
382
|
-
entrypoint
|
|
383
|
-
chainId
|
|
384
|
-
|
|
412
|
+
entrypoint,
|
|
413
|
+
chainId,
|
|
414
|
+
// Intent of the first bundle is used
|
|
415
|
+
intent: bundles[0].intent,
|
|
385
416
|
transactions: [
|
|
386
417
|
...bootstrapBundle.transactions,
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
418
|
+
...bundles.map(
|
|
419
|
+
(bundle): commons.transaction.Transaction => ({
|
|
420
|
+
to: bundle.entrypoint,
|
|
421
|
+
data: commons.transaction.encodeBundleExecData(bundle),
|
|
422
|
+
gasLimit: 0,
|
|
423
|
+
delegateCall: false,
|
|
424
|
+
revertOnError: true,
|
|
425
|
+
value: 0
|
|
426
|
+
})
|
|
427
|
+
)
|
|
395
428
|
]
|
|
396
429
|
}
|
|
397
430
|
|
|
@@ -399,7 +432,7 @@ export class Account {
|
|
|
399
432
|
if (!status.onChain.deployed) {
|
|
400
433
|
decoratedBundle.intent.id = commons.transaction.subdigestOfGuestModuleTransactions(
|
|
401
434
|
this.contexts[this.version].guestModule,
|
|
402
|
-
|
|
435
|
+
chainId,
|
|
403
436
|
decoratedBundle.transactions
|
|
404
437
|
)
|
|
405
438
|
}
|
|
@@ -407,12 +440,12 @@ export class Account {
|
|
|
407
440
|
return decoratedBundle
|
|
408
441
|
}
|
|
409
442
|
|
|
410
|
-
decorateSignature<T extends ethers.BytesLike>(
|
|
443
|
+
async decorateSignature<T extends ethers.BytesLike>(
|
|
411
444
|
signature: T,
|
|
412
445
|
status: Partial<Pick<AccountStatus, 'presignedConfigurations'>>
|
|
413
446
|
): Promise<T | string> {
|
|
414
447
|
if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) {
|
|
415
|
-
return
|
|
448
|
+
return signature
|
|
416
449
|
}
|
|
417
450
|
|
|
418
451
|
const coder = this.coders.signature
|
|
@@ -434,7 +467,8 @@ export class Account {
|
|
|
434
467
|
digest: ethers.BytesLike,
|
|
435
468
|
chainId: ethers.BigNumberish,
|
|
436
469
|
decorate: boolean = true,
|
|
437
|
-
cantValidateBehavior: 'ignore' | 'eip6492' | 'throw' = 'ignore'
|
|
470
|
+
cantValidateBehavior: 'ignore' | 'eip6492' | 'throw' = 'ignore',
|
|
471
|
+
metadata?: object
|
|
438
472
|
): Promise<string> {
|
|
439
473
|
// If we are signing a digest for chainId zero then we can never be fully migrated
|
|
440
474
|
// because Sequence v1 doesn't allow for signing a message on "all chains"
|
|
@@ -442,10 +476,8 @@ export class Account {
|
|
|
442
476
|
// So we ignore the state on "chain zero" and instead use one of the states of the networks
|
|
443
477
|
// wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic
|
|
444
478
|
// the behaviour of being migrated on all chains
|
|
445
|
-
|
|
446
479
|
const chainRef = ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId
|
|
447
480
|
const status = await this.status(chainRef)
|
|
448
|
-
|
|
449
481
|
this.mustBeFullyMigrated(status)
|
|
450
482
|
|
|
451
483
|
// Check if we can validate onchain and what to do if we can't
|
|
@@ -455,7 +487,7 @@ export class Account {
|
|
|
455
487
|
}
|
|
456
488
|
|
|
457
489
|
const wallet = this.walletForStatus(chainId, status)
|
|
458
|
-
const signature = await wallet.signDigest(digest)
|
|
490
|
+
const signature = await wallet.signDigest(digest, metadata)
|
|
459
491
|
|
|
460
492
|
const decorated = decorate ? this.decorateSignature(signature, status) : signature
|
|
461
493
|
|
|
@@ -478,8 +510,8 @@ export class Account {
|
|
|
478
510
|
return decorated
|
|
479
511
|
}
|
|
480
512
|
|
|
481
|
-
private buildEIP6492Signature(signature: string, status: AccountStatus, chainId: ethers.BigNumberish): string {
|
|
482
|
-
const bootstrapBundle = this.buildBootstrapTransactions(status, chainId)
|
|
513
|
+
private async buildEIP6492Signature(signature: string, status: AccountStatus, chainId: ethers.BigNumberish): Promise<string> {
|
|
514
|
+
const bootstrapBundle = await this.buildBootstrapTransactions(status, chainId)
|
|
483
515
|
if (bootstrapBundle.transactions.length === 0) {
|
|
484
516
|
throw new Error('Cannot build EIP-6492 signature without bootstrap transactions')
|
|
485
517
|
}
|
|
@@ -550,8 +582,12 @@ export class Account {
|
|
|
550
582
|
* by any of the migrations.
|
|
551
583
|
*
|
|
552
584
|
*/
|
|
553
|
-
buildBootstrapTransactions(
|
|
554
|
-
|
|
585
|
+
async buildBootstrapTransactions(
|
|
586
|
+
status: AccountStatus,
|
|
587
|
+
chainId: ethers.BigNumberish
|
|
588
|
+
): Promise<commons.transaction.IntendedTransactionBundle> {
|
|
589
|
+
const bundle = await this.orchestrator.buildDeployTransaction({ chainId })
|
|
590
|
+
const transactions: commons.transaction.Transaction[] = bundle?.transactions ?? []
|
|
555
591
|
|
|
556
592
|
// Add wallet deployment if needed
|
|
557
593
|
if (!status.onChain.deployed) {
|
|
@@ -561,6 +597,7 @@ export class Account {
|
|
|
561
597
|
|
|
562
598
|
transactions.push(...deployTransaction.transactions)
|
|
563
599
|
}
|
|
600
|
+
const len = transactions.length
|
|
564
601
|
|
|
565
602
|
// Get pending migrations
|
|
566
603
|
transactions.push(
|
|
@@ -622,7 +659,17 @@ export class Account {
|
|
|
622
659
|
this.mustBeFullyMigrated(status)
|
|
623
660
|
|
|
624
661
|
const wallet = this.walletForStatus(chainId, status)
|
|
625
|
-
|
|
662
|
+
|
|
663
|
+
const metadata: WalletSignRequestMetadata = {
|
|
664
|
+
address: this.address,
|
|
665
|
+
digest: '', // Set in wallet.signTransactions
|
|
666
|
+
chainId,
|
|
667
|
+
config: { version: this.version },
|
|
668
|
+
decorate: true,
|
|
669
|
+
cantValidateBehavior: 'ignore'
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const signed = await wallet.signTransactions(txs, options?.nonceSpace && { space: options?.nonceSpace }, metadata)
|
|
626
673
|
|
|
627
674
|
return {
|
|
628
675
|
...signed,
|
|
@@ -707,16 +754,19 @@ export class Account {
|
|
|
707
754
|
}
|
|
708
755
|
|
|
709
756
|
async sendSignedTransactions(
|
|
710
|
-
signedBundle: commons.transaction.IntendedTransactionBundle,
|
|
757
|
+
signedBundle: commons.transaction.IntendedTransactionBundle | commons.transaction.IntendedTransactionBundle[],
|
|
711
758
|
chainId: ethers.BigNumberish,
|
|
712
759
|
quote?: FeeQuote,
|
|
713
760
|
pstatus?: AccountStatus,
|
|
714
761
|
callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void
|
|
715
762
|
): Promise<ethers.providers.TransactionResponse> {
|
|
716
|
-
|
|
763
|
+
if (!Array.isArray(signedBundle)) {
|
|
764
|
+
return this.sendSignedTransactions([signedBundle], chainId, quote, pstatus, callback)
|
|
765
|
+
}
|
|
766
|
+
const status = pstatus || (await this.status(signedBundle[0].chainId))
|
|
717
767
|
this.mustBeFullyMigrated(status)
|
|
718
768
|
|
|
719
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, status)
|
|
769
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, status)
|
|
720
770
|
callback?.(decoratedBundle)
|
|
721
771
|
|
|
722
772
|
return this.relayer(chainId).relay(decoratedBundle, quote)
|
|
@@ -735,7 +785,10 @@ export class Account {
|
|
|
735
785
|
txs: commons.transaction.Transactionish,
|
|
736
786
|
chainId: ethers.BigNumberish,
|
|
737
787
|
stubSignatureOverrides: Map<string, string>,
|
|
738
|
-
status?: AccountStatus
|
|
788
|
+
status?: AccountStatus,
|
|
789
|
+
options?: {
|
|
790
|
+
simulate?: boolean
|
|
791
|
+
}
|
|
739
792
|
): Promise<{
|
|
740
793
|
options: FeeOption[]
|
|
741
794
|
quote?: FeeQuote
|
|
@@ -766,9 +819,9 @@ export class Account {
|
|
|
766
819
|
nonce: 0 // The relayer also ignored the nonce
|
|
767
820
|
}
|
|
768
821
|
|
|
769
|
-
const decoratedBundle = this.decorateTransactions(signedBundle, wstatus)
|
|
822
|
+
const decoratedBundle = await this.decorateTransactions(signedBundle, wstatus)
|
|
770
823
|
const data = commons.transaction.encodeBundleExecData(decoratedBundle)
|
|
771
|
-
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data)
|
|
824
|
+
const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data, options)
|
|
772
825
|
return { ...res, decorated: decoratedBundle }
|
|
773
826
|
}
|
|
774
827
|
|
|
@@ -776,11 +829,14 @@ export class Account {
|
|
|
776
829
|
txs: commons.transaction.Transactionish
|
|
777
830
|
chainId: ethers.BigNumberish
|
|
778
831
|
stubSignatureOverrides: Map<string, string>
|
|
832
|
+
simulateForFeeOptions?: boolean
|
|
779
833
|
}): Promise<PreparedTransactions> {
|
|
780
834
|
const status = await this.status(args.chainId)
|
|
781
835
|
|
|
782
836
|
const transactions = await this.fillGasLimits(args.txs, args.chainId, status)
|
|
783
|
-
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status
|
|
837
|
+
const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status, {
|
|
838
|
+
simulate: args.simulateForFeeOptions
|
|
839
|
+
})
|
|
784
840
|
const flatDecorated = commons.transaction.unwind(this.address, gasRefundQuote.decorated.transactions)
|
|
785
841
|
|
|
786
842
|
return {
|
|
@@ -800,13 +856,27 @@ export class Account {
|
|
|
800
856
|
options?: {
|
|
801
857
|
nonceSpace?: ethers.BigNumberish
|
|
802
858
|
}
|
|
803
|
-
): Promise<ethers.providers.TransactionResponse> {
|
|
859
|
+
): Promise<ethers.providers.TransactionResponse | undefined> {
|
|
804
860
|
const status = await this.status(chainId)
|
|
805
861
|
|
|
806
862
|
const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId)
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
|
|
863
|
+
const hasTxs = commons.transaction.fromTransactionish(this.address, predecorated).length > 0
|
|
864
|
+
const signed = hasTxs ? await this.signTransactions(predecorated, chainId, undefined, options) : undefined
|
|
865
|
+
|
|
866
|
+
const childBundles = await this.orchestrator.predecorateSignedTransactions({ chainId })
|
|
867
|
+
|
|
868
|
+
const bundles: commons.transaction.SignedTransactionBundle[] = []
|
|
869
|
+
if (signed !== undefined && signed.transactions.length > 0) {
|
|
870
|
+
bundles.push(signed)
|
|
871
|
+
}
|
|
872
|
+
bundles.push(...childBundles.filter(b => b.transactions.length > 0))
|
|
873
|
+
|
|
874
|
+
if (bundles.length === 0) {
|
|
875
|
+
// Nothing to send?
|
|
876
|
+
return
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return this.sendSignedTransactions(bundles, chainId, quote, undefined, callback)
|
|
810
880
|
}
|
|
811
881
|
|
|
812
882
|
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
|
@@ -125,7 +125,10 @@ export class AccountSigner implements ethers.Signer {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
async sendTransaction(
|
|
128
|
-
txsPromise: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish
|
|
128
|
+
txsPromise: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish,
|
|
129
|
+
options?: {
|
|
130
|
+
simulateForFeeOptions?: boolean
|
|
131
|
+
}
|
|
129
132
|
): Promise<ethers.providers.TransactionResponse> {
|
|
130
133
|
const txs = isDeferrable(txsPromise)
|
|
131
134
|
? await ethers.utils.resolveProperties(txsPromise as ethers.utils.Deferrable<ethers.providers.TransactionRequest>)
|
|
@@ -134,7 +137,8 @@ export class AccountSigner implements ethers.Signer {
|
|
|
134
137
|
const prepare = await this.account.prepareTransactions({
|
|
135
138
|
txs,
|
|
136
139
|
chainId: this.chainId,
|
|
137
|
-
stubSignatureOverrides: this.options?.stubSignatureOverrides ?? new Map()
|
|
140
|
+
stubSignatureOverrides: this.options?.stubSignatureOverrides ?? new Map(),
|
|
141
|
+
simulateForFeeOptions: options?.simulateForFeeOptions
|
|
138
142
|
})
|
|
139
143
|
|
|
140
144
|
const selectMethod = this.options?.selectFee ?? this.defaultSelectFee.bind(this)
|
|
@@ -153,7 +157,7 @@ export class AccountSigner implements ethers.Signer {
|
|
|
153
157
|
nonceSpace: this.options.nonceSpace
|
|
154
158
|
}
|
|
155
159
|
: undefined
|
|
156
|
-
)
|
|
160
|
+
) as Promise<ethers.providers.TransactionResponse> // Will always have a transaction response
|
|
157
161
|
}
|
|
158
162
|
|
|
159
163
|
getBalance(blockTag?: ethers.providers.BlockTag | undefined): Promise<ethers.BigNumber> {
|