Migration of the SanR dApp from ethers v5 to v6
mqklin
Posted on May 31, 2023
Introduction
SanR (https://sanr.app), a decentralized application (dApp) built on the Ethereum blockchain, recently underwent a migration from ethers v5 to ethers v6. This migration involved several changes in the codebase to ensure compatibility and leverage the enhanced features and improvements introduced in the latest version of ethers.js. In this article, we will explore the key modifications made during the migration process and their implications for the SanR dApp.
Transition from BigNumber to BigInt
One significant change in ethers v6 is the replacement of the BigNumber class with BigInt. BigInt provides more precise handling of large numbers and aligns with the JavaScript standard. It is important to note that JavaScript requires explicit conversion from BigInt to perform operations with regular numbers (Number).
- if (ethers.BigNumber.isBigNumber(value[key])) {
+ if (typeof value[key] === 'bigint') {
// token.createdAt is a BigNum now
- const daysPassed = Math.ceil((now - token.createdAt) / 60 / 60 / 24);
+ const daysPassed = Math.ceil((now - Number(token.createdAt)) / 60 / 60 / 24);
Restructuring of Methods
Many methods have been moved from the utils
and providers
namespaces directly to the root object in ethers v6. This restructuring simplifies the access and usage of these methods.
- const wallet = new Wallet(privateKey, new ethers.providers.JsonRpcProvider('https://sanrchain-node.santiment.net/'));
+ const wallet = new Wallet(privateKey, new ethers.JsonRpcProvider('https://sanrchain-node.santiment.net/'));
- await adminWallet.sendTransaction({to: wallet.address, value: ethers.utils.parseEther('1.0')});
+ await adminWallet.sendTransaction({to: wallet.address, value: ethers.parseEther('1.0')});
- const salt = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('default'));
+ const salt = ethers.keccak256(ethers.toUtf8Bytes('default'));
- filter += `, "issuerAddress":["${ethers.utils.getAddress(issuerAddress)}"]`;
+ filter += `, "issuerAddress":["${ethers.getAddress(issuerAddress)}"]`;
Introduction of Transaction Batching
In ethers v6, transaction batching allows developers to bundle multiple transactions into a single batch, reducing network costs and improving efficiency. By default, transaction batching is enabled in ethers v6, allowing users to take advantage of this feature. However, in the case of SanR, which utilizes its own blockchain called the SanR Network that currently does not support transaction batching, the feature can be explicitly disabled using the new batchMaxCount
parameter.
- const L2Provider = new ethers.providers.JsonRpcProvider(L2.url);
+ const L2Provider = new ethers.JsonRpcProvider(L2.url, null, {batchMaxCount: 1});
Please note that transaction batching was already available in ethers v5, and it remains a valuable feature in ethers v6, with default activation unless explicitly disabled when not needed.
Method Invocation Changes
Several changes were made regarding method invocations in contract interactions:
- The
populateTransaction
method and the method name have switched positions. - In ethers v6, there is no longer a need to specify the function signature when a method can accept multiple arguments.
- await L2CompetitionsContract.populateTransaction.createGame(
+ await L2CompetitionsContract.createGame.populateTransaction(
- await L2CompetitionsContract.populateTransaction['makeStake(uint256)'](ethers.utils.parseUnits('500')),
+ await L2CompetitionsContract.makeStake.populateTransaction(ethers.parseUnits('500')),
- await L2SignalsContract.populateTransaction['closeSignals(uint256[],bytes32[],bool[],uint256[],uint256[])'](
+ await L2SignalsContract.closeSignals.populateTransaction(
Renaming of Contract Address Field
In ethers v6, the address
field of a contract has been renamed to target
. This change brings greater clarity and consistency to the naming conventions within ethers.js.
- <SubheaderValue>{L2CompetitionsContract.address}</SubheaderValue>
+ <SubheaderValue>{L2CompetitionsContract.target}</SubheaderValue>
Renaming of Event Field
The event
field in events has been renamed to fragment.name
in ethers v6. This modification provides a more descriptive and precise representation of event fragments.
- <JustifyCenter>{el.event === 'Staked' ? 'stake' : el.event === 'Rewarded' ? 'reward' : 'unstake'}</JustifyCenter>
+ <JustifyCenter>{el.fragment.name === 'Staked' ? 'stake' : el.fragment.name === 'Rewarded' ? 'reward' : 'unstake'}</JustifyCenter>
Contract Proxy Transition
In ethers v6, the Contract object has transitioned from being an object
to an element of Proxy
. This change affected the React.propTypes
validation for contract objects, requiring them to be replaced with any. However, it is worth noting that the ethers.js main developer, @ricmoo.eth, has announced plans to rectify this issue, allowing developers to use object validation once again.
- L1SANContract: object.isRequired,
+ L1SANContract: any.isRequired,
Copying Response from Contract Calls
The transformation of contracts into Proxy also impacted how responses from contract method calls are shallow copied. Failure to perform this shallow copy could result in missing object properties in the new object. Developers need to ensure proper handling of copying contract call responses to maintain data integrity.
- const _token = {...await Contract.followings(tokenId)};
- const token = {..._token};
+ const token = (await Contract.followings(tokenId)).toObject();
Method Changes in defaultAbiCoder
In ethers v6, the defaultAbiCoder
is now a method instead of a property. Developers should update their code accordingly to utilize this method.
- const infoHash = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(
+ const infoHash = ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(
Renaming of formatBytes32String
The function formatBytes32String
has been renamed to encodeBytes32String
in ethers v6. This change aligns with the naming conventions and offers a more descriptive name for the functionality.
- const currentPrice = await L2PricesContract.currentPrice(ethers.utils.formatBytes32String('BTC/USD'))).price;
+ const currentPrice = await L2PricesContract.currentPrice(ethers.encodeBytes32String('BTC/USD'))).price;
Provider Renaming and GSN Adjustment
The Web3Provider
has been renamed to BrowserProvider
in ethers.js v6. Additionally, if you are using OpenGSN version 2, modifications to the request method are necessary for GSN provider integration.
+ const gsnProvider = await RelayProvider.newProvider({
+ provider: window.ethereum,
+ config,
+ }).init();
+ gsnProvider.request = function(payload) {
+ return new Promise((resolve, reject) => {
+ gsnProvider.send(payload, function(error, result) {
+ if (error) {
+ reject(error);
+ }
+ else {
+ resolve(result?.result);
+ }
+ });
+ });
+ };
const Provider = GSN_CONTRACTS.includes(params.to.toLowerCase())
- ? new ethers.providers.Web3Provider(
- await RelayProvider.newProvider({
- provider: window.ethereum,
- config,
- }).init(),
- )
+ ? new BrowserProvider(gsnProvider)
: WindowEthereumProvider
;
Asynchronous getSigner Method
In ethers v6, the getSigner method
has been updated to be asynchronous, allowing for more efficient handling of signer-related operations.
- const tx = await Provider.getSigner().sendTransaction(params);
+ const tx = await (await Provider.getSigner()).sendTransaction(params);
- const signedMessage = await Provider.getSigner().signMessage(originalMessage);
+ const signedMessage = await (await Provider.getSigner()).signMessage(originalMessage);
Renaming of transactionHash Field
The transactionHash
field of the transaction receipt has been renamed to hash
in ethers v6. This change provides a more consistent naming convention across the library.
- const tx = await Provider.getTransaction(receipt.transactionHash);
+ const tx = await Provider.getTransaction(receipt.hash);
Removal of confirmations Field
The confirmations
field has been removed from Transaction objects in ethers v6. To determine the number of confirmations for a mined transaction, developers should access the confirmations property from the TransactionReceipt
object.
- const isMined = await Provider.getTransaction(sentTx.hash)).confirmations > 0;
+ const isMined = Boolean(await Provider.getTransactionReceipt(sentTx.hash))
Renaming of error reason Field
The field containing the error message encountered during the invocation of sendTransaction
has been moved from data.message
to reason
.
- if (error.data.message.includes('gas required exceeds allowance')) {
+ if (error.reason.includes('gas required exceeds allowance')) {
Conclusion
The migration of the SanR dApp from ethers.js v5 to v6 involved several notable changes in code structure and functionality. These modifications were necessary to leverage the advancements and improvements offered by ethers.js v6.
Please note that this article is a summary of the changes introduced during the migration process. For more detailed information and guidance, we recommend referring to the ethers.js documentation and release notes.
Posted on May 31, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024