Commit e5fc3fcf authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

feat: storage slot setting + memory db (#3216)

* state-surgery: add solc package

Move types into solc package that are associated
with solc. Add `CompilerInput` and `CompilerOutput`
types.

* state-surgery: clean up hardhat package

Use some solc types

* state-surgery: add state package

Implement some of smock's utils in go

* state-surgery: cleanup

* state-surgery: refactor + add better test coverage

* state-surgery: cleanup

* state-surgery: add tests for merging storage slots

* state-surgery: godoc

* state-surgery: more tests

* state-surgery: more cleanup
parent e4ef92a6
surgery: surgery:
go build -o ./surgery ./cmd/main.go go build -o ./surgery ./cmd/main.go
.PHONY: surgery
test:
go test ./...
.PHONY: surgery test
...@@ -8,6 +8,8 @@ import ( ...@@ -8,6 +8,8 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"github.com/ethereum-optimism/optimism/state-surgery/solc"
) )
// `Hardhat` encapsulates all of the functionality required to interact // `Hardhat` encapsulates all of the functionality required to interact
...@@ -75,11 +77,11 @@ func (h *Hardhat) initDeployments() error { ...@@ -75,11 +77,11 @@ func (h *Hardhat) initDeployments() error {
if strings.Contains(path, "solcInputs") { if strings.Contains(path, "solcInputs") {
return nil return nil
} }
if !strings.HasSuffix(path, ".json") {
name := filepath.Join(deploymentPath, h.network, path)
if !strings.HasSuffix(name, ".json") {
return nil return nil
} }
name := filepath.Join(deploymentPath, h.network, path)
file, err := os.ReadFile(name) file, err := os.ReadFile(name)
if err != nil { if err != nil {
return err return err
...@@ -247,3 +249,23 @@ func (h *Hardhat) GetBuildInfo(name string) (*BuildInfo, error) { ...@@ -247,3 +249,23 @@ func (h *Hardhat) GetBuildInfo(name string) (*BuildInfo, error) {
return buildInfos[0], nil return buildInfos[0], nil
} }
// TODO(tynes): handle fully qualified names properly
func (h *Hardhat) GetStorageLayout(name string) (*solc.StorageLayout, error) {
fqn := ParseFullyQualifiedName(name)
buildInfo, err := h.GetBuildInfo(name)
if err != nil {
return nil, err
}
for _, source := range buildInfo.Output.Contracts {
for name, contract := range source {
if name == fqn.ContractName {
return &contract.StorageLayout, nil
}
}
}
return nil, fmt.Errorf("contract not found for %s", fqn.ContractName)
}
...@@ -132,6 +132,8 @@ func TestHardhatGetBuildInfo(t *testing.T) { ...@@ -132,6 +132,8 @@ func TestHardhatGetBuildInfo(t *testing.T) {
} }
func TestHardhatGetDeployments(t *testing.T) { func TestHardhatGetDeployments(t *testing.T) {
t.Parallel()
hh, err := hardhat.New( hh, err := hardhat.New(
"goerli", "goerli",
[]string{"testdata/artifacts"}, []string{"testdata/artifacts"},
...@@ -143,3 +145,18 @@ func TestHardhatGetDeployments(t *testing.T) { ...@@ -143,3 +145,18 @@ func TestHardhatGetDeployments(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, deployment) require.NotNil(t, deployment)
} }
func TestHardhatGetStorageLayout(t *testing.T) {
t.Parallel()
hh, err := hardhat.New(
"goerli",
[]string{"testdata/artifacts"},
[]string{"testdata/deployments"},
)
require.Nil(t, err)
storageLayout, err := hh.GetStorageLayout("HelloWorld")
require.Nil(t, err)
require.NotNil(t, storageLayout)
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"id": "c5729209e616d57e62ada8bf0034436e",
"_format": "hh-sol-build-info-1",
"solcVersion": "0.8.15",
"solcLongVersion": "0.8.15+commit.e14f2714",
"input": {
"language": "Solidity",
"sources": {
"contracts/HelloWorld.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\ncontract HelloWorld {\n uint256 public time;\n\n constructor() {\n time = block.timestamp;\n }\n\n function gm() external returns (uint256) {\n uint256 prev = time;\n time = block.timestamp;\n return prev;\n }\n}\n"
}
},
"settings": {
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
],
"": [
"ast"
]
}
}
}
},
"output": {
"contracts": {
"contracts/HelloWorld.sol": {
"HelloWorld": {
"abi": [
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "gm",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "time",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
],
"evm": {
"bytecode": {
"functionDebugData": {
"@_12": {
"entryPoint": null,
"id": 12,
"parameterSlots": 0,
"returnSlots": 0
}
},
"generatedSources": [],
"linkReferences": {},
"object": "608060405234801561001057600080fd5b504260008190555060ed806100266000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806316ada547146037578063c0129d43146051575b600080fd5b603d606b565b60405160489190609e565b60405180910390f35b60576071565b60405160629190609e565b60405180910390f35b60005481565b6000806000549050426000819055508091505090565b6000819050919050565b6098816087565b82525050565b600060208201905060b160008301846091565b9291505056fea2646970667358221220880ac624623135a410271b0c719fe0f86b85ff1eb258d2f766c58f2e87907b8864736f6c634300080f0033",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP TIMESTAMP PUSH1 0x0 DUP2 SWAP1 SSTORE POP PUSH1 0xED DUP1 PUSH2 0x26 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x32 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x16ADA547 EQ PUSH1 0x37 JUMPI DUP1 PUSH4 0xC0129D43 EQ PUSH1 0x51 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3D PUSH1 0x6B JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x48 SWAP2 SWAP1 PUSH1 0x9E JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x57 PUSH1 0x71 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x62 SWAP2 SWAP1 PUSH1 0x9E JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x0 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 SLOAD SWAP1 POP TIMESTAMP PUSH1 0x0 DUP2 SWAP1 SSTORE POP DUP1 SWAP2 POP POP SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x98 DUP2 PUSH1 0x87 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 ADD SWAP1 POP PUSH1 0xB1 PUSH1 0x0 DUP4 ADD DUP5 PUSH1 0x91 JUMP JUMPDEST SWAP3 SWAP2 POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DUP9 EXP 0xC6 0x24 PUSH3 0x3135A4 LT 0x27 SHL 0xC PUSH18 0x9FE0F86B85FF1EB258D2F766C58F2E87907B DUP9 PUSH5 0x736F6C6343 STOP ADDMOD 0xF STOP CALLER ",
"sourceMap": "57:243:0:-:0;;;109:53;;;;;;;;;;140:15;133:4;:22;;;;57:243;;;;;;"
},
"deployedBytecode": {
"functionDebugData": {
"@gm_29": {
"entryPoint": 113,
"id": 29,
"parameterSlots": 0,
"returnSlots": 1
},
"@time_3": {
"entryPoint": 107,
"id": 3,
"parameterSlots": 0,
"returnSlots": 0
},
"abi_encode_t_uint256_to_t_uint256_fromStack": {
"entryPoint": 145,
"id": null,
"parameterSlots": 2,
"returnSlots": 0
},
"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed": {
"entryPoint": 158,
"id": null,
"parameterSlots": 2,
"returnSlots": 1
},
"cleanup_t_uint256": {
"entryPoint": 135,
"id": null,
"parameterSlots": 1,
"returnSlots": 1
}
},
"generatedSources": [
{
"ast": {
"nodeType": "YulBlock",
"src": "0:439:1",
"statements": [
{
"body": {
"nodeType": "YulBlock",
"src": "52:32:1",
"statements": [
{
"nodeType": "YulAssignment",
"src": "62:16:1",
"value": {
"name": "value",
"nodeType": "YulIdentifier",
"src": "73:5:1"
},
"variableNames": [
{
"name": "cleaned",
"nodeType": "YulIdentifier",
"src": "62:7:1"
}
]
}
]
},
"name": "cleanup_t_uint256",
"nodeType": "YulFunctionDefinition",
"parameters": [
{
"name": "value",
"nodeType": "YulTypedName",
"src": "34:5:1",
"type": ""
}
],
"returnVariables": [
{
"name": "cleaned",
"nodeType": "YulTypedName",
"src": "44:7:1",
"type": ""
}
],
"src": "7:77:1"
},
{
"body": {
"nodeType": "YulBlock",
"src": "155:53:1",
"statements": [
{
"expression": {
"arguments": [
{
"name": "pos",
"nodeType": "YulIdentifier",
"src": "172:3:1"
},
{
"arguments": [
{
"name": "value",
"nodeType": "YulIdentifier",
"src": "195:5:1"
}
],
"functionName": {
"name": "cleanup_t_uint256",
"nodeType": "YulIdentifier",
"src": "177:17:1"
},
"nodeType": "YulFunctionCall",
"src": "177:24:1"
}
],
"functionName": {
"name": "mstore",
"nodeType": "YulIdentifier",
"src": "165:6:1"
},
"nodeType": "YulFunctionCall",
"src": "165:37:1"
},
"nodeType": "YulExpressionStatement",
"src": "165:37:1"
}
]
},
"name": "abi_encode_t_uint256_to_t_uint256_fromStack",
"nodeType": "YulFunctionDefinition",
"parameters": [
{
"name": "value",
"nodeType": "YulTypedName",
"src": "143:5:1",
"type": ""
},
{
"name": "pos",
"nodeType": "YulTypedName",
"src": "150:3:1",
"type": ""
}
],
"src": "90:118:1"
},
{
"body": {
"nodeType": "YulBlock",
"src": "312:124:1",
"statements": [
{
"nodeType": "YulAssignment",
"src": "322:26:1",
"value": {
"arguments": [
{
"name": "headStart",
"nodeType": "YulIdentifier",
"src": "334:9:1"
},
{
"kind": "number",
"nodeType": "YulLiteral",
"src": "345:2:1",
"type": "",
"value": "32"
}
],
"functionName": {
"name": "add",
"nodeType": "YulIdentifier",
"src": "330:3:1"
},
"nodeType": "YulFunctionCall",
"src": "330:18:1"
},
"variableNames": [
{
"name": "tail",
"nodeType": "YulIdentifier",
"src": "322:4:1"
}
]
},
{
"expression": {
"arguments": [
{
"name": "value0",
"nodeType": "YulIdentifier",
"src": "402:6:1"
},
{
"arguments": [
{
"name": "headStart",
"nodeType": "YulIdentifier",
"src": "415:9:1"
},
{
"kind": "number",
"nodeType": "YulLiteral",
"src": "426:1:1",
"type": "",
"value": "0"
}
],
"functionName": {
"name": "add",
"nodeType": "YulIdentifier",
"src": "411:3:1"
},
"nodeType": "YulFunctionCall",
"src": "411:17:1"
}
],
"functionName": {
"name": "abi_encode_t_uint256_to_t_uint256_fromStack",
"nodeType": "YulIdentifier",
"src": "358:43:1"
},
"nodeType": "YulFunctionCall",
"src": "358:71:1"
},
"nodeType": "YulExpressionStatement",
"src": "358:71:1"
}
]
},
"name": "abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed",
"nodeType": "YulFunctionDefinition",
"parameters": [
{
"name": "headStart",
"nodeType": "YulTypedName",
"src": "284:9:1",
"type": ""
},
{
"name": "value0",
"nodeType": "YulTypedName",
"src": "296:6:1",
"type": ""
}
],
"returnVariables": [
{
"name": "tail",
"nodeType": "YulTypedName",
"src": "307:4:1",
"type": ""
}
],
"src": "214:222:1"
}
]
},
"contents": "{\n\n function cleanup_t_uint256(value) -> cleaned {\n cleaned := value\n }\n\n function abi_encode_t_uint256_to_t_uint256_fromStack(value, pos) {\n mstore(pos, cleanup_t_uint256(value))\n }\n\n function abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed(headStart , value0) -> tail {\n tail := add(headStart, 32)\n\n abi_encode_t_uint256_to_t_uint256_fromStack(value0, add(headStart, 0))\n\n }\n\n}\n",
"id": 1,
"language": "Yul",
"name": "#utility.yul"
}
],
"immutableReferences": {},
"linkReferences": {},
"object": "6080604052348015600f57600080fd5b506004361060325760003560e01c806316ada547146037578063c0129d43146051575b600080fd5b603d606b565b60405160489190609e565b60405180910390f35b60576071565b60405160629190609e565b60405180910390f35b60005481565b6000806000549050426000819055508091505090565b6000819050919050565b6098816087565b82525050565b600060208201905060b160008301846091565b9291505056fea2646970667358221220880ac624623135a410271b0c719fe0f86b85ff1eb258d2f766c58f2e87907b8864736f6c634300080f0033",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x32 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x16ADA547 EQ PUSH1 0x37 JUMPI DUP1 PUSH4 0xC0129D43 EQ PUSH1 0x51 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3D PUSH1 0x6B JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x48 SWAP2 SWAP1 PUSH1 0x9E JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x57 PUSH1 0x71 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x62 SWAP2 SWAP1 PUSH1 0x9E JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x0 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 SLOAD SWAP1 POP TIMESTAMP PUSH1 0x0 DUP2 SWAP1 SSTORE POP DUP1 SWAP2 POP POP SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x98 DUP2 PUSH1 0x87 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 ADD SWAP1 POP PUSH1 0xB1 PUSH1 0x0 DUP4 ADD DUP5 PUSH1 0x91 JUMP JUMPDEST SWAP3 SWAP2 POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DUP9 EXP 0xC6 0x24 PUSH3 0x3135A4 LT 0x27 SHL 0xC PUSH18 0x9FE0F86B85FF1EB258D2F766C58F2E87907B DUP9 PUSH5 0x736F6C6343 STOP ADDMOD 0xF STOP CALLER ",
"sourceMap": "57:243:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;83:19;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;168:130;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;83:19;;;;:::o;168:130::-;200:7;219:12;234:4;;219:19;;255:15;248:4;:22;;;;287:4;280:11;;;168:130;:::o;7:77:1:-;44:7;73:5;62:16;;7:77;;;:::o;90:118::-;177:24;195:5;177:24;:::i;:::-;172:3;165:37;90:118;;:::o;214:222::-;307:4;345:2;334:9;330:18;322:26;;358:71;426:1;415:9;411:17;402:6;358:71;:::i;:::-;214:222;;;;:::o"
},
"methodIdentifiers": {
"gm()": "c0129d43",
"time()": "16ada547"
}
},
"metadata": "{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"gm\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"time\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/HelloWorld.sol\":\"HelloWorld\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/HelloWorld.sol\":{\"keccak256\":\"0x01a0d5ca485fe13a0d88010439d7fd3c0a7a3528169470b39c695f616b2dd7c5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a4c476b397ce1cab9c9d86bb3aabf252aecbcf580ae3ccd982c3c05195ec0e1a\",\"dweb:/ipfs/QmPoVtkyCEVCbGNiFzahpLRNoT28eWejtwmd2aguZiNxAA\"]}},\"version\":1}"
}
}
},
"sources": {
"contracts/HelloWorld.sol": {
"ast": {
"absolutePath": "contracts/HelloWorld.sol",
"exportedSymbols": {
"HelloWorld": [
30
]
},
"id": 31,
"license": "MIT",
"nodeType": "SourceUnit",
"nodes": [
{
"id": 1,
"literals": [
"solidity",
"0.8",
".15"
],
"nodeType": "PragmaDirective",
"src": "32:23:0"
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "HelloWorld",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 30,
"linearizedBaseContracts": [
30
],
"name": "HelloWorld",
"nameLocation": "66:10:0",
"nodeType": "ContractDefinition",
"nodes": [
{
"constant": false,
"functionSelector": "16ada547",
"id": 3,
"mutability": "mutable",
"name": "time",
"nameLocation": "98:4:0",
"nodeType": "VariableDeclaration",
"scope": 30,
"src": "83:19:0",
"stateVariable": true,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName": {
"id": 2,
"name": "uint256",
"nodeType": "ElementaryTypeName",
"src": "83:7:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"visibility": "public"
},
{
"body": {
"id": 11,
"nodeType": "Block",
"src": "123:39:0",
"statements": [
{
"expression": {
"id": 9,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"leftHandSide": {
"id": 6,
"name": "time",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 3,
"src": "133:4:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"nodeType": "Assignment",
"operator": "=",
"rightHandSide": {
"expression": {
"id": 7,
"name": "block",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": -4,
"src": "140:5:0",
"typeDescriptions": {
"typeIdentifier": "t_magic_block",
"typeString": "block"
}
},
"id": 8,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"memberName": "timestamp",
"nodeType": "MemberAccess",
"src": "140:15:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"src": "133:22:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"id": 10,
"nodeType": "ExpressionStatement",
"src": "133:22:0"
}
]
},
"id": 12,
"implemented": true,
"kind": "constructor",
"modifiers": [],
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "FunctionDefinition",
"parameters": {
"id": 4,
"nodeType": "ParameterList",
"parameters": [],
"src": "120:2:0"
},
"returnParameters": {
"id": 5,
"nodeType": "ParameterList",
"parameters": [],
"src": "123:0:0"
},
"scope": 30,
"src": "109:53:0",
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
},
{
"body": {
"id": 28,
"nodeType": "Block",
"src": "209:89:0",
"statements": [
{
"assignments": [
18
],
"declarations": [
{
"constant": false,
"id": 18,
"mutability": "mutable",
"name": "prev",
"nameLocation": "227:4:0",
"nodeType": "VariableDeclaration",
"scope": 28,
"src": "219:12:0",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName": {
"id": 17,
"name": "uint256",
"nodeType": "ElementaryTypeName",
"src": "219:7:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"visibility": "internal"
}
],
"id": 20,
"initialValue": {
"id": 19,
"name": "time",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 3,
"src": "234:4:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"nodeType": "VariableDeclarationStatement",
"src": "219:19:0"
},
{
"expression": {
"id": 24,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"leftHandSide": {
"id": 21,
"name": "time",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 3,
"src": "248:4:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"nodeType": "Assignment",
"operator": "=",
"rightHandSide": {
"expression": {
"id": 22,
"name": "block",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": -4,
"src": "255:5:0",
"typeDescriptions": {
"typeIdentifier": "t_magic_block",
"typeString": "block"
}
},
"id": 23,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"memberName": "timestamp",
"nodeType": "MemberAccess",
"src": "255:15:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"src": "248:22:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"id": 25,
"nodeType": "ExpressionStatement",
"src": "248:22:0"
},
{
"expression": {
"id": 26,
"name": "prev",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 18,
"src": "287:4:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"functionReturnParameters": 16,
"id": 27,
"nodeType": "Return",
"src": "280:11:0"
}
]
},
"functionSelector": "c0129d43",
"id": 29,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "gm",
"nameLocation": "177:2:0",
"nodeType": "FunctionDefinition",
"parameters": {
"id": 13,
"nodeType": "ParameterList",
"parameters": [],
"src": "179:2:0"
},
"returnParameters": {
"id": 16,
"nodeType": "ParameterList",
"parameters": [
{
"constant": false,
"id": 15,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 29,
"src": "200:7:0",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName": {
"id": 14,
"name": "uint256",
"nodeType": "ElementaryTypeName",
"src": "200:7:0",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"visibility": "internal"
}
],
"src": "199:9:0"
},
"scope": 30,
"src": "168:130:0",
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "external"
}
],
"scope": 31,
"src": "57:243:0",
"usedErrors": []
}
],
"src": "32:269:0"
},
"id": 0
}
}
}
}
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/c5729209e616d57e62ada8bf0034436e.json" "buildInfo": "../../build-info/41b5106372a301360350245ee188494f.json"
} }
...@@ -8,6 +8,51 @@ ...@@ -8,6 +8,51 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "constructor" "type": "constructor"
}, },
{
"inputs": [],
"name": "addr",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "addresses",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "boolean",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "gm", "name": "gm",
...@@ -21,6 +66,19 @@ ...@@ -21,6 +66,19 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "small",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "time", "name": "time",
...@@ -35,8 +93,8 @@ ...@@ -35,8 +93,8 @@
"type": "function" "type": "function"
} }
], ],
"bytecode": "0x608060405234801561001057600080fd5b504260008190555060ed806100266000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806316ada547146037578063c0129d43146051575b600080fd5b603d606b565b60405160489190609e565b60405180910390f35b60576071565b60405160629190609e565b60405180910390f35b60005481565b6000806000549050426000819055508091505090565b6000819050919050565b6098816087565b82525050565b600060208201905060b160008301846091565b9291505056fea2646970667358221220880ac624623135a410271b0c719fe0f86b85ff1eb258d2f766c58f2e87907b8864736f6c634300080f0033", "bytecode": "0x608060405234801561001057600080fd5b504260005561014d806100246000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806316ada547146100675780636cf3c25e14610083578063767800de146100a2578063c0129d43146100cd578063c5b57bdb146100da578063edf26d9b146100fe575b600080fd5b61007060005481565b6040519081526020015b60405180910390f35b6003546100909060ff1681565b60405160ff909116815260200161007a565b6001546100b5906001600160a01b031681565b6040516001600160a01b03909116815260200161007a565b6000805442909155610070565b6001546100ee90600160a01b900460ff1681565b604051901515815260200161007a565b6100b561010c366004610127565b6002602052600090815260409020546001600160a01b031681565b60006020828403121561013957600080fd5b503591905056fea164736f6c634300080f000a",
"deployedBytecode": "0x6080604052348015600f57600080fd5b506004361060325760003560e01c806316ada547146037578063c0129d43146051575b600080fd5b603d606b565b60405160489190609e565b60405180910390f35b60576071565b60405160629190609e565b60405180910390f35b60005481565b6000806000549050426000819055508091505090565b6000819050919050565b6098816087565b82525050565b600060208201905060b160008301846091565b9291505056fea2646970667358221220880ac624623135a410271b0c719fe0f86b85ff1eb258d2f766c58f2e87907b8864736f6c634300080f0033", "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100625760003560e01c806316ada547146100675780636cf3c25e14610083578063767800de146100a2578063c0129d43146100cd578063c5b57bdb146100da578063edf26d9b146100fe575b600080fd5b61007060005481565b6040519081526020015b60405180910390f35b6003546100909060ff1681565b60405160ff909116815260200161007a565b6001546100b5906001600160a01b031681565b6040516001600160a01b03909116815260200161007a565b6000805442909155610070565b6001546100ee90600160a01b900460ff1681565b604051901515815260200161007a565b6100b561010c366004610127565b6002602052600090815260409020546001600160a01b031681565b60006020828403121561013957600080fd5b503591905056fea164736f6c634300080f000a",
"linkReferences": {}, "linkReferences": {},
"deployedLinkReferences": {} "deployedLinkReferences": {}
} }
...@@ -3,6 +3,7 @@ package hardhat ...@@ -3,6 +3,7 @@ package hardhat
import ( import (
"encoding/json" "encoding/json"
"github.com/ethereum-optimism/optimism/state-surgery/solc"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
...@@ -11,18 +12,18 @@ import ( ...@@ -11,18 +12,18 @@ import (
// Deployment represents a hardhat-deploy artifact file // Deployment represents a hardhat-deploy artifact file
type Deployment struct { type Deployment struct {
Name string Name string
Abi abi.ABI `json:"abi"` Abi abi.ABI `json:"abi"`
Address common.Address `json:"address"` Address common.Address `json:"address"`
Args []any `json:"args"` Args []any `json:"args"`
Bytecode hexutil.Bytes `json:"bytecode"` Bytecode hexutil.Bytes `json:"bytecode"`
DeployedBytecode hexutil.Bytes `json:"deployedBytecode"` DeployedBytecode hexutil.Bytes `json:"deployedBytecode"`
Devdoc json.RawMessage `json:"devdoc"` Devdoc json.RawMessage `json:"devdoc"`
Metadata string `json:"metadata"` Metadata string `json:"metadata"`
Receipt Receipt `json:"receipt"` Receipt Receipt `json:"receipt"`
SolcInputHash string `json:"solcInputHash"` SolcInputHash string `json:"solcInputHash"`
StorageLayout StorageLayout `json:"storageLayout"` StorageLayout solc.StorageLayout `json:"storageLayout"`
TransactionHash common.Hash `json:"transactionHash"` TransactionHash common.Hash `json:"transactionHash"`
Userdoc json.RawMessage `json:"userdoc"` Userdoc json.RawMessage `json:"userdoc"`
} }
// Receipt represents the receipt held in a hardhat-deploy // Receipt represents the receipt held in a hardhat-deploy
...@@ -55,31 +56,6 @@ type Log struct { ...@@ -55,31 +56,6 @@ type Log struct {
Blockhash common.Hash `json:"blockHash"` Blockhash common.Hash `json:"blockHash"`
} }
// StorageLayout represents the storage layout of a contract
type StorageLayout struct {
Storage []StorageLayoutEntry `json:"storage"`
Types map[string]StorageLayoutType `json:"types"`
}
// StorageLayoutEntry represents a single entry in the StorageLayout
type StorageLayoutEntry struct {
AstId uint `json:"astId"`
Contract string `json:"contract"`
Label string `json:"label"`
Offset uint `json:"offset"`
Slot uint `json:"slot,string"`
Type string `json"type"`
}
// StorageLayoutType represents the type of storage layout
type StorageLayoutType struct {
Encoding string `json:"encoding"`
Label string `json:"label"`
NumberOfBytes string `json:"numberOfBytes"`
Key string `json:"key,omitempty"`
Value string `json:"value,omitempty"`
}
// Artifact represents a hardhat compilation artifact // Artifact represents a hardhat compilation artifact
type Artifact struct { type Artifact struct {
Format string `json:"_format"` Format string `json:"_format"`
...@@ -114,10 +90,10 @@ type DebugFile struct { ...@@ -114,10 +90,10 @@ type DebugFile struct {
// BuildInfo represents a hardhat build info artifact that is created // BuildInfo represents a hardhat build info artifact that is created
// after compilation // after compilation
type BuildInfo struct { type BuildInfo struct {
Format string `json:"_format"` Format string `json:"_format"`
Id string `json:"id"` Id string `json:"id"`
SolcVersion string `json:"solcVersion"` SolcVersion string `json:"solcVersion"`
SolcLongVersion string `json:"solcLongVersion"` SolcLongVersion string `json:"solcLongVersion"`
Input json.RawMessage `json:"input"` Input solc.CompilerInput `json:"input"`
Output json.RawMessage `json:"output"` Output solc.CompilerOutput `json:"output"`
} }
package solc
import (
"encoding/json"
"github.com/ethereum-optimism/optimism/l2geth/accounts/abi"
)
type CompilerInput struct {
Language string `json:"language"`
Sources map[string]map[string]string `json:"sources"`
Settings CompilerSettings `json:"settings"`
}
type CompilerSettings struct {
Optimizer OptimizerSettings `json:"optimizer"`
Metadata CompilerInputMetadata `json:"metadata"`
OutputSelection map[string]map[string][]string `json:"outputSelection"`
EvmVersion string `json:"evmVersion,omitempty"`
Libraries map[string]map[string]string `json:"libraries,omitempty"`
}
type OptimizerSettings struct {
Enabled bool `json:"enabled"`
Runs uint `json:"runs"`
}
type CompilerInputMetadata struct {
UseLiteralContent bool `json:"useLiteralContent"`
}
type CompilerOutput struct {
Contracts map[string]CompilerOutputContracts `json:"contracts"`
Sources CompilerOutputSources `json:"sources"`
}
type CompilerOutputContracts map[string]CompilerOutputContract
// TODO(tynes): ignoring devdoc and userdoc for now
type CompilerOutputContract struct {
Abi abi.ABI `json:"abi"`
Evm CompilerOutputEvm `json:"evm"`
Metadata string `json:"metadata"`
StorageLayout StorageLayout `json:"storageLayout"`
}
type StorageLayout struct {
Storage []StorageLayoutEntry `json:"storage"`
Types map[string]StorageLayoutType `json:"types"`
}
type StorageLayoutEntry struct {
AstId uint `json:"astId"`
Contract string `json:"contract"`
Label string `json:"label"`
Offset uint `json:"offset"`
Slot uint `json:"slot,string"`
Type string `json"type"`
}
type StorageLayoutType struct {
Encoding string `json:"encoding"`
Label string `json:"label"`
NumberOfBytes uint `json:"numberOfBytes,string"`
Key string `json:"key,omitempty"`
Value string `json:"value,omitempty"`
}
type CompilerOutputEvm struct {
Bytecode CompilerOutputBytecode `json:"bytecode"`
DeployedBytecode CompilerOutputBytecode `json:"deployedBytecode"`
GasEstimates map[string]map[string]string `json:"gasEstimates"`
MethodIdentifiers map[string]string `json:"methodIdentifiers"`
}
// Object must be a string because its not guranteed to be
// a hex string
type CompilerOutputBytecode struct {
Object string `json:"object"`
Opcodes string `json:"opcodes"`
SourceMap string `json:"sourceMap"`
LinkReferences LinkReferences `json:"linkReferences"`
}
type LinkReferences map[string]LinkReference
type LinkReference map[string][]LinkReferenceOffset
type LinkReferenceOffset struct {
Length uint `json:"length"`
Start uint `json:"start"`
}
type CompilerOutputSources map[string]CompilerOutputSource
type CompilerOutputSource struct {
Id uint `json:"id"`
Ast Ast `json:"ast"`
}
type Ast struct {
AbsolutePath string `json:"absolutePath"`
ExportedSymbols map[string][]uint `json:"exportedSymbols"`
Id uint `json:"id"`
License string `json:"license"`
NodeType string `json:"nodeType"`
Nodes json.RawMessage `json:"nodes"`
}
package state
import (
"errors"
"fmt"
"math/big"
"reflect"
"strings"
"github.com/ethereum-optimism/optimism/state-surgery/solc"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
// EncodeStorageKeyValue encodes the key value pair that is stored in state
// given a StorageLayoutEntry and StorageLayoutType. A single input may result
// in multiple outputs. Unknown or unimplemented types will return an error.
// Note that encoding uints is *not* overflow safe, so be sure to check
// the ABI before setting very large values
func EncodeStorageKeyValue(value any, entry solc.StorageLayoutEntry, storageType solc.StorageLayoutType) ([]*EncodedStorage, error) {
label := storageType.Label
encoded := make([]*EncodedStorage, 0)
switch storageType.Encoding {
case "inplace":
key := encodeSlotKey(entry)
switch label {
case "bool":
val, err := EncodeBoolValue(value, entry.Offset)
if err != nil {
return nil, err
}
encoded = append(encoded, &EncodedStorage{key, val})
case "address":
val, err := EncodeAddressValue(value, entry.Offset)
if err != nil {
return nil, err
}
encoded = append(encoded, &EncodedStorage{key, val})
case "bytes":
return nil, fmt.Errorf("%w: %s", errUnimplemented, label)
default:
switch true {
case strings.HasPrefix(label, "contract"):
val, err := EncodeAddressValue(value, entry.Offset)
if err != nil {
return nil, err
}
encoded = append(encoded, &EncodedStorage{key, val})
case strings.HasPrefix(label, "uint"):
val, err := EncodeUintValue(value, entry.Offset)
if err != nil {
return nil, err
}
encoded = append(encoded, &EncodedStorage{key, val})
default:
// structs are not supported
return nil, fmt.Errorf("%w: %s", errUnimplemented, label)
}
}
case "dynamic_array":
case "bytes":
return nil, fmt.Errorf("%w: %s", errUnimplemented, label)
case "mapping":
if strings.HasPrefix(storageType.Value, "mapping") {
return nil, fmt.Errorf("%w: %s", errUnimplemented, "nested mappings")
}
values, ok := value.(map[any]any)
if !ok {
return nil, fmt.Errorf("cannot parse mapping")
}
keyEncoder, err := getElementEncoder(storageType.Key)
if err != nil {
return nil, err
}
valueEncoder, err := getElementEncoder(storageType.Value)
if err != nil {
return nil, err
}
// Mapping values have 0 offset
for rawKey, rawVal := range values {
encodedKey, err := keyEncoder(rawKey, 0)
if err != nil {
return nil, err
}
encodedSlot := encodeSlotKey(entry)
preimage := [64]byte{}
copy(preimage[0:32], encodedKey.Bytes())
copy(preimage[32:64], encodedSlot.Bytes())
hash := crypto.Keccak256(preimage[:])
key := common.BytesToHash(hash)
val, err := valueEncoder(rawVal, 0)
if err != nil {
return nil, err
}
encoded = append(encoded, &EncodedStorage{key, val})
}
default:
return nil, fmt.Errorf("unknown encoding: %s", storageType.Encoding)
}
return encoded, nil
}
// encodeSlotKey will encode the storage slot key. This does not
// support mappings.
func encodeSlotKey(entry solc.StorageLayoutEntry) common.Hash {
slot := new(big.Int).SetUint64(uint64(entry.Slot))
return common.BigToHash(slot)
}
// ElementEncoder is a function that can encode an element
// based on a solidity type
type ElementEncoder func(value any, offset uint) (common.Hash, error)
// getElementEncoder will return the correct ElementEncoder
// given a solidity type.
func getElementEncoder(kind string) (ElementEncoder, error) {
switch kind {
case "t_address":
return EncodeAddressValue, nil
case "t_bool":
return EncodeBoolValue, nil
default:
if strings.HasPrefix(kind, "t_uint") {
return EncodeUintValue, nil
}
}
return nil, fmt.Errorf("unsupported type: %s", kind)
}
// EncodeBoolValue will encode a boolean value given a storage
// offset.
func EncodeBoolValue(value any, offset uint) (common.Hash, error) {
val, err := encodeBoolValue(value)
if err != nil {
return common.Hash{}, err
}
return handleOffset(val, offset), nil
}
// encodeBoolValue will encode a boolean value into a type
// suitable for solidity storage.
func encodeBoolValue(value any) (common.Hash, error) {
name := reflect.TypeOf(value).Name()
switch name {
case "bool":
boolean, ok := value.(bool)
if !ok {
return common.Hash{}, errInvalidType
}
if boolean {
return common.BigToHash(common.Big1), nil
} else {
return common.Hash{}, nil
}
case "string":
boolean, ok := value.(string)
if !ok {
return common.Hash{}, errInvalidType
}
if boolean == "true" {
return common.BigToHash(common.Big1), nil
} else {
return common.Hash{}, nil
}
default:
return common.Hash{}, errInvalidType
}
}
// EncodeAddressValue will encode an address like value given a
// storage offset.
func EncodeAddressValue(value any, offset uint) (common.Hash, error) {
val, err := encodeAddressValue(value)
if err != nil {
return common.Hash{}, err
}
return handleOffset(val, offset), nil
}
// encodeAddressValue will encode an address value into
// a type suitable for solidity storage.
func encodeAddressValue(value any) (common.Hash, error) {
name := reflect.TypeOf(value).Name()
switch name {
case "Address":
address, ok := value.(common.Address)
if !ok {
return common.Hash{}, errInvalidType
}
return address.Hash(), nil
case "string":
address, ok := value.(string)
if !ok {
return common.Hash{}, errInvalidType
}
return common.HexToAddress(address).Hash(), nil
default:
return common.Hash{}, errInvalidType
}
}
// EncodeUintValue will encode a uint value given a storage offset
func EncodeUintValue(value any, offset uint) (common.Hash, error) {
val, err := encodeUintValue(value)
if err != nil {
return common.Hash{}, err
}
return handleOffset(val, offset), nil
}
// encodeUintValue will encode a uint like type into a
// type suitable for solidity storage.
func encodeUintValue(value any) (common.Hash, error) {
val := reflect.ValueOf(value)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
name := val.Type().Name()
switch name {
case "uint":
val, ok := value.(uint)
if !ok {
return common.Hash{}, errInvalidType
}
result := new(big.Int).SetUint64((uint64(val)))
return common.BigToHash(result), nil
case "int":
val, ok := value.(int)
if !ok {
return common.Hash{}, errInvalidType
}
result := new(big.Int).SetUint64(uint64(val))
return common.BigToHash(result), nil
case "uint64":
val, ok := value.(uint64)
if !ok {
return common.Hash{}, errInvalidType
}
result := new(big.Int).SetUint64(val)
return common.BigToHash(result), nil
case "uint32":
val, ok := value.(uint32)
if !ok {
return common.Hash{}, errInvalidType
}
result := new(big.Int).SetUint64(uint64(val))
return common.BigToHash(result), nil
case "uint16":
val, ok := value.(uint16)
if !ok {
return common.Hash{}, errInvalidType
}
result := new(big.Int).SetUint64(uint64(val))
return common.BigToHash(result), nil
case "uint8":
val, ok := value.(uint8)
if !ok {
return common.Hash{}, errInvalidType
}
result := new(big.Int).SetUint64(uint64(val))
return common.BigToHash(result), nil
case "string":
val, ok := value.(string)
if !ok {
return common.Hash{}, errInvalidType
}
number, err := hexutil.DecodeBig(val)
if err != nil {
if errors.Is(err, hexutil.ErrMissingPrefix) {
number, ok = new(big.Int).SetString(val, 10)
if !ok {
return common.Hash{}, errInvalidType
}
} else if errors.Is(err, hexutil.ErrLeadingZero) {
number, ok = new(big.Int).SetString(val[2:], 16)
if !ok {
return common.Hash{}, errInvalidType
}
}
}
return common.BigToHash(number), nil
case "Int":
val, ok := value.(*big.Int)
if !ok {
return common.Hash{}, errInvalidType
}
return common.BigToHash(val), nil
default:
return common.Hash{}, errInvalidType
}
}
// handleOffset will offset a value in storage by shifting
// it to the left. This is useful for when multiple variables
// are tightly packed in a storage slot.
func handleOffset(hash common.Hash, offset uint) common.Hash {
if offset == 0 {
return hash
}
number := hash.Big()
shifted := new(big.Int).Lsh(number, offset*8)
return common.BigToHash(shifted)
}
package state
import (
"bytes"
"fmt"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
)
var _ vm.StateDB = (*MemoryStateDB)(nil)
var emptyCodeHash = crypto.Keccak256(nil)
// MemoryStateDB implements geth's StateDB interface
// but operates on a core.Genesis so that a genesis.json
// can easily be created.
type MemoryStateDB struct {
rw sync.RWMutex
genesis *core.Genesis
}
func NewMemoryStateDB(genesis *core.Genesis) *MemoryStateDB {
if genesis == nil {
genesis = core.DeveloperGenesisBlock(15, 15_000_000, common.Address{})
}
return &MemoryStateDB{
genesis: genesis,
rw: sync.RWMutex{},
}
}
// Genesis is a getter for the underlying core.Genesis
func (db *MemoryStateDB) Genesis() *core.Genesis {
return db.genesis
}
// GetAccount is a getter for a core.GenesisAccount found in
// the core.Genesis
func (db *MemoryStateDB) GetAccount(addr common.Address) *core.GenesisAccount {
db.rw.RLock()
defer db.rw.RUnlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return nil
}
return &account
}
// StateDB interface implemented below
func (db *MemoryStateDB) CreateAccount(addr common.Address) {
db.rw.Lock()
defer db.rw.Unlock()
db.genesis.Alloc[addr] = core.GenesisAccount{
Code: []byte{},
Storage: make(map[common.Hash]common.Hash),
Balance: big.NewInt(0),
Nonce: 0,
}
}
func (db *MemoryStateDB) SubBalance(addr common.Address, amount *big.Int) {
db.rw.Lock()
defer db.rw.Unlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
panic(fmt.Sprintf("%s not in state", addr))
}
if account.Balance.Sign() == 0 {
return
}
account.Balance = new(big.Int).Sub(account.Balance, amount)
db.genesis.Alloc[addr] = account
}
func (db *MemoryStateDB) AddBalance(addr common.Address, amount *big.Int) {
db.rw.Lock()
defer db.rw.Unlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
panic(fmt.Sprintf("%s not in state", addr))
}
account.Balance = new(big.Int).Add(account.Balance, amount)
db.genesis.Alloc[addr] = account
}
func (db *MemoryStateDB) GetBalance(addr common.Address) *big.Int {
db.rw.RLock()
defer db.rw.RUnlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return common.Big0
}
return account.Balance
}
func (db *MemoryStateDB) GetNonce(addr common.Address) uint64 {
db.rw.RLock()
defer db.rw.RUnlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return 0
}
return account.Nonce
}
func (db *MemoryStateDB) SetNonce(addr common.Address, value uint64) {
db.rw.Lock()
defer db.rw.Unlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return
}
account.Nonce = value
db.genesis.Alloc[addr] = account
}
func (db *MemoryStateDB) GetCodeHash(addr common.Address) common.Hash {
db.rw.RLock()
defer db.rw.RUnlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return common.Hash{}
}
if len(account.Code) == 0 {
return common.BytesToHash(emptyCodeHash)
}
return common.BytesToHash(crypto.Keccak256(account.Code))
}
func (db *MemoryStateDB) GetCode(addr common.Address) []byte {
db.rw.RLock()
defer db.rw.RUnlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return nil
}
if bytes.Equal(crypto.Keccak256(account.Code), emptyCodeHash) {
return nil
}
return account.Code
}
func (db *MemoryStateDB) SetCode(addr common.Address, code []byte) {
db.rw.Lock()
defer db.rw.Unlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return
}
account.Code = code
db.genesis.Alloc[addr] = account
}
func (db *MemoryStateDB) GetCodeSize(addr common.Address) int {
db.rw.Lock()
defer db.rw.Unlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return 0
}
if bytes.Equal(crypto.Keccak256(account.Code), emptyCodeHash) {
return 0
}
return len(account.Code)
}
func (db *MemoryStateDB) AddRefund(uint64) {
panic("AddRefund unimplemented")
}
func (db *MemoryStateDB) SubRefund(uint64) {
panic("SubRefund unimplemented")
}
func (db *MemoryStateDB) GetRefund() uint64 {
panic("GetRefund unimplemented")
}
func (db *MemoryStateDB) GetCommittedState(common.Address, common.Hash) common.Hash {
panic("GetCommittedState unimplemented")
}
func (db *MemoryStateDB) GetState(addr common.Address, key common.Hash) common.Hash {
db.rw.RLock()
defer db.rw.RUnlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return common.Hash{}
}
return account.Storage[key]
}
func (db *MemoryStateDB) SetState(addr common.Address, key, value common.Hash) {
db.rw.Lock()
defer db.rw.Unlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return
}
account.Storage[key] = value
db.genesis.Alloc[addr] = account
}
func (db *MemoryStateDB) Suicide(common.Address) bool {
panic("Suicide unimplemented")
}
func (db *MemoryStateDB) HasSuicided(common.Address) bool {
panic("HasSuicided unimplemented")
}
// Exist reports whether the given account exists in state.
// Notably this should also return true for suicided accounts.
func (db *MemoryStateDB) Exist(addr common.Address) bool {
db.rw.RLock()
defer db.rw.RUnlock()
_, ok := db.genesis.Alloc[addr]
return ok
}
// Empty returns whether the given account is empty. Empty
// is defined according to EIP161 (balance = nonce = code = 0).
func (db *MemoryStateDB) Empty(addr common.Address) bool {
db.rw.RLock()
defer db.rw.RUnlock()
account, ok := db.genesis.Alloc[addr]
isZeroNonce := account.Nonce == 0
isZeroValue := account.Balance.Sign() == 0
isEmptyCode := bytes.Equal(crypto.Keccak256(account.Code), emptyCodeHash)
return ok || (isZeroNonce && isZeroValue && isEmptyCode)
}
func (db *MemoryStateDB) PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) {
panic("PrepareAccessList unimplemented")
}
func (db *MemoryStateDB) AddressInAccessList(addr common.Address) bool {
panic("AddressInAccessList unimplemented")
}
func (db *MemoryStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
panic("SlotInAccessList unimplemented")
}
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
func (db *MemoryStateDB) AddAddressToAccessList(addr common.Address) {
panic("AddAddressToAccessList unimplemented")
}
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
func (db *MemoryStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
panic("AddSlotToAccessList unimplemented")
}
func (db *MemoryStateDB) RevertToSnapshot(int) {
panic("RevertToSnapshot unimplemented")
}
func (db *MemoryStateDB) Snapshot() int {
panic("Snapshot unimplemented")
}
func (db *MemoryStateDB) AddLog(*types.Log) {
panic("AddLog unimplemented")
}
func (db *MemoryStateDB) AddPreimage(common.Hash, []byte) {
panic("AddPreimage unimplemented")
}
func (db *MemoryStateDB) ForEachStorage(addr common.Address, cb func(common.Hash, common.Hash) bool) error {
db.rw.RLock()
defer db.rw.RUnlock()
account, ok := db.genesis.Alloc[addr]
if !ok {
return nil
}
for key, value := range account.Storage {
if !cb(key, value) {
return nil
}
}
return nil
}
package state_test
import (
"math/big"
"math/rand"
"testing"
"time"
"github.com/ethereum-optimism/optimism/state-surgery/state"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)
func TestAddBalance(t *testing.T) {
t.Parallel()
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
db := state.NewMemoryStateDB(nil)
for i := 0; i < 100; i++ {
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
value := new(big.Int).Rand(rng, big.NewInt(1000))
db.CreateAccount(addr)
db.AddBalance(addr, value)
account := db.GetAccount(addr)
require.NotNil(t, account)
require.Equal(t, account.Balance, value)
}
}
func TestCode(t *testing.T) {
t.Parallel()
db := state.NewMemoryStateDB(nil)
for i := 0; i < 100; i++ {
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
db.CreateAccount(addr)
pre := db.GetCode(addr)
require.Nil(t, pre)
code := make([]byte, rand.Intn(1024))
rand.Read(code)
db.SetCode(addr, code)
post := db.GetCode(addr)
require.Equal(t, post, code)
size := db.GetCodeSize(addr)
require.Equal(t, size, len(code))
codeHash := db.GetCodeHash(addr)
require.Equal(t, codeHash, common.BytesToHash(crypto.Keccak256(code)))
}
}
package state
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/state-surgery/solc"
"github.com/ethereum/go-ethereum/common"
)
// StorageValues represents the values to be set in storage.
// The key is the name of the storage variable and the value
// is the value to set in storage.
type StorageValues map[string]any
// EncodedStorage represents the storage key and value serialized
// to be placed in Ethereum state.
type EncodedStorage struct {
Key common.Hash
Value common.Hash
}
// EncodedStorage will encode a storage layout
func EncodeStorage(entry solc.StorageLayoutEntry, value any, storageType solc.StorageLayoutType) ([]*EncodedStorage, error) {
if storageType.NumberOfBytes > 32 {
return nil, fmt.Errorf("%s is larger than 32 bytes", entry.Label)
}
encoded, err := EncodeStorageKeyValue(value, entry, storageType)
if err != nil {
return nil, err
}
return encoded, nil
}
var errInvalidType = errors.New("invalid type")
var errUnimplemented = errors.New("type unimplemented")
// ComputeStorageSlots will compute the storage slots for a given contract.
func ComputeStorageSlots(layout *solc.StorageLayout, values StorageValues) ([]*EncodedStorage, error) {
encodedStorage := make([]*EncodedStorage, 0)
for label, value := range values {
var target solc.StorageLayoutEntry
for _, entry := range layout.Storage {
if label == entry.Label {
target = entry
}
}
if target.Label == "" {
return nil, fmt.Errorf("storage layout entry for %s not found", label)
}
storageType := layout.Types[target.Type]
if storageType.Label == "" {
return nil, fmt.Errorf("storage type for %s not found", label)
}
storage, err := EncodeStorage(target, value, storageType)
if err != nil {
return nil, fmt.Errorf("cannot encode storage: %w", err)
}
encodedStorage = append(encodedStorage, storage...)
}
results := MergeStorage(encodedStorage)
return results, nil
}
// MergeStorage will combine any overlapping storage slots for
// when values are tightly packed. Do this by checking to see if any
// of the produced storage slots have a matching key, if so use a
// binary or to add the storage values together
func MergeStorage(storage []*EncodedStorage) []*EncodedStorage {
encoded := make(map[common.Hash]common.Hash)
for _, storage := range storage {
if prev, ok := encoded[storage.Key]; ok {
combined := new(big.Int).Or(prev.Big(), storage.Value.Big())
encoded[storage.Key] = common.BigToHash(combined)
} else {
encoded[storage.Key] = storage.Value
}
}
results := make([]*EncodedStorage, 0)
for key, val := range encoded {
results = append(results, &EncodedStorage{key, val})
}
return results
}
package state_test
import (
"encoding/json"
"math/big"
"os"
"testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/state-surgery/solc"
"github.com/ethereum-optimism/optimism/state-surgery/state"
"github.com/ethereum-optimism/optimism/state-surgery/state/testdata"
"github.com/stretchr/testify/require"
)
var (
// layout is the storage layout used in tests
layout solc.StorageLayout
// testKey is the same test key that geth uses
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// chainID is the chain id used for simulated backends
chainID = big.NewInt(1337)
)
// Read the test data from disk asap
func init() {
data, err := os.ReadFile("./testdata/layout.json")
if err != nil {
panic("layout.json not found")
}
if err := json.Unmarshal(data, &layout); err != nil {
panic("cannot unmarshal storage layout")
}
}
func TestSetAndGetStorageSlots(t *testing.T) {
values := state.StorageValues{}
values["_uint256"] = new(big.Int).SetUint64(0xafff_ffff_ffff_ffff)
values["_address"] = common.HexToAddress("0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8")
values["_bool"] = true
values["offset0"] = uint8(0xaa)
values["offset1"] = uint8(0xbb)
values["offset2"] = uint16(0x0c0c)
values["offset3"] = uint32(0xf33d35)
values["offset4"] = uint64(0xd34dd34d00)
values["offset5"] = new(big.Int).SetUint64(0x43ad0043ad0043ad)
addresses := make(map[any]any)
addresses[big.NewInt(1)] = common.Address{19: 0xff}
values["addresses"] = addresses
slots, err := state.ComputeStorageSlots(&layout, values)
require.Nil(t, err)
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
},
15000000,
)
opts, err := bind.NewKeyedTransactorWithChainID(testKey, chainID)
require.Nil(t, err)
_, _, contract, err := testdata.DeployTestdata(opts, backend)
require.Nil(t, err)
backend.Commit()
// Call each of the methods to make sure that they are set to their 0 values
testContractStateValuesAreEmpty(t, contract)
// Send transactions through the set storage API on the contract
for _, slot := range slots {
_, err := contract.SetStorage(opts, slot.Key, slot.Value)
require.Nil(t, err)
}
backend.Commit()
testContractStateValuesAreSet(t, contract, values)
// Call the get storage API on the contract to double check
// that the storage slots have been set correctly
for _, slot := range slots {
value, err := contract.GetStorage(&bind.CallOpts{}, slot.Key)
require.Nil(t, err)
require.Equal(t, value[:], slot.Value.Bytes())
}
}
// Ensure that all the storage variables are set after setting storage
// through the contract
func testContractStateValuesAreSet(t *testing.T, contract *testdata.Testdata, values state.StorageValues) {
OUTER:
for key, value := range values {
var res any
var err error
switch key {
case "_uint256":
res, err = contract.Uint256(&bind.CallOpts{})
case "_address":
res, err = contract.Address(&bind.CallOpts{})
case "_bool":
res, err = contract.Bool(&bind.CallOpts{})
case "offset0":
res, err = contract.Offset0(&bind.CallOpts{})
case "offset1":
res, err = contract.Offset1(&bind.CallOpts{})
case "offset2":
res, err = contract.Offset2(&bind.CallOpts{})
case "offset3":
res, err = contract.Offset3(&bind.CallOpts{})
case "offset4":
res, err = contract.Offset4(&bind.CallOpts{})
case "offset5":
res, err = contract.Offset5(&bind.CallOpts{})
case "addresses":
addrs, ok := value.(map[any]any)
require.Equal(t, ok, true)
for mapKey, mapVal := range addrs {
res, err = contract.Addresses(&bind.CallOpts{}, mapKey.(*big.Int))
require.Nil(t, err)
require.Equal(t, mapVal, res)
continue OUTER
}
default:
require.Fail(t, "Unknown variable label", key)
}
require.Nil(t, err)
require.Equal(t, res, value)
}
}
func testContractStateValuesAreEmpty(t *testing.T, contract *testdata.Testdata) {
addr, err := contract.Address(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, addr, common.Address{})
boolean, err := contract.Bool(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, boolean, false)
uint256, err := contract.Uint256(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, uint256.Uint64(), uint64(0))
offset0, err := contract.Offset0(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, offset0, uint8(0))
offset1, err := contract.Offset1(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, offset1, uint8(0))
offset2, err := contract.Offset2(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, offset2, uint16(0))
offset3, err := contract.Offset3(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, offset3, uint32(0))
offset4, err := contract.Offset4(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, offset4, uint64(0))
offset5, err := contract.Offset5(&bind.CallOpts{})
require.Nil(t, err)
require.Equal(t, offset5.Uint64(), uint64(0))
}
func TestMergeStorage(t *testing.T) {
cases := []struct {
input []*state.EncodedStorage
expect []*state.EncodedStorage
}{
{
// One input should be the same result
input: []*state.EncodedStorage{
{
Key: common.Hash{},
Value: common.Hash{},
},
},
expect: []*state.EncodedStorage{
{
Key: common.Hash{},
Value: common.Hash{},
},
},
},
{
// Two duplicate inputs should be merged
input: []*state.EncodedStorage{
{
Key: common.Hash{1},
Value: common.Hash{},
},
{
Key: common.Hash{1},
Value: common.Hash{},
},
},
expect: []*state.EncodedStorage{
{
Key: common.Hash{1},
Value: common.Hash{},
},
},
},
{
// Two different inputs should be the same result
input: []*state.EncodedStorage{
{
Key: common.Hash{1},
Value: common.Hash{},
},
{
Key: common.Hash{2},
Value: common.Hash{},
},
},
expect: []*state.EncodedStorage{
{
Key: common.Hash{1},
Value: common.Hash{},
},
{
Key: common.Hash{2},
Value: common.Hash{},
},
},
},
{
// Two matching keys should be merged bitwise
input: []*state.EncodedStorage{
{
Key: common.Hash{},
Value: common.Hash{0x00, 0x01},
},
{
Key: common.Hash{},
Value: common.Hash{0x02, 0x00},
},
},
expect: []*state.EncodedStorage{
{
Key: common.Hash{},
Value: common.Hash{0x02, 0x01},
},
},
},
}
for _, test := range cases {
got := state.MergeStorage(test.input)
require.Equal(t, got, test.expect)
}
}
func TestEncodeUintValue(t *testing.T) {
cases := []struct {
number any
offset uint
expect common.Hash
}{
{
number: 0,
offset: 0,
expect: common.Hash{},
},
{
number: big.NewInt(1),
offset: 0,
expect: common.Hash{31: 0x01},
},
{
number: uint64(2),
offset: 0,
expect: common.Hash{31: 0x02},
},
{
number: uint8(3),
offset: 0,
expect: common.Hash{31: 0x03},
},
{
number: uint16(4),
offset: 0,
expect: common.Hash{31: 0x04},
},
{
number: uint32(5),
offset: 0,
expect: common.Hash{31: 0x05},
},
{
number: int(6),
offset: 0,
expect: common.Hash{31: 0x06},
},
{
number: 1,
offset: 1,
expect: common.Hash{30: 0x01},
},
{
number: 1,
offset: 10,
expect: common.Hash{21: 0x01},
},
}
for _, test := range cases {
got, err := state.EncodeUintValue(test.number, test.offset)
require.Nil(t, err)
require.Equal(t, got, test.expect)
}
}
func TestEncodeBoolValue(t *testing.T) {
cases := []struct {
boolean any
offset uint
expect common.Hash
}{
{
boolean: true,
offset: 0,
expect: common.Hash{31: 0x01},
},
{
boolean: false,
offset: 0,
expect: common.Hash{},
},
{
boolean: true,
offset: 1,
expect: common.Hash{30: 0x01},
},
{
boolean: false,
offset: 1,
expect: common.Hash{},
},
{
boolean: "true",
offset: 0,
expect: common.Hash{31: 0x01},
},
{
boolean: "false",
offset: 0,
expect: common.Hash{},
},
}
for _, test := range cases {
got, err := state.EncodeBoolValue(test.boolean, test.offset)
require.Nil(t, err)
require.Equal(t, got, test.expect)
}
}
func TestEncodeAddressValue(t *testing.T) {
cases := []struct {
addr any
offset uint
expect common.Hash
}{
{
addr: common.Address{},
offset: 0,
expect: common.Hash{},
},
{
addr: common.Address{0x01},
offset: 0,
expect: common.Hash{12: 0x01},
},
{
addr: "0x829BD824B016326A401d083B33D092293333A830",
offset: 0,
expect: common.HexToHash("0x829BD824B016326A401d083B33D092293333A830"),
},
{
addr: common.Address{19: 0x01},
offset: 1,
expect: common.Hash{30: 0x01},
},
}
for _, test := range cases {
got, err := state.EncodeAddressValue(test.addr, test.offset)
require.Nil(t, err)
require.Equal(t, got, test.expect)
}
}
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package testdata
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// TestdataMetaData contains all meta data concerning the Testdata contract.
var TestdataMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[],\"name\":\"_address\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"_bool\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"_uint256\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"addresses\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"}],\"name\":\"getStorage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"offset0\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"offset1\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"offset2\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"offset3\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"offset4\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"offset5\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"value\",\"type\":\"bytes32\"}],\"name\":\"setStorage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
Bin: "0x608060405234801561001057600080fd5b506102a4806100206000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c8063502a6c5e11610071578063502a6c5e1461019a5780635e006859146101cc5780639b0b0fda146101d55780639e6ba9c2146101e9578063a753307d14610206578063edf26d9b1461023357600080fd5b8063099ea180146100b957806309f395f1146100dd57806318bad217146100ef57806332da25e11461011a578063350e81cc146101535780634ba664e71461017a575b600080fd5b6004546100c69060ff1681565b60405160ff90911681526020015b60405180910390f35b6004546100c690610100900460ff1681565b600054610102906001600160a01b031681565b6040516001600160a01b0390911681526020016100d4565b60045461013a9068010000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016100d4565b6004546101679062010000900461ffff1681565b60405161ffff90911681526020016100d4565b61018c61018836600461025c565b5490565b6040519081526020016100d4565b6004546101b490600160801b90046001600160801b031681565b6040516001600160801b0390911681526020016100d4565b61018c60035481565b6101e76101e3366004610275565b9055565b005b6002546101f69060ff1681565b60405190151581526020016100d4565b60045461021e90640100000000900463ffffffff1681565b60405163ffffffff90911681526020016100d4565b61010261024136600461025c565b6001602052600090815260409020546001600160a01b031681565b60006020828403121561026e57600080fd5b5035919050565b6000806040838503121561028857600080fd5b5050803592602090910135915056fea164736f6c634300080f000a",
}
// TestdataABI is the input ABI used to generate the binding from.
// Deprecated: Use TestdataMetaData.ABI instead.
var TestdataABI = TestdataMetaData.ABI
// TestdataBin is the compiled bytecode used for deploying new contracts.
// Deprecated: Use TestdataMetaData.Bin instead.
var TestdataBin = TestdataMetaData.Bin
// DeployTestdata deploys a new Ethereum contract, binding an instance of Testdata to it.
func DeployTestdata(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Testdata, error) {
parsed, err := TestdataMetaData.GetAbi()
if err != nil {
return common.Address{}, nil, nil, err
}
if parsed == nil {
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
}
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TestdataBin), backend)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &Testdata{TestdataCaller: TestdataCaller{contract: contract}, TestdataTransactor: TestdataTransactor{contract: contract}, TestdataFilterer: TestdataFilterer{contract: contract}}, nil
}
// Testdata is an auto generated Go binding around an Ethereum contract.
type Testdata struct {
TestdataCaller // Read-only binding to the contract
TestdataTransactor // Write-only binding to the contract
TestdataFilterer // Log filterer for contract events
}
// TestdataCaller is an auto generated read-only Go binding around an Ethereum contract.
type TestdataCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TestdataTransactor is an auto generated write-only Go binding around an Ethereum contract.
type TestdataTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TestdataFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type TestdataFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TestdataSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type TestdataSession struct {
Contract *Testdata // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// TestdataCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type TestdataCallerSession struct {
Contract *TestdataCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// TestdataTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type TestdataTransactorSession struct {
Contract *TestdataTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// TestdataRaw is an auto generated low-level Go binding around an Ethereum contract.
type TestdataRaw struct {
Contract *Testdata // Generic contract binding to access the raw methods on
}
// TestdataCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type TestdataCallerRaw struct {
Contract *TestdataCaller // Generic read-only contract binding to access the raw methods on
}
// TestdataTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type TestdataTransactorRaw struct {
Contract *TestdataTransactor // Generic write-only contract binding to access the raw methods on
}
// NewTestdata creates a new instance of Testdata, bound to a specific deployed contract.
func NewTestdata(address common.Address, backend bind.ContractBackend) (*Testdata, error) {
contract, err := bindTestdata(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &Testdata{TestdataCaller: TestdataCaller{contract: contract}, TestdataTransactor: TestdataTransactor{contract: contract}, TestdataFilterer: TestdataFilterer{contract: contract}}, nil
}
// NewTestdataCaller creates a new read-only instance of Testdata, bound to a specific deployed contract.
func NewTestdataCaller(address common.Address, caller bind.ContractCaller) (*TestdataCaller, error) {
contract, err := bindTestdata(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &TestdataCaller{contract: contract}, nil
}
// NewTestdataTransactor creates a new write-only instance of Testdata, bound to a specific deployed contract.
func NewTestdataTransactor(address common.Address, transactor bind.ContractTransactor) (*TestdataTransactor, error) {
contract, err := bindTestdata(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &TestdataTransactor{contract: contract}, nil
}
// NewTestdataFilterer creates a new log filterer instance of Testdata, bound to a specific deployed contract.
func NewTestdataFilterer(address common.Address, filterer bind.ContractFilterer) (*TestdataFilterer, error) {
contract, err := bindTestdata(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &TestdataFilterer{contract: contract}, nil
}
// bindTestdata binds a generic wrapper to an already deployed contract.
func bindTestdata(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(TestdataABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_Testdata *TestdataRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _Testdata.Contract.TestdataCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_Testdata *TestdataRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _Testdata.Contract.TestdataTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_Testdata *TestdataRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _Testdata.Contract.TestdataTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_Testdata *TestdataCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _Testdata.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_Testdata *TestdataTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _Testdata.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_Testdata *TestdataTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _Testdata.Contract.contract.Transact(opts, method, params...)
}
// Address is a free data retrieval call binding the contract method 0x18bad217.
//
// Solidity: function _address() view returns(address)
func (_Testdata *TestdataCaller) Address(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "_address")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// Address is a free data retrieval call binding the contract method 0x18bad217.
//
// Solidity: function _address() view returns(address)
func (_Testdata *TestdataSession) Address() (common.Address, error) {
return _Testdata.Contract.Address(&_Testdata.CallOpts)
}
// Address is a free data retrieval call binding the contract method 0x18bad217.
//
// Solidity: function _address() view returns(address)
func (_Testdata *TestdataCallerSession) Address() (common.Address, error) {
return _Testdata.Contract.Address(&_Testdata.CallOpts)
}
// Bool is a free data retrieval call binding the contract method 0x9e6ba9c2.
//
// Solidity: function _bool() view returns(bool)
func (_Testdata *TestdataCaller) Bool(opts *bind.CallOpts) (bool, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "_bool")
if err != nil {
return *new(bool), err
}
out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
return out0, err
}
// Bool is a free data retrieval call binding the contract method 0x9e6ba9c2.
//
// Solidity: function _bool() view returns(bool)
func (_Testdata *TestdataSession) Bool() (bool, error) {
return _Testdata.Contract.Bool(&_Testdata.CallOpts)
}
// Bool is a free data retrieval call binding the contract method 0x9e6ba9c2.
//
// Solidity: function _bool() view returns(bool)
func (_Testdata *TestdataCallerSession) Bool() (bool, error) {
return _Testdata.Contract.Bool(&_Testdata.CallOpts)
}
// Uint256 is a free data retrieval call binding the contract method 0x5e006859.
//
// Solidity: function _uint256() view returns(uint256)
func (_Testdata *TestdataCaller) Uint256(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "_uint256")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// Uint256 is a free data retrieval call binding the contract method 0x5e006859.
//
// Solidity: function _uint256() view returns(uint256)
func (_Testdata *TestdataSession) Uint256() (*big.Int, error) {
return _Testdata.Contract.Uint256(&_Testdata.CallOpts)
}
// Uint256 is a free data retrieval call binding the contract method 0x5e006859.
//
// Solidity: function _uint256() view returns(uint256)
func (_Testdata *TestdataCallerSession) Uint256() (*big.Int, error) {
return _Testdata.Contract.Uint256(&_Testdata.CallOpts)
}
// Addresses is a free data retrieval call binding the contract method 0xedf26d9b.
//
// Solidity: function addresses(uint256 ) view returns(address)
func (_Testdata *TestdataCaller) Addresses(opts *bind.CallOpts, arg0 *big.Int) (common.Address, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "addresses", arg0)
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// Addresses is a free data retrieval call binding the contract method 0xedf26d9b.
//
// Solidity: function addresses(uint256 ) view returns(address)
func (_Testdata *TestdataSession) Addresses(arg0 *big.Int) (common.Address, error) {
return _Testdata.Contract.Addresses(&_Testdata.CallOpts, arg0)
}
// Addresses is a free data retrieval call binding the contract method 0xedf26d9b.
//
// Solidity: function addresses(uint256 ) view returns(address)
func (_Testdata *TestdataCallerSession) Addresses(arg0 *big.Int) (common.Address, error) {
return _Testdata.Contract.Addresses(&_Testdata.CallOpts, arg0)
}
// GetStorage is a free data retrieval call binding the contract method 0x4ba664e7.
//
// Solidity: function getStorage(bytes32 key) view returns(bytes32)
func (_Testdata *TestdataCaller) GetStorage(opts *bind.CallOpts, key [32]byte) ([32]byte, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "getStorage", key)
if err != nil {
return *new([32]byte), err
}
out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
return out0, err
}
// GetStorage is a free data retrieval call binding the contract method 0x4ba664e7.
//
// Solidity: function getStorage(bytes32 key) view returns(bytes32)
func (_Testdata *TestdataSession) GetStorage(key [32]byte) ([32]byte, error) {
return _Testdata.Contract.GetStorage(&_Testdata.CallOpts, key)
}
// GetStorage is a free data retrieval call binding the contract method 0x4ba664e7.
//
// Solidity: function getStorage(bytes32 key) view returns(bytes32)
func (_Testdata *TestdataCallerSession) GetStorage(key [32]byte) ([32]byte, error) {
return _Testdata.Contract.GetStorage(&_Testdata.CallOpts, key)
}
// Offset0 is a free data retrieval call binding the contract method 0x099ea180.
//
// Solidity: function offset0() view returns(uint8)
func (_Testdata *TestdataCaller) Offset0(opts *bind.CallOpts) (uint8, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "offset0")
if err != nil {
return *new(uint8), err
}
out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8)
return out0, err
}
// Offset0 is a free data retrieval call binding the contract method 0x099ea180.
//
// Solidity: function offset0() view returns(uint8)
func (_Testdata *TestdataSession) Offset0() (uint8, error) {
return _Testdata.Contract.Offset0(&_Testdata.CallOpts)
}
// Offset0 is a free data retrieval call binding the contract method 0x099ea180.
//
// Solidity: function offset0() view returns(uint8)
func (_Testdata *TestdataCallerSession) Offset0() (uint8, error) {
return _Testdata.Contract.Offset0(&_Testdata.CallOpts)
}
// Offset1 is a free data retrieval call binding the contract method 0x09f395f1.
//
// Solidity: function offset1() view returns(uint8)
func (_Testdata *TestdataCaller) Offset1(opts *bind.CallOpts) (uint8, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "offset1")
if err != nil {
return *new(uint8), err
}
out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8)
return out0, err
}
// Offset1 is a free data retrieval call binding the contract method 0x09f395f1.
//
// Solidity: function offset1() view returns(uint8)
func (_Testdata *TestdataSession) Offset1() (uint8, error) {
return _Testdata.Contract.Offset1(&_Testdata.CallOpts)
}
// Offset1 is a free data retrieval call binding the contract method 0x09f395f1.
//
// Solidity: function offset1() view returns(uint8)
func (_Testdata *TestdataCallerSession) Offset1() (uint8, error) {
return _Testdata.Contract.Offset1(&_Testdata.CallOpts)
}
// Offset2 is a free data retrieval call binding the contract method 0x350e81cc.
//
// Solidity: function offset2() view returns(uint16)
func (_Testdata *TestdataCaller) Offset2(opts *bind.CallOpts) (uint16, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "offset2")
if err != nil {
return *new(uint16), err
}
out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16)
return out0, err
}
// Offset2 is a free data retrieval call binding the contract method 0x350e81cc.
//
// Solidity: function offset2() view returns(uint16)
func (_Testdata *TestdataSession) Offset2() (uint16, error) {
return _Testdata.Contract.Offset2(&_Testdata.CallOpts)
}
// Offset2 is a free data retrieval call binding the contract method 0x350e81cc.
//
// Solidity: function offset2() view returns(uint16)
func (_Testdata *TestdataCallerSession) Offset2() (uint16, error) {
return _Testdata.Contract.Offset2(&_Testdata.CallOpts)
}
// Offset3 is a free data retrieval call binding the contract method 0xa753307d.
//
// Solidity: function offset3() view returns(uint32)
func (_Testdata *TestdataCaller) Offset3(opts *bind.CallOpts) (uint32, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "offset3")
if err != nil {
return *new(uint32), err
}
out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32)
return out0, err
}
// Offset3 is a free data retrieval call binding the contract method 0xa753307d.
//
// Solidity: function offset3() view returns(uint32)
func (_Testdata *TestdataSession) Offset3() (uint32, error) {
return _Testdata.Contract.Offset3(&_Testdata.CallOpts)
}
// Offset3 is a free data retrieval call binding the contract method 0xa753307d.
//
// Solidity: function offset3() view returns(uint32)
func (_Testdata *TestdataCallerSession) Offset3() (uint32, error) {
return _Testdata.Contract.Offset3(&_Testdata.CallOpts)
}
// Offset4 is a free data retrieval call binding the contract method 0x32da25e1.
//
// Solidity: function offset4() view returns(uint64)
func (_Testdata *TestdataCaller) Offset4(opts *bind.CallOpts) (uint64, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "offset4")
if err != nil {
return *new(uint64), err
}
out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64)
return out0, err
}
// Offset4 is a free data retrieval call binding the contract method 0x32da25e1.
//
// Solidity: function offset4() view returns(uint64)
func (_Testdata *TestdataSession) Offset4() (uint64, error) {
return _Testdata.Contract.Offset4(&_Testdata.CallOpts)
}
// Offset4 is a free data retrieval call binding the contract method 0x32da25e1.
//
// Solidity: function offset4() view returns(uint64)
func (_Testdata *TestdataCallerSession) Offset4() (uint64, error) {
return _Testdata.Contract.Offset4(&_Testdata.CallOpts)
}
// Offset5 is a free data retrieval call binding the contract method 0x502a6c5e.
//
// Solidity: function offset5() view returns(uint128)
func (_Testdata *TestdataCaller) Offset5(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _Testdata.contract.Call(opts, &out, "offset5")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// Offset5 is a free data retrieval call binding the contract method 0x502a6c5e.
//
// Solidity: function offset5() view returns(uint128)
func (_Testdata *TestdataSession) Offset5() (*big.Int, error) {
return _Testdata.Contract.Offset5(&_Testdata.CallOpts)
}
// Offset5 is a free data retrieval call binding the contract method 0x502a6c5e.
//
// Solidity: function offset5() view returns(uint128)
func (_Testdata *TestdataCallerSession) Offset5() (*big.Int, error) {
return _Testdata.Contract.Offset5(&_Testdata.CallOpts)
}
// SetStorage is a paid mutator transaction binding the contract method 0x9b0b0fda.
//
// Solidity: function setStorage(bytes32 key, bytes32 value) returns()
func (_Testdata *TestdataTransactor) SetStorage(opts *bind.TransactOpts, key [32]byte, value [32]byte) (*types.Transaction, error) {
return _Testdata.contract.Transact(opts, "setStorage", key, value)
}
// SetStorage is a paid mutator transaction binding the contract method 0x9b0b0fda.
//
// Solidity: function setStorage(bytes32 key, bytes32 value) returns()
func (_Testdata *TestdataSession) SetStorage(key [32]byte, value [32]byte) (*types.Transaction, error) {
return _Testdata.Contract.SetStorage(&_Testdata.TransactOpts, key, value)
}
// SetStorage is a paid mutator transaction binding the contract method 0x9b0b0fda.
//
// Solidity: function setStorage(bytes32 key, bytes32 value) returns()
func (_Testdata *TestdataTransactorSession) SetStorage(key [32]byte, value [32]byte) (*types.Transaction, error) {
return _Testdata.Contract.SetStorage(&_Testdata.TransactOpts, key, value)
}
{
"storage": [
{
"astId": 3,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "_address",
"offset": 0,
"slot": "0",
"type": "t_address"
},
{
"astId": 7,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "addresses",
"offset": 0,
"slot": "1",
"type": "t_mapping(t_uint256,t_address)"
},
{
"astId": 9,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "_bool",
"offset": 0,
"slot": "2",
"type": "t_bool"
},
{
"astId": 11,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "_uint256",
"offset": 0,
"slot": "3",
"type": "t_uint256"
},
{
"astId": 13,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "offset0",
"offset": 0,
"slot": "4",
"type": "t_uint8"
},
{
"astId": 15,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "offset1",
"offset": 1,
"slot": "4",
"type": "t_uint8"
},
{
"astId": 17,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "offset2",
"offset": 2,
"slot": "4",
"type": "t_uint16"
},
{
"astId": 19,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "offset3",
"offset": 4,
"slot": "4",
"type": "t_uint32"
},
{
"astId": 21,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "offset4",
"offset": 8,
"slot": "4",
"type": "t_uint64"
},
{
"astId": 23,
"contract": "contracts/HelloWorld.sol:HelloWorld",
"label": "offset5",
"offset": 16,
"slot": "4",
"type": "t_uint128"
}
],
"types": {
"t_address": {
"encoding": "inplace",
"label": "address",
"numberOfBytes": "20"
},
"t_bool": {
"encoding": "inplace",
"label": "bool",
"numberOfBytes": "1"
},
"t_mapping(t_uint256,t_address)": {
"encoding": "mapping",
"key": "t_uint256",
"label": "mapping(uint256 => address)",
"numberOfBytes": "32",
"value": "t_address"
},
"t_uint128": {
"encoding": "inplace",
"label": "uint128",
"numberOfBytes": "16"
},
"t_uint16": {
"encoding": "inplace",
"label": "uint16",
"numberOfBytes": "2"
},
"t_uint256": {
"encoding": "inplace",
"label": "uint256",
"numberOfBytes": "32"
},
"t_uint32": {
"encoding": "inplace",
"label": "uint32",
"numberOfBytes": "4"
},
"t_uint64": {
"encoding": "inplace",
"label": "uint64",
"numberOfBytes": "8"
},
"t_uint8": {
"encoding": "inplace",
"label": "uint8",
"numberOfBytes": "1"
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment