Skip to content

Commit

Permalink
feat: add getVariable function
Browse files Browse the repository at this point in the history
  • Loading branch information
0xGorilla authored Jul 12, 2022
2 parents 823b0ea + 71e5a52 commit ce79653
Show file tree
Hide file tree
Showing 9 changed files with 641 additions and 6 deletions.
25 changes: 24 additions & 1 deletion docs/source/mocks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,27 @@ Setting the value of multiple variables
[myKey]: 1234
}
})
Getting the value of an internal variable
********************

.. warning::
This is an experimental feature and it does not support multidimensional or packed arrays

.. code-block:: typescript
const myUint256 = await myMock.getVariable('myUint256VariableName');
Getting the value of an internal mapping given the value's key
#######################################

.. code-block:: typescript
const myMappingValue = await myMock.getVariable('myMappingVariableName', [mappingKey]);
Getting the value of an internal nested mapping given the value's keys
#######################################

.. code-block:: typescript
const myMappingValue = await myMock.getVariable('myMappingVariableName', [mappingKeyA, mappingKeyB]);
3 changes: 3 additions & 0 deletions src/factories/smock-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Observable } from 'rxjs';
import { distinct, filter, map, share, withLatestFrom } from 'rxjs/operators';
import { EditableStorageLogic as EditableStorage } from '../logic/editable-storage-logic';
import { ProgrammableFunctionLogic, SafeProgrammableContract } from '../logic/programmable-function-logic';
import { ReadableStorageLogic as ReadableStorage } from '../logic/readable-storage-logic';
import { ObservableVM } from '../observable-vm';
import { Sandbox } from '../sandbox';
import { ContractCall, FakeContract, MockContractFactory, ProgrammableContractFunction, ProgrammedReturnValue } from '../types';
Expand Down Expand Up @@ -51,8 +52,10 @@ function mockifyContractFactory<T extends ContractFactory>(

// attach to every internal variable, all the editable logic
const editableStorage = new EditableStorage(await getStorageLayout(contractName), vm.getManager(), mock.address);
const readableStorage = new ReadableStorage(await getStorageLayout(contractName), vm.getManager(), mock.address);
mock.setVariable = editableStorage.setVariable.bind(editableStorage);
mock.setVariables = editableStorage.setVariables.bind(editableStorage);
mock.getVariable = readableStorage.getVariable.bind(readableStorage);

// We attach a wallet to the contract so that users can send transactions *from* a watchablecontract.
mock.wallet = await impersonate(mock.address);
Expand Down
40 changes: 40 additions & 0 deletions src/logic/readable-storage-logic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { SmockVMManager } from '../types';
import { fromHexString, remove0x, toFancyAddress, toHexString } from '../utils';
import {
decodeVariable,
getVariableStorageSlots,
SolidityStorageLayout,
StorageSlotKeyTypePair,
StorageSlotKeyValuePair,
} from '../utils/storage';

export class ReadableStorageLogic {
private storageLayout: SolidityStorageLayout;
private contractAddress: string;
private vmManager: SmockVMManager;

constructor(storageLayout: SolidityStorageLayout, vmManager: SmockVMManager, contractAddress: string) {
this.storageLayout = storageLayout;
this.vmManager = vmManager;
this.contractAddress = contractAddress;
}

async getVariable(variableName: string, mappingKeys?: string[] | number[]): Promise<unknown> {
const slots: StorageSlotKeyTypePair[] = await getVariableStorageSlots(
this.storageLayout,
variableName,
this.vmManager,
this.contractAddress,
mappingKeys
);
const slotValueTypePairs: StorageSlotKeyValuePair[] = await Promise.all(
slots.map(async (slotKeyPair) => ({
...slotKeyPair,
value: remove0x(
toHexString(await this.vmManager.getContractStorage(toFancyAddress(this.contractAddress), fromHexString(slotKeyPair.key)))
),
}))
);
return decodeVariable(slotValueTypePairs);
}
}
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Provider } from '@ethersproject/abstract-provider';
import { Signer } from '@ethersproject/abstract-signer';
import { BaseContract, ContractFactory, ethers } from 'ethers';
import { EditableStorageLogic } from './logic/editable-storage-logic';
import { ReadableStorageLogic } from './logic/readable-storage-logic';
import { WatchableFunctionLogic } from './logic/watchable-function-logic';

type Abi = ReadonlyArray<
Expand Down Expand Up @@ -72,6 +73,7 @@ export type MockContract<T extends BaseContract = BaseContract> = SmockContractB
connect: (...args: Parameters<T['connect']>) => MockContract<T>;
setVariable: EditableStorageLogic['setVariable'];
setVariables: EditableStorageLogic['setVariables'];
getVariable: ReadableStorageLogic['getVariable'];
} & {
[Property in keyof T['functions']]: ProgrammableContractFunction;
};
Expand Down
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;
}
Loading

0 comments on commit ce79653

Please sign in to comment.