Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add getVariable function #134

Merged
merged 9 commits into from
Jul 12, 2022
Prev Previous commit
Next Next commit
fix: bug with negative ints
  • Loading branch information
tonykogias committed Jul 6, 2022
commit ed30ea7bb0758d01eb7bce102560aa4c962d27cd
18 changes: 18 additions & 0 deletions src/utils/hex-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,21 @@ function bitnot(bi: BigInt) {
.join('');
return BigInt('0b' + prefix + bin) + BigInt(1);
}

/**
* XOR operation between 2 Buffers
* Source: https://github.com/crypto-browserify/buffer-xor/blob/master/index.js
* @param a Buffer to XOR
* @param b Buffer is the mask
* @returns hex representation of the big number
*/
export function xor(a: Buffer, b: Buffer) {
var length = Math.max(a.length, b.length);
var buffer = Buffer.allocUnsafe(length);

for (var i = 0; i < length; ++i) {
buffer[i] = a[i] ^ b[i];
}

return buffer;
}
20 changes: 16 additions & 4 deletions src/utils/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BigNumber, ethers } from 'ethers';
import { artifacts } from 'hardhat';
import semver from 'semver';
import { SmockVMManager } from '../types';
import { bigNumberToHex, fromHexString, remove0x, toFancyAddress, toHexString } from '../utils';
import { bigNumberToHex, fromHexString, remove0x, toFancyAddress, toHexString, xor } from '../utils';

// Represents the JSON objects outputted by the Solidity compiler that describe the structure of
// state within the contract. See
Expand Down Expand Up @@ -675,9 +675,21 @@ export function decodeVariable(slotValueTypePairs: StorageSlotKeyValuePair | Sto
result = BigNumber.from('0x' + value);
} else if (slotValueTypePairs[0].type.label.startsWith('int')) {
// When we deal with signed integers we have to convert the value from signed hex to decimal
// Doesn't work for negative numbers
// TODO: convert 2's complement hex to decimal to make it work properly
result = parseInt(slotValueTypePairs[0].value, 16).toString();

let intHex = slotValueTypePairs[0].value;
// If the first character is `f` then we know we have to deal with a negative number
if (intHex.slice(0, 1) === 'f') {
// In order to get the negative number we need to find the two's complement of the hex value (more info: https://en.wikipedia.org/wiki/Two%27s_complement)
// To do that we have to XOR our hex with the appropriate mask and then add 1 to the result
// First convert the hexStrings to Buffer in order to XOR them
intHex = fromHexString('0x' + intHex);
// We choose this mask because we want to flip all the hex bytes in order to find the two's complement
const mask = fromHexString('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
// After the XOR and the addition we have the positive number of the original hex value, we want the negative value so we add `-` infront
intHex = -BigNumber.from(toHexString(xor(intHex, mask))).add(BigNumber.from(1));
}

result = intHex;
} else if (slotValueTypePairs[0].type.label.startsWith('struct')) {
// We remove the first pair since we only need the members now
slotValueTypePairs.shift();
Expand Down
20 changes: 3 additions & 17 deletions test/unit/mock/readable-storage-logic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,16 @@ describe('Mock: Readable storage logic', () => {
expect(getValue).to.equal(await mock.getPackedUintB());
});

it('should be able to get a int56', async () => {
it('should be able to get an int56', async () => {
const value = 1;
await mock.setVariable('_int56', value);

const getValue = await mock.getVariable('_int56');
expect(getValue).to.equal(await mock.getInt56());
});

// TODO: Make this work for negatives
it.skip('should be able to get a int256', async () => {
const value = utils.parseUnits('-1');
it('should be able to get an int256', async () => {
const value = BigNumber.from(-1);
await mock.setVariable('_int256', value);

const getValue = await mock.getVariable('_int256');
Expand Down Expand Up @@ -113,19 +112,6 @@ describe('Mock: Readable storage logic', () => {
expect(getValue).to.deep.equal(struct);
});

// it('should be able to get an address in a packed struct', async () => {
// const struct = {
// packedA: BigNumber.from(2),
// packedB: BigNumber.from(1),
// packedC: BigNumber.from(2),
// packedD: BigNumber.from(1),
// packedE: ADDRESS_EXAMPLE,
// };
// await mock.setVariable('_packedStruct2', struct);
// const getValue = await mock.getVariable('_packedStruct2');
// expect(getValue).to.deep.equal(struct);
// });

it('should be able to get a uint256 mapping value', async () => {
const mapKey = 1234;
const mapValue = 5678;
Expand Down