EVM contract upgrade compatibility checks for MoonBit
flowchart LR
old["旧产物<br/>Standard JSON · Foundry · Hardhat build info"] --> oldStage["提取,然后规范化<br/>无损 slot 与语义类型"]
new["新产物<br/>同样的产物形态"] --> newStage["提取,然后规范化<br/>无损 slot 与语义类型"]
oldStage --> compare["比较<br/>常规存储 · transient storage · ABI"]
newStage --> compare
compare --> report["诊断<br/>稳定诊断码,排序后的文本或 JSON"]
report --> exit["退出码<br/>0 兼容 · 1 不兼容 · 2 输入不受支持"]moon check --deny-warn
moon test --deny-warn
moon build --target native_build/native/debug/build/cmd/moonupgradeguard/moonupgradeguard.exemoonupgradeguard check OLD NEW [--format text|json] [--contract NAME|SOURCE:NAME]
moonupgradeguard storage OLD NEW [--format text|json] [--contract NAME|SOURCE:NAME]
moonupgradeguard abi OLD NEW [--format text|json] [--contract NAME|SOURCE:NAME]
moonupgradeguard --versiongit clone https://github.com/snorfyang/moon-upgrade-guard.git
cd moon-upgrade-guard
moon build --target native$ moon run src/cmd/moonupgradeguard -- check fixtures/compatible/old.json fixtures/compatible/new.json
$ echo $?
0$ moon run src/cmd/moonupgradeguard -- check fixtures/append/old.json fixtures/append/new.json
info[abi.event.added] abi.events: event "Paused(address)" was added to the new ABI (new: Paused(address))
info[abi.function.added] abi.functions: function "pause()" was added to the new ABI (new: pause())
info[storage.entry.added] contracts/Token.sol:Token storage[2]: variable "paused" was added at slot 2, offset 0 (new: paused)
$ echo $?
0$ moon run src/cmd/moonupgradeguard -- check fixtures/storage-moved/old.json fixtures/storage-moved/new.json
error[storage.entry.slot.changed] contracts/Token.sol:Token storage[0]: variable "totalSupply" moved from slot 0 to slot 1 (old: 0, new: 1)
error[storage.entry.slot.changed] contracts/Token.sol:Token storage[1]: variable "owner" moved from slot 1 to slot 0 (old: 1, new: 0)
$ echo $?
1$ moon run src/cmd/moonupgradeguard -- check fixtures/abi-function-removed/old.json fixtures/abi-function-removed/new.json --format json
[
{
"code": "abi.function.removed",
"severity": "error",
"location": {"path": "abi.functions"},
"message": "function \"transfer(address,uint256)\" is missing from the new ABI",
"oldValue": "transfer(address,uint256)"
}
]
$ echo $?
1./scripts/e2e.sh- name: upgrade compatibility
run: |
moon build --target native
_build/native/debug/build/cmd/moonupgradeguard/moonupgradeguard.exe \
check build/old.json build/new.json --format jsonflowchart TB
subgraph oldLayout["旧布局"]
o0["slot 0 — totalSupply"]
o1["slot 1 — owner"]
end
subgraph newLayout["新布局"]
n0["slot 0 — owner"]
n1["slot 1 — totalSupply"]
end
o1 -. "移动:错误" .-> n0pub(all) struct AbiFunction {
kind : AbiEntryKind
name : String
signature : String
selector : String
inputs : Array[AbiParam]
outputs : Array[AbiParam]
mutability : Mutability
} derive(Eq, Debug)pub(all) struct Artifact {
kind : ArtifactKind
source : String?
contract : String?
layout : RawStorageLayout
abi : Abi?
transient : RawStorageLayout?
} derive(Eq, Debug)impl Compare for Diagnosticfn Diagnostic::with_values(self : Diagnostic, old_value? : String, new_value? : String) -> Diagnosticpub(all) enum DiagnosticCode {
StorageEntryFieldMissing
StorageEntrySlotInvalid
StorageEntryOffsetInvalid
StorageEntryTypeMissing
StorageTypeIdMissing
StorageTypeIdDuplicate
StorageTypeFieldMissing
StorageTypeEncodingUnsupported
StorageTypeSizeInvalid
StorageTypeReferenceMissing
StorageTypeArrayLengthInvalid
ArtifactJsonInvalid
ArtifactShapeUnsupported
ArtifactShapeAmbiguous
ArtifactFieldInvalid
ArtifactLayoutMissing
ArtifactContractAmbiguous
ArtifactContractMissing
ArtifactAbiMissing
StorageEntryRemoved
StorageEntryAdded
StorageEntrySlotChanged
StorageEntryOffsetChanged
StorageEntryLabelChanged
StorageEntryTypeChanged
StorageGapChanged
ArtifactNamespacedStorageUnsupported
CliArgumentInvalid
CliInputUnreadable
AbiFunctionRemoved
AbiFunctionAdded
AbiFunctionMutabilityChanged
AbiFunctionMutabilityIncompatible
AbiFunctionOutputsChanged
AbiEventRemoved
AbiEventAdded
AbiEventIndexedChanged
AbiEventAnonymousChanged
AbiErrorRemoved
AbiErrorAdded
AbiSignatureCollision
} derive(Eq, Debug)pub(all) struct RawStorageLayout {
storage : Array[RawStorageEntry]
types : Array[RawStorageType]
} derive(Eq, Debug)pub(all) struct RawStorageType {
id : String?
encoding : String?
label : String?
number_of_bytes : String?
base : String?
key : String?
value : String?
members : Array[RawStorageEntry]?
} derive(Eq, Debug)pub(all) enum TypeShape {
Value
StaticArray(String, String)
DynamicArray(String)
Mapping(String, String)
Bytes
Struct(Array[StorageEntry])
} derive(Eq, Debug)[
{
"code": "storage.entry.slot.invalid",
"severity": "error",
"location": {"contract": "fileA:A", "path": "storage[1].slot"},
"message": "slot \"0x1f\" is not a non-negative decimal integer"
}
]error[storage.entry.slot.invalid] fileA:A storage[1].slot: slot "0x1f" is not a non-negative decimal integerInstall
Download zipEVM contract upgrade compatibility checks for MoonBit