-
Notifications
You must be signed in to change notification settings - Fork 41
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
fix: slot-overwrite bug #133
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import { SetVariablesType, SmockVMManager } from '../types'; | ||
import { fromHexString, toFancyAddress } from '../utils'; | ||
import { fromHexString, remove0x, toFancyAddress, toHexString } from '../utils'; | ||
import { computeStorageSlots, SolidityStorageLayout } from '../utils/storage'; | ||
|
||
export class EditableStorageLogic { | ||
|
@@ -19,7 +19,14 @@ export class EditableStorageLogic { | |
const slots = computeStorageSlots(this.storageLayout, { [variableName]: value }); | ||
|
||
for (const slot of slots) { | ||
await this.vmManager.putContractStorage(toFancyAddress(this.contractAddress), fromHexString(slot.key), fromHexString(slot.val)); | ||
let prevStorageValue = await this.vmManager.getContractStorage(toFancyAddress(this.contractAddress), fromHexString(slot.key)); | ||
let stringValue = remove0x(toHexString(prevStorageValue)); | ||
if (stringValue && (slot.type === 'address' || slot.type === 'bool' || slot.type.startsWith('contract'))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm interesting code here, is there any way of generalizing this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only address and bool has same slot value |
||
let lastResult = slot.val.slice(0, slot.val.length - stringValue.length).concat(stringValue); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how come this works if you are always slicing starting in index 0? What if the variable packed slot starts from the middle? |
||
await this.vmManager.putContractStorage(toFancyAddress(this.contractAddress), fromHexString(slot.key), fromHexString(lastResult)); | ||
} else { | ||
await this.vmManager.putContractStorage(toFancyAddress(this.contractAddress), fromHexString(slot.key), fromHexString(slot.val)); | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.4; | ||
|
||
contract Bug { | ||
address public myAddress; | ||
bool public myBool; | ||
address public newAddress; | ||
address public newAddress2; | ||
bool public newBool; | ||
bool public newBool2; | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,11 +3,10 @@ import { makeRandomAddress } from '@src/utils'; | |
import { Receiver, Receiver__factory, Returner } from '@typechained'; | ||
import receiverArtifact from 'artifacts/test/contracts/watchable-function-logic/Receiver.sol/Receiver.json'; | ||
import chai, { expect } from 'chai'; | ||
import chaiAsPromised from 'chai-as-promised'; | ||
import { ethers, network } from 'hardhat'; | ||
import storageArtifact from 'test/unit/fake/testdata/Storage.json'; | ||
|
||
chai.use(chaiAsPromised); | ||
chai.use(smock.matchers); | ||
|
||
describe('Fake: Initialization', () => { | ||
it('should work with the contract name', async () => { | ||
|
@@ -91,21 +90,4 @@ describe('Fake: Initialization', () => { | |
const fake = await smock.fake(storageArtifact); | ||
expect(fake.store._watchable).not.to.be.undefined; | ||
}); | ||
|
||
it('should throw error for invalid json string abi', async () => { | ||
await expect(smock.fake(`{invalid}`)).to.be.rejectedWith( | ||
Error, | ||
`unable to generate smock spec from abi string.\nUnexpected token i in JSON at position 1` | ||
); | ||
}); | ||
|
||
it('should throw error for non existent contract', async () => { | ||
let regex: RegExp = /unable to generate smock spec from contract name*/; | ||
await expect(smock.fake('NonExistentContract')).to.be.rejectedWith(Error, regex); | ||
}); | ||
|
||
it('should throw error if contract name is ambiguous', async () => { | ||
let regex: RegExp = /unable to generate smock spec from contract name*/; | ||
await expect(smock.fake('Storage')).to.be.rejectedWith(Error, regex); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why deleting this?? |
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { MockContract, MockContractFactory, smock } from '@src'; | ||
import { ADDRESS_EXAMPLE, ADDRESS_EXAMPLE2, ADDRESS_EXAMPLE3 } from '@test-utils'; | ||
import { Bug, Bug__factory } from '@typechained'; | ||
import { expect } from 'chai'; | ||
|
||
describe('Bug Fix', () => { | ||
let bugFactory: MockContractFactory<Bug__factory>; | ||
let mock: MockContract<Bug>; | ||
|
||
before(async () => { | ||
bugFactory = await smock.mock('Bug'); | ||
}); | ||
|
||
beforeEach(async () => { | ||
mock = await bugFactory.deploy(); | ||
}); | ||
|
||
describe('setVariable', () => { | ||
it('should be able to fix slot overwrite on packed variables', async () => { | ||
await mock.setVariable('myAddress', ADDRESS_EXAMPLE); | ||
await mock.setVariable('myBool', true); | ||
await mock.setVariable('newAddress', ADDRESS_EXAMPLE2); | ||
await mock.setVariable('newAddress2', ADDRESS_EXAMPLE3); | ||
await mock.setVariable('newBool', false); | ||
await mock.setVariable('newBool2', true); | ||
console.log('____myAddress____', await mock.myAddress()); | ||
console.log('____myBool____', await mock.myBool()); | ||
console.log('____newAddress____', await mock.newAddress()); | ||
console.log('____newAddress2____', await mock.newAddress2()); | ||
console.log('____newBool____', await mock.newBool()); | ||
console.log('____newBool2____', await mock.newBool2()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please, remove all console.logs... |
||
expect(await mock.myAddress()).to.equal(ADDRESS_EXAMPLE); | ||
expect(await mock.myBool()).to.equal(true); | ||
expect(await mock.newAddress()).to.equal(ADDRESS_EXAMPLE2); | ||
expect(await mock.newAddress2()).to.equal(ADDRESS_EXAMPLE3); | ||
expect(await mock.newBool()).to.equal(false); | ||
expect(await mock.newBool2()).to.equal(true); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { MockContract, MockContractFactory, smock } from '@src'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this file named |
||
import { ADDRESS_EXAMPLE } from '@test-utils'; | ||
import { StorageGetter, StorageGetter__factory } from '@typechained'; | ||
import { expect } from 'chai'; | ||
|
||
describe('Mock: Editable storage logic', () => { | ||
let storageGetterFactory: MockContractFactory<StorageGetter__factory>; | ||
let mock: MockContract<StorageGetter>; | ||
|
||
before(async () => { | ||
storageGetterFactory = await smock.mock('StorageGetter'); | ||
}); | ||
|
||
beforeEach(async () => { | ||
mock = await storageGetterFactory.deploy(1); | ||
}); | ||
|
||
describe('setVariable', () => { | ||
it('should be able to fix bug', async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't reference the bug, this will stay in documentation for the future |
||
await mock.setVariable('_address', ADDRESS_EXAMPLE); | ||
await mock.setVariable('_bool', true); | ||
expect(await mock.getAddress()).to.equal(ADDRESS_EXAMPLE); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,10 @@ import { utils } from 'ethers'; | |
|
||
export const ADDRESS_EXAMPLE = '0x558ba9b8d78713fbf768c1f8a584485B4003f43F'; | ||
|
||
export const ADDRESS_EXAMPLE2 = '0x4fd96172C40913c8100a47337f21DBf25C9e2fC6'; | ||
|
||
export const ADDRESS_EXAMPLE3 = '0x62eA762d9f2672f82d26b94f56849881831B9eaB'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i prefer you added a |
||
|
||
export const BYTES32_EXAMPLE = '0x1234123412341234123412341234123412341234123412341234123412341234'; | ||
|
||
export const BYTES_EXAMPLE = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -914,13 +914,6 @@ | |
dependencies: | ||
"@types/node" "*" | ||
|
||
"@types/[email protected]": | ||
version "7.1.5" | ||
resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz#6e016811f6c7a64f2eed823191c3a6955094e255" | ||
integrity sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ== | ||
dependencies: | ||
"@types/chai" "*" | ||
|
||
"@types/chai@*": | ||
version "4.2.21" | ||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.21.tgz#9f35a5643129df132cf3b5c1ec64046ea1af0650" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we removing this import?