@0xsequence/relayer 1.10.15 → 2.0.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-relayer.cjs.dev.js +41 -23
- package/dist/0xsequence-relayer.cjs.prod.js +41 -23
- package/dist/0xsequence-relayer.esm.js +43 -25
- package/dist/declarations/src/index.d.ts +3 -3
- package/dist/declarations/src/local-relayer.d.ts +6 -6
- package/dist/declarations/src/provider-relayer.d.ts +7 -7
- package/dist/declarations/src/rpc-relayer/index.d.ts +3 -3
- package/package.json +10 -10
- package/src/index.ts +3 -3
- package/src/local-relayer.ts +16 -12
- package/src/provider-relayer.ts +25 -21
- package/src/rpc-relayer/index.ts +21 -15
|
@@ -22,14 +22,14 @@ function _extends() {
|
|
|
22
22
|
return _extends.apply(this, arguments);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const DEFAULT_GAS_LIMIT =
|
|
25
|
+
const DEFAULT_GAS_LIMIT = 800000n;
|
|
26
26
|
const ProviderRelayerDefaults = {
|
|
27
27
|
waitPollRate: 1000,
|
|
28
28
|
deltaBlocksLog: 12,
|
|
29
29
|
fromBlockLog: -1024
|
|
30
30
|
};
|
|
31
31
|
function isProviderRelayerOptions(obj) {
|
|
32
|
-
return obj
|
|
32
|
+
return typeof obj === 'object' && obj.provider instanceof ethers.ethers.AbstractProvider;
|
|
33
33
|
}
|
|
34
34
|
class ProviderRelayer {
|
|
35
35
|
constructor(options) {
|
|
@@ -47,7 +47,7 @@ class ProviderRelayer {
|
|
|
47
47
|
var _this = this;
|
|
48
48
|
return (await Promise.all(transactions.map(async function (tx) {
|
|
49
49
|
// Respect gasLimit request of the transaction (as long as its not 0)
|
|
50
|
-
if (tx.gasLimit &&
|
|
50
|
+
if (tx.gasLimit && BigInt(tx.gasLimit || 0) !== 0n) {
|
|
51
51
|
return tx.gasLimit;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -57,7 +57,7 @@ class ProviderRelayer {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Fee can't be estimated for self-called if wallet hasn't been deployed
|
|
60
|
-
if (tx.to === wallet && (await _this.provider.getCode(wallet).then(code => ethers.ethers.
|
|
60
|
+
if (tx.to === wallet && (await _this.provider.getCode(wallet).then(code => ethers.ethers.getBytes(code).length === 0))) {
|
|
61
61
|
return DEFAULT_GAS_LIMIT;
|
|
62
62
|
}
|
|
63
63
|
if (!_this.provider) {
|
|
@@ -75,8 +75,8 @@ class ProviderRelayer {
|
|
|
75
75
|
}))).map(gasLimit => ({
|
|
76
76
|
executed: true,
|
|
77
77
|
succeeded: true,
|
|
78
|
-
gasUsed:
|
|
79
|
-
gasLimit:
|
|
78
|
+
gasUsed: Number(gasLimit),
|
|
79
|
+
gasLimit: Number(gasLimit)
|
|
80
80
|
}));
|
|
81
81
|
}
|
|
82
82
|
async getNonce(address, space, blockTag) {
|
|
@@ -147,15 +147,23 @@ class ProviderRelayer {
|
|
|
147
147
|
const txs = await Promise.all(logs.map(l => retry(() => _this2.provider.getTransactionReceipt(l.transactionHash), `unable to get receipt for transaction ${l.transactionHash}`)));
|
|
148
148
|
|
|
149
149
|
// Find a transaction with a TxExecuted log
|
|
150
|
-
const found = txs.find(tx => tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 &&
|
|
150
|
+
const found = txs.find(tx => tx == null ? void 0 : tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 &&
|
|
151
151
|
// TxFailed event topic
|
|
152
152
|
l.topics[0] === '0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7' && l.data.length >= 64 && l.data.replace('0x', '').startsWith(normalMetaTxnId)));
|
|
153
153
|
|
|
154
154
|
// If found return that
|
|
155
155
|
if (found) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
const response = await retry(() => _this2.provider.getTransaction(found.hash), `unable to get transaction ${found.hash}`);
|
|
157
|
+
if (!response) {
|
|
158
|
+
throw new Error(`Transaction response not found for ${metaTxnId}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// NOTE: we have to do this, because ethers-v6 uses private fields
|
|
162
|
+
// and we can't just extend the class and override the method, so
|
|
163
|
+
// we just modify the response object directly by adding the receipt to it.
|
|
164
|
+
const out = response;
|
|
165
|
+
out.receipt = found;
|
|
166
|
+
return out;
|
|
159
167
|
}
|
|
160
168
|
|
|
161
169
|
// Otherwise wait and try again
|
|
@@ -177,18 +185,18 @@ class ProviderRelayer {
|
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
function isLocalRelayerOptions(obj) {
|
|
180
|
-
return obj
|
|
188
|
+
return typeof obj === 'object' && obj.signer instanceof ethers.ethers.AbstractSigner;
|
|
181
189
|
}
|
|
182
190
|
class LocalRelayer extends ProviderRelayer {
|
|
183
191
|
constructor(options) {
|
|
184
|
-
super(ethers.
|
|
192
|
+
super(options instanceof ethers.ethers.AbstractSigner ? {
|
|
185
193
|
provider: options.provider
|
|
186
194
|
} : _extends({}, options, {
|
|
187
195
|
provider: options.signer.provider
|
|
188
196
|
}));
|
|
189
197
|
this.signer = void 0;
|
|
190
198
|
this.txnOptions = void 0;
|
|
191
|
-
this.signer = ethers.
|
|
199
|
+
this.signer = options instanceof ethers.ethers.AbstractSigner ? options : options.signer;
|
|
192
200
|
if (!this.signer.provider) throw new Error('Signer must have a provider');
|
|
193
201
|
}
|
|
194
202
|
async getFeeOptions(_address, ..._transactions) {
|
|
@@ -218,7 +226,7 @@ class LocalRelayer extends ProviderRelayer {
|
|
|
218
226
|
|
|
219
227
|
// TODO: think about computing gas limit individually, summing together and passing across
|
|
220
228
|
// NOTE: we expect that all txns have set their gasLimit ahead of time through proper estimation
|
|
221
|
-
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum
|
|
229
|
+
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum + tx.gasLimit, 0n)
|
|
222
230
|
// txRequest.gasLimit = gasLimit
|
|
223
231
|
|
|
224
232
|
const responsePromise = this.signer.sendTransaction(_extends({
|
|
@@ -966,7 +974,7 @@ var relayer_gen = /*#__PURE__*/Object.freeze({
|
|
|
966
974
|
const FINAL_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.SUCCEEDED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
|
|
967
975
|
const FAILED_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
|
|
968
976
|
function isRpcRelayerOptions(obj) {
|
|
969
|
-
return obj.url !== undefined && typeof obj.url === 'string' && obj.provider !== undefined && ethers.ethers.
|
|
977
|
+
return obj.url !== undefined && typeof obj.url === 'string' && obj.provider !== undefined && obj.provider instanceof ethers.ethers.AbstractProvider;
|
|
970
978
|
}
|
|
971
979
|
const fetch = globalThis.fetch;
|
|
972
980
|
|
|
@@ -996,15 +1004,17 @@ class RpcRelayer {
|
|
|
996
1004
|
return fetch(input, init);
|
|
997
1005
|
};
|
|
998
1006
|
this.service = new Relayer(options.url, this._fetch);
|
|
999
|
-
if (ethers.ethers.
|
|
1007
|
+
if (options.provider instanceof ethers.ethers.AbstractProvider) {
|
|
1000
1008
|
this.provider = options.provider;
|
|
1001
1009
|
} else {
|
|
1002
1010
|
const {
|
|
1003
1011
|
jwtAuth,
|
|
1004
1012
|
projectAccessKey
|
|
1005
1013
|
} = this.options;
|
|
1006
|
-
const
|
|
1007
|
-
this.provider = new ethers.ethers.
|
|
1014
|
+
const fetchRequest = utils.getFetchRequest(options.provider.url, projectAccessKey, jwtAuth);
|
|
1015
|
+
this.provider = new ethers.ethers.JsonRpcProvider(fetchRequest, undefined, {
|
|
1016
|
+
staticNetwork: true
|
|
1017
|
+
});
|
|
1008
1018
|
}
|
|
1009
1019
|
}
|
|
1010
1020
|
async waitReceipt(metaTxnId, delay = 1000, maxFails = 5, isCancelled) {
|
|
@@ -1038,7 +1048,7 @@ class RpcRelayer {
|
|
|
1038
1048
|
throw new Error(`Cancelled waiting for transaction receipt ${metaTxnId}`);
|
|
1039
1049
|
}
|
|
1040
1050
|
async simulate(wallet, ...transactions) {
|
|
1041
|
-
const coder = ethers.ethers.
|
|
1051
|
+
const coder = ethers.ethers.AbiCoder.defaultAbiCoder();
|
|
1042
1052
|
const encoded = coder.encode([core.commons.transaction.MetaTransactionsType], [core.commons.transaction.sequenceTxAbiEncode(transactions)]);
|
|
1043
1053
|
return (await this.service.simulate({
|
|
1044
1054
|
wallet,
|
|
@@ -1092,7 +1102,7 @@ class RpcRelayer {
|
|
|
1092
1102
|
} = await this.service.feeOptions({
|
|
1093
1103
|
wallet: entrypoint,
|
|
1094
1104
|
to: entrypoint,
|
|
1095
|
-
data: ethers.ethers.
|
|
1105
|
+
data: ethers.ethers.hexlify(data),
|
|
1096
1106
|
simulate: options == null ? void 0 : options.simulate
|
|
1097
1107
|
});
|
|
1098
1108
|
return {
|
|
@@ -1111,12 +1121,12 @@ class RpcRelayer {
|
|
|
1111
1121
|
}
|
|
1112
1122
|
async getNonce(address, space) {
|
|
1113
1123
|
utils.logger.info(`[rpc-relayer/getNonce] get nonce for wallet ${address} space: ${space}`);
|
|
1114
|
-
const encodedNonce = space !== undefined ?
|
|
1124
|
+
const encodedNonce = space !== undefined ? utils.toHexString(BigInt(space)) : undefined;
|
|
1115
1125
|
const resp = await this.service.getMetaTxnNonce({
|
|
1116
1126
|
walletContractAddress: address,
|
|
1117
1127
|
space: encodedNonce
|
|
1118
1128
|
});
|
|
1119
|
-
const nonce =
|
|
1129
|
+
const nonce = BigInt(resp.nonce);
|
|
1120
1130
|
const [decodedSpace, decodedNonce] = core.commons.transaction.decodeNonce(nonce);
|
|
1121
1131
|
utils.logger.info(`[rpc-relayer/getNonce] got next nonce for wallet ${address} ${decodedNonce} space: ${decodedSpace}`);
|
|
1122
1132
|
return nonce;
|
|
@@ -1169,6 +1179,10 @@ class RpcRelayer {
|
|
|
1169
1179
|
return _this.provider.waitForTransaction(transactionHash, confirmations);
|
|
1170
1180
|
};
|
|
1171
1181
|
response.wait = wait;
|
|
1182
|
+
|
|
1183
|
+
// NOTE: we just ignore these errors which come from the private fields
|
|
1184
|
+
// of ethers-v6 .. but, we should probably rework this instead..
|
|
1185
|
+
// @ts-ignore
|
|
1172
1186
|
return response;
|
|
1173
1187
|
}
|
|
1174
1188
|
}
|
|
@@ -1185,9 +1199,13 @@ class RpcRelayer {
|
|
|
1185
1199
|
throw new MetaTransactionResponseException(receipt);
|
|
1186
1200
|
}
|
|
1187
1201
|
const txReceipt = JSON.parse(receipt.txnReceipt);
|
|
1202
|
+
|
|
1203
|
+
// NOTE: we just ignore these errors which come from the private fields
|
|
1204
|
+
// of ethers-v6 .. but, we should probably rework this instead..
|
|
1205
|
+
// @ts-ignore
|
|
1188
1206
|
return {
|
|
1189
1207
|
blockHash: txReceipt.blockHash,
|
|
1190
|
-
blockNumber:
|
|
1208
|
+
blockNumber: Number(txReceipt.blockNumber),
|
|
1191
1209
|
confirmations: 1,
|
|
1192
1210
|
from: typeof metaTxnId === 'string' ? undefined : metaTxnId.intent.wallet,
|
|
1193
1211
|
hash: txReceipt.transactionHash,
|
|
@@ -22,14 +22,14 @@ function _extends() {
|
|
|
22
22
|
return _extends.apply(this, arguments);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const DEFAULT_GAS_LIMIT =
|
|
25
|
+
const DEFAULT_GAS_LIMIT = 800000n;
|
|
26
26
|
const ProviderRelayerDefaults = {
|
|
27
27
|
waitPollRate: 1000,
|
|
28
28
|
deltaBlocksLog: 12,
|
|
29
29
|
fromBlockLog: -1024
|
|
30
30
|
};
|
|
31
31
|
function isProviderRelayerOptions(obj) {
|
|
32
|
-
return obj
|
|
32
|
+
return typeof obj === 'object' && obj.provider instanceof ethers.ethers.AbstractProvider;
|
|
33
33
|
}
|
|
34
34
|
class ProviderRelayer {
|
|
35
35
|
constructor(options) {
|
|
@@ -47,7 +47,7 @@ class ProviderRelayer {
|
|
|
47
47
|
var _this = this;
|
|
48
48
|
return (await Promise.all(transactions.map(async function (tx) {
|
|
49
49
|
// Respect gasLimit request of the transaction (as long as its not 0)
|
|
50
|
-
if (tx.gasLimit &&
|
|
50
|
+
if (tx.gasLimit && BigInt(tx.gasLimit || 0) !== 0n) {
|
|
51
51
|
return tx.gasLimit;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -57,7 +57,7 @@ class ProviderRelayer {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Fee can't be estimated for self-called if wallet hasn't been deployed
|
|
60
|
-
if (tx.to === wallet && (await _this.provider.getCode(wallet).then(code => ethers.ethers.
|
|
60
|
+
if (tx.to === wallet && (await _this.provider.getCode(wallet).then(code => ethers.ethers.getBytes(code).length === 0))) {
|
|
61
61
|
return DEFAULT_GAS_LIMIT;
|
|
62
62
|
}
|
|
63
63
|
if (!_this.provider) {
|
|
@@ -75,8 +75,8 @@ class ProviderRelayer {
|
|
|
75
75
|
}))).map(gasLimit => ({
|
|
76
76
|
executed: true,
|
|
77
77
|
succeeded: true,
|
|
78
|
-
gasUsed:
|
|
79
|
-
gasLimit:
|
|
78
|
+
gasUsed: Number(gasLimit),
|
|
79
|
+
gasLimit: Number(gasLimit)
|
|
80
80
|
}));
|
|
81
81
|
}
|
|
82
82
|
async getNonce(address, space, blockTag) {
|
|
@@ -147,15 +147,23 @@ class ProviderRelayer {
|
|
|
147
147
|
const txs = await Promise.all(logs.map(l => retry(() => _this2.provider.getTransactionReceipt(l.transactionHash), `unable to get receipt for transaction ${l.transactionHash}`)));
|
|
148
148
|
|
|
149
149
|
// Find a transaction with a TxExecuted log
|
|
150
|
-
const found = txs.find(tx => tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 &&
|
|
150
|
+
const found = txs.find(tx => tx == null ? void 0 : tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 &&
|
|
151
151
|
// TxFailed event topic
|
|
152
152
|
l.topics[0] === '0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7' && l.data.length >= 64 && l.data.replace('0x', '').startsWith(normalMetaTxnId)));
|
|
153
153
|
|
|
154
154
|
// If found return that
|
|
155
155
|
if (found) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
const response = await retry(() => _this2.provider.getTransaction(found.hash), `unable to get transaction ${found.hash}`);
|
|
157
|
+
if (!response) {
|
|
158
|
+
throw new Error(`Transaction response not found for ${metaTxnId}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// NOTE: we have to do this, because ethers-v6 uses private fields
|
|
162
|
+
// and we can't just extend the class and override the method, so
|
|
163
|
+
// we just modify the response object directly by adding the receipt to it.
|
|
164
|
+
const out = response;
|
|
165
|
+
out.receipt = found;
|
|
166
|
+
return out;
|
|
159
167
|
}
|
|
160
168
|
|
|
161
169
|
// Otherwise wait and try again
|
|
@@ -177,18 +185,18 @@ class ProviderRelayer {
|
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
function isLocalRelayerOptions(obj) {
|
|
180
|
-
return obj
|
|
188
|
+
return typeof obj === 'object' && obj.signer instanceof ethers.ethers.AbstractSigner;
|
|
181
189
|
}
|
|
182
190
|
class LocalRelayer extends ProviderRelayer {
|
|
183
191
|
constructor(options) {
|
|
184
|
-
super(ethers.
|
|
192
|
+
super(options instanceof ethers.ethers.AbstractSigner ? {
|
|
185
193
|
provider: options.provider
|
|
186
194
|
} : _extends({}, options, {
|
|
187
195
|
provider: options.signer.provider
|
|
188
196
|
}));
|
|
189
197
|
this.signer = void 0;
|
|
190
198
|
this.txnOptions = void 0;
|
|
191
|
-
this.signer = ethers.
|
|
199
|
+
this.signer = options instanceof ethers.ethers.AbstractSigner ? options : options.signer;
|
|
192
200
|
if (!this.signer.provider) throw new Error('Signer must have a provider');
|
|
193
201
|
}
|
|
194
202
|
async getFeeOptions(_address, ..._transactions) {
|
|
@@ -218,7 +226,7 @@ class LocalRelayer extends ProviderRelayer {
|
|
|
218
226
|
|
|
219
227
|
// TODO: think about computing gas limit individually, summing together and passing across
|
|
220
228
|
// NOTE: we expect that all txns have set their gasLimit ahead of time through proper estimation
|
|
221
|
-
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum
|
|
229
|
+
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum + tx.gasLimit, 0n)
|
|
222
230
|
// txRequest.gasLimit = gasLimit
|
|
223
231
|
|
|
224
232
|
const responsePromise = this.signer.sendTransaction(_extends({
|
|
@@ -966,7 +974,7 @@ var relayer_gen = /*#__PURE__*/Object.freeze({
|
|
|
966
974
|
const FINAL_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.SUCCEEDED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
|
|
967
975
|
const FAILED_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
|
|
968
976
|
function isRpcRelayerOptions(obj) {
|
|
969
|
-
return obj.url !== undefined && typeof obj.url === 'string' && obj.provider !== undefined && ethers.ethers.
|
|
977
|
+
return obj.url !== undefined && typeof obj.url === 'string' && obj.provider !== undefined && obj.provider instanceof ethers.ethers.AbstractProvider;
|
|
970
978
|
}
|
|
971
979
|
const fetch = globalThis.fetch;
|
|
972
980
|
|
|
@@ -996,15 +1004,17 @@ class RpcRelayer {
|
|
|
996
1004
|
return fetch(input, init);
|
|
997
1005
|
};
|
|
998
1006
|
this.service = new Relayer(options.url, this._fetch);
|
|
999
|
-
if (ethers.ethers.
|
|
1007
|
+
if (options.provider instanceof ethers.ethers.AbstractProvider) {
|
|
1000
1008
|
this.provider = options.provider;
|
|
1001
1009
|
} else {
|
|
1002
1010
|
const {
|
|
1003
1011
|
jwtAuth,
|
|
1004
1012
|
projectAccessKey
|
|
1005
1013
|
} = this.options;
|
|
1006
|
-
const
|
|
1007
|
-
this.provider = new ethers.ethers.
|
|
1014
|
+
const fetchRequest = utils.getFetchRequest(options.provider.url, projectAccessKey, jwtAuth);
|
|
1015
|
+
this.provider = new ethers.ethers.JsonRpcProvider(fetchRequest, undefined, {
|
|
1016
|
+
staticNetwork: true
|
|
1017
|
+
});
|
|
1008
1018
|
}
|
|
1009
1019
|
}
|
|
1010
1020
|
async waitReceipt(metaTxnId, delay = 1000, maxFails = 5, isCancelled) {
|
|
@@ -1038,7 +1048,7 @@ class RpcRelayer {
|
|
|
1038
1048
|
throw new Error(`Cancelled waiting for transaction receipt ${metaTxnId}`);
|
|
1039
1049
|
}
|
|
1040
1050
|
async simulate(wallet, ...transactions) {
|
|
1041
|
-
const coder = ethers.ethers.
|
|
1051
|
+
const coder = ethers.ethers.AbiCoder.defaultAbiCoder();
|
|
1042
1052
|
const encoded = coder.encode([core.commons.transaction.MetaTransactionsType], [core.commons.transaction.sequenceTxAbiEncode(transactions)]);
|
|
1043
1053
|
return (await this.service.simulate({
|
|
1044
1054
|
wallet,
|
|
@@ -1092,7 +1102,7 @@ class RpcRelayer {
|
|
|
1092
1102
|
} = await this.service.feeOptions({
|
|
1093
1103
|
wallet: entrypoint,
|
|
1094
1104
|
to: entrypoint,
|
|
1095
|
-
data: ethers.ethers.
|
|
1105
|
+
data: ethers.ethers.hexlify(data),
|
|
1096
1106
|
simulate: options == null ? void 0 : options.simulate
|
|
1097
1107
|
});
|
|
1098
1108
|
return {
|
|
@@ -1111,12 +1121,12 @@ class RpcRelayer {
|
|
|
1111
1121
|
}
|
|
1112
1122
|
async getNonce(address, space) {
|
|
1113
1123
|
utils.logger.info(`[rpc-relayer/getNonce] get nonce for wallet ${address} space: ${space}`);
|
|
1114
|
-
const encodedNonce = space !== undefined ?
|
|
1124
|
+
const encodedNonce = space !== undefined ? utils.toHexString(BigInt(space)) : undefined;
|
|
1115
1125
|
const resp = await this.service.getMetaTxnNonce({
|
|
1116
1126
|
walletContractAddress: address,
|
|
1117
1127
|
space: encodedNonce
|
|
1118
1128
|
});
|
|
1119
|
-
const nonce =
|
|
1129
|
+
const nonce = BigInt(resp.nonce);
|
|
1120
1130
|
const [decodedSpace, decodedNonce] = core.commons.transaction.decodeNonce(nonce);
|
|
1121
1131
|
utils.logger.info(`[rpc-relayer/getNonce] got next nonce for wallet ${address} ${decodedNonce} space: ${decodedSpace}`);
|
|
1122
1132
|
return nonce;
|
|
@@ -1169,6 +1179,10 @@ class RpcRelayer {
|
|
|
1169
1179
|
return _this.provider.waitForTransaction(transactionHash, confirmations);
|
|
1170
1180
|
};
|
|
1171
1181
|
response.wait = wait;
|
|
1182
|
+
|
|
1183
|
+
// NOTE: we just ignore these errors which come from the private fields
|
|
1184
|
+
// of ethers-v6 .. but, we should probably rework this instead..
|
|
1185
|
+
// @ts-ignore
|
|
1172
1186
|
return response;
|
|
1173
1187
|
}
|
|
1174
1188
|
}
|
|
@@ -1185,9 +1199,13 @@ class RpcRelayer {
|
|
|
1185
1199
|
throw new MetaTransactionResponseException(receipt);
|
|
1186
1200
|
}
|
|
1187
1201
|
const txReceipt = JSON.parse(receipt.txnReceipt);
|
|
1202
|
+
|
|
1203
|
+
// NOTE: we just ignore these errors which come from the private fields
|
|
1204
|
+
// of ethers-v6 .. but, we should probably rework this instead..
|
|
1205
|
+
// @ts-ignore
|
|
1188
1206
|
return {
|
|
1189
1207
|
blockHash: txReceipt.blockHash,
|
|
1190
|
-
blockNumber:
|
|
1208
|
+
blockNumber: Number(txReceipt.blockNumber),
|
|
1191
1209
|
confirmations: 1,
|
|
1192
1210
|
from: typeof metaTxnId === 'string' ? undefined : metaTxnId.intent.wallet,
|
|
1193
1211
|
hash: txReceipt.transactionHash,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ethers
|
|
2
|
-
import { logger,
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import { logger, getFetchRequest, toHexString } from '@0xsequence/utils';
|
|
3
3
|
import { walletContracts } from '@0xsequence/abi';
|
|
4
4
|
import { commons } from '@0xsequence/core';
|
|
5
5
|
|
|
@@ -18,14 +18,14 @@ function _extends() {
|
|
|
18
18
|
return _extends.apply(this, arguments);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const DEFAULT_GAS_LIMIT =
|
|
21
|
+
const DEFAULT_GAS_LIMIT = 800000n;
|
|
22
22
|
const ProviderRelayerDefaults = {
|
|
23
23
|
waitPollRate: 1000,
|
|
24
24
|
deltaBlocksLog: 12,
|
|
25
25
|
fromBlockLog: -1024
|
|
26
26
|
};
|
|
27
27
|
function isProviderRelayerOptions(obj) {
|
|
28
|
-
return obj
|
|
28
|
+
return typeof obj === 'object' && obj.provider instanceof ethers.AbstractProvider;
|
|
29
29
|
}
|
|
30
30
|
class ProviderRelayer {
|
|
31
31
|
constructor(options) {
|
|
@@ -43,7 +43,7 @@ class ProviderRelayer {
|
|
|
43
43
|
var _this = this;
|
|
44
44
|
return (await Promise.all(transactions.map(async function (tx) {
|
|
45
45
|
// Respect gasLimit request of the transaction (as long as its not 0)
|
|
46
|
-
if (tx.gasLimit &&
|
|
46
|
+
if (tx.gasLimit && BigInt(tx.gasLimit || 0) !== 0n) {
|
|
47
47
|
return tx.gasLimit;
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -53,7 +53,7 @@ class ProviderRelayer {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// Fee can't be estimated for self-called if wallet hasn't been deployed
|
|
56
|
-
if (tx.to === wallet && (await _this.provider.getCode(wallet).then(code => ethers.
|
|
56
|
+
if (tx.to === wallet && (await _this.provider.getCode(wallet).then(code => ethers.getBytes(code).length === 0))) {
|
|
57
57
|
return DEFAULT_GAS_LIMIT;
|
|
58
58
|
}
|
|
59
59
|
if (!_this.provider) {
|
|
@@ -71,8 +71,8 @@ class ProviderRelayer {
|
|
|
71
71
|
}))).map(gasLimit => ({
|
|
72
72
|
executed: true,
|
|
73
73
|
succeeded: true,
|
|
74
|
-
gasUsed:
|
|
75
|
-
gasLimit:
|
|
74
|
+
gasUsed: Number(gasLimit),
|
|
75
|
+
gasLimit: Number(gasLimit)
|
|
76
76
|
}));
|
|
77
77
|
}
|
|
78
78
|
async getNonce(address, space, blockTag) {
|
|
@@ -143,15 +143,23 @@ class ProviderRelayer {
|
|
|
143
143
|
const txs = await Promise.all(logs.map(l => retry(() => _this2.provider.getTransactionReceipt(l.transactionHash), `unable to get receipt for transaction ${l.transactionHash}`)));
|
|
144
144
|
|
|
145
145
|
// Find a transaction with a TxExecuted log
|
|
146
|
-
const found = txs.find(tx => tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 &&
|
|
146
|
+
const found = txs.find(tx => tx == null ? void 0 : tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 &&
|
|
147
147
|
// TxFailed event topic
|
|
148
148
|
l.topics[0] === '0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7' && l.data.length >= 64 && l.data.replace('0x', '').startsWith(normalMetaTxnId)));
|
|
149
149
|
|
|
150
150
|
// If found return that
|
|
151
151
|
if (found) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
const response = await retry(() => _this2.provider.getTransaction(found.hash), `unable to get transaction ${found.hash}`);
|
|
153
|
+
if (!response) {
|
|
154
|
+
throw new Error(`Transaction response not found for ${metaTxnId}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// NOTE: we have to do this, because ethers-v6 uses private fields
|
|
158
|
+
// and we can't just extend the class and override the method, so
|
|
159
|
+
// we just modify the response object directly by adding the receipt to it.
|
|
160
|
+
const out = response;
|
|
161
|
+
out.receipt = found;
|
|
162
|
+
return out;
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
// Otherwise wait and try again
|
|
@@ -173,18 +181,18 @@ class ProviderRelayer {
|
|
|
173
181
|
}
|
|
174
182
|
|
|
175
183
|
function isLocalRelayerOptions(obj) {
|
|
176
|
-
return obj
|
|
184
|
+
return typeof obj === 'object' && obj.signer instanceof ethers.AbstractSigner;
|
|
177
185
|
}
|
|
178
186
|
class LocalRelayer extends ProviderRelayer {
|
|
179
187
|
constructor(options) {
|
|
180
|
-
super(
|
|
188
|
+
super(options instanceof ethers.AbstractSigner ? {
|
|
181
189
|
provider: options.provider
|
|
182
190
|
} : _extends({}, options, {
|
|
183
191
|
provider: options.signer.provider
|
|
184
192
|
}));
|
|
185
193
|
this.signer = void 0;
|
|
186
194
|
this.txnOptions = void 0;
|
|
187
|
-
this.signer =
|
|
195
|
+
this.signer = options instanceof ethers.AbstractSigner ? options : options.signer;
|
|
188
196
|
if (!this.signer.provider) throw new Error('Signer must have a provider');
|
|
189
197
|
}
|
|
190
198
|
async getFeeOptions(_address, ..._transactions) {
|
|
@@ -214,7 +222,7 @@ class LocalRelayer extends ProviderRelayer {
|
|
|
214
222
|
|
|
215
223
|
// TODO: think about computing gas limit individually, summing together and passing across
|
|
216
224
|
// NOTE: we expect that all txns have set their gasLimit ahead of time through proper estimation
|
|
217
|
-
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum
|
|
225
|
+
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum + tx.gasLimit, 0n)
|
|
218
226
|
// txRequest.gasLimit = gasLimit
|
|
219
227
|
|
|
220
228
|
const responsePromise = this.signer.sendTransaction(_extends({
|
|
@@ -962,7 +970,7 @@ var relayer_gen = /*#__PURE__*/Object.freeze({
|
|
|
962
970
|
const FINAL_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.SUCCEEDED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
|
|
963
971
|
const FAILED_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
|
|
964
972
|
function isRpcRelayerOptions(obj) {
|
|
965
|
-
return obj.url !== undefined && typeof obj.url === 'string' && obj.provider !== undefined &&
|
|
973
|
+
return obj.url !== undefined && typeof obj.url === 'string' && obj.provider !== undefined && obj.provider instanceof ethers.AbstractProvider;
|
|
966
974
|
}
|
|
967
975
|
const fetch = globalThis.fetch;
|
|
968
976
|
|
|
@@ -992,15 +1000,17 @@ class RpcRelayer {
|
|
|
992
1000
|
return fetch(input, init);
|
|
993
1001
|
};
|
|
994
1002
|
this.service = new Relayer(options.url, this._fetch);
|
|
995
|
-
if (
|
|
1003
|
+
if (options.provider instanceof ethers.AbstractProvider) {
|
|
996
1004
|
this.provider = options.provider;
|
|
997
1005
|
} else {
|
|
998
1006
|
const {
|
|
999
1007
|
jwtAuth,
|
|
1000
1008
|
projectAccessKey
|
|
1001
1009
|
} = this.options;
|
|
1002
|
-
const
|
|
1003
|
-
this.provider = new ethers.
|
|
1010
|
+
const fetchRequest = getFetchRequest(options.provider.url, projectAccessKey, jwtAuth);
|
|
1011
|
+
this.provider = new ethers.JsonRpcProvider(fetchRequest, undefined, {
|
|
1012
|
+
staticNetwork: true
|
|
1013
|
+
});
|
|
1004
1014
|
}
|
|
1005
1015
|
}
|
|
1006
1016
|
async waitReceipt(metaTxnId, delay = 1000, maxFails = 5, isCancelled) {
|
|
@@ -1034,7 +1044,7 @@ class RpcRelayer {
|
|
|
1034
1044
|
throw new Error(`Cancelled waiting for transaction receipt ${metaTxnId}`);
|
|
1035
1045
|
}
|
|
1036
1046
|
async simulate(wallet, ...transactions) {
|
|
1037
|
-
const coder = ethers.
|
|
1047
|
+
const coder = ethers.AbiCoder.defaultAbiCoder();
|
|
1038
1048
|
const encoded = coder.encode([commons.transaction.MetaTransactionsType], [commons.transaction.sequenceTxAbiEncode(transactions)]);
|
|
1039
1049
|
return (await this.service.simulate({
|
|
1040
1050
|
wallet,
|
|
@@ -1088,7 +1098,7 @@ class RpcRelayer {
|
|
|
1088
1098
|
} = await this.service.feeOptions({
|
|
1089
1099
|
wallet: entrypoint,
|
|
1090
1100
|
to: entrypoint,
|
|
1091
|
-
data: ethers.
|
|
1101
|
+
data: ethers.hexlify(data),
|
|
1092
1102
|
simulate: options == null ? void 0 : options.simulate
|
|
1093
1103
|
});
|
|
1094
1104
|
return {
|
|
@@ -1107,12 +1117,12 @@ class RpcRelayer {
|
|
|
1107
1117
|
}
|
|
1108
1118
|
async getNonce(address, space) {
|
|
1109
1119
|
logger.info(`[rpc-relayer/getNonce] get nonce for wallet ${address} space: ${space}`);
|
|
1110
|
-
const encodedNonce = space !== undefined ?
|
|
1120
|
+
const encodedNonce = space !== undefined ? toHexString(BigInt(space)) : undefined;
|
|
1111
1121
|
const resp = await this.service.getMetaTxnNonce({
|
|
1112
1122
|
walletContractAddress: address,
|
|
1113
1123
|
space: encodedNonce
|
|
1114
1124
|
});
|
|
1115
|
-
const nonce =
|
|
1125
|
+
const nonce = BigInt(resp.nonce);
|
|
1116
1126
|
const [decodedSpace, decodedNonce] = commons.transaction.decodeNonce(nonce);
|
|
1117
1127
|
logger.info(`[rpc-relayer/getNonce] got next nonce for wallet ${address} ${decodedNonce} space: ${decodedSpace}`);
|
|
1118
1128
|
return nonce;
|
|
@@ -1165,6 +1175,10 @@ class RpcRelayer {
|
|
|
1165
1175
|
return _this.provider.waitForTransaction(transactionHash, confirmations);
|
|
1166
1176
|
};
|
|
1167
1177
|
response.wait = wait;
|
|
1178
|
+
|
|
1179
|
+
// NOTE: we just ignore these errors which come from the private fields
|
|
1180
|
+
// of ethers-v6 .. but, we should probably rework this instead..
|
|
1181
|
+
// @ts-ignore
|
|
1168
1182
|
return response;
|
|
1169
1183
|
}
|
|
1170
1184
|
}
|
|
@@ -1181,9 +1195,13 @@ class RpcRelayer {
|
|
|
1181
1195
|
throw new MetaTransactionResponseException(receipt);
|
|
1182
1196
|
}
|
|
1183
1197
|
const txReceipt = JSON.parse(receipt.txnReceipt);
|
|
1198
|
+
|
|
1199
|
+
// NOTE: we just ignore these errors which come from the private fields
|
|
1200
|
+
// of ethers-v6 .. but, we should probably rework this instead..
|
|
1201
|
+
// @ts-ignore
|
|
1184
1202
|
return {
|
|
1185
1203
|
blockHash: txReceipt.blockHash,
|
|
1186
|
-
blockNumber:
|
|
1204
|
+
blockNumber: Number(txReceipt.blockNumber),
|
|
1187
1205
|
confirmations: 1,
|
|
1188
1206
|
from: typeof metaTxnId === 'string' ? undefined : metaTxnId.intent.wallet,
|
|
1189
1207
|
hash: txReceipt.transactionHash,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ethers
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
2
|
import { proto } from "./rpc-relayer/index.js";
|
|
3
3
|
import { commons } from '@0xsequence/core';
|
|
4
4
|
export interface Relayer {
|
|
@@ -7,14 +7,14 @@ export interface Relayer {
|
|
|
7
7
|
options: FeeOption[];
|
|
8
8
|
quote?: FeeQuote;
|
|
9
9
|
}>;
|
|
10
|
-
getFeeOptionsRaw(entrypoint: string, data: ethers.
|
|
10
|
+
getFeeOptionsRaw(entrypoint: string, data: ethers.BytesLike, options?: {
|
|
11
11
|
simulate?: boolean;
|
|
12
12
|
}): Promise<{
|
|
13
13
|
options: FeeOption[];
|
|
14
14
|
quote?: FeeQuote;
|
|
15
15
|
}>;
|
|
16
16
|
gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise<FeeOption[]>;
|
|
17
|
-
getNonce(address: string, space?: ethers.BigNumberish, blockTag?:
|
|
17
|
+
getNonce(address: string, space?: ethers.BigNumberish, blockTag?: ethers.BlockTag): Promise<ethers.BigNumberish>;
|
|
18
18
|
relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise<commons.transaction.TransactionResponse>;
|
|
19
19
|
wait(metaTxnId: string | commons.transaction.SignedTransactionBundle, timeout?: number, delay?: number, maxFails?: number): Promise<commons.transaction.TransactionResponse>;
|
|
20
20
|
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
2
|
import { FeeOption, FeeQuote, Relayer } from "./index.js";
|
|
3
3
|
import { ProviderRelayer, ProviderRelayerOptions } from "./provider-relayer.js";
|
|
4
4
|
import { commons } from '@0xsequence/core';
|
|
5
5
|
export type LocalRelayerOptions = Omit<ProviderRelayerOptions, 'provider'> & {
|
|
6
|
-
signer:
|
|
6
|
+
signer: ethers.Signer;
|
|
7
7
|
};
|
|
8
8
|
export declare function isLocalRelayerOptions(obj: any): obj is LocalRelayerOptions;
|
|
9
9
|
export declare class LocalRelayer extends ProviderRelayer implements Relayer {
|
|
10
10
|
private signer;
|
|
11
11
|
private txnOptions;
|
|
12
|
-
constructor(options: LocalRelayerOptions | AbstractSigner);
|
|
12
|
+
constructor(options: LocalRelayerOptions | ethers.AbstractSigner);
|
|
13
13
|
getFeeOptions(_address: string, ..._transactions: commons.transaction.Transaction[]): Promise<{
|
|
14
14
|
options: FeeOption[];
|
|
15
15
|
}>;
|
|
16
|
-
getFeeOptionsRaw(_entrypoint: string, _data: BytesLike, _options?: {
|
|
16
|
+
getFeeOptionsRaw(_entrypoint: string, _data: ethers.BytesLike, _options?: {
|
|
17
17
|
simulate?: boolean;
|
|
18
18
|
}): Promise<{
|
|
19
19
|
options: FeeOption[];
|
|
20
20
|
}>;
|
|
21
21
|
gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise<FeeOption[]>;
|
|
22
|
-
setTransactionOptions(transactionRequest:
|
|
23
|
-
relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise<commons.transaction.TransactionResponse<
|
|
22
|
+
setTransactionOptions(transactionRequest: ethers.TransactionRequest): void;
|
|
23
|
+
relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise<commons.transaction.TransactionResponse<ethers.TransactionReceipt>>;
|
|
24
24
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ethers
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
2
|
import { FeeOption, FeeQuote, Relayer, SimulateResult } from "./index.js";
|
|
3
3
|
import { Optionals } from '@0xsequence/utils';
|
|
4
4
|
import { commons } from '@0xsequence/core';
|
|
5
5
|
export interface ProviderRelayerOptions {
|
|
6
|
-
provider:
|
|
6
|
+
provider: ethers.Provider;
|
|
7
7
|
waitPollRate?: number;
|
|
8
8
|
deltaBlocksLog?: number;
|
|
9
9
|
fromBlockLog?: number;
|
|
@@ -11,7 +11,7 @@ export interface ProviderRelayerOptions {
|
|
|
11
11
|
export declare const ProviderRelayerDefaults: Required<Optionals<ProviderRelayerOptions>>;
|
|
12
12
|
export declare function isProviderRelayerOptions(obj: any): obj is ProviderRelayerOptions;
|
|
13
13
|
export declare abstract class ProviderRelayer implements Relayer {
|
|
14
|
-
provider:
|
|
14
|
+
provider: ethers.Provider;
|
|
15
15
|
waitPollRate: number;
|
|
16
16
|
deltaBlocksLog: number;
|
|
17
17
|
fromBlockLog: number;
|
|
@@ -20,7 +20,7 @@ export declare abstract class ProviderRelayer implements Relayer {
|
|
|
20
20
|
options: FeeOption[];
|
|
21
21
|
quote?: FeeQuote;
|
|
22
22
|
}>;
|
|
23
|
-
abstract getFeeOptionsRaw(entrypoint: string, data: ethers.
|
|
23
|
+
abstract getFeeOptionsRaw(entrypoint: string, data: ethers.BytesLike, options?: {
|
|
24
24
|
simulate?: boolean;
|
|
25
25
|
}): Promise<{
|
|
26
26
|
options: FeeOption[];
|
|
@@ -29,8 +29,8 @@ export declare abstract class ProviderRelayer implements Relayer {
|
|
|
29
29
|
abstract gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise<FeeOption[]>;
|
|
30
30
|
abstract relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise<commons.transaction.TransactionResponse>;
|
|
31
31
|
simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise<SimulateResult[]>;
|
|
32
|
-
getNonce(address: string, space?: ethers.BigNumberish, blockTag?:
|
|
33
|
-
wait(metaTxnId: string | commons.transaction.SignedTransactionBundle, timeoutDuration?: number, delay?: number, maxFails?: number): Promise<
|
|
34
|
-
receipt:
|
|
32
|
+
getNonce(address: string, space?: ethers.BigNumberish, blockTag?: ethers.BlockTag): Promise<ethers.BigNumberish>;
|
|
33
|
+
wait(metaTxnId: string | commons.transaction.SignedTransactionBundle, timeoutDuration?: number, delay?: number, maxFails?: number): Promise<ethers.TransactionResponse & {
|
|
34
|
+
receipt: ethers.TransactionReceipt;
|
|
35
35
|
}>;
|
|
36
36
|
}
|
|
@@ -4,7 +4,7 @@ import * as proto from "./relayer.gen.js";
|
|
|
4
4
|
import { commons } from '@0xsequence/core';
|
|
5
5
|
export { proto };
|
|
6
6
|
export interface RpcRelayerOptions {
|
|
7
|
-
provider: ethers.
|
|
7
|
+
provider: ethers.AbstractProvider | {
|
|
8
8
|
url: string;
|
|
9
9
|
};
|
|
10
10
|
url: string;
|
|
@@ -15,7 +15,7 @@ export declare function isRpcRelayerOptions(obj: any): obj is RpcRelayerOptions;
|
|
|
15
15
|
export declare class RpcRelayer implements Relayer {
|
|
16
16
|
options: RpcRelayerOptions;
|
|
17
17
|
private readonly service;
|
|
18
|
-
readonly provider: ethers.
|
|
18
|
+
readonly provider: ethers.Provider;
|
|
19
19
|
constructor(options: RpcRelayerOptions);
|
|
20
20
|
_fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
|
|
21
21
|
waitReceipt(metaTxnId: string | commons.transaction.SignedTransactionBundle, delay?: number, maxFails?: number, isCancelled?: () => boolean): Promise<proto.GetMetaTxnReceiptReturn>;
|
|
@@ -24,7 +24,7 @@ export declare class RpcRelayer implements Relayer {
|
|
|
24
24
|
options: FeeOption[];
|
|
25
25
|
quote?: FeeQuote;
|
|
26
26
|
}>;
|
|
27
|
-
getFeeOptionsRaw(entrypoint: string, data: ethers.
|
|
27
|
+
getFeeOptionsRaw(entrypoint: string, data: ethers.BytesLike, options?: {
|
|
28
28
|
simulate?: boolean;
|
|
29
29
|
}): Promise<{
|
|
30
30
|
options: FeeOption[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xsequence/relayer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "relayer sub-package for Sequence",
|
|
5
5
|
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/relayer",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
"author": "Horizon Blockchain Games",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@0xsequence/abi": "
|
|
13
|
-
"@0xsequence/core": "
|
|
14
|
-
"@0xsequence/utils": "
|
|
12
|
+
"@0xsequence/abi": "2.0.0",
|
|
13
|
+
"@0xsequence/core": "2.0.0",
|
|
14
|
+
"@0xsequence/utils": "2.0.0"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
|
-
"ethers": ">=
|
|
17
|
+
"ethers": ">=6"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@0xsequence/wallet-contracts": "^
|
|
21
|
-
"ethers": "^
|
|
22
|
-
"@0xsequence/signhub": "
|
|
23
|
-
"@0xsequence/tests": "
|
|
20
|
+
"@0xsequence/wallet-contracts": "^3.0.1",
|
|
21
|
+
"ethers": "^6.13.0",
|
|
22
|
+
"@0xsequence/signhub": "2.0.0",
|
|
23
|
+
"@0xsequence/tests": "2.0.0"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"src",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"test": "pnpm test:concurrently 'pnpm test:run'",
|
|
31
31
|
"test:run": "pnpm test:file tests/**/*.spec.ts",
|
|
32
|
-
"test:file": "NODE_OPTIONS='--import tsx' mocha --timeout
|
|
32
|
+
"test:file": "NODE_OPTIONS='--import tsx' mocha --timeout 60000",
|
|
33
33
|
"test:concurrently": "concurrently -k --success first 'pnpm start:hardhat > /dev/null' ",
|
|
34
34
|
"start:hardhat": "pnpm hardhat node --port 9547",
|
|
35
35
|
"typecheck": "tsc --noEmit"
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ethers
|
|
1
|
+
import { ethers } from 'ethers'
|
|
2
2
|
import { proto } from './rpc-relayer'
|
|
3
3
|
|
|
4
4
|
import { commons } from '@0xsequence/core'
|
|
@@ -19,7 +19,7 @@ export interface Relayer {
|
|
|
19
19
|
// It doesn't make any assumptions about the transaction format.
|
|
20
20
|
getFeeOptionsRaw(
|
|
21
21
|
entrypoint: string,
|
|
22
|
-
data: ethers.
|
|
22
|
+
data: ethers.BytesLike,
|
|
23
23
|
options?: {
|
|
24
24
|
simulate?: boolean
|
|
25
25
|
}
|
|
@@ -32,7 +32,7 @@ export interface Relayer {
|
|
|
32
32
|
// getNonce returns the transaction count/nonce for a wallet, encoded with nonce space.
|
|
33
33
|
// If space is undefined, the relayer can choose a nonce space to encode the result with.
|
|
34
34
|
// Otherwise, the relayer must return a nonce encoded for the given nonce space.
|
|
35
|
-
getNonce(address: string, space?: ethers.BigNumberish, blockTag?:
|
|
35
|
+
getNonce(address: string, space?: ethers.BigNumberish, blockTag?: ethers.BlockTag): Promise<ethers.BigNumberish>
|
|
36
36
|
|
|
37
37
|
// relayer will submit the transaction(s) to the network and return the transaction response.
|
|
38
38
|
// The quote should be the one returned from getFeeOptions, if any.
|
package/src/local-relayer.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ethers } from 'ethers'
|
|
2
2
|
import { logger } from '@0xsequence/utils'
|
|
3
3
|
import { FeeOption, FeeQuote, Relayer } from '.'
|
|
4
4
|
import { ProviderRelayer, ProviderRelayerOptions } from './provider-relayer'
|
|
5
5
|
import { commons } from '@0xsequence/core'
|
|
6
6
|
|
|
7
7
|
export type LocalRelayerOptions = Omit<ProviderRelayerOptions, 'provider'> & {
|
|
8
|
-
signer:
|
|
8
|
+
signer: ethers.Signer
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function isLocalRelayerOptions(obj: any): obj is LocalRelayerOptions {
|
|
12
|
-
return obj
|
|
12
|
+
return typeof obj === 'object' && obj.signer instanceof ethers.AbstractSigner
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export class LocalRelayer extends ProviderRelayer implements Relayer {
|
|
16
|
-
private signer:
|
|
17
|
-
private txnOptions:
|
|
16
|
+
private signer: ethers.Signer
|
|
17
|
+
private txnOptions: ethers.TransactionRequest
|
|
18
18
|
|
|
19
|
-
constructor(options: LocalRelayerOptions | AbstractSigner) {
|
|
20
|
-
super(
|
|
21
|
-
|
|
19
|
+
constructor(options: LocalRelayerOptions | ethers.AbstractSigner) {
|
|
20
|
+
super(
|
|
21
|
+
options instanceof ethers.AbstractSigner
|
|
22
|
+
? { provider: options.provider! }
|
|
23
|
+
: { ...options, provider: options.signer.provider! }
|
|
24
|
+
)
|
|
25
|
+
this.signer = options instanceof ethers.AbstractSigner ? options : options.signer
|
|
22
26
|
if (!this.signer.provider) throw new Error('Signer must have a provider')
|
|
23
27
|
}
|
|
24
28
|
|
|
@@ -28,7 +32,7 @@ export class LocalRelayer extends ProviderRelayer implements Relayer {
|
|
|
28
32
|
|
|
29
33
|
async getFeeOptionsRaw(
|
|
30
34
|
_entrypoint: string,
|
|
31
|
-
_data: BytesLike,
|
|
35
|
+
_data: ethers.BytesLike,
|
|
32
36
|
_options?: {
|
|
33
37
|
simulate?: boolean
|
|
34
38
|
}
|
|
@@ -41,7 +45,7 @@ export class LocalRelayer extends ProviderRelayer implements Relayer {
|
|
|
41
45
|
return options
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
setTransactionOptions(transactionRequest:
|
|
48
|
+
setTransactionOptions(transactionRequest: ethers.TransactionRequest) {
|
|
45
49
|
this.txnOptions = transactionRequest
|
|
46
50
|
}
|
|
47
51
|
|
|
@@ -49,7 +53,7 @@ export class LocalRelayer extends ProviderRelayer implements Relayer {
|
|
|
49
53
|
signedTxs: commons.transaction.IntendedTransactionBundle,
|
|
50
54
|
quote?: FeeQuote,
|
|
51
55
|
waitForReceipt: boolean = true
|
|
52
|
-
): Promise<commons.transaction.TransactionResponse<
|
|
56
|
+
): Promise<commons.transaction.TransactionResponse<ethers.TransactionReceipt>> {
|
|
53
57
|
if (quote !== undefined) {
|
|
54
58
|
logger.warn(`LocalRelayer doesn't accept fee quotes`)
|
|
55
59
|
}
|
|
@@ -58,7 +62,7 @@ export class LocalRelayer extends ProviderRelayer implements Relayer {
|
|
|
58
62
|
|
|
59
63
|
// TODO: think about computing gas limit individually, summing together and passing across
|
|
60
64
|
// NOTE: we expect that all txns have set their gasLimit ahead of time through proper estimation
|
|
61
|
-
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum
|
|
65
|
+
// const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum + tx.gasLimit, 0n)
|
|
62
66
|
// txRequest.gasLimit = gasLimit
|
|
63
67
|
|
|
64
68
|
const responsePromise = this.signer.sendTransaction({
|
package/src/provider-relayer.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { ethers
|
|
1
|
+
import { ethers } from 'ethers'
|
|
2
2
|
import { walletContracts } from '@0xsequence/abi'
|
|
3
3
|
import { FeeOption, FeeQuote, Relayer, SimulateResult } from '.'
|
|
4
4
|
import { logger, Optionals } from '@0xsequence/utils'
|
|
5
5
|
import { commons } from '@0xsequence/core'
|
|
6
6
|
|
|
7
|
-
const DEFAULT_GAS_LIMIT =
|
|
7
|
+
const DEFAULT_GAS_LIMIT = 800000n
|
|
8
8
|
|
|
9
9
|
export interface ProviderRelayerOptions {
|
|
10
|
-
provider:
|
|
10
|
+
provider: ethers.Provider
|
|
11
11
|
waitPollRate?: number
|
|
12
12
|
deltaBlocksLog?: number
|
|
13
13
|
fromBlockLog?: number
|
|
@@ -20,11 +20,11 @@ export const ProviderRelayerDefaults: Required<Optionals<ProviderRelayerOptions>
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function isProviderRelayerOptions(obj: any): obj is ProviderRelayerOptions {
|
|
23
|
-
return obj
|
|
23
|
+
return typeof obj === 'object' && obj.provider instanceof ethers.AbstractProvider
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export abstract class ProviderRelayer implements Relayer {
|
|
27
|
-
public provider:
|
|
27
|
+
public provider: ethers.Provider
|
|
28
28
|
public waitPollRate: number
|
|
29
29
|
public deltaBlocksLog: number
|
|
30
30
|
public fromBlockLog: number
|
|
@@ -45,7 +45,7 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
45
45
|
|
|
46
46
|
abstract getFeeOptionsRaw(
|
|
47
47
|
entrypoint: string,
|
|
48
|
-
data: ethers.
|
|
48
|
+
data: ethers.BytesLike,
|
|
49
49
|
options?: {
|
|
50
50
|
simulate?: boolean
|
|
51
51
|
}
|
|
@@ -64,7 +64,7 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
64
64
|
await Promise.all(
|
|
65
65
|
transactions.map(async tx => {
|
|
66
66
|
// Respect gasLimit request of the transaction (as long as its not 0)
|
|
67
|
-
if (tx.gasLimit &&
|
|
67
|
+
if (tx.gasLimit && BigInt(tx.gasLimit || 0) !== 0n) {
|
|
68
68
|
return tx.gasLimit
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -74,7 +74,7 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Fee can't be estimated for self-called if wallet hasn't been deployed
|
|
77
|
-
if (tx.to === wallet && (await this.provider.getCode(wallet).then(code => ethers.
|
|
77
|
+
if (tx.to === wallet && (await this.provider.getCode(wallet).then(code => ethers.getBytes(code).length === 0))) {
|
|
78
78
|
return DEFAULT_GAS_LIMIT
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -95,12 +95,12 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
95
95
|
).map(gasLimit => ({
|
|
96
96
|
executed: true,
|
|
97
97
|
succeeded: true,
|
|
98
|
-
gasUsed:
|
|
99
|
-
gasLimit:
|
|
98
|
+
gasUsed: Number(gasLimit),
|
|
99
|
+
gasLimit: Number(gasLimit)
|
|
100
100
|
}))
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
async getNonce(address: string, space?: ethers.BigNumberish, blockTag?:
|
|
103
|
+
async getNonce(address: string, space?: ethers.BigNumberish, blockTag?: ethers.BlockTag): Promise<ethers.BigNumberish> {
|
|
104
104
|
if (!this.provider) {
|
|
105
105
|
throw new Error('provider is not set')
|
|
106
106
|
}
|
|
@@ -123,7 +123,7 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
123
123
|
timeoutDuration?: number,
|
|
124
124
|
delay: number = this.waitPollRate,
|
|
125
125
|
maxFails: number = 5
|
|
126
|
-
): Promise<
|
|
126
|
+
): Promise<ethers.TransactionResponse & { receipt: ethers.TransactionReceipt }> {
|
|
127
127
|
if (typeof metaTxnId !== 'string') {
|
|
128
128
|
metaTxnId = commons.transaction.intendedTransactionID(metaTxnId)
|
|
129
129
|
}
|
|
@@ -155,7 +155,7 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
155
155
|
throw new Error(`timed out after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`)
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const waitReceipt = async (): Promise<
|
|
158
|
+
const waitReceipt = async (): Promise<ethers.TransactionResponse & { receipt: ethers.TransactionReceipt }> => {
|
|
159
159
|
// Transactions can only get executed on nonce change
|
|
160
160
|
// get all nonce changes and look for metaTxnIds in between logs
|
|
161
161
|
let lastBlock: number = this.fromBlockLog
|
|
@@ -199,7 +199,7 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
199
199
|
|
|
200
200
|
// Find a transaction with a TxExecuted log
|
|
201
201
|
const found = txs.find(tx =>
|
|
202
|
-
tx
|
|
202
|
+
tx?.logs.find(
|
|
203
203
|
l =>
|
|
204
204
|
(l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId) ||
|
|
205
205
|
(l.topics.length === 1 &&
|
|
@@ -212,13 +212,17 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
212
212
|
|
|
213
213
|
// If found return that
|
|
214
214
|
if (found) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
() => this.provider.getTransaction(found.transactionHash),
|
|
219
|
-
`unable to get transaction ${found.transactionHash}`
|
|
220
|
-
))
|
|
215
|
+
const response = await retry(() => this.provider.getTransaction(found.hash), `unable to get transaction ${found.hash}`)
|
|
216
|
+
if (!response) {
|
|
217
|
+
throw new Error(`Transaction response not found for ${metaTxnId}`)
|
|
221
218
|
}
|
|
219
|
+
|
|
220
|
+
// NOTE: we have to do this, because ethers-v6 uses private fields
|
|
221
|
+
// and we can't just extend the class and override the method, so
|
|
222
|
+
// we just modify the response object directly by adding the receipt to it.
|
|
223
|
+
const out: any = response
|
|
224
|
+
out.receipt = found
|
|
225
|
+
return out
|
|
222
226
|
}
|
|
223
227
|
|
|
224
228
|
// Otherwise wait and try again
|
|
@@ -233,7 +237,7 @@ export abstract class ProviderRelayer implements Relayer {
|
|
|
233
237
|
if (timeoutDuration !== undefined) {
|
|
234
238
|
return Promise.race([
|
|
235
239
|
waitReceipt(),
|
|
236
|
-
new Promise<
|
|
240
|
+
new Promise<ethers.TransactionResponse & { receipt: ethers.TransactionReceipt }>((_, reject) =>
|
|
237
241
|
setTimeout(() => {
|
|
238
242
|
timedOut = true
|
|
239
243
|
reject(`Timeout waiting for transaction receipt ${metaTxnId}`)
|
package/src/rpc-relayer/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ethers } from 'ethers'
|
|
|
2
2
|
import { FeeOption, FeeQuote, Relayer, SimulateResult } from '..'
|
|
3
3
|
import * as proto from './relayer.gen'
|
|
4
4
|
import { commons } from '@0xsequence/core'
|
|
5
|
-
import {
|
|
5
|
+
import { getFetchRequest, logger, toHexString } from '@0xsequence/utils'
|
|
6
6
|
|
|
7
7
|
export { proto }
|
|
8
8
|
|
|
@@ -16,7 +16,7 @@ const FINAL_STATUSES = [
|
|
|
16
16
|
const FAILED_STATUSES = [proto.ETHTxnStatus.DROPPED, proto.ETHTxnStatus.PARTIALLY_FAILED, proto.ETHTxnStatus.FAILED]
|
|
17
17
|
|
|
18
18
|
export interface RpcRelayerOptions {
|
|
19
|
-
provider: ethers.
|
|
19
|
+
provider: ethers.AbstractProvider | { url: string }
|
|
20
20
|
url: string
|
|
21
21
|
projectAccessKey?: string
|
|
22
22
|
jwtAuth?: string
|
|
@@ -27,7 +27,7 @@ export function isRpcRelayerOptions(obj: any): obj is RpcRelayerOptions {
|
|
|
27
27
|
obj.url !== undefined &&
|
|
28
28
|
typeof obj.url === 'string' &&
|
|
29
29
|
obj.provider !== undefined &&
|
|
30
|
-
|
|
30
|
+
obj.provider instanceof ethers.AbstractProvider
|
|
31
31
|
)
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -36,17 +36,17 @@ const fetch = globalThis.fetch
|
|
|
36
36
|
// TODO: rename to SequenceRelayer
|
|
37
37
|
export class RpcRelayer implements Relayer {
|
|
38
38
|
private readonly service: proto.Relayer
|
|
39
|
-
public readonly provider: ethers.
|
|
39
|
+
public readonly provider: ethers.Provider
|
|
40
40
|
|
|
41
41
|
constructor(public options: RpcRelayerOptions) {
|
|
42
42
|
this.service = new proto.Relayer(options.url, this._fetch)
|
|
43
43
|
|
|
44
|
-
if (
|
|
44
|
+
if (options.provider instanceof ethers.AbstractProvider) {
|
|
45
45
|
this.provider = options.provider
|
|
46
46
|
} else {
|
|
47
47
|
const { jwtAuth, projectAccessKey } = this.options
|
|
48
|
-
const
|
|
49
|
-
this.provider = new ethers.
|
|
48
|
+
const fetchRequest = getFetchRequest(options.provider.url, projectAccessKey, jwtAuth)
|
|
49
|
+
this.provider = new ethers.JsonRpcProvider(fetchRequest, undefined, { staticNetwork: true })
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -114,7 +114,7 @@ export class RpcRelayer implements Relayer {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
async simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise<SimulateResult[]> {
|
|
117
|
-
const coder = ethers.
|
|
117
|
+
const coder = ethers.AbiCoder.defaultAbiCoder()
|
|
118
118
|
const encoded = coder.encode(
|
|
119
119
|
[commons.transaction.MetaTransactionsType],
|
|
120
120
|
[commons.transaction.sequenceTxAbiEncode(transactions)]
|
|
@@ -162,7 +162,7 @@ export class RpcRelayer implements Relayer {
|
|
|
162
162
|
|
|
163
163
|
async getFeeOptionsRaw(
|
|
164
164
|
entrypoint: string,
|
|
165
|
-
data: ethers.
|
|
165
|
+
data: ethers.BytesLike,
|
|
166
166
|
options?: {
|
|
167
167
|
simulate?: boolean
|
|
168
168
|
}
|
|
@@ -170,7 +170,7 @@ export class RpcRelayer implements Relayer {
|
|
|
170
170
|
const { options: feeOptions, quote } = await this.service.feeOptions({
|
|
171
171
|
wallet: entrypoint,
|
|
172
172
|
to: entrypoint,
|
|
173
|
-
data: ethers.
|
|
173
|
+
data: ethers.hexlify(data),
|
|
174
174
|
simulate: options?.simulate
|
|
175
175
|
})
|
|
176
176
|
|
|
@@ -184,9 +184,9 @@ export class RpcRelayer implements Relayer {
|
|
|
184
184
|
|
|
185
185
|
async getNonce(address: string, space?: ethers.BigNumberish): Promise<ethers.BigNumberish> {
|
|
186
186
|
logger.info(`[rpc-relayer/getNonce] get nonce for wallet ${address} space: ${space}`)
|
|
187
|
-
const encodedNonce = space !== undefined ?
|
|
187
|
+
const encodedNonce = space !== undefined ? toHexString(BigInt(space)) : undefined
|
|
188
188
|
const resp = await this.service.getMetaTxnNonce({ walletContractAddress: address, space: encodedNonce })
|
|
189
|
-
const nonce =
|
|
189
|
+
const nonce = BigInt(resp.nonce)
|
|
190
190
|
const [decodedSpace, decodedNonce] = commons.transaction.decodeNonce(nonce)
|
|
191
191
|
logger.info(`[rpc-relayer/getNonce] got next nonce for wallet ${address} ${decodedNonce} space: ${decodedSpace}`)
|
|
192
192
|
return nonce
|
|
@@ -234,10 +234,10 @@ export class RpcRelayer implements Relayer {
|
|
|
234
234
|
hash: signedTxs.intent.id,
|
|
235
235
|
confirmations: 0,
|
|
236
236
|
from: signedTxs.intent.wallet,
|
|
237
|
-
wait: (_confirmations?: number): Promise<ethers.
|
|
237
|
+
wait: (_confirmations?: number): Promise<ethers.TransactionReceipt | null> => Promise.reject(new Error('impossible'))
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
const wait = async (confirmations?: number): Promise<ethers.
|
|
240
|
+
const wait = async (confirmations?: number): Promise<ethers.TransactionReceipt | null> => {
|
|
241
241
|
if (!this.provider) {
|
|
242
242
|
throw new Error('cannot wait for receipt, relayer has no provider set')
|
|
243
243
|
}
|
|
@@ -256,6 +256,9 @@ export class RpcRelayer implements Relayer {
|
|
|
256
256
|
|
|
257
257
|
response.wait = wait
|
|
258
258
|
|
|
259
|
+
// NOTE: we just ignore these errors which come from the private fields
|
|
260
|
+
// of ethers-v6 .. but, we should probably rework this instead..
|
|
261
|
+
// @ts-ignore
|
|
259
262
|
return response as commons.transaction.TransactionResponse
|
|
260
263
|
}
|
|
261
264
|
}
|
|
@@ -286,9 +289,12 @@ export class RpcRelayer implements Relayer {
|
|
|
286
289
|
|
|
287
290
|
const txReceipt = JSON.parse(receipt.txnReceipt) as RelayerTxReceipt
|
|
288
291
|
|
|
292
|
+
// NOTE: we just ignore these errors which come from the private fields
|
|
293
|
+
// of ethers-v6 .. but, we should probably rework this instead..
|
|
294
|
+
// @ts-ignore
|
|
289
295
|
return {
|
|
290
296
|
blockHash: txReceipt.blockHash,
|
|
291
|
-
blockNumber:
|
|
297
|
+
blockNumber: Number(txReceipt.blockNumber),
|
|
292
298
|
confirmations: 1,
|
|
293
299
|
from: typeof metaTxnId === 'string' ? undefined : metaTxnId.intent.wallet,
|
|
294
300
|
hash: txReceipt.transactionHash,
|