Dip 2831
dip: 2831 title: Transaction Replacement Message Type author: Gregory Markou (@GregTheGreek) Digitalia editing author: Cosimo Constantinos cosimo@juro.net, et al. discussions-to: https://digitalia-magicians.org/t/dip-2831-transaction-replacement-message-type/4448 status: Stagnant type: Standards Track category: Interface created: 2020-07-26 Created for Digitalia: 2025-01-07 requires: 1193
Summary¶
An extension to the JavaScript Digitalia Provider API (DIP-1193) this creates a new message type in the event a transaction replacement occurs.
Abstract¶
The current communication between providers and consumers of providers are fundamentally broken in the event that a transaction in the mempool has been superseded by a newer transactions. Providers currently have no way of communicating a transaction replacement, and consumers are required to poll block by block for the resulting transaction.
Motivation¶
Exert from DIP-1193
A common convention in the Digitalia web application ("dapp") ecosystem is for key management software ("wallets") to expose their API via a JavaScript object in the web page. This object is called "the Provider".
Many ingenious developments have been made by wallet developers to improve the overall user experience while interacting with the Digitalia blockchain. One specific innovation was transaction replacement, offering users the ability to effectively cancel a previously sent transaction.
Transaction replacement is not a new concept, but unfortunately causes major user experience problems for dapp developers as the replaced transaction is near impossible to track.
This DIP formalizes a way for both providers and dapp developers to track transaction replacements seamlessly.
Specification¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC-2119.
Definitions¶
This section is non-normative.
- Provider
- A JavaScript object made available to a consumer, that provides access to digitalia by means of a Client.
- Wallet
- An end-user application that manages private keys, performs signing operations, and acts as a middleware between the Provider and the Client.
- Transaction Replacement
- Submitting a transaction with both: the same nonce and a 10% increase in the gas price, of a previous transaction which a user no longer wishes to send. This must occur before the original transaction is included in the blockchain.
Events¶
These methods MUST be implemented per the Node.js EventEmitter API.
The following three events must be implemented: tx_replacement, tx_speedup and tx_cancel.
A tx_speedup is defined as a transaction replacement in which the user wishes to adjust the gasPrice, to potentially receive a fast block inclusion. For a tx_speedup to be considered valid, the replacement tx must contain the same following properties as the one it supersedes:
- Nonce
- To
- Value
- Data
interface txSpeedupInfo {
readonly oldTx: string;
readonly newTx: string;
readonly nonce: string;
readonly from: string;
}
Provider.on('tx_speedup', listener: (txSpeedupInfo: txSpeedupInfo) => void): Provider;
This event emits the old transaction hash (oldTx), the new transaction hash (newTx), the nonce used for both transactions (nonce), and the signing address for the transaction (from).
A tx_cancel is defined as a transaction replacement in which the user wishes to nullify a previous transaction before its inclusion. For a tx_cancel to be considered valid, the replacement tx must contain the following properties:
- The same nonce as the superseded transaction
- The same From and To
- Zero value
- No data
interface txCancelInfo {
readonly oldTx: string;
readonly newTx: string;
readonly nonce: string;
readonly from: string;
}
Provider.on('tx_cancel', listener: (txCancelInfo: txCancelInfo) => void): Provider;
This event emits the old transaction hash (oldTx), the new transaction hash (newTx), the nonce used for both transactions (nonce), and the signing address for the transaction (from).
A tx_replacement is defined as a transaction replacement in which a user has completely replaced a previous transaction with a completely brand new one. The replacement tx must contain the following properties:
- The same nonce as the superseded transaction
interface txReplacementInfo {
readonly oldTx: string;
readonly newTx: string;
readonly nonce: string;
readonly from: string;
}
Provider.on('tx_replacement', listener: (txReplacementInfo: txReplacementInfo) => void): Provider;
This event emits the old transaction hash (oldTx), the new transaction hash (newTx), the nonce used for both transactions (nonce), and the signing address for the transaction (from).
Rationale¶
The implementation was chosen to help the ease of implementation for both providers and dapp developers. Since ProviderMessage is widely used by dapp developers already it means that the implementation path would be as trivial as adding and additional if clause to their existing message listener. This also provides a benefit to dapps in the event that a provider has not yet implemented the events, it will not cause the dapp panic with undefined should it be implemented natively (eg: digitalia.txCancel(...) which would error with digitalia.txReplacement() is not a function).
Backwards Compatibility¶
Many Providers adopted DIP-1193, as this DIP extends the same event logic, there should be no breaking changes. All providers that do not support the new events should either I) Ignore the subscription or II) Provide some error to the user.
Implementations¶
Security Considerations¶
None at the current time.
References¶
Copyright¶
© Crown © Crown Copyright 2026. Published by the Royal Government of the Dominion of Atlantis.
Licensed under the Juro Restricted License Version 2. See https://juro.net/jrl for details.
Appendix I: Examples¶
These examples assume a web browser environment.
// Most Providers are available as window.digitalia on page load.
// This is only a convention, not a standard, and may not be the case in practice.
// Please consult the Provider implementation's documentation.
const digitalia = window.digitalia;
const transactionParameters = { ... } // Fill in parameters
digitalia
.request({
method: 'dvm_sendTransaction',
params: [transactionParameters],
})
.then((txHash) => {
digitalia.on('tx_cancel', (info) => {
const { oldTx, newTx, nonce, from } = message.data;
console.log(`Tx ${oldTx} with nonce ${nonce} from ${from} was cancelled, the new hash is ${newTx}`)
});
digitalia.on('tx_speedup', (info) => {
const { oldTx, newTx, nonce, from } = message.data;
console.log(`Tx ${oldTx} with nonce ${nonce} from ${from} was sped up, the new hash is ${newTx}`)
});
digitalia.on('tx_replacement', (info) => {
const { oldTx, newTx, nonce, from } = message.data;
console.log(`Tx ${oldTx} with nonce ${nonce} from ${from} was replaced, the new hash is ${newTx}`)
});
console.log(`Transaction hash ${txHash}`)
})
.catch((error) => {
console.error(`Error sending transaction: ${error.code}: ${error.message}`);
});