mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-07-27 10:42:06 +00:00
Tests for getStorageValue on mapping and nested mappings with different key types (#52)
* Tests for mapping with value type keys. * Add test for mapping with string type keys. * Add test for mapping with dynamically-sized byte array as keys. * Add tests for nested mapping. Co-authored-by: nikugogoi <95nikass@gmail.com>
This commit is contained in:
parent
c916e61a9b
commit
df025433ec
0
packages/cache/src/cache.spec.ts
vendored
Normal file
0
packages/cache/src/cache.spec.ts
vendored
Normal file
0
packages/ipld-eth-client/src/eth-client.spec.ts
Normal file
0
packages/ipld-eth-client/src/eth-client.spec.ts
Normal file
@ -56,10 +56,11 @@ $ yarn test
|
|||||||
* [ ] Value Types
|
* [ ] Value Types
|
||||||
* [ ] Reference Types
|
* [ ] Reference Types
|
||||||
* [ ] Mapping Types
|
* [ ] Mapping Types
|
||||||
* [ ] Value Type keys
|
* [x] Value Type keys
|
||||||
* [ ] Dynamically-sized byte array keys
|
* [ ] Fixed-size byte array keys
|
||||||
|
* [x] Dynamically-sized byte array keys
|
||||||
* [ ] Reference Type Mapping values
|
* [ ] Reference Type Mapping values
|
||||||
* [ ] Nested Mapping
|
* [x] Nested Mapping
|
||||||
|
|
||||||
## Observations
|
## Observations
|
||||||
|
|
||||||
|
@ -310,17 +310,17 @@ describe('Get value from storage', () => {
|
|||||||
expect(value).to.eql(expectedValue.map(el => BigInt(el)));
|
expect(value).to.eql(expectedValue.map(el => BigInt(el)));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('mapping type', () => {
|
describe('basic mapping type', () => {
|
||||||
let testMappingTypes: Contract, storageLayout: StorageLayout;
|
let testMappingTypes: Contract, storageLayout: StorageLayout;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const TestMappingTypes = await ethers.getContractFactory('TestMappingTypes');
|
const TestMappingTypes = await ethers.getContractFactory('TestBasicMapping');
|
||||||
testMappingTypes = await TestMappingTypes.deploy();
|
testMappingTypes = await TestMappingTypes.deploy();
|
||||||
await testMappingTypes.deployed();
|
await testMappingTypes.deployed();
|
||||||
storageLayout = await getStorageLayout('TestMappingTypes');
|
storageLayout = await getStorageLayout('TestBasicMapping');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for basic mapping type', async () => {
|
it('get value for mapping with address type keys', async () => {
|
||||||
const expectedValue = 123;
|
const expectedValue = 123;
|
||||||
const [, signer1] = await ethers.getSigners();
|
const [, signer1] = await ethers.getSigners();
|
||||||
await testMappingTypes.connect(signer1).setAddressUintMap(expectedValue);
|
await testMappingTypes.connect(signer1).setAddressUintMap(expectedValue);
|
||||||
@ -329,12 +329,126 @@ describe('Get value from storage', () => {
|
|||||||
expect(value).to.equal(BigInt(expectedValue));
|
expect(value).to.equal(BigInt(expectedValue));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get value for nested mapping type', async () => {
|
it('get value for mapping with boolean type keys', async () => {
|
||||||
|
const expectedValue = 123;
|
||||||
|
const mapKey = true;
|
||||||
|
await testMappingTypes.setBoolIntMap(mapKey, expectedValue);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'boolIntMap', mapKey);
|
||||||
|
expect(value).to.equal(BigInt(expectedValue));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for mapping with signed integer type keys', async () => {
|
||||||
|
const mapKey = 123;
|
||||||
|
const [, signer1] = await ethers.getSigners();
|
||||||
|
await testMappingTypes.setIntAddressMap(mapKey, signer1.address);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'intAddressMap', mapKey);
|
||||||
|
expect(value).to.equal(signer1.address.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for mapping with unsigned integer type keys', async () => {
|
||||||
|
const expectedValue = ethers.utils.hexlify(ethers.utils.randomBytes(16));
|
||||||
|
const mapKey = 123;
|
||||||
|
await testMappingTypes.setUintBytesMap(mapKey, expectedValue);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'uintBytesMap', mapKey);
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('get value for mapping with fixed-size byte array keys', async () => {
|
||||||
|
const mapKey = ethers.utils.hexlify(ethers.utils.randomBytes(8));
|
||||||
|
const [, signer1] = await ethers.getSigners();
|
||||||
|
await testMappingTypes.setBytesAddressMap(mapKey, signer1.address);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'bytesAddressMap', mapKey);
|
||||||
|
expect(value).to.equal(signer1.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for mapping with enum type keys', async () => {
|
||||||
|
const mapKey = 1;
|
||||||
|
const expectedValue = 123;
|
||||||
|
await testMappingTypes.setEnumIntMap(mapKey, expectedValue);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'enumIntMap', mapKey);
|
||||||
|
expect(value).to.equal(BigInt(expectedValue));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for mapping with string type keys', async () => {
|
||||||
|
const mapKey = 'abc';
|
||||||
|
const expectedValue = 123;
|
||||||
|
await testMappingTypes.setStringIntMap(mapKey, expectedValue);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'stringIntMap', mapKey);
|
||||||
|
expect(value).to.equal(BigInt(expectedValue));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for mapping with dynamically-sized byte array as keys', async () => {
|
||||||
|
const mapKey = ethers.utils.hexlify(ethers.utils.randomBytes(64));
|
||||||
|
const expectedValue = 123;
|
||||||
|
await testMappingTypes.setBytesUintMap(mapKey, expectedValue);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'bytesUintMap', mapKey);
|
||||||
|
expect(value).to.equal(BigInt(expectedValue));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('nested mapping type', () => {
|
||||||
|
let testNestedMapping: Contract, storageLayout: StorageLayout;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
const TestNestedMapping = await ethers.getContractFactory('TestNestedMapping');
|
||||||
|
testNestedMapping = await TestNestedMapping.deploy();
|
||||||
|
await testNestedMapping.deployed();
|
||||||
|
storageLayout = await getStorageLayout('TestNestedMapping');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for nested mapping with address type keys', async () => {
|
||||||
const expectedValue = 123;
|
const expectedValue = 123;
|
||||||
const [, signer1, signer2] = await ethers.getSigners();
|
const [, signer1, signer2] = await ethers.getSigners();
|
||||||
await testMappingTypes.connect(signer1).setNestedAddressUintMap(signer2.address, expectedValue);
|
await testNestedMapping.connect(signer1).setNestedAddressUintMap(signer2.address, expectedValue);
|
||||||
const blockHash = await getBlockHash();
|
const blockHash = await getBlockHash();
|
||||||
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testMappingTypes.address, 'addressUintMap', signer1.address, signer2.address);
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testNestedMapping.address, 'nestedAddressUintMap', signer1.address, signer2.address);
|
||||||
|
expect(value).to.equal(BigInt(expectedValue));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for nested mapping with signed integer type keys', async () => {
|
||||||
|
const expectedValue = false;
|
||||||
|
const key = 123;
|
||||||
|
const [, signer1] = await ethers.getSigners();
|
||||||
|
await testNestedMapping.setIntAddressBoolMap(key, signer1.address, expectedValue);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testNestedMapping.address, 'intAddressBoolMap', key, signer1.address);
|
||||||
|
expect(value).to.equal(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for nested mapping with unsigned integer type keys', async () => {
|
||||||
|
const expectedValue = 123;
|
||||||
|
const key = 456;
|
||||||
|
const nestedKey = 'abc';
|
||||||
|
await testNestedMapping.setUintStringIntMap(key, nestedKey, expectedValue);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testNestedMapping.address, 'uintStringIntMap', key, nestedKey);
|
||||||
|
expect(value).to.equal(BigInt(expectedValue));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for nested mapping with dynamically-sized byte array as keys', async () => {
|
||||||
|
const key = ethers.utils.hexlify(ethers.utils.randomBytes(64));
|
||||||
|
const nestedKey = 123;
|
||||||
|
const [, signer1] = await ethers.getSigners();
|
||||||
|
await testNestedMapping.setBytesIntAddressMap(key, nestedKey, signer1.address);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testNestedMapping.address, 'bytesIntAddressMap', key, nestedKey);
|
||||||
|
expect(value).to.equal(signer1.address.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get value for nested mapping with string type keys', async () => {
|
||||||
|
const key = 'abc';
|
||||||
|
const expectedValue = 123;
|
||||||
|
const [, signer1] = await ethers.getSigners();
|
||||||
|
await testNestedMapping.setStringAddressIntMap(key, signer1.address, expectedValue);
|
||||||
|
const blockHash = await getBlockHash();
|
||||||
|
const { value } = await getStorageValue(storageLayout, getStorageAt, blockHash, testNestedMapping.address, 'stringAddressIntMap', key, signer1.address);
|
||||||
expect(value).to.equal(BigInt(expectedValue));
|
expect(value).to.equal(BigInt(expectedValue));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,9 +14,12 @@ interface Types {
|
|||||||
label: string;
|
label: string;
|
||||||
base?: string;
|
base?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
key?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MappingKey = string | boolean | number;
|
||||||
|
|
||||||
export interface StorageLayout {
|
export interface StorageLayout {
|
||||||
storage: Storage[];
|
storage: Storage[];
|
||||||
types: Types
|
types: Types
|
||||||
@ -58,7 +61,7 @@ export const getStorageInfo = (storageLayout: StorageLayout, variableName: strin
|
|||||||
* @param variableName
|
* @param variableName
|
||||||
*/
|
*/
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
export const getStorageValue = async (storageLayout: StorageLayout, getStorageAt: GetStorageAt, blockHash: string, address: string, variableName: string, ...mappingKeys: Array<string>): Promise<{ value: any, proof: { data: string } }> => {
|
export const getStorageValue = async (storageLayout: StorageLayout, getStorageAt: GetStorageAt, blockHash: string, address: string, variableName: string, ...mappingKeys: Array<MappingKey>): Promise<{ value: any, proof: { data: string } }> => {
|
||||||
const { slot, offset, type, types } = getStorageInfo(storageLayout, variableName);
|
const { slot, offset, type, types } = getStorageInfo(storageLayout, variableName);
|
||||||
|
|
||||||
return getDecodedValue(getStorageAt, blockHash, address, types, { slot, offset, type }, mappingKeys);
|
return getDecodedValue(getStorageAt, blockHash, address, types, { slot, offset, type }, mappingKeys);
|
||||||
@ -97,9 +100,9 @@ export const getValueByType = (storageValue: string, typeLabel: string): bigint
|
|||||||
* @param storageInfo
|
* @param storageInfo
|
||||||
*/
|
*/
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
const getDecodedValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, types: Types, storageInfo: { slot: string, offset: number, type: string }, mappingKeys: Array<string>): Promise<{ value: any, proof: { data: string } }> => {
|
const getDecodedValue = async (getStorageAt: GetStorageAt, blockHash: string, address: string, types: Types, storageInfo: { slot: string, offset: number, type: string }, mappingKeys: Array<MappingKey>): Promise<{ value: any, proof: { data: string } }> => {
|
||||||
const { slot, offset, type } = storageInfo;
|
const { slot, offset, type } = storageInfo;
|
||||||
const { encoding, numberOfBytes, label: typeLabel, base, value: mappingValueType } = types[type];
|
const { encoding, numberOfBytes, label: typeLabel, base, value: mappingValueType, key: mappingKeyType } = types[type];
|
||||||
|
|
||||||
const [isArray, arraySize] = typeLabel.match(/\[([0-9]*)\]/) || [false];
|
const [isArray, arraySize] = typeLabel.match(/\[([0-9]*)\]/) || [false];
|
||||||
let value: string, proof: { data: string };
|
let value: string, proof: { data: string };
|
||||||
@ -142,10 +145,10 @@ const getDecodedValue = async (getStorageAt: GetStorageAt, blockHash: string, ad
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mapping': {
|
case 'mapping': {
|
||||||
const mappingSlot = await getMappingSlot(slot, mappingKeys[0]);
|
if (mappingValueType && mappingKeyType) {
|
||||||
|
const mappingSlot = await getMappingSlot(slot, types, mappingKeyType, mappingKeys[0]);
|
||||||
|
|
||||||
if (mappingValueType) {
|
return getDecodedValue(getStorageAt, blockHash, address, types, { slot: mappingSlot, offset: 0, type: mappingValueType }, mappingKeys.slice(1));
|
||||||
({ value, proof } = await getDecodedValue(getStorageAt, blockHash, address, types, { slot: mappingSlot, offset: 0, type: mappingValueType }, mappingKeys.slice(1)));
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Mapping value type not specified for ${mappingKeys[0]}`);
|
throw new Error(`Mapping value type not specified for ${mappingKeys[0]}`);
|
||||||
}
|
}
|
||||||
@ -168,11 +171,32 @@ const getDecodedValue = async (getStorageAt: GetStorageAt, blockHash: string, ad
|
|||||||
* @param mappingSlot
|
* @param mappingSlot
|
||||||
* @param key
|
* @param key
|
||||||
*/
|
*/
|
||||||
export const getMappingSlot = (mappingSlot: string, key: string): string => {
|
export const getMappingSlot = (mappingSlot: string, types: Types, keyType: string, key: MappingKey): string => {
|
||||||
// https://github.com/ethers-io/ethers.js/issues/1079#issuecomment-703056242
|
const { encoding, label: typeLabel } = types[keyType];
|
||||||
const mappingSlotPadded = utils.hexZeroPad(BigNumber.from(mappingSlot).toHexString(), 32);
|
|
||||||
const keyPadded = utils.hexZeroPad(key, 32);
|
|
||||||
|
|
||||||
|
// If key is boolean type convert to 1 or 0 which is the way value is stored in memory.
|
||||||
|
if (typeLabel === 'bool') {
|
||||||
|
key = key ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If key is string convert to hex string representation.
|
||||||
|
if (typeLabel.includes('string') && typeof key === 'string') {
|
||||||
|
key = utils.hexlify(utils.toUtf8Bytes(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If key is still boolean type the argument passed as key is invalid.
|
||||||
|
if (typeof key === 'boolean') {
|
||||||
|
throw new Error('Invalid key.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/ethers-io/ethers.js/issues/1079#issuecomment-703056242
|
||||||
|
const mappingSlotPadded = utils.hexZeroPad(mappingSlot, 32);
|
||||||
|
|
||||||
|
const keyPadded = encoding === 'bytes'
|
||||||
|
? utils.hexlify(key)
|
||||||
|
: utils.hexZeroPad(utils.hexlify(key), 32);
|
||||||
|
|
||||||
|
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays
|
||||||
const fullKey = utils.concat([
|
const fullKey = utils.concat([
|
||||||
keyPadded,
|
keyPadded,
|
||||||
mappingSlotPadded
|
mappingSlotPadded
|
||||||
|
72
packages/solidity-mapper/test/contracts/TestBasicMapping.sol
Normal file
72
packages/solidity-mapper/test/contracts/TestBasicMapping.sol
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
|
||||||
|
contract TestBasicMapping {
|
||||||
|
// Mapping type variable occupies one single slot but the actual elements are stored at a different storage slot that is computed using a Keccak-256 hash.
|
||||||
|
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays
|
||||||
|
mapping(address => uint) public addressUintMap;
|
||||||
|
|
||||||
|
// Mapping type variable occupies the next single slot.
|
||||||
|
mapping(bool => int) public boolIntMap;
|
||||||
|
|
||||||
|
// Mapping with int128 keys and contract type values.
|
||||||
|
mapping(int128 => address) public intAddressMap;
|
||||||
|
|
||||||
|
// Mapping with uint32 keys and fixed-size byte array values.
|
||||||
|
mapping(uint32 => bytes16) public uintBytesMap;
|
||||||
|
|
||||||
|
// Mapping with fixed-size byte array keys and address type values.
|
||||||
|
mapping(bytes8 => address) public bytesAddressMap;
|
||||||
|
|
||||||
|
// Enum declaration.
|
||||||
|
enum Choices { Choice0, Choice1, Choice2, Choice3 }
|
||||||
|
|
||||||
|
// Mapping with enum type keys and integer type values.
|
||||||
|
mapping(Choices => int) public enumIntMap;
|
||||||
|
|
||||||
|
// Mapping with string type keys and integer type values.
|
||||||
|
mapping(string => int) public stringIntMap;
|
||||||
|
|
||||||
|
// Mapping with dynamically-sized byte array as keys and unsigned integer type values.
|
||||||
|
mapping(bytes => uint) public bytesUintMap;
|
||||||
|
|
||||||
|
// Set variable addressUintMap.
|
||||||
|
function setAddressUintMap(uint value) external {
|
||||||
|
addressUintMap[msg.sender] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable boolIntMap.
|
||||||
|
function setBoolIntMap(bool key, int value) external {
|
||||||
|
boolIntMap[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable intAddressMap.
|
||||||
|
function setIntAddressMap(int128 key, address value) external {
|
||||||
|
intAddressMap[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable uintBytesMap.
|
||||||
|
function setUintBytesMap(uint32 key, bytes16 value) external {
|
||||||
|
uintBytesMap[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable bytesAddressMap.
|
||||||
|
function setBytesAddressMap(bytes8 key, address value) external {
|
||||||
|
bytesAddressMap[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable enumIntMap.
|
||||||
|
function setEnumIntMap(Choices key, int value) external {
|
||||||
|
enumIntMap[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable stringIntMap.
|
||||||
|
function setStringIntMap(string calldata key, int value) external {
|
||||||
|
stringIntMap[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable bytesUintMap.
|
||||||
|
function setBytesUintMap(bytes calldata key, uint value) external {
|
||||||
|
bytesUintMap[key] = value;
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.7.0;
|
|
||||||
|
|
||||||
contract TestMappingTypes {
|
|
||||||
// Mapping type variable occupies one single slot but the actual elements are stored at a different storage slot that is computed using a Keccak-256 hash.
|
|
||||||
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays
|
|
||||||
mapping (address => mapping (address => uint)) private nestedAddressUintMap;
|
|
||||||
|
|
||||||
// Mapping type variable occupies the next single slot.
|
|
||||||
mapping(address => uint) public addressUintMap;
|
|
||||||
|
|
||||||
// Set variable addressUintMap.
|
|
||||||
function setAddressUintMap(uint value) external {
|
|
||||||
addressUintMap[msg.sender] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set variable nestedAddressUintMap.
|
|
||||||
function setNestedAddressUintMap(address addressValue, uint uintValue) external {
|
|
||||||
nestedAddressUintMap[msg.sender][addressValue] = uintValue;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
|
||||||
|
contract TestNestedMapping {
|
||||||
|
// Mapping type variable occupies one single slot but the actual elements are stored at a different storage slot that is computed using a Keccak-256 hash.
|
||||||
|
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays
|
||||||
|
mapping (address => mapping (address => uint)) private nestedAddressUintMap;
|
||||||
|
|
||||||
|
mapping (int => mapping (address => bool)) private intAddressBoolMap;
|
||||||
|
|
||||||
|
mapping (uint => mapping (string => int)) private uintStringIntMap;
|
||||||
|
|
||||||
|
mapping (bytes => mapping (int => address)) private bytesIntAddressMap;
|
||||||
|
|
||||||
|
mapping (string => mapping (address => int)) private stringAddressIntMap;
|
||||||
|
|
||||||
|
// Set variable nestedAddressUintMap.
|
||||||
|
function setNestedAddressUintMap(address nestedKey, uint value) external {
|
||||||
|
nestedAddressUintMap[msg.sender][nestedKey] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable intAddressBoolMap.
|
||||||
|
function setIntAddressBoolMap(int key, address nestedKey, bool value) external {
|
||||||
|
intAddressBoolMap[key][nestedKey] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable uintStringIntMap.
|
||||||
|
function setUintStringIntMap(uint key, string calldata nestedKey, int value) external {
|
||||||
|
uintStringIntMap[key][nestedKey] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable bytesIntAddressMap.
|
||||||
|
function setBytesIntAddressMap(bytes calldata key, int nestedKey, address value) external {
|
||||||
|
bytesIntAddressMap[key][nestedKey] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variable stringAddressIntMap.
|
||||||
|
function setStringAddressIntMap(string calldata key, address nestedKey, int value) external {
|
||||||
|
stringAddressIntMap[key][nestedKey] = value;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user