How to Upgrade Your Precompiles and Subnet-EVM for Durango
Introductionβ
Durango will be activated on the Avalanche Mainnet at 11 AM ET (4 PM UTC) on Wednesday, March 6th, 2024. Subnet-EVM introduces a set of new features and backwards-incompatible changes with the Durango network upgrade. This guide will walk you through the process of upgrading your Subnet-EVM and precompiles for the Durango network upgrade. The Durango network upgrade introduces new features and improvements to the Avalanche platform and Subnet-EVM. This guide will help you ensure that your Subnet-EVM and precompiles are compatible with the Durango network upgrade.
Note: Subnet-EVM already performs these upgrades in native stateful precompiles. This guide is for users who have custom precompiles and need to upgrade them for Durango.
Durango introduces following changes to Subnet-EVM:
- Avalanche Warp Messaging
- Events in Precompiles
- Manager Role
- Non-Strict Mode
- Shanghai Upgrade (ACP-24)
- Shangai Upgrade will be activated with Durango without any further modification in Subnet-EVM.
Avalanche Warp Messagingβ
Avalanche Warp Messaging (AWM) is a new feature introduced with the Durango network upgrade. It enables native cross-Subnet communication and allows Virtual Machine (VM) developers to implement arbitrary communication protocols between any two Subnets. For more information about AWM see here.
Warp Precompile must be enabled for Subnets after Durango. For more information the precompile and activation see here
Eventsβ
Subnet-EVM Native Precompiles will start emitting events with the Durango network upgrade. This will allow you to listen to events emitted by Subnet-EVM native precompiles. Following events will be introduced:
event RoleSet(uint256 indexed role, address indexed account, address indexed sender, uint256 oldRole)
: This event will be emitted when a role is set for an account. Precompiles that usesIAllowList
interface will emit this event without requiring any changes. The event contains the role, account, sender as indexed parameters and old role as non-indexed parameter.event FeeConfigChanged(address indexed sender, FeeConfig oldFeeConfig, FeeConfig newFeeConfig)
: This event will be emitted when the fee configuration is changed inFeeManager
precompile. The event contains the sender as indexed parameter and old and new fee configuration as non-indexed parameters.event NativeCoinMinted(address indexed sender, address indexed recipient, uint256 amount)
: This event will be emitted when new native coins are minted. The event contains the sender, recipient as indexed parameters and the amount as non-indexed parameter.event RewardAddressChanged(address indexed sender, address indexed oldRewardAddress, address indexed newRewardAddress)
: This event will be emitted when the reward address is changed inRewardManager
precompile. The event contains the sender, old and new reward addresses as non-indexed parameters.event FeeRecipientsAllowed(address indexed sender)
: This event will be emitted when the fee recipients are allowed inRewardManager
precompile. The event contains the sender as indexed parameter.event RewardsDisabled(address indexed sender)
: This event will be emitted when the rewards are disabled inRewardManager
precompile. The event contains the sender as indexed parameter.
Custom Eventsβ
Events above are already introduced and handled in Subnet-EVM native precompiles. If you have a custom precompile, you can start emitting your custom events using Durango activation. In order to do this you can define your custom event in your solidity interface and regenerate the go bindings using precompilegen
tool, for more information see here.
Generally you will generate an event.go
file in addition to your existing precompile files. You need to implement how to emit your events and your events' gas costs, as in hello world example. In this guide we will use the hello world example to demonstrate how to emit custom events. The event that will be introduced is:
event GreetingChanged(address indexed sender, string oldGreeting, string newGreeting)
It will be emitted when the greeting is changed in the hello world precompile. You can find the hello world precompile here. We also assume that hello world precompile is already deployed before Durango and we will be upgrading it for Durango.
Adjusting Gas Costsβ
Adjusting gas costs for your custom events is very important. Emitted events are written to state and consume resources. You should make sure you're charging the right amount of gas before emitting your event. precompilegen
tool automatically generates a scaffold for your gas calculations, however you should review and adjust the gas costs according to your needs, especially if you're using any arbitrary size data in your events. Gas cost function for the event GreetingChanged(address indexed sender, string oldGreeting, string newGreeting)
looks like this:
func GetGreetingChangedEventGasCost(data GreetingChangedEventData) uint64 {
gas := contract.LogGas // base gas cost
// Add topics gas cost (2 topics)
// Topics always include the signature hash of the event. The rest are the indexed event arguments.
gas += contract.LogTopicGas * 2
// CUSTOM CODE STARTS HERE
// Keep in mind that the data here will be encoded using the ABI encoding scheme.
// So the computation cost might change according to the data type + data size and should be charged accordingly.
// i.e gas += LogDataGas * uint64(len(data.oldGreeting))
gas += contract.LogDataGas * uint64(len(data.OldGreeting)) // * ...
// CUSTOM CODE ENDS HERE
// CUSTOM CODE STARTS HERE
// Keep in mind that the data here will be encoded using the ABI encoding scheme.
// So the computation cost might change according to the data type + data size and should be charged accordingly.
// i.e gas += LogDataGas * uint64(len(data.newGreeting))
gas += contract.LogDataGas * uint64(len(data.NewGreeting)) // * ...
// CUSTOM CODE ENDS HERE
// CUSTOM CODE STARTS HERE
return gas
}
We have charged the base gas cost, topics gas cost and data gas cost for the old and new greeting. Topic gas cost includes 2 topics, one for the signature hash of the event and the other for the indexed sender argument. Data gas cost is calculated according to the data type and size. If you're not using any arbitrary size data in your events, you can define it as a constant.
Durango Activation Checkβ
After completing defining your custom events, you need to pack your events, charge your event gas and emit them in your precompile functions. Since this procedure is non-backward compatible, you need to ensure that this will only be activated after the Durango network upgrade.
You can use the contract.IsDurangoActivated
function to check if the Durango network upgrade is activated. For Hello World example, we will be changing setGreeting
function starting here via following:
if contract.IsDurangoActivated(accessibleState) {
...
}
Note: this activation won't be needed if you plan to deploy your custom precompile after Durango.
Event Packersβ
Event packers and unpackers will be automatically generated with the precompilegen
tool. You can use these packers and unpackers to pack and unpack your custom events. For hello world example:
if remainingGas, err = contract.DeductGas(remainingGas, contract.ReadGasCostPerSlot); err != nil {
return nil, 0, err
}
oldGreeting := GetGreeting(stateDB)
eventData := GreetingChangedEventData{
OldGreeting: oldGreeting,
NewGreeting: inputStruct,
}
topics, data, err := PackGreetingChangedEvent(caller, eventData)
if err != nil {
return nil, remainingGas, err
}
This will first charge gas for fetching the old greeting from the state fetch and then fetch it from the state. Then it will pack the event data with old and new greeting as non-indexed event data.