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
Merged
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]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's please add a warning, mentioning the things that don't work. Will links to PRs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

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);
tonykogias marked this conversation as resolved.
Show resolved Hide resolved
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