@0xsequence/account 1.1.14 → 1.2.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.
@@ -24,6 +24,166 @@ function _extends() {
24
24
  return _extends.apply(this, arguments);
25
25
  }
26
26
 
27
+ function isPromise(value) {
28
+ return !!value && typeof value.then === 'function';
29
+ }
30
+ function isDeferrable(value) {
31
+ // The value is deferrable if any of the properties is a Promises
32
+ if (typeof value === "object") {
33
+ return Object.keys(value).some(key => isPromise(value[key]));
34
+ }
35
+ return false;
36
+ }
37
+
38
+ function encodeGasRefundTransaction(option) {
39
+ if (!option) return [];
40
+ const value = ethers.ethers.BigNumber.from(option.value);
41
+ switch (option.token.type) {
42
+ case relayer.proto.FeeTokenType.UNKNOWN:
43
+ return [{
44
+ delegateCall: false,
45
+ revertOnError: true,
46
+ gasLimit: option.gasLimit,
47
+ to: option.to,
48
+ value: value.toHexString(),
49
+ data: []
50
+ }];
51
+ case relayer.proto.FeeTokenType.ERC20_TOKEN:
52
+ if (!option.token.contractAddress) {
53
+ throw new Error(`No contract address for ERC-20 fee option`);
54
+ }
55
+ return [{
56
+ delegateCall: false,
57
+ revertOnError: true,
58
+ gasLimit: option.gasLimit,
59
+ to: option.token.contractAddress,
60
+ value: 0,
61
+ data: new ethers.ethers.utils.Interface([{
62
+ "constant": false,
63
+ "inputs": [{
64
+ "type": "address"
65
+ }, {
66
+ "type": "uint256"
67
+ }],
68
+ "name": "transfer",
69
+ "outputs": [],
70
+ "type": "function"
71
+ }]).encodeFunctionData('transfer', [option.to, value.toHexString()])
72
+ }];
73
+ default:
74
+ throw new Error(`Unhandled fee token type ${option.token.type}`);
75
+ }
76
+ }
77
+ class AccountSigner {
78
+ constructor(account, chainId, options) {
79
+ this._isSigner = true;
80
+ this.account = account;
81
+ this.chainId = chainId;
82
+ this.options = options;
83
+ }
84
+ get provider() {
85
+ return this.account.providerFor(this.chainId);
86
+ }
87
+ async getAddress() {
88
+ return this.account.address;
89
+ }
90
+ signMessage(message) {
91
+ var _this$options$cantVal, _this$options;
92
+ return this.account.signMessage(message, this.chainId, (_this$options$cantVal = (_this$options = this.options) == null ? void 0 : _this$options.cantValidateBehavior) != null ? _this$options$cantVal : 'throw');
93
+ }
94
+ async defaultSelectFee(_txs, options) {
95
+ // If no options, return undefined
96
+ if (options.length === 0) return undefined;
97
+
98
+ // If there are multiple options, try them one by one
99
+ // until we find one that satisfies the balance requirement
100
+ const balanceOfAbi = [{
101
+ "constant": true,
102
+ "inputs": [{
103
+ "type": "address"
104
+ }],
105
+ "name": "balanceOf",
106
+ "outputs": [{
107
+ "type": "uint256"
108
+ }],
109
+ "type": "function"
110
+ }];
111
+ for (const option of options) {
112
+ if (option.token.type === relayer.proto.FeeTokenType.UNKNOWN) {
113
+ // Native token
114
+ const balance = await this.getBalance();
115
+ if (balance.gte(ethers.ethers.BigNumber.from(option.value))) {
116
+ return option;
117
+ }
118
+ } else if (option.token.contractAddress && option.token.type === relayer.proto.FeeTokenType.ERC20_TOKEN) {
119
+ // ERC20 token
120
+ const token = new ethers.ethers.Contract(option.token.contractAddress, balanceOfAbi, this.provider);
121
+ const balance = await token.balanceOf(this.account.address);
122
+ if (balance.gte(ethers.ethers.BigNumber.from(option.value))) {
123
+ return option;
124
+ }
125
+ } else ;
126
+ }
127
+ throw new Error("No fee option available - not enough balance");
128
+ }
129
+ async sendTransaction(txsPromise) {
130
+ var _this$options$stubSig, _this$options2, _this$options$selectF, _this$options3, _this$options4;
131
+ const txs = isDeferrable(txsPromise) ? await ethers.ethers.utils.resolveProperties(txsPromise) : txsPromise;
132
+ const prepare = await this.account.prepareTransactions({
133
+ txs,
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()
136
+ });
137
+ const selectMethod = (_this$options$selectF = (_this$options3 = this.options) == null ? void 0 : _this$options3.selectFee) != null ? _this$options$selectF : this.defaultSelectFee.bind(this);
138
+ const feeOption = await selectMethod(txs, prepare.feeOptions);
139
+ const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
140
+ return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
141
+ nonceSpace: this.options.nonceSpace
142
+ } : undefined);
143
+ }
144
+ getBalance(blockTag) {
145
+ return this.provider.getBalance(this.account.address, blockTag);
146
+ }
147
+ call(transaction, blockTag) {
148
+ return this.provider.call(transaction, blockTag);
149
+ }
150
+ async resolveName(name) {
151
+ const res = await this.provider.resolveName(name);
152
+ if (!res) throw new Error(`Could not resolve name ${name}`);
153
+ return res;
154
+ }
155
+ connect(_provider) {
156
+ throw new Error("Method not implemented.");
157
+ }
158
+ signTransaction(transaction) {
159
+ throw new Error("Method not implemented.");
160
+ }
161
+ getTransactionCount(blockTag) {
162
+ throw new Error("Method not implemented.");
163
+ }
164
+ estimateGas(transaction) {
165
+ throw new Error("Method not implemented.");
166
+ }
167
+ getChainId() {
168
+ return Promise.resolve(ethers.ethers.BigNumber.from(this.chainId).toNumber());
169
+ }
170
+ getGasPrice() {
171
+ throw new Error("Method not implemented.");
172
+ }
173
+ getFeeData() {
174
+ throw new Error("Method not implemented.");
175
+ }
176
+ checkTransaction(transaction) {
177
+ throw new Error("Method not implemented.");
178
+ }
179
+ populateTransaction(transaction) {
180
+ throw new Error("Method not implemented.");
181
+ }
182
+ _checkProvider(operation) {
183
+ throw new Error("Method not implemented.");
184
+ }
185
+ }
186
+
27
187
  class Chain0Reader {
28
188
  async isDeployed(_wallet) {
29
189
  return false;
@@ -51,6 +211,9 @@ class Account {
51
211
  this.migrations = options.migrations || migration.defaults.DefaultMigrations;
52
212
  this.migrator = new migration.migrator.Migrator(options.tracker, this.migrations, this.contexts);
53
213
  }
214
+ getSigner(chainId, options) {
215
+ return new AccountSigner(this, chainId, options);
216
+ }
54
217
  static async new(options) {
55
218
  var _options$migrations;
56
219
  const mig = new migration.migrator.Migrator(options.tracker, (_options$migrations = options.migrations) != null ? _options$migrations : migration.defaults.DefaultMigrations, options.contexts);
@@ -92,7 +255,7 @@ class Account {
92
255
  if (!found) throw new Error(`Network not found for chainId ${chainId}`);
93
256
  return found;
94
257
  }
95
- provider(chainId) {
258
+ providerFor(chainId) {
96
259
  const found = this.network(chainId);
97
260
  if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`);
98
261
  return found.provider || new ethers.ethers.providers.StaticJsonRpcProvider(utils.getDefaultConnectionInfo(found.rpcUrl), {
@@ -105,7 +268,7 @@ class Account {
105
268
 
106
269
  // TODO: Networks should be able to provide a reader directly
107
270
  // and we should default to the on-chain reader
108
- return new core.commons.reader.OnChainReader(this.provider(chainId));
271
+ return new core.commons.reader.OnChainReader(this.providerFor(chainId));
109
272
  }
110
273
  relayer(chainId) {
111
274
  const found = this.network(chainId);
@@ -452,11 +615,13 @@ class Account {
452
615
  signMessage(message, chainId, cantValidateBehavior = 'ignore') {
453
616
  return this.signDigest(ethers.ethers.utils.keccak256(message), chainId, true, cantValidateBehavior);
454
617
  }
455
- async signTransactions(txs, chainId, pstatus) {
618
+ async signTransactions(txs, chainId, pstatus, options) {
456
619
  const status = pstatus || (await this.status(chainId));
457
620
  this.mustBeFullyMigrated(status);
458
621
  const wallet = this.walletForStatus(chainId, status);
459
- const signed = await wallet.signTransactions(txs);
622
+ const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
623
+ space: options == null ? void 0 : options.nonceSpace
624
+ });
460
625
  return _extends({}, signed, {
461
626
  signature: await this.decorateSignature(signed.signature, status)
462
627
  });
@@ -589,10 +754,10 @@ class Account {
589
754
  feeQuote: gasRefundQuote.quote
590
755
  };
591
756
  }
592
- async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback) {
757
+ async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
593
758
  const status = await this.status(chainId);
594
759
  const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
595
- const signed = await this.signTransactions(predecorated, chainId);
760
+ const signed = await this.signTransactions(predecorated, chainId, undefined, options);
596
761
  // TODO: is it safe to pass status again here?
597
762
  return this.sendSignedTransactions(signed, chainId, quote, undefined, callback);
598
763
  }
@@ -24,6 +24,166 @@ function _extends() {
24
24
  return _extends.apply(this, arguments);
25
25
  }
26
26
 
27
+ function isPromise(value) {
28
+ return !!value && typeof value.then === 'function';
29
+ }
30
+ function isDeferrable(value) {
31
+ // The value is deferrable if any of the properties is a Promises
32
+ if (typeof value === "object") {
33
+ return Object.keys(value).some(key => isPromise(value[key]));
34
+ }
35
+ return false;
36
+ }
37
+
38
+ function encodeGasRefundTransaction(option) {
39
+ if (!option) return [];
40
+ const value = ethers.ethers.BigNumber.from(option.value);
41
+ switch (option.token.type) {
42
+ case relayer.proto.FeeTokenType.UNKNOWN:
43
+ return [{
44
+ delegateCall: false,
45
+ revertOnError: true,
46
+ gasLimit: option.gasLimit,
47
+ to: option.to,
48
+ value: value.toHexString(),
49
+ data: []
50
+ }];
51
+ case relayer.proto.FeeTokenType.ERC20_TOKEN:
52
+ if (!option.token.contractAddress) {
53
+ throw new Error(`No contract address for ERC-20 fee option`);
54
+ }
55
+ return [{
56
+ delegateCall: false,
57
+ revertOnError: true,
58
+ gasLimit: option.gasLimit,
59
+ to: option.token.contractAddress,
60
+ value: 0,
61
+ data: new ethers.ethers.utils.Interface([{
62
+ "constant": false,
63
+ "inputs": [{
64
+ "type": "address"
65
+ }, {
66
+ "type": "uint256"
67
+ }],
68
+ "name": "transfer",
69
+ "outputs": [],
70
+ "type": "function"
71
+ }]).encodeFunctionData('transfer', [option.to, value.toHexString()])
72
+ }];
73
+ default:
74
+ throw new Error(`Unhandled fee token type ${option.token.type}`);
75
+ }
76
+ }
77
+ class AccountSigner {
78
+ constructor(account, chainId, options) {
79
+ this._isSigner = true;
80
+ this.account = account;
81
+ this.chainId = chainId;
82
+ this.options = options;
83
+ }
84
+ get provider() {
85
+ return this.account.providerFor(this.chainId);
86
+ }
87
+ async getAddress() {
88
+ return this.account.address;
89
+ }
90
+ signMessage(message) {
91
+ var _this$options$cantVal, _this$options;
92
+ return this.account.signMessage(message, this.chainId, (_this$options$cantVal = (_this$options = this.options) == null ? void 0 : _this$options.cantValidateBehavior) != null ? _this$options$cantVal : 'throw');
93
+ }
94
+ async defaultSelectFee(_txs, options) {
95
+ // If no options, return undefined
96
+ if (options.length === 0) return undefined;
97
+
98
+ // If there are multiple options, try them one by one
99
+ // until we find one that satisfies the balance requirement
100
+ const balanceOfAbi = [{
101
+ "constant": true,
102
+ "inputs": [{
103
+ "type": "address"
104
+ }],
105
+ "name": "balanceOf",
106
+ "outputs": [{
107
+ "type": "uint256"
108
+ }],
109
+ "type": "function"
110
+ }];
111
+ for (const option of options) {
112
+ if (option.token.type === relayer.proto.FeeTokenType.UNKNOWN) {
113
+ // Native token
114
+ const balance = await this.getBalance();
115
+ if (balance.gte(ethers.ethers.BigNumber.from(option.value))) {
116
+ return option;
117
+ }
118
+ } else if (option.token.contractAddress && option.token.type === relayer.proto.FeeTokenType.ERC20_TOKEN) {
119
+ // ERC20 token
120
+ const token = new ethers.ethers.Contract(option.token.contractAddress, balanceOfAbi, this.provider);
121
+ const balance = await token.balanceOf(this.account.address);
122
+ if (balance.gte(ethers.ethers.BigNumber.from(option.value))) {
123
+ return option;
124
+ }
125
+ } else ;
126
+ }
127
+ throw new Error("No fee option available - not enough balance");
128
+ }
129
+ async sendTransaction(txsPromise) {
130
+ var _this$options$stubSig, _this$options2, _this$options$selectF, _this$options3, _this$options4;
131
+ const txs = isDeferrable(txsPromise) ? await ethers.ethers.utils.resolveProperties(txsPromise) : txsPromise;
132
+ const prepare = await this.account.prepareTransactions({
133
+ txs,
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()
136
+ });
137
+ const selectMethod = (_this$options$selectF = (_this$options3 = this.options) == null ? void 0 : _this$options3.selectFee) != null ? _this$options$selectF : this.defaultSelectFee.bind(this);
138
+ const feeOption = await selectMethod(txs, prepare.feeOptions);
139
+ const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
140
+ return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
141
+ nonceSpace: this.options.nonceSpace
142
+ } : undefined);
143
+ }
144
+ getBalance(blockTag) {
145
+ return this.provider.getBalance(this.account.address, blockTag);
146
+ }
147
+ call(transaction, blockTag) {
148
+ return this.provider.call(transaction, blockTag);
149
+ }
150
+ async resolveName(name) {
151
+ const res = await this.provider.resolveName(name);
152
+ if (!res) throw new Error(`Could not resolve name ${name}`);
153
+ return res;
154
+ }
155
+ connect(_provider) {
156
+ throw new Error("Method not implemented.");
157
+ }
158
+ signTransaction(transaction) {
159
+ throw new Error("Method not implemented.");
160
+ }
161
+ getTransactionCount(blockTag) {
162
+ throw new Error("Method not implemented.");
163
+ }
164
+ estimateGas(transaction) {
165
+ throw new Error("Method not implemented.");
166
+ }
167
+ getChainId() {
168
+ return Promise.resolve(ethers.ethers.BigNumber.from(this.chainId).toNumber());
169
+ }
170
+ getGasPrice() {
171
+ throw new Error("Method not implemented.");
172
+ }
173
+ getFeeData() {
174
+ throw new Error("Method not implemented.");
175
+ }
176
+ checkTransaction(transaction) {
177
+ throw new Error("Method not implemented.");
178
+ }
179
+ populateTransaction(transaction) {
180
+ throw new Error("Method not implemented.");
181
+ }
182
+ _checkProvider(operation) {
183
+ throw new Error("Method not implemented.");
184
+ }
185
+ }
186
+
27
187
  class Chain0Reader {
28
188
  async isDeployed(_wallet) {
29
189
  return false;
@@ -51,6 +211,9 @@ class Account {
51
211
  this.migrations = options.migrations || migration.defaults.DefaultMigrations;
52
212
  this.migrator = new migration.migrator.Migrator(options.tracker, this.migrations, this.contexts);
53
213
  }
214
+ getSigner(chainId, options) {
215
+ return new AccountSigner(this, chainId, options);
216
+ }
54
217
  static async new(options) {
55
218
  var _options$migrations;
56
219
  const mig = new migration.migrator.Migrator(options.tracker, (_options$migrations = options.migrations) != null ? _options$migrations : migration.defaults.DefaultMigrations, options.contexts);
@@ -92,7 +255,7 @@ class Account {
92
255
  if (!found) throw new Error(`Network not found for chainId ${chainId}`);
93
256
  return found;
94
257
  }
95
- provider(chainId) {
258
+ providerFor(chainId) {
96
259
  const found = this.network(chainId);
97
260
  if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`);
98
261
  return found.provider || new ethers.ethers.providers.StaticJsonRpcProvider(utils.getDefaultConnectionInfo(found.rpcUrl), {
@@ -105,7 +268,7 @@ class Account {
105
268
 
106
269
  // TODO: Networks should be able to provide a reader directly
107
270
  // and we should default to the on-chain reader
108
- return new core.commons.reader.OnChainReader(this.provider(chainId));
271
+ return new core.commons.reader.OnChainReader(this.providerFor(chainId));
109
272
  }
110
273
  relayer(chainId) {
111
274
  const found = this.network(chainId);
@@ -452,11 +615,13 @@ class Account {
452
615
  signMessage(message, chainId, cantValidateBehavior = 'ignore') {
453
616
  return this.signDigest(ethers.ethers.utils.keccak256(message), chainId, true, cantValidateBehavior);
454
617
  }
455
- async signTransactions(txs, chainId, pstatus) {
618
+ async signTransactions(txs, chainId, pstatus, options) {
456
619
  const status = pstatus || (await this.status(chainId));
457
620
  this.mustBeFullyMigrated(status);
458
621
  const wallet = this.walletForStatus(chainId, status);
459
- const signed = await wallet.signTransactions(txs);
622
+ const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
623
+ space: options == null ? void 0 : options.nonceSpace
624
+ });
460
625
  return _extends({}, signed, {
461
626
  signature: await this.decorateSignature(signed.signature, status)
462
627
  });
@@ -589,10 +754,10 @@ class Account {
589
754
  feeQuote: gasRefundQuote.quote
590
755
  };
591
756
  }
592
- async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback) {
757
+ async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
593
758
  const status = await this.status(chainId);
594
759
  const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
595
- const signed = await this.signTransactions(predecorated, chainId);
760
+ const signed = await this.signTransactions(predecorated, chainId, undefined, options);
596
761
  // TODO: is it safe to pass status again here?
597
762
  return this.sendSignedTransactions(signed, chainId, quote, undefined, callback);
598
763
  }
@@ -1,6 +1,6 @@
1
1
  import { commons, universal } from '@0xsequence/core';
2
2
  import { defaults, migrator, version } from '@0xsequence/migration';
3
- import { isRelayer, RpcRelayer } from '@0xsequence/relayer';
3
+ import { proto, isRelayer, RpcRelayer } from '@0xsequence/relayer';
4
4
  import { getDefaultConnectionInfo, encodeTypedDataDigest } from '@0xsequence/utils';
5
5
  import { Wallet } from '@0xsequence/wallet';
6
6
  import { ethers } from 'ethers';
@@ -20,6 +20,166 @@ function _extends() {
20
20
  return _extends.apply(this, arguments);
21
21
  }
22
22
 
23
+ function isPromise(value) {
24
+ return !!value && typeof value.then === 'function';
25
+ }
26
+ function isDeferrable(value) {
27
+ // The value is deferrable if any of the properties is a Promises
28
+ if (typeof value === "object") {
29
+ return Object.keys(value).some(key => isPromise(value[key]));
30
+ }
31
+ return false;
32
+ }
33
+
34
+ function encodeGasRefundTransaction(option) {
35
+ if (!option) return [];
36
+ const value = ethers.BigNumber.from(option.value);
37
+ switch (option.token.type) {
38
+ case proto.FeeTokenType.UNKNOWN:
39
+ return [{
40
+ delegateCall: false,
41
+ revertOnError: true,
42
+ gasLimit: option.gasLimit,
43
+ to: option.to,
44
+ value: value.toHexString(),
45
+ data: []
46
+ }];
47
+ case proto.FeeTokenType.ERC20_TOKEN:
48
+ if (!option.token.contractAddress) {
49
+ throw new Error(`No contract address for ERC-20 fee option`);
50
+ }
51
+ return [{
52
+ delegateCall: false,
53
+ revertOnError: true,
54
+ gasLimit: option.gasLimit,
55
+ to: option.token.contractAddress,
56
+ value: 0,
57
+ data: new ethers.utils.Interface([{
58
+ "constant": false,
59
+ "inputs": [{
60
+ "type": "address"
61
+ }, {
62
+ "type": "uint256"
63
+ }],
64
+ "name": "transfer",
65
+ "outputs": [],
66
+ "type": "function"
67
+ }]).encodeFunctionData('transfer', [option.to, value.toHexString()])
68
+ }];
69
+ default:
70
+ throw new Error(`Unhandled fee token type ${option.token.type}`);
71
+ }
72
+ }
73
+ class AccountSigner {
74
+ constructor(account, chainId, options) {
75
+ this._isSigner = true;
76
+ this.account = account;
77
+ this.chainId = chainId;
78
+ this.options = options;
79
+ }
80
+ get provider() {
81
+ return this.account.providerFor(this.chainId);
82
+ }
83
+ async getAddress() {
84
+ return this.account.address;
85
+ }
86
+ signMessage(message) {
87
+ var _this$options$cantVal, _this$options;
88
+ return this.account.signMessage(message, this.chainId, (_this$options$cantVal = (_this$options = this.options) == null ? void 0 : _this$options.cantValidateBehavior) != null ? _this$options$cantVal : 'throw');
89
+ }
90
+ async defaultSelectFee(_txs, options) {
91
+ // If no options, return undefined
92
+ if (options.length === 0) return undefined;
93
+
94
+ // If there are multiple options, try them one by one
95
+ // until we find one that satisfies the balance requirement
96
+ const balanceOfAbi = [{
97
+ "constant": true,
98
+ "inputs": [{
99
+ "type": "address"
100
+ }],
101
+ "name": "balanceOf",
102
+ "outputs": [{
103
+ "type": "uint256"
104
+ }],
105
+ "type": "function"
106
+ }];
107
+ for (const option of options) {
108
+ if (option.token.type === proto.FeeTokenType.UNKNOWN) {
109
+ // Native token
110
+ const balance = await this.getBalance();
111
+ if (balance.gte(ethers.BigNumber.from(option.value))) {
112
+ return option;
113
+ }
114
+ } else if (option.token.contractAddress && option.token.type === proto.FeeTokenType.ERC20_TOKEN) {
115
+ // ERC20 token
116
+ const token = new ethers.Contract(option.token.contractAddress, balanceOfAbi, this.provider);
117
+ const balance = await token.balanceOf(this.account.address);
118
+ if (balance.gte(ethers.BigNumber.from(option.value))) {
119
+ return option;
120
+ }
121
+ } else ;
122
+ }
123
+ throw new Error("No fee option available - not enough balance");
124
+ }
125
+ async sendTransaction(txsPromise) {
126
+ var _this$options$stubSig, _this$options2, _this$options$selectF, _this$options3, _this$options4;
127
+ const txs = isDeferrable(txsPromise) ? await ethers.utils.resolveProperties(txsPromise) : txsPromise;
128
+ const prepare = await this.account.prepareTransactions({
129
+ txs,
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()
132
+ });
133
+ const selectMethod = (_this$options$selectF = (_this$options3 = this.options) == null ? void 0 : _this$options3.selectFee) != null ? _this$options$selectF : this.defaultSelectFee.bind(this);
134
+ const feeOption = await selectMethod(txs, prepare.feeOptions);
135
+ const finalTransactions = [...prepare.transactions, ...encodeGasRefundTransaction(feeOption)];
136
+ return this.account.sendTransaction(finalTransactions, this.chainId, prepare.feeQuote, undefined, undefined, (_this$options4 = this.options) != null && _this$options4.nonceSpace ? {
137
+ nonceSpace: this.options.nonceSpace
138
+ } : undefined);
139
+ }
140
+ getBalance(blockTag) {
141
+ return this.provider.getBalance(this.account.address, blockTag);
142
+ }
143
+ call(transaction, blockTag) {
144
+ return this.provider.call(transaction, blockTag);
145
+ }
146
+ async resolveName(name) {
147
+ const res = await this.provider.resolveName(name);
148
+ if (!res) throw new Error(`Could not resolve name ${name}`);
149
+ return res;
150
+ }
151
+ connect(_provider) {
152
+ throw new Error("Method not implemented.");
153
+ }
154
+ signTransaction(transaction) {
155
+ throw new Error("Method not implemented.");
156
+ }
157
+ getTransactionCount(blockTag) {
158
+ throw new Error("Method not implemented.");
159
+ }
160
+ estimateGas(transaction) {
161
+ throw new Error("Method not implemented.");
162
+ }
163
+ getChainId() {
164
+ return Promise.resolve(ethers.BigNumber.from(this.chainId).toNumber());
165
+ }
166
+ getGasPrice() {
167
+ throw new Error("Method not implemented.");
168
+ }
169
+ getFeeData() {
170
+ throw new Error("Method not implemented.");
171
+ }
172
+ checkTransaction(transaction) {
173
+ throw new Error("Method not implemented.");
174
+ }
175
+ populateTransaction(transaction) {
176
+ throw new Error("Method not implemented.");
177
+ }
178
+ _checkProvider(operation) {
179
+ throw new Error("Method not implemented.");
180
+ }
181
+ }
182
+
23
183
  class Chain0Reader {
24
184
  async isDeployed(_wallet) {
25
185
  return false;
@@ -47,6 +207,9 @@ class Account {
47
207
  this.migrations = options.migrations || defaults.DefaultMigrations;
48
208
  this.migrator = new migrator.Migrator(options.tracker, this.migrations, this.contexts);
49
209
  }
210
+ getSigner(chainId, options) {
211
+ return new AccountSigner(this, chainId, options);
212
+ }
50
213
  static async new(options) {
51
214
  var _options$migrations;
52
215
  const mig = new migrator.Migrator(options.tracker, (_options$migrations = options.migrations) != null ? _options$migrations : defaults.DefaultMigrations, options.contexts);
@@ -88,7 +251,7 @@ class Account {
88
251
  if (!found) throw new Error(`Network not found for chainId ${chainId}`);
89
252
  return found;
90
253
  }
91
- provider(chainId) {
254
+ providerFor(chainId) {
92
255
  const found = this.network(chainId);
93
256
  if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`);
94
257
  return found.provider || new ethers.providers.StaticJsonRpcProvider(getDefaultConnectionInfo(found.rpcUrl), {
@@ -101,7 +264,7 @@ class Account {
101
264
 
102
265
  // TODO: Networks should be able to provide a reader directly
103
266
  // and we should default to the on-chain reader
104
- return new commons.reader.OnChainReader(this.provider(chainId));
267
+ return new commons.reader.OnChainReader(this.providerFor(chainId));
105
268
  }
106
269
  relayer(chainId) {
107
270
  const found = this.network(chainId);
@@ -448,11 +611,13 @@ class Account {
448
611
  signMessage(message, chainId, cantValidateBehavior = 'ignore') {
449
612
  return this.signDigest(ethers.utils.keccak256(message), chainId, true, cantValidateBehavior);
450
613
  }
451
- async signTransactions(txs, chainId, pstatus) {
614
+ async signTransactions(txs, chainId, pstatus, options) {
452
615
  const status = pstatus || (await this.status(chainId));
453
616
  this.mustBeFullyMigrated(status);
454
617
  const wallet = this.walletForStatus(chainId, status);
455
- const signed = await wallet.signTransactions(txs);
618
+ const signed = await wallet.signTransactions(txs, (options == null ? void 0 : options.nonceSpace) && {
619
+ space: options == null ? void 0 : options.nonceSpace
620
+ });
456
621
  return _extends({}, signed, {
457
622
  signature: await this.decorateSignature(signed.signature, status)
458
623
  });
@@ -585,10 +750,10 @@ class Account {
585
750
  feeQuote: gasRefundQuote.quote
586
751
  };
587
752
  }
588
- async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback) {
753
+ async sendTransaction(txs, chainId, quote, skipPreDecorate = false, callback, options) {
589
754
  const status = await this.status(chainId);
590
755
  const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId);
591
- const signed = await this.signTransactions(predecorated, chainId);
756
+ const signed = await this.signTransactions(predecorated, chainId, undefined, options);
592
757
  // TODO: is it safe to pass status again here?
593
758
  return this.sendSignedTransactions(signed, chainId, quote, undefined, callback);
594
759
  }
@@ -1,11 +1,12 @@
1
1
  import { commons } from '@0xsequence/core';
2
2
  import { migrator } from '@0xsequence/migration';
3
- import { NetworkConfig } from '@0xsequence/network';
3
+ import { ChainId, NetworkConfig } from '@0xsequence/network';
4
4
  import { FeeOption, FeeQuote, Relayer } from '@0xsequence/relayer';
5
5
  import { tracker } from '@0xsequence/sessions';
6
6
  import { Orchestrator } from '@0xsequence/signhub';
7
7
  import { Wallet } from '@0xsequence/wallet';
8
8
  import { ethers, TypedDataDomain, TypedDataField } from 'ethers';
9
+ import { AccountSigner, AccountSignerOptions } from "./signer.js";
9
10
  export type AccountStatus = {
10
11
  original: {
11
12
  version: number;
@@ -50,6 +51,7 @@ export declare class Account {
50
51
  readonly migrations: migrator.Migrations;
51
52
  private orchestrator;
52
53
  constructor(options: AccountOptions);
54
+ getSigner(chainId: ChainId, options?: AccountSignerOptions): AccountSigner;
53
55
  static new(options: {
54
56
  config: commons.config.SimpleConfig;
55
57
  tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker;
@@ -65,7 +67,7 @@ export declare class Account {
65
67
  config: commons.config.ConfigCoder;
66
68
  };
67
69
  network(chainId: ethers.BigNumberish): NetworkConfig;
68
- provider(chainId: ethers.BigNumberish): ethers.providers.Provider;
70
+ providerFor(chainId: ethers.BigNumberish): ethers.providers.Provider;
69
71
  reader(chainId: ethers.BigNumberish): commons.reader.Reader;
70
72
  relayer(chainId: ethers.BigNumberish): Relayer;
71
73
  setOrchestrator(orchestrator: Orchestrator): void;
@@ -101,7 +103,9 @@ export declare class Account {
101
103
  bootstrapTransactions(chainId: ethers.BigNumberish, prestatus?: AccountStatus): Promise<Omit<commons.transaction.IntendedTransactionBundle, 'chainId'>>;
102
104
  doBootstrap(chainId: ethers.BigNumberish, feeQuote?: FeeQuote, prestatus?: AccountStatus): Promise<commons.transaction.TransactionResponse<any>>;
103
105
  signMessage(message: ethers.BytesLike, chainId: ethers.BigNumberish, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw'): Promise<string>;
104
- signTransactions(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, pstatus?: AccountStatus): Promise<commons.transaction.SignedTransactionBundle>;
106
+ signTransactions(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, pstatus?: AccountStatus, options?: {
107
+ nonceSpace?: ethers.BigNumberish;
108
+ }): Promise<commons.transaction.SignedTransactionBundle>;
105
109
  signMigrations(chainId: ethers.BigNumberish, editConfig: (prevConfig: commons.config.Config) => commons.config.Config): Promise<boolean>;
106
110
  signAllMigrations(editConfig: (prevConfig: commons.config.Config) => commons.config.Config): Promise<{
107
111
  signedMigrations: Array<any>;
@@ -123,7 +127,9 @@ export declare class Account {
123
127
  chainId: ethers.BigNumberish;
124
128
  stubSignatureOverrides: Map<string, string>;
125
129
  }): Promise<PreparedTransactions>;
126
- sendTransaction(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, quote?: FeeQuote, skipPreDecorate?: boolean, callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void): Promise<ethers.providers.TransactionResponse>;
130
+ sendTransaction(txs: commons.transaction.Transactionish, chainId: ethers.BigNumberish, quote?: FeeQuote, skipPreDecorate?: boolean, callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void, options?: {
131
+ nonceSpace?: ethers.BigNumberish;
132
+ }): Promise<ethers.providers.TransactionResponse>;
127
133
  signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, message: Record<string, any>, chainId: ethers.BigNumberish, cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw'): Promise<string>;
128
134
  getAllSigners(): Promise<{
129
135
  address: string;
@@ -0,0 +1,36 @@
1
+ import { ChainId } from "@0xsequence/network";
2
+ import { Account } from "./account.js";
3
+ import { ethers } from "ethers";
4
+ import { commons } from "@0xsequence/core";
5
+ import { FeeOption } from "@0xsequence/relayer";
6
+ export type AccountSignerOptions = {
7
+ nonceSpace?: ethers.BigNumberish;
8
+ cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw';
9
+ stubSignatureOverrides?: Map<string, string>;
10
+ selectFee?: (txs: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish, options: FeeOption[]) => Promise<FeeOption | undefined>;
11
+ };
12
+ export declare class AccountSigner implements ethers.Signer {
13
+ account: Account;
14
+ chainId: ChainId;
15
+ readonly options?: AccountSignerOptions | undefined;
16
+ readonly _isSigner = true;
17
+ constructor(account: Account, chainId: ChainId, options?: AccountSignerOptions | undefined);
18
+ get provider(): ethers.providers.Provider;
19
+ getAddress(): Promise<string>;
20
+ signMessage(message: string | ethers.utils.Bytes): Promise<string>;
21
+ private defaultSelectFee;
22
+ sendTransaction(txsPromise: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish): Promise<ethers.providers.TransactionResponse>;
23
+ getBalance(blockTag?: ethers.providers.BlockTag | undefined): Promise<ethers.BigNumber>;
24
+ call(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>, blockTag?: ethers.providers.BlockTag | undefined): Promise<string>;
25
+ resolveName(name: string): Promise<string>;
26
+ connect(_provider: ethers.providers.Provider): ethers.Signer;
27
+ signTransaction(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>): Promise<string>;
28
+ getTransactionCount(blockTag?: ethers.providers.BlockTag | undefined): Promise<number>;
29
+ estimateGas(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>): Promise<ethers.BigNumber>;
30
+ getChainId(): Promise<number>;
31
+ getGasPrice(): Promise<ethers.BigNumber>;
32
+ getFeeData(): Promise<ethers.providers.FeeData>;
33
+ checkTransaction(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>): ethers.utils.Deferrable<ethers.providers.TransactionRequest>;
34
+ populateTransaction(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>): Promise<ethers.providers.TransactionRequest>;
35
+ _checkProvider(operation?: string | undefined): void;
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xsequence/account",
3
- "version": "1.1.14",
3
+ "version": "1.2.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.1.14",
14
- "@0xsequence/migration": "1.1.14",
15
- "@0xsequence/network": "1.1.14",
16
- "@0xsequence/sessions": "1.1.14",
17
- "@0xsequence/relayer": "1.1.14",
18
- "@0xsequence/utils": "1.1.14",
19
- "@0xsequence/wallet": "1.1.14"
13
+ "@0xsequence/core": "1.2.0",
14
+ "@0xsequence/migration": "1.2.0",
15
+ "@0xsequence/relayer": "1.2.0",
16
+ "@0xsequence/network": "1.2.0",
17
+ "@0xsequence/sessions": "1.2.0",
18
+ "@0xsequence/utils": "1.2.0",
19
+ "@0xsequence/wallet": "1.2.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.1.14",
25
- "@0xsequence/tests": "1.1.14"
24
+ "@0xsequence/signhub": "1.2.0",
25
+ "@0xsequence/tests": "1.2.0"
26
26
  },
27
27
  "files": [
28
28
  "src",
package/src/account.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { commons, universal } from '@0xsequence/core'
2
2
  import { migrator, defaults, version } from '@0xsequence/migration'
3
- import { NetworkConfig } from '@0xsequence/network'
3
+ import { ChainId, NetworkConfig } from '@0xsequence/network'
4
4
  import { FeeOption, FeeQuote, isRelayer, Relayer, RpcRelayer } from '@0xsequence/relayer'
5
5
  import { tracker } from '@0xsequence/sessions'
6
6
  import { Orchestrator } from '@0xsequence/signhub'
7
7
  import { encodeTypedDataDigest, getDefaultConnectionInfo } from '@0xsequence/utils'
8
8
  import { Wallet } from '@0xsequence/wallet'
9
9
  import { ethers, TypedDataDomain, TypedDataField } from 'ethers'
10
+ import { AccountSigner, AccountSignerOptions } from './signer'
10
11
 
11
12
  export type AccountStatus = {
12
13
  original: {
@@ -106,6 +107,10 @@ export class Account {
106
107
  this.migrator = new migrator.Migrator(options.tracker, this.migrations, this.contexts)
107
108
  }
108
109
 
110
+ getSigner(chainId: ChainId, options?: AccountSignerOptions): AccountSigner {
111
+ return new AccountSigner(this, chainId, options)
112
+ }
113
+
109
114
  static async new(options: {
110
115
  config: commons.config.SimpleConfig
111
116
  tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker
@@ -163,7 +168,7 @@ export class Account {
163
168
  return found
164
169
  }
165
170
 
166
- provider(chainId: ethers.BigNumberish): ethers.providers.Provider {
171
+ providerFor(chainId: ethers.BigNumberish): ethers.providers.Provider {
167
172
  const found = this.network(chainId)
168
173
  if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`)
169
174
  return (
@@ -180,7 +185,7 @@ export class Account {
180
185
 
181
186
  // TODO: Networks should be able to provide a reader directly
182
187
  // and we should default to the on-chain reader
183
- return new commons.reader.OnChainReader(this.provider(chainId))
188
+ return new commons.reader.OnChainReader(this.providerFor(chainId))
184
189
  }
185
190
 
186
191
  relayer(chainId: ethers.BigNumberish): Relayer {
@@ -608,13 +613,16 @@ export class Account {
608
613
  async signTransactions(
609
614
  txs: commons.transaction.Transactionish,
610
615
  chainId: ethers.BigNumberish,
611
- pstatus?: AccountStatus
616
+ pstatus?: AccountStatus,
617
+ options?: {
618
+ nonceSpace?: ethers.BigNumberish
619
+ }
612
620
  ): Promise<commons.transaction.SignedTransactionBundle> {
613
621
  const status = pstatus || (await this.status(chainId))
614
622
  this.mustBeFullyMigrated(status)
615
623
 
616
624
  const wallet = this.walletForStatus(chainId, status)
617
- const signed = await wallet.signTransactions(txs)
625
+ const signed = await wallet.signTransactions(txs, options?.nonceSpace && { space: options?.nonceSpace })
618
626
 
619
627
  return {
620
628
  ...signed,
@@ -788,11 +796,15 @@ export class Account {
788
796
  chainId: ethers.BigNumberish,
789
797
  quote?: FeeQuote,
790
798
  skipPreDecorate: boolean = false,
791
- callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void
799
+ callback?: (bundle: commons.transaction.IntendedTransactionBundle) => void,
800
+ options?: {
801
+ nonceSpace?: ethers.BigNumberish
802
+ }
792
803
  ): Promise<ethers.providers.TransactionResponse> {
793
804
  const status = await this.status(chainId)
805
+
794
806
  const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId)
795
- const signed = await this.signTransactions(predecorated, chainId)
807
+ const signed = await this.signTransactions(predecorated, chainId, undefined, options)
796
808
  // TODO: is it safe to pass status again here?
797
809
  return this.sendSignedTransactions(signed, chainId, quote, undefined, callback)
798
810
  }
package/src/signer.ts ADDED
@@ -0,0 +1,221 @@
1
+ import { ChainId } from "@0xsequence/network"
2
+ import { Account } from "./account"
3
+ import { ethers } from "ethers"
4
+ import { commons } from "@0xsequence/core"
5
+ import { FeeOption, proto } from "@0xsequence/relayer"
6
+ import { isDeferrable } from "./utils"
7
+
8
+ export type AccountSignerOptions = {
9
+ nonceSpace?: ethers.BigNumberish,
10
+ cantValidateBehavior?: 'ignore' | 'eip6492' | 'throw',
11
+ stubSignatureOverrides?: Map<string, string>,
12
+ selectFee?: (
13
+ txs: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish,
14
+ options: FeeOption[]
15
+ ) => Promise<FeeOption | undefined>
16
+ }
17
+
18
+ function encodeGasRefundTransaction(option?: FeeOption) {
19
+ if (!option) return []
20
+
21
+ const value = ethers.BigNumber.from(option.value)
22
+
23
+ switch (option.token.type) {
24
+ case proto.FeeTokenType.UNKNOWN:
25
+ return [{
26
+ delegateCall: false,
27
+ revertOnError: true,
28
+ gasLimit: option.gasLimit,
29
+ to: option.to,
30
+ value: value.toHexString(),
31
+ data: []
32
+ }]
33
+
34
+ case proto.FeeTokenType.ERC20_TOKEN:
35
+ if (!option.token.contractAddress) {
36
+ throw new Error(`No contract address for ERC-20 fee option`)
37
+ }
38
+
39
+ return [{
40
+ delegateCall: false,
41
+ revertOnError: true,
42
+ gasLimit: option.gasLimit,
43
+ to: option.token.contractAddress,
44
+ value: 0,
45
+ data: new ethers.utils.Interface([{
46
+ "constant": false,
47
+ "inputs": [
48
+ {"type": "address"},
49
+ {"type": "uint256"}
50
+ ],
51
+ "name": "transfer",
52
+ "outputs": [],
53
+ "type": "function"
54
+ }]).encodeFunctionData('transfer', [option.to, value.toHexString()])
55
+ }]
56
+
57
+ default:
58
+ throw new Error(`Unhandled fee token type ${option.token.type}`)
59
+ }
60
+ }
61
+
62
+ export class AccountSigner implements ethers.Signer {
63
+ public readonly _isSigner = true
64
+
65
+ constructor (
66
+ public account: Account,
67
+ public chainId: ChainId,
68
+ public readonly options?: AccountSignerOptions
69
+ ) {}
70
+
71
+ get provider() {
72
+ return this.account.providerFor(this.chainId)
73
+ }
74
+
75
+ async getAddress(): Promise<string> {
76
+ return this.account.address
77
+ }
78
+
79
+ signMessage(
80
+ message: string | ethers.utils.Bytes
81
+ ): Promise<string> {
82
+ return this.account.signMessage(
83
+ message,
84
+ this.chainId,
85
+ this.options?.cantValidateBehavior ?? 'throw'
86
+ )
87
+ }
88
+
89
+ private async defaultSelectFee(
90
+ _txs: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish,
91
+ options: FeeOption[]
92
+ ): Promise<FeeOption | undefined> {
93
+ // If no options, return undefined
94
+ if (options.length === 0) return undefined
95
+
96
+ // If there are multiple options, try them one by one
97
+ // until we find one that satisfies the balance requirement
98
+ const balanceOfAbi = [{
99
+ "constant": true,
100
+ "inputs": [{"type": "address"}],
101
+ "name": "balanceOf",
102
+ "outputs": [{"type": "uint256"}],
103
+ "type": "function"
104
+ }]
105
+
106
+ for (const option of options) {
107
+ if (option.token.type === proto.FeeTokenType.UNKNOWN) {
108
+ // Native token
109
+ const balance = await this.getBalance()
110
+ if (balance.gte(ethers.BigNumber.from(option.value))) {
111
+ return option
112
+ }
113
+ } else if (option.token.contractAddress && option.token.type === proto.FeeTokenType.ERC20_TOKEN) {
114
+ // ERC20 token
115
+ const token = new ethers.Contract(option.token.contractAddress, balanceOfAbi, this.provider)
116
+ const balance = await token.balanceOf(this.account.address)
117
+ if (balance.gte(ethers.BigNumber.from(option.value))) {
118
+ return option
119
+ }
120
+ } else {
121
+ // Unsupported token type
122
+ }
123
+ }
124
+
125
+ throw new Error("No fee option available - not enough balance")
126
+ }
127
+
128
+ async sendTransaction(
129
+ txsPromise: ethers.utils.Deferrable<ethers.providers.TransactionRequest> | commons.transaction.Transactionish
130
+ ): Promise<ethers.providers.TransactionResponse> {
131
+ const txs = isDeferrable(txsPromise) ? (
132
+ await ethers.utils.resolveProperties(txsPromise as ethers.utils.Deferrable<ethers.providers.TransactionRequest>))
133
+ : txsPromise
134
+
135
+ const prepare = await this.account.prepareTransactions({
136
+ txs,
137
+ chainId: this.chainId,
138
+ stubSignatureOverrides: this.options?.stubSignatureOverrides ?? new Map()
139
+ })
140
+
141
+ const selectMethod = this.options?.selectFee ?? this.defaultSelectFee.bind(this)
142
+ const feeOption = await selectMethod(txs, prepare.feeOptions)
143
+
144
+ const finalTransactions = [
145
+ ...prepare.transactions,
146
+ ...encodeGasRefundTransaction(feeOption)
147
+ ]
148
+
149
+ return this.account.sendTransaction(
150
+ finalTransactions,
151
+ this.chainId,
152
+ prepare.feeQuote,
153
+ undefined,
154
+ undefined,
155
+ this.options?.nonceSpace ? {
156
+ nonceSpace: this.options.nonceSpace
157
+ } : undefined
158
+ )
159
+ }
160
+
161
+ getBalance(blockTag?: ethers.providers.BlockTag | undefined): Promise<ethers.BigNumber> {
162
+ return this.provider.getBalance(this.account.address, blockTag)
163
+ }
164
+
165
+ call(
166
+ transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
167
+ blockTag?: ethers.providers.BlockTag | undefined
168
+ ): Promise<string> {
169
+ return this.provider.call(transaction, blockTag)
170
+ }
171
+
172
+ async resolveName(name: string): Promise<string> {
173
+ const res = await this.provider.resolveName(name)
174
+ if (!res) throw new Error(`Could not resolve name ${name}`)
175
+ return res
176
+ }
177
+
178
+ connect(_provider: ethers.providers.Provider): ethers.Signer {
179
+ throw new Error("Method not implemented.")
180
+ }
181
+
182
+ signTransaction(
183
+ transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>
184
+ ): Promise<string> {
185
+ throw new Error("Method not implemented.")
186
+ }
187
+
188
+ getTransactionCount(blockTag?: ethers.providers.BlockTag | undefined): Promise<number> {
189
+ throw new Error("Method not implemented.")
190
+ }
191
+
192
+ estimateGas(
193
+ transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>
194
+ ): Promise<ethers.BigNumber> {
195
+ throw new Error("Method not implemented.")
196
+ }
197
+
198
+ getChainId(): Promise<number> {
199
+ return Promise.resolve(ethers.BigNumber.from(this.chainId).toNumber())
200
+ }
201
+
202
+ getGasPrice(): Promise<ethers.BigNumber> {
203
+ throw new Error("Method not implemented.")
204
+ }
205
+
206
+ getFeeData(): Promise<ethers.providers.FeeData> {
207
+ throw new Error("Method not implemented.")
208
+ }
209
+
210
+ checkTransaction(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>): ethers.utils.Deferrable<ethers.providers.TransactionRequest> {
211
+ throw new Error("Method not implemented.")
212
+ }
213
+
214
+ populateTransaction(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>): Promise<ethers.providers.TransactionRequest> {
215
+ throw new Error("Method not implemented.")
216
+ }
217
+
218
+ _checkProvider(operation?: string | undefined): void {
219
+ throw new Error("Method not implemented.")
220
+ }
221
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { ethers } from "ethers"
2
+
3
+ function isPromise(value: any): value is Promise<any> {
4
+ return !!value && typeof value.then === 'function'
5
+ }
6
+
7
+ export function isDeferrable<T>(value: any): value is ethers.utils.Deferrable<T> {
8
+ // The value is deferrable if any of the properties is a Promises
9
+ if (typeof(value) === "object") {
10
+ return Object.keys(value).some((key) => isPromise(value[key]))
11
+ }
12
+
13
+ return false
14
+ }