@trustvc/trustvc 2.10.0 → 2.11.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/README.md CHANGED
@@ -27,6 +27,7 @@ TrustVC is a comprehensive wrapper library designed to simplify the signing and
27
27
  - [b) Token Registry V5](#b-token-registry-v5)
28
28
  - [8. **Document Builder**](#8-document-builder)
29
29
  - [9. **Document Store**](#9-document-store)
30
+ - [10. **Transaction Cancel**](#10-transaction-cancel)
30
31
 
31
32
  ## Installation
32
33
 
@@ -1107,3 +1108,56 @@ for (const hash of documentHashes) {
1107
1108
  await documentStoreIssue(storeAddress, hash, signer);
1108
1109
  }
1109
1110
  ```
1111
+
1112
+ ---
1113
+
1114
+ ## 10. Transaction Cancel
1115
+
1116
+ TrustVC provides a utility to cancel a pending Ethereum transaction by replacing it with a 0-value transaction to the same address, using the same nonce and a higher gas price (replace-by-fee). This works with both ethers v5 and v6 signers.
1117
+
1118
+ **Reference:** [How to cancel Ethereum pending transactions](https://info.etherscan.com/how-to-cancel-ethereum-pending-transactions/)
1119
+
1120
+ ### cancelTransaction
1121
+
1122
+ #### Description
1123
+
1124
+ Cancels a pending transaction by sending a 0-value transaction to the signer’s address with the same nonce and a higher gas price. You can specify the pending transaction either by **transaction hash** (nonce and gas price are fetched; gas price is doubled) or by **nonce and gas price** explicitly. Transactions that use EIP-1559 (no legacy `gasPrice`) must be cancelled using nonce and gas price.
1125
+
1126
+ #### Parameters
1127
+
1128
+ - **signer** – Signer with a connected provider; type `CancelTransactionSigner` (compatible with ethers v5 and v6 signers).
1129
+ - **options** – `CancelTransactionOptions`:
1130
+ - **transactionHash** (optional): Pending transaction hash (`0x...`). If provided, nonce and gas price are read from the chain and gas price is increased by 100%.
1131
+ - **nonce** (optional): Pending transaction nonce. Must be used together with `gasPrice`.
1132
+ - **gasPrice** (optional): Gas price in wei for the replacement transaction. Must be used together with `nonce`.
1133
+
1134
+ Either `(nonce, gasPrice)` or `transactionHash` must be provided.
1135
+
1136
+ #### Returns
1137
+
1138
+ **Promise<string>** – The replacement transaction hash.
1139
+
1140
+ #### Throws
1141
+
1142
+ - If the signer has no provider.
1143
+ - If neither `(nonce, gasPrice)` nor `transactionHash` is provided.
1144
+ - If `transactionHash` is given but the transaction is not found.
1145
+ - If the transaction uses EIP-1559 (no `gasPrice`); in that case use nonce and gas price explicitly.
1146
+
1147
+ #### Example
1148
+
1149
+ ```ts
1150
+ import { cancelTransaction } from '@trustvc/trustvc';
1151
+
1152
+ // Cancel by transaction hash (gas price is fetched and doubled)
1153
+ const replacementHash = await cancelTransaction(signer, {
1154
+ transactionHash: '0x...',
1155
+ });
1156
+ console.log('Replacement tx:', replacementHash);
1157
+
1158
+ // Or cancel by nonce and gas price (e.g. for EIP-1559 txs)
1159
+ const replacementHash2 = await cancelTransaction(signer, {
1160
+ nonce: '5',
1161
+ gasPrice: '25000000000', // 25 gwei in wei
1162
+ });
1163
+ ```
package/dist/cjs/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var tokenRegistryV4 = require('./token-registry-v4');
4
4
  var tokenRegistryV5 = require('./token-registry-v5');
5
5
  var documentStore = require('./document-store');
6
+ var transaction = require('./transaction');
6
7
  var tokenRegistryFunctions = require('./token-registry-functions');
7
8
  var core = require('./core');
8
9
  var openAttestation = require('./open-attestation');
@@ -118,6 +119,16 @@ Object.defineProperty(exports, "getRoleString", {
118
119
  enumerable: true,
119
120
  get: function () { return documentStore.getRoleString; }
120
121
  });
122
+ Object.defineProperty(exports, "cancelTransaction", {
123
+ enumerable: true,
124
+ get: function () { return transaction.cancelTransaction; }
125
+ });
126
+ Object.keys(transaction).forEach(function (k) {
127
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
128
+ enumerable: true,
129
+ get: function () { return transaction[k]; }
130
+ });
131
+ });
121
132
  Object.keys(tokenRegistryFunctions).forEach(function (k) {
122
133
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
123
134
  enumerable: true,
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ var ethers = require('ethers');
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ const GAS_PRICE_SCALE_WHEN_FROM_HASH = 2;
8
+ const cancelTransaction = /* @__PURE__ */ __name(async (signer, options) => {
9
+ if (!signer.provider) {
10
+ throw new Error("Provider is required on signer");
11
+ }
12
+ const { nonce, gasPrice, transactionHash } = options;
13
+ let transactionNonce = nonce;
14
+ let transactionGasPrice = gasPrice;
15
+ if (transactionHash) {
16
+ if (typeof signer.provider.getTransaction !== "function") {
17
+ throw new Error("Provider does not support getTransaction");
18
+ }
19
+ const tx = await signer.provider.getTransaction(transactionHash);
20
+ if (!tx) {
21
+ throw new Error(`Transaction not found: ${transactionHash}`);
22
+ }
23
+ const txNonce = tx.nonce;
24
+ const txGasPrice = tx.gasPrice;
25
+ if (txGasPrice == null) {
26
+ throw new Error(
27
+ "Transaction uses EIP-1559 (no gasPrice). Cancel by providing --nonce and --gas-price explicitly."
28
+ );
29
+ }
30
+ transactionNonce = String(txNonce);
31
+ const scaled = typeof txGasPrice === "bigint" ? txGasPrice * BigInt(GAS_PRICE_SCALE_WHEN_FROM_HASH) : ethers.BigNumber.from(txGasPrice).mul(GAS_PRICE_SCALE_WHEN_FROM_HASH);
32
+ transactionGasPrice = String(scaled);
33
+ }
34
+ if (!transactionNonce || !transactionGasPrice) {
35
+ throw new Error(
36
+ "Provide either (--nonce and --gas-price) or --transaction-hash to cancel the pending transaction"
37
+ );
38
+ }
39
+ const address = await signer.getAddress();
40
+ if (!/^\d+$/.test(transactionNonce)) {
41
+ throw new Error("Invalid nonce: expected a non-negative integer");
42
+ }
43
+ if (!/^\d+$/.test(transactionGasPrice) || transactionGasPrice === "0") {
44
+ throw new Error("Invalid gasPrice: expected a positive integer string in wei");
45
+ }
46
+ const nonceNum = Number(transactionNonce);
47
+ const txResponse = await signer.sendTransaction({
48
+ to: address,
49
+ value: 0,
50
+ nonce: nonceNum,
51
+ gasPrice: transactionGasPrice,
52
+ gasLimit: 21e3
53
+ });
54
+ return txResponse.hash;
55
+ }, "cancelTransaction");
56
+
57
+ exports.cancelTransaction = cancelTransaction;
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var cancel = require('./cancel');
4
+
5
+
6
+
7
+ Object.defineProperty(exports, "cancelTransaction", {
8
+ enumerable: true,
9
+ get: function () { return cancel.cancelTransaction; }
10
+ });
package/dist/esm/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  export { v4ComputeInterfaceId, v4ComputeTitleEscrowAddress, v4ContractAddress, v4Contracts, v4EncodeInitParams, v4GetEventFromReceipt, v4RoleHash, v4SupportInterfaceIds, v4Utils } from './token-registry-v4';
2
2
  export { v5ComputeInterfaceId, v5ContractAddress, v5Contracts, v5EncodeInitParams, v5GetEventFromReceipt, v5RoleHash, v5SupportInterfaceIds, v5Utils } from './token-registry-v5';
3
3
  export { DocumentStore__factory, TransferableDocumentStore__factory, deployDocumentStore, documentStoreGrantRole, documentStoreIssue, documentStoreRevoke, documentStoreRevokeRole, documentStoreTransferOwnership, getRoleString } from './document-store';
4
+ export * from './transaction';
5
+ export { cancelTransaction } from './transaction';
4
6
  export * from './token-registry-functions';
5
7
  export * from './core';
6
8
  export * from './open-attestation';
@@ -0,0 +1,55 @@
1
+ import { BigNumber } from 'ethers';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
+ const GAS_PRICE_SCALE_WHEN_FROM_HASH = 2;
6
+ const cancelTransaction = /* @__PURE__ */ __name(async (signer, options) => {
7
+ if (!signer.provider) {
8
+ throw new Error("Provider is required on signer");
9
+ }
10
+ const { nonce, gasPrice, transactionHash } = options;
11
+ let transactionNonce = nonce;
12
+ let transactionGasPrice = gasPrice;
13
+ if (transactionHash) {
14
+ if (typeof signer.provider.getTransaction !== "function") {
15
+ throw new Error("Provider does not support getTransaction");
16
+ }
17
+ const tx = await signer.provider.getTransaction(transactionHash);
18
+ if (!tx) {
19
+ throw new Error(`Transaction not found: ${transactionHash}`);
20
+ }
21
+ const txNonce = tx.nonce;
22
+ const txGasPrice = tx.gasPrice;
23
+ if (txGasPrice == null) {
24
+ throw new Error(
25
+ "Transaction uses EIP-1559 (no gasPrice). Cancel by providing --nonce and --gas-price explicitly."
26
+ );
27
+ }
28
+ transactionNonce = String(txNonce);
29
+ const scaled = typeof txGasPrice === "bigint" ? txGasPrice * BigInt(GAS_PRICE_SCALE_WHEN_FROM_HASH) : BigNumber.from(txGasPrice).mul(GAS_PRICE_SCALE_WHEN_FROM_HASH);
30
+ transactionGasPrice = String(scaled);
31
+ }
32
+ if (!transactionNonce || !transactionGasPrice) {
33
+ throw new Error(
34
+ "Provide either (--nonce and --gas-price) or --transaction-hash to cancel the pending transaction"
35
+ );
36
+ }
37
+ const address = await signer.getAddress();
38
+ if (!/^\d+$/.test(transactionNonce)) {
39
+ throw new Error("Invalid nonce: expected a non-negative integer");
40
+ }
41
+ if (!/^\d+$/.test(transactionGasPrice) || transactionGasPrice === "0") {
42
+ throw new Error("Invalid gasPrice: expected a positive integer string in wei");
43
+ }
44
+ const nonceNum = Number(transactionNonce);
45
+ const txResponse = await signer.sendTransaction({
46
+ to: address,
47
+ value: 0,
48
+ nonce: nonceNum,
49
+ gasPrice: transactionGasPrice,
50
+ gasLimit: 21e3
51
+ });
52
+ return txResponse.hash;
53
+ }, "cancelTransaction");
54
+
55
+ export { cancelTransaction };
@@ -0,0 +1 @@
1
+ export { cancelTransaction } from './cancel';
@@ -19,6 +19,7 @@ export { documentStoreTransferOwnership } from './document-store/transferOwnersh
19
19
  export { deployDocumentStore } from './deploy/document-store.js';
20
20
  export { getRoleString } from './document-store/document-store-roles.js';
21
21
  export { DocumentStore__factory, TransferableDocumentStore__factory } from '@trustvc/document-store';
22
+ export { CancelTransactionOptions, CancelTransactionSigner, cancelTransaction } from './transaction/cancel.js';
22
23
  export { nominate, transferBeneficiary, transferHolder, transferOwners } from './token-registry-functions/transfer.js';
23
24
  export { rejectTransferBeneficiary, rejectTransferHolder, rejectTransferOwners } from './token-registry-functions/rejectTransfers.js';
24
25
  export { acceptReturned, rejectReturned, returnToIssuer } from './token-registry-functions/returnToken.js';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Transaction Cancel Module
3
+ *
4
+ * Cancels a pending transaction by replacing it with a 0-value transaction to self
5
+ * using the same nonce and a higher gas price (e.g. 2x when using --transaction-hash).
6
+ * @see https://info.etherscan.com/how-to-cancel-ethereum-pending-transactions/
7
+ */
8
+ interface CancelTransactionOptions {
9
+ /** Pending transaction nonce (use with gasPrice, or use transactionHash instead) */
10
+ nonce?: string;
11
+ /** Gas price higher than the pending transaction (use with nonce) */
12
+ gasPrice?: string;
13
+ /** Pending transaction hash; nonce and gas price will be fetched and gas price increased by 100% */
14
+ transactionHash?: string;
15
+ }
16
+ /** Signer with provider, compatible with ethers v5 and v6. */
17
+ interface CancelTransactionSigner {
18
+ getAddress(): Promise<string>;
19
+ provider: {
20
+ getTransaction?(hash: string): Promise<unknown>;
21
+ } | null;
22
+ sendTransaction(tx: Record<string, unknown>): Promise<{
23
+ hash: string;
24
+ }>;
25
+ }
26
+ /**
27
+ * Cancels a pending transaction by sending a 0-value transaction to self with the same nonce
28
+ * and a higher gas price. Supports both ethers v5 and v6 signers via a unified transaction object.
29
+ * @param {CancelTransactionSigner} signer - Signer with provider (ethers v5 or v6)
30
+ * @param {CancelTransactionOptions} options - Either (nonce + gasPrice) or transactionHash
31
+ * @returns {Promise<string>} The replacement transaction hash
32
+ * @throws If neither (nonce and gasPrice) nor transactionHash is provided
33
+ * @throws If provider is not available on signer
34
+ */
35
+ declare const cancelTransaction: (signer: CancelTransactionSigner, options: CancelTransactionOptions) => Promise<string>;
36
+
37
+ export { type CancelTransactionOptions, type CancelTransactionSigner, cancelTransaction };
@@ -0,0 +1 @@
1
+ export { CancelTransactionOptions, CancelTransactionSigner, cancelTransaction } from './cancel.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustvc/trustvc",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "TrustVC library",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",