mirror of
https://github.com/getwax/eth-global-lisbon-hackathon.git
synced 2026-01-09 15:57:55 -05:00
Updates for Safe v1.4.0
This commit is contained in:
237
account-abstraction/contracts/mock-contract/README.md
Normal file
237
account-abstraction/contracts/mock-contract/README.md
Normal file
@@ -0,0 +1,237 @@
|
||||
[](https://travis-ci.org/gnosis/mock-contract)
|
||||
|
||||
# MockContract
|
||||
|
||||
Simple Solidity contract to mock dependencies in truffle tests. It enables you to
|
||||
- Make dependent contracts return predefined values for different methods and arguments
|
||||
- Simulate exceptions such as `revert` and `outOfGas`
|
||||
- Assert on how often a dependency is called
|
||||
|
||||
*MockContract* allows for all of that without having to write a separate test contract each time.
|
||||
|
||||
# Usage in your project
|
||||
|
||||
Install module from npm
|
||||
```bash
|
||||
npm i -D @gnosis.pm/mock-contract
|
||||
```
|
||||
|
||||
Enable compilation of MockContract
|
||||
* Add a new `Imports.sol` to the `contracts` folder the project
|
||||
* Copy the following code into `Imports.sol`:
|
||||
```js
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
// We import the contract so truffle compiles it, and we have the ABI
|
||||
// available when working from truffle console.
|
||||
import "@gnosis.pm/mock-contract/contracts/MockContract.sol";
|
||||
```
|
||||
* Use in javascript unit test:
|
||||
```
|
||||
const MockContract = artifacts.require("./MockContract.sol")
|
||||
|
||||
// Instantiate mock and make it return true for any invocation
|
||||
const mock = await MockContract.new()
|
||||
await mock.givenAnyReturnBool(true)
|
||||
|
||||
// instantiate "object under test" with mocked contract.
|
||||
const contractToTest = await ComplextContract.new(mock.address)
|
||||
```
|
||||
|
||||
# Step by Step Example
|
||||
|
||||
Let's assume we want to test the following smart contract, which implements a simple bidding procedure:
|
||||
|
||||
```js
|
||||
pragma solidity ^0.6.0;
|
||||
import "./Token.sol";
|
||||
|
||||
/**
|
||||
* Contract that stores the highest bidder while securing payment from a given ERC20 Token.
|
||||
* Upon each bid the cost of bidding is incremented.
|
||||
*/
|
||||
contract SimpleAuction {
|
||||
address public captor; // Address of the highest bidder
|
||||
|
||||
Token private token;
|
||||
uint256 private cost;
|
||||
|
||||
constructor(Token _token) public {
|
||||
token = _token;
|
||||
}
|
||||
|
||||
function bid() public {
|
||||
if (token.transferFrom(msg.sender, this, cost + 1)) {
|
||||
require(token.transfer(captor, cost), "Refund failed");
|
||||
captor = msg.sender;
|
||||
cost += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If we were to write unit tests for this class, we would have to provide an implementation of an ERC20 token contract. There are commonly two ways to deal with that:
|
||||
1. Use the real ERC20 token contract and configure it in a way that it will work for the test (e.g. call `token.approve` before bidding)
|
||||
2. Implement a fake Token contract that instead of real logic contains dummy implementations (e.g. `return true` for everything)
|
||||
|
||||
The problem with 1) is that the logic required to make our dependency behave in the intended way can be very complex and incurs additional maintenance work. It also doesn't isolate our tests - instead of only testing the unit under test it is testing the integration of multiple components.
|
||||
|
||||
Solution 2) requires writing a *Fake* contract for each dependency. This takes time and pollutes the repository and migration files with a lot of non-production code.
|
||||
|
||||
## Mocking General Interactions
|
||||
|
||||
*MockContract* can act as a generic fake object for any type of contract. We can tell our mock what it should return upon certain invocations. For the example above, assume we want to write a test case where ERC20 transfers work just fine:
|
||||
|
||||
```js
|
||||
const MockContract = artifacts.require("./MockContract.sol")
|
||||
const SimpleAuction = artifacts.require("./SimpleAuction.sol")
|
||||
...
|
||||
it('updates the captor', async () => {
|
||||
const mock = await MockContract.new()
|
||||
const auction = await SimpleAuction.new(mock.address)
|
||||
|
||||
const trueEncoded = web3.eth.abi.encodeParameter("bool", true)
|
||||
await mock.givenAnyReturnBool(trueEncoded)
|
||||
await auction.bid({from: accounts[0]})
|
||||
|
||||
assert.equal(accounts[0], await auction.captor.call())
|
||||
})
|
||||
```
|
||||
|
||||
In particular `await mock.givenAnyReturnBool(true)` will make it so that mock returns `true` on any method invocation.
|
||||
|
||||
A plain mock without any expectations will return *nullish* values by default (e.g. `false` for bool, `0` for uint, etc).
|
||||
|
||||
There are convenience methods for other types such as `givenAnyReturnAddress` or `givenAnyReturnUint`. The full mock interface can be found [here](https://github.com/fleupold/mock-contract/blob/master/contracts/MockContract.sol#L3).
|
||||
|
||||
## Mocking Methods Individually
|
||||
|
||||
Now let's assume we want to test that the bid gets reverted if `token.transfer` succeeds but `token.transferFrom` fails:
|
||||
|
||||
```js
|
||||
const Token = artifacts.require("./Token.sol")
|
||||
...
|
||||
it('should fail if we fail to refund', async () => {
|
||||
const mock = await MockContract.new()
|
||||
const auction = await SimpleAuction.new(mock.address)
|
||||
const token = await Token.new();
|
||||
|
||||
const transferFrom = token.contract.methods.transferFrom(0, 0, 0).encodeABI() // arguments don't matter
|
||||
const transfer = token.contract.methods.transfer(0,0).encodeABI() // arguments don't matter
|
||||
|
||||
await mock.givenMethodReturnBool(transferFrom, true)
|
||||
await mock.givenMethodReturnBool(transfer, false)
|
||||
|
||||
try {
|
||||
await auction.bid({from: accounts[1]})
|
||||
assert.fail("Should have reverted")
|
||||
} catch (e) {}
|
||||
})
|
||||
```
|
||||
|
||||
Different methods have different ABI encodings. `mock.givenMethodReturnBool(bytes, boolean)` takes the ABI encoded methodId as a first parameter and will only replace behavior for this method. There are two ways to construct the methodId. We recommend using the `encodeABI` call on the original contract's ABI:
|
||||
|
||||
```js
|
||||
// Arguments do not matter, mock will only extract methodId
|
||||
const transferFrom = token.contract.transferFrom(0, 0, 0).encodeABI()
|
||||
```
|
||||
|
||||
We could also create it manually using e.g.:
|
||||
|
||||
```js
|
||||
const transferFrom = web3.sha3("transferFrom(address,address,uint256)").slice(0,10) // first 4 bytes
|
||||
```
|
||||
|
||||
However, the latter approach is not type-safe and can lead to unexpected test behavior if the ABI on the original contract changes. The first approach would give a much more descriptive compilation error in that case.
|
||||
|
||||
Again there are convenience functions for other return types (e.g. `givenMethoReturnUint`).
|
||||
|
||||
Mocked methods will take priority over mocks using *any*.
|
||||
|
||||
## Mocking Methods & Arguments
|
||||
|
||||
We can also specify different behaviors for when the same method is called with different arguments:
|
||||
|
||||
```js
|
||||
it('Keeps the old bidder if the new bidder fails to transfer', async () => {
|
||||
...
|
||||
const transferFromA = token.contract.methods.transferFrom(accounts[0], auction.address, 1).encodeABI()
|
||||
const transferFromB = token.contract.methods.transferFrom(accounts[1], auction.address, 2).encodeABI()
|
||||
|
||||
await mock.givenCalldataReturnBool(transferFromA, true)
|
||||
await mock.givenCalldataReturnBool(transferFromB, false)
|
||||
|
||||
await auction.bid({from: accounts[0]})
|
||||
await auction.bid({from: accounts[1]})
|
||||
|
||||
assert.equal(accounts[0], await auction.captor.call())
|
||||
})
|
||||
```
|
||||
|
||||
This time we need to provide the full `calldata`. We can easily use the original contract's ABI `encodeABI`call to generate it. Again, convenience functions for other return types exist (e.g. `givenMethoReturnUint`).
|
||||
|
||||
Mocked calls with exact calldata will takes priority over *method* mocks and *any* mocks.
|
||||
|
||||
## Simulating Failure
|
||||
|
||||
We can also simulate EVM exceptions using `MockContract`. All methods are available for *any*, *method* and *calldata* specific calls:
|
||||
|
||||
```js
|
||||
// Revert upon any invocation on mock without a specific message
|
||||
await mock.givenAnyRevert()
|
||||
|
||||
// Revert upon any invocation of `methodId` with the specific message
|
||||
await mock.givenMethodRevertWithMessage(methodId, "Some specific message")
|
||||
|
||||
// Run out of gas, if mock is called with `calldata`
|
||||
await mock.givenCalldataRunOutOfGas(calldata)
|
||||
```
|
||||
|
||||
## Inspect Invocations
|
||||
|
||||
It can sometime be useful to see how often a dependency has been called during a test-case. E.g. we might want to assert that `transfer` is not called if `transferFrom` failed in the first place:
|
||||
|
||||
```js
|
||||
it('only does the second transfer if the first transfer succeed', async () => {
|
||||
...
|
||||
await mock.givenAnyReturnBool(false)
|
||||
await auction.bid()
|
||||
|
||||
const transfer = token.contract.methods.transfer(0,0).encodeABI()
|
||||
|
||||
const invocationCount = await mock.invocationCountForMethod.call(transfer)
|
||||
assert.equal(0, invocationCount)
|
||||
})
|
||||
```
|
||||
|
||||
We can inspect the total invocation of `mock` for all methods combined using `await invocationCount.call()` and for individual arguments using `await invocationCountForCalldata.call(calldata)`.
|
||||
|
||||
## Resetting `mock`
|
||||
|
||||
We can override existing behavior throughout the lifetime of the test:
|
||||
|
||||
```js
|
||||
await mock.givenAnyReturnBool(true) // will return true from now on
|
||||
await auction.bid()
|
||||
await mock.givenAnyReturnBool(false) // will return false from now on
|
||||
await auction.bid()
|
||||
```
|
||||
Note that previously specified *method* and *calldata* based behavior will be unaffected by overriding *any* mock behavior.
|
||||
|
||||
To completely reset all behavior and clear invocation counts, we can call:
|
||||
```js
|
||||
await mock.reset()
|
||||
```
|
||||
|
||||
## Complex `return` types
|
||||
If the methods for returning the most commonly used types are not enough, we can manually ABI encode our responses with arbitrary solidity types:
|
||||
|
||||
```js
|
||||
const hello_world = web3.eth.abi.encodeParameter("string", 'Hello World!')
|
||||
await mock.givenAnyReturn(hello_world);
|
||||
```
|
||||
|
||||
|
||||
---------------------------------------
|
||||
|
||||
*This is a work in progress and feedback is highly appreciated. Please open an issue on GitHub and/or submit a pull request.*
|
||||
@@ -0,0 +1,15 @@
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
/**
|
||||
* @dev Used for unit testing MockContract functionality.
|
||||
*/
|
||||
interface ComplexInterface {
|
||||
function methodA() external;
|
||||
function methodB() external;
|
||||
function acceptAdressUintReturnBool(address recipient, uint amount) external returns (bool);
|
||||
function acceptUintReturnString(uint) external returns (string memory);
|
||||
function acceptUintReturnBool(uint) external returns (bool);
|
||||
function acceptUintReturnUint(uint) external returns (uint);
|
||||
function acceptUintReturnAddress(uint) external returns (address);
|
||||
function acceptUintReturnUintView(uint) external view returns (uint);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import './ComplexInterface.sol';
|
||||
|
||||
contract ExampleContractUnderTest {
|
||||
ComplexInterface complexInterface;
|
||||
|
||||
constructor(address _complexInterface) public {
|
||||
complexInterface = ComplexInterface(_complexInterface);
|
||||
}
|
||||
|
||||
function callMockedFunction3Times() public view returns (bool) {
|
||||
complexInterface.acceptUintReturnUintView(1);
|
||||
complexInterface.acceptUintReturnUintView(1);
|
||||
complexInterface.acceptUintReturnUintView(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
function callMethodThatReturnsAddress() public returns (address) {
|
||||
address foo = complexInterface.acceptUintReturnAddress(1);
|
||||
return foo;
|
||||
}
|
||||
|
||||
function callMethodThatReturnsBool() public returns (bool) {
|
||||
return complexInterface.acceptUintReturnBool(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
interface MockInterface {
|
||||
/**
|
||||
* @dev After calling this method, the mock will return `response` when it is called
|
||||
* with any calldata that is not mocked more specifically below
|
||||
* (e.g. using givenMethodReturn).
|
||||
* @param response ABI encoded response that will be returned if method is invoked
|
||||
*/
|
||||
function givenAnyReturn(bytes calldata response) external;
|
||||
function givenAnyReturnBool(bool response) external;
|
||||
function givenAnyReturnUint(uint response) external;
|
||||
function givenAnyReturnAddress(address response) external;
|
||||
|
||||
function givenAnyRevert() external;
|
||||
function givenAnyRevertWithMessage(string calldata message) external;
|
||||
function givenAnyRunOutOfGas() external;
|
||||
|
||||
/**
|
||||
* @dev After calling this method, the mock will return `response` when the given
|
||||
* methodId is called regardless of arguments. If the methodId and arguments
|
||||
* are mocked more specifically (using `givenMethodAndArguments`) the latter
|
||||
* will take precedence.
|
||||
* @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it
|
||||
* @param response ABI encoded response that will be returned if method is invoked
|
||||
*/
|
||||
function givenMethodReturn(bytes calldata method, bytes calldata response) external;
|
||||
function givenMethodReturnBool(bytes calldata method, bool response) external;
|
||||
function givenMethodReturnUint(bytes calldata method, uint response) external;
|
||||
function givenMethodReturnAddress(bytes calldata method, address response) external;
|
||||
|
||||
function givenMethodRevert(bytes calldata method) external;
|
||||
function givenMethodRevertWithMessage(bytes calldata method, string calldata message) external;
|
||||
function givenMethodRunOutOfGas(bytes calldata method) external;
|
||||
|
||||
/**
|
||||
* @dev After calling this method, the mock will return `response` when the given
|
||||
* methodId is called with matching arguments. These exact calldataMocks will take
|
||||
* precedence over all other calldataMocks.
|
||||
* @param call ABI encoded calldata (methodId and arguments)
|
||||
* @param response ABI encoded response that will be returned if contract is invoked with calldata
|
||||
*/
|
||||
function givenCalldataReturn(bytes calldata call, bytes calldata response) external;
|
||||
function givenCalldataReturnBool(bytes calldata call, bool response) external;
|
||||
function givenCalldataReturnUint(bytes calldata call, uint response) external;
|
||||
function givenCalldataReturnAddress(bytes calldata call, address response) external;
|
||||
|
||||
function givenCalldataRevert(bytes calldata call) external;
|
||||
function givenCalldataRevertWithMessage(bytes calldata call, string calldata message) external;
|
||||
function givenCalldataRunOutOfGas(bytes calldata call) external;
|
||||
|
||||
/**
|
||||
* @dev Returns the number of times anything has been called on this mock since last reset
|
||||
*/
|
||||
function invocationCount() external returns (uint);
|
||||
|
||||
/**
|
||||
* @dev Returns the number of times the given method has been called on this mock since last reset
|
||||
* @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it
|
||||
*/
|
||||
function invocationCountForMethod(bytes calldata method) external returns (uint);
|
||||
|
||||
/**
|
||||
* @dev Returns the number of times this mock has been called with the exact calldata since last reset.
|
||||
* @param call ABI encoded calldata (methodId and arguments)
|
||||
*/
|
||||
function invocationCountForCalldata(bytes calldata call) external returns (uint);
|
||||
|
||||
/**
|
||||
* @dev Resets all mocked methods and invocation counts.
|
||||
*/
|
||||
function reset() external;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the MockInterface.
|
||||
*/
|
||||
contract MockContract is MockInterface {
|
||||
enum MockType { Return, Revert, OutOfGas }
|
||||
|
||||
bytes32 public constant MOCKS_LIST_START = hex"01";
|
||||
bytes public constant MOCKS_LIST_END = "0xff";
|
||||
bytes32 public constant MOCKS_LIST_END_HASH = keccak256(MOCKS_LIST_END);
|
||||
bytes4 public constant SENTINEL_ANY_MOCKS = hex"01";
|
||||
bytes public constant DEFAULT_FALLBACK_VALUE = abi.encode(false);
|
||||
|
||||
// A linked list allows easy iteration and inclusion checks
|
||||
mapping(bytes32 => bytes) calldataMocks;
|
||||
mapping(bytes => MockType) calldataMockTypes;
|
||||
mapping(bytes => bytes) calldataExpectations;
|
||||
mapping(bytes => string) calldataRevertMessage;
|
||||
mapping(bytes32 => uint) calldataInvocations;
|
||||
|
||||
mapping(bytes4 => bytes4) methodIdMocks;
|
||||
mapping(bytes4 => MockType) methodIdMockTypes;
|
||||
mapping(bytes4 => bytes) methodIdExpectations;
|
||||
mapping(bytes4 => string) methodIdRevertMessages;
|
||||
mapping(bytes32 => uint) methodIdInvocations;
|
||||
|
||||
MockType fallbackMockType;
|
||||
bytes fallbackExpectation = DEFAULT_FALLBACK_VALUE;
|
||||
string fallbackRevertMessage;
|
||||
uint invocations;
|
||||
uint resetCount;
|
||||
|
||||
constructor() public {
|
||||
calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END;
|
||||
methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS;
|
||||
}
|
||||
|
||||
function trackCalldataMock(bytes memory call) private {
|
||||
bytes32 callHash = keccak256(call);
|
||||
if (calldataMocks[callHash].length == 0) {
|
||||
calldataMocks[callHash] = calldataMocks[MOCKS_LIST_START];
|
||||
calldataMocks[MOCKS_LIST_START] = call;
|
||||
}
|
||||
}
|
||||
|
||||
function trackMethodIdMock(bytes4 methodId) private {
|
||||
if (methodIdMocks[methodId] == 0x0) {
|
||||
methodIdMocks[methodId] = methodIdMocks[SENTINEL_ANY_MOCKS];
|
||||
methodIdMocks[SENTINEL_ANY_MOCKS] = methodId;
|
||||
}
|
||||
}
|
||||
|
||||
function _givenAnyReturn(bytes memory response) internal {
|
||||
fallbackMockType = MockType.Return;
|
||||
fallbackExpectation = response;
|
||||
}
|
||||
|
||||
function givenAnyReturn(bytes calldata response) override external {
|
||||
_givenAnyReturn(response);
|
||||
}
|
||||
|
||||
function givenAnyReturnBool(bool response) override external {
|
||||
uint flag = response ? 1 : 0;
|
||||
_givenAnyReturn(uintToBytes(flag));
|
||||
}
|
||||
|
||||
function givenAnyReturnUint(uint response) override external {
|
||||
_givenAnyReturn(uintToBytes(response));
|
||||
}
|
||||
|
||||
function givenAnyReturnAddress(address response) override external {
|
||||
_givenAnyReturn(uintToBytes(uint256(uint160(response))));
|
||||
}
|
||||
|
||||
function givenAnyRevert() override external {
|
||||
fallbackMockType = MockType.Revert;
|
||||
fallbackRevertMessage = "";
|
||||
}
|
||||
|
||||
function givenAnyRevertWithMessage(string calldata message) override external {
|
||||
fallbackMockType = MockType.Revert;
|
||||
fallbackRevertMessage = message;
|
||||
}
|
||||
|
||||
function givenAnyRunOutOfGas() override external {
|
||||
fallbackMockType = MockType.OutOfGas;
|
||||
}
|
||||
|
||||
function _givenCalldataReturn(bytes memory call, bytes memory response) private {
|
||||
calldataMockTypes[call] = MockType.Return;
|
||||
calldataExpectations[call] = response;
|
||||
trackCalldataMock(call);
|
||||
}
|
||||
|
||||
function givenCalldataReturn(bytes calldata call, bytes calldata response) override external {
|
||||
_givenCalldataReturn(call, response);
|
||||
}
|
||||
|
||||
function givenCalldataReturnBool(bytes calldata call, bool response) override external {
|
||||
uint flag = response ? 1 : 0;
|
||||
_givenCalldataReturn(call, uintToBytes(flag));
|
||||
}
|
||||
|
||||
function givenCalldataReturnUint(bytes calldata call, uint response) override external {
|
||||
_givenCalldataReturn(call, uintToBytes(response));
|
||||
}
|
||||
|
||||
function givenCalldataReturnAddress(bytes calldata call, address response) override external {
|
||||
_givenCalldataReturn(call, uintToBytes(uint256(uint160(response))));
|
||||
}
|
||||
|
||||
function _givenMethodReturn(bytes memory call, bytes memory response) private {
|
||||
bytes4 method = bytesToBytes4(call);
|
||||
methodIdMockTypes[method] = MockType.Return;
|
||||
methodIdExpectations[method] = response;
|
||||
trackMethodIdMock(method);
|
||||
}
|
||||
|
||||
function givenMethodReturn(bytes calldata call, bytes calldata response) override external {
|
||||
_givenMethodReturn(call, response);
|
||||
}
|
||||
|
||||
function givenMethodReturnBool(bytes calldata call, bool response) override external {
|
||||
uint flag = response ? 1 : 0;
|
||||
_givenMethodReturn(call, uintToBytes(flag));
|
||||
}
|
||||
|
||||
function givenMethodReturnUint(bytes calldata call, uint response) override external {
|
||||
_givenMethodReturn(call, uintToBytes(response));
|
||||
}
|
||||
|
||||
function givenMethodReturnAddress(bytes calldata call, address response) override external {
|
||||
_givenMethodReturn(call, uintToBytes(uint256(uint160(response))));
|
||||
}
|
||||
|
||||
function givenCalldataRevert(bytes calldata call) override external {
|
||||
calldataMockTypes[call] = MockType.Revert;
|
||||
calldataRevertMessage[call] = "";
|
||||
trackCalldataMock(call);
|
||||
}
|
||||
|
||||
function givenMethodRevert(bytes calldata call) override external {
|
||||
bytes4 method = bytesToBytes4(call);
|
||||
methodIdMockTypes[method] = MockType.Revert;
|
||||
trackMethodIdMock(method);
|
||||
}
|
||||
|
||||
function givenCalldataRevertWithMessage(bytes calldata call, string calldata message) override external {
|
||||
calldataMockTypes[call] = MockType.Revert;
|
||||
calldataRevertMessage[call] = message;
|
||||
trackCalldataMock(call);
|
||||
}
|
||||
|
||||
function givenMethodRevertWithMessage(bytes calldata call, string calldata message) override external {
|
||||
bytes4 method = bytesToBytes4(call);
|
||||
methodIdMockTypes[method] = MockType.Revert;
|
||||
methodIdRevertMessages[method] = message;
|
||||
trackMethodIdMock(method);
|
||||
}
|
||||
|
||||
function givenCalldataRunOutOfGas(bytes calldata call) override external {
|
||||
calldataMockTypes[call] = MockType.OutOfGas;
|
||||
trackCalldataMock(call);
|
||||
}
|
||||
|
||||
function givenMethodRunOutOfGas(bytes calldata call) override external {
|
||||
bytes4 method = bytesToBytes4(call);
|
||||
methodIdMockTypes[method] = MockType.OutOfGas;
|
||||
trackMethodIdMock(method);
|
||||
}
|
||||
|
||||
function invocationCount() override external returns (uint) {
|
||||
return invocations;
|
||||
}
|
||||
|
||||
function invocationCountForMethod(bytes calldata call) override external returns (uint) {
|
||||
bytes4 method = bytesToBytes4(call);
|
||||
return methodIdInvocations[keccak256(abi.encodePacked(resetCount, method))];
|
||||
}
|
||||
|
||||
function invocationCountForCalldata(bytes calldata call) override external returns (uint) {
|
||||
return calldataInvocations[keccak256(abi.encodePacked(resetCount, call))];
|
||||
}
|
||||
|
||||
function reset() override external {
|
||||
// Reset all exact calldataMocks
|
||||
bytes memory nextMock = calldataMocks[MOCKS_LIST_START];
|
||||
bytes32 mockHash = keccak256(nextMock);
|
||||
// We cannot compary bytes
|
||||
while(mockHash != MOCKS_LIST_END_HASH) {
|
||||
// Reset all mock maps
|
||||
calldataMockTypes[nextMock] = MockType.Return;
|
||||
calldataExpectations[nextMock] = hex"";
|
||||
calldataRevertMessage[nextMock] = "";
|
||||
// Set next mock to remove
|
||||
nextMock = calldataMocks[mockHash];
|
||||
// Remove from linked list
|
||||
calldataMocks[mockHash] = "";
|
||||
// Update mock hash
|
||||
mockHash = keccak256(nextMock);
|
||||
}
|
||||
// Clear list
|
||||
calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END;
|
||||
|
||||
// Reset all any calldataMocks
|
||||
bytes4 nextAnyMock = methodIdMocks[SENTINEL_ANY_MOCKS];
|
||||
while(nextAnyMock != SENTINEL_ANY_MOCKS) {
|
||||
bytes4 currentAnyMock = nextAnyMock;
|
||||
methodIdMockTypes[currentAnyMock] = MockType.Return;
|
||||
methodIdExpectations[currentAnyMock] = hex"";
|
||||
methodIdRevertMessages[currentAnyMock] = "";
|
||||
nextAnyMock = methodIdMocks[currentAnyMock];
|
||||
// Remove from linked list
|
||||
methodIdMocks[currentAnyMock] = 0x0;
|
||||
}
|
||||
// Clear list
|
||||
methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS;
|
||||
|
||||
fallbackExpectation = DEFAULT_FALLBACK_VALUE;
|
||||
fallbackMockType = MockType.Return;
|
||||
invocations = 0;
|
||||
resetCount += 1;
|
||||
}
|
||||
|
||||
function useAllGas() private {
|
||||
while(true) {
|
||||
bool s;
|
||||
assembly {
|
||||
//expensive call to EC multiply contract
|
||||
s := call(sub(gas(), 2000), 6, 0, 0x0, 0xc0, 0x0, 0x60)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bytesToBytes4(bytes memory b) private pure returns (bytes4) {
|
||||
bytes4 out;
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
out |= bytes4(b[i] & 0xFF) >> (i * 8);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function uintToBytes(uint256 x) private pure returns (bytes memory b) {
|
||||
b = new bytes(32);
|
||||
assembly { mstore(add(b, 32), x) }
|
||||
}
|
||||
|
||||
function updateInvocationCount(bytes4 methodId, bytes memory originalMsgData) public {
|
||||
require(msg.sender == address(this), "Can only be called from the contract itself");
|
||||
invocations += 1;
|
||||
methodIdInvocations[keccak256(abi.encodePacked(resetCount, methodId))] += 1;
|
||||
calldataInvocations[keccak256(abi.encodePacked(resetCount, originalMsgData))] += 1;
|
||||
}
|
||||
|
||||
fallback () payable external {
|
||||
bytes4 methodId;
|
||||
assembly {
|
||||
methodId := calldataload(0)
|
||||
}
|
||||
|
||||
// First, check exact matching overrides
|
||||
if (calldataMockTypes[msg.data] == MockType.Revert) {
|
||||
revert(calldataRevertMessage[msg.data]);
|
||||
}
|
||||
if (calldataMockTypes[msg.data] == MockType.OutOfGas) {
|
||||
useAllGas();
|
||||
}
|
||||
bytes memory result = calldataExpectations[msg.data];
|
||||
|
||||
// Then check method Id overrides
|
||||
if (result.length == 0) {
|
||||
if (methodIdMockTypes[methodId] == MockType.Revert) {
|
||||
revert(methodIdRevertMessages[methodId]);
|
||||
}
|
||||
if (methodIdMockTypes[methodId] == MockType.OutOfGas) {
|
||||
useAllGas();
|
||||
}
|
||||
result = methodIdExpectations[methodId];
|
||||
}
|
||||
|
||||
// Last, use the fallback override
|
||||
if (result.length == 0) {
|
||||
if (fallbackMockType == MockType.Revert) {
|
||||
revert(fallbackRevertMessage);
|
||||
}
|
||||
if (fallbackMockType == MockType.OutOfGas) {
|
||||
useAllGas();
|
||||
}
|
||||
result = fallbackExpectation;
|
||||
}
|
||||
|
||||
// Record invocation as separate call so we don't rollback in case we are called with STATICCALL
|
||||
(, bytes memory r) = address(this).call{gas: 100000}(abi.encodeWithSignature("updateInvocationCount(bytes4,bytes)", methodId, msg.data));
|
||||
assert(r.length == 0);
|
||||
|
||||
assembly {
|
||||
return(add(0x20, result), mload(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
32
account-abstraction/contracts/mock-contract/package.json
Normal file
32
account-abstraction/contracts/mock-contract/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@gnosis.pm/mock-contract",
|
||||
"version": "4.0.0",
|
||||
"description": "Simple Solidity contract to mock dependent contracts in truffle tests.",
|
||||
"main": "truffle-config.js",
|
||||
"files": [
|
||||
"contracts",
|
||||
"test"
|
||||
],
|
||||
"scripts": {
|
||||
"test-norpc": "truffle test",
|
||||
"test": "run-with-testrpc -l 20000000 --noVMErrorsOnRPCResponse true 'truffle test'"
|
||||
},
|
||||
"keywords": [
|
||||
"solidity",
|
||||
"mock",
|
||||
"unit-testing",
|
||||
"ethereum",
|
||||
"truffle",
|
||||
"stub",
|
||||
"contract"
|
||||
],
|
||||
"homepage": "https://github.com/fleupold/mock-contract",
|
||||
"author": "Felix Leupold",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"run-with-testrpc": "^0.3.0",
|
||||
"truffle": "^5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
547
account-abstraction/contracts/mock-contract/test/MockContract.js
Normal file
547
account-abstraction/contracts/mock-contract/test/MockContract.js
Normal file
@@ -0,0 +1,547 @@
|
||||
const utils = require('./utils')
|
||||
const MockContract = artifacts.require("./MockContract.sol")
|
||||
const ComplexInterface = artifacts.require("./ComplexInterface.sol")
|
||||
const ExampleContractUnderTest = artifacts.require("./ExampleContractUnderTest.sol")
|
||||
|
||||
contract('MockContract', function(accounts) {
|
||||
|
||||
describe("cleanState", function() {
|
||||
it("should return null if not mocked", async function() {
|
||||
const mock = await MockContract.new()
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10);
|
||||
assert.equal(result, false)
|
||||
});
|
||||
|
||||
it("should return null if not mocked when called by contract under test", async function() {
|
||||
const mock = await MockContract.new()
|
||||
const exampleContract = await ExampleContractUnderTest.new(mock.address);
|
||||
|
||||
result = await exampleContract.callMethodThatReturnsBool.call();
|
||||
assert.equal(result, false)
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenAnyReturn", function() {
|
||||
it("should return the mocked value", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
await mock.givenAnyReturn(web3.eth.abi.encodeParameter("bool", true))
|
||||
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, true)
|
||||
|
||||
// Check that other methods also return true
|
||||
result = await complex.acceptUintReturnBool.call(10);
|
||||
assert.equal(result, true)
|
||||
|
||||
// Check that we can reset
|
||||
await mock.reset()
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, false)
|
||||
|
||||
// Check convenience methods
|
||||
await mock.givenAnyReturnBool(true)
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, true)
|
||||
|
||||
await mock.givenAnyReturnUint(42)
|
||||
result = await complex.acceptUintReturnUint.call(7);
|
||||
assert.equal(result, 42)
|
||||
|
||||
await mock.givenAnyReturnAddress(accounts[0])
|
||||
result = await complex.acceptUintReturnAddress.call(7);
|
||||
assert.equal(result, accounts[0])
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenAnyRevert", function() {
|
||||
it("should revert if mocked", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
await mock.givenAnyRevert();
|
||||
|
||||
// On error it should return the error message for a call
|
||||
const encoded = await complex.contract.methods.methodA().encodeABI();
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "")
|
||||
await utils.assertRevert(complex.methodA())
|
||||
|
||||
// Check that other calls also error
|
||||
await utils.assertRevert(complex.methodB())
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
|
||||
// Transaction should be successful
|
||||
await complex.methodA()
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenAnyRevertWithMessage", function() {
|
||||
it("should revert if mocked and return message", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
await mock.givenAnyRevertWithMessage("This is Sparta!!!");
|
||||
|
||||
// On error it should return the error message for a call
|
||||
const encoded = await complex.contract.methods.methodA().encodeABI();
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "This is Sparta!!!")
|
||||
await utils.assertRevert(complex.methodA())
|
||||
|
||||
// Check that other calls also error
|
||||
await utils.assertRevert(complex.methodB())
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
|
||||
// Transaction should be successful
|
||||
await complex.methodA()
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenAnyRunOutOfGas", function() {
|
||||
it("should run out of gas if mocked", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
await mock.givenAnyRunOutOfGas()
|
||||
|
||||
await utils.assertOutOfGas(complex.methodA())
|
||||
|
||||
// Check that other calls also run out of gas
|
||||
await utils.assertOutOfGas(complex.methodB())
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
// Transaction should be successful
|
||||
await complex.methodA()
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenCalldataReturn", function() {
|
||||
it("should return the mocked value", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
let encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10).encodeABI()
|
||||
await mock.givenCalldataReturn(encoded, web3.eth.abi.encodeParameter("bool", true))
|
||||
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, true)
|
||||
// Check that other calls return default
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000001", 10);
|
||||
assert.equal(result, false)
|
||||
|
||||
// Check that we can reset
|
||||
await mock.reset()
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, false)
|
||||
|
||||
// Check convenience methods
|
||||
await mock.givenCalldataReturnBool(encoded, "true")
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, true)
|
||||
|
||||
encoded = await complex.contract.methods.acceptUintReturnUint(7).encodeABI();
|
||||
await mock.givenCalldataReturnUint(encoded, 42)
|
||||
result = await complex.acceptUintReturnUint.call(7);
|
||||
assert.equal(result, 42)
|
||||
|
||||
encoded = await complex.contract.methods.acceptUintReturnAddress(7).encodeABI();
|
||||
await mock.givenCalldataReturnAddress(encoded, accounts[0])
|
||||
result = await complex.acceptUintReturnAddress.call(7);
|
||||
assert.equal(result, accounts[0])
|
||||
});
|
||||
|
||||
it("should allow mocking the same method with different paramters", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
encodedA = await complex.contract.methods.acceptUintReturnUint(7).encodeABI();
|
||||
encodedB = await complex.contract.methods.acceptUintReturnUint(8).encodeABI();
|
||||
|
||||
await mock.givenCalldataReturnUint(encodedA, 7)
|
||||
await mock.givenCalldataReturnUint(encodedB, 8)
|
||||
|
||||
let result = await complex.acceptUintReturnUint.call(7)
|
||||
assert.equal(7, result)
|
||||
|
||||
result = await complex.acceptUintReturnUint.call(8)
|
||||
assert.equal(8, result)
|
||||
});
|
||||
|
||||
it("should allow contract under test to call mocked method 3 times in 1 transaction", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const exampleContract = await ExampleContractUnderTest.new(mock.address);
|
||||
|
||||
mock.givenAnyReturnUint(1)
|
||||
const result = await exampleContract.callMockedFunction3Times()
|
||||
assert.equal(result, true)
|
||||
});
|
||||
})
|
||||
|
||||
describe("givenCalldataRevert", function() {
|
||||
it("should revert if mocked", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
const encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10).encodeABI();
|
||||
await mock.givenCalldataRevert(encoded);
|
||||
|
||||
// On error it should return the error message for a call
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "")
|
||||
// Check that other calls return default
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000001", 10);
|
||||
assert.equal(result, false)
|
||||
|
||||
await utils.assertRevert(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10))
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
// Transaction should be successful
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10)
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenCalldataRevertWithMessage", function() {
|
||||
it("should revert if mocked and return message", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
const encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10).encodeABI();
|
||||
await mock.givenCalldataRevertWithMessage(encoded, "This is Sparta!!!");
|
||||
|
||||
// On error it should return the error message for a call
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "This is Sparta!!!")
|
||||
|
||||
await utils.assertRevert(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10))
|
||||
|
||||
// Check that other calls return default
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000001", 10);
|
||||
assert.equal(result, false)
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
// Transactions should be successful
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10)
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenCalldataRunOutOfGas", function() {
|
||||
it("should run out of gas if mocked", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
const encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10).encodeABI();
|
||||
await mock.givenCalldataRunOutOfGas(encoded);
|
||||
|
||||
// On error it should return the error message for a call
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "")
|
||||
|
||||
await utils.assertOutOfGas(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10))
|
||||
|
||||
// Check that other calls return default
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000001", 10);
|
||||
assert.equal(result, false)
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
// Transaction should be successful
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10)
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* Tests for "any" functionality
|
||||
*/
|
||||
describe("givenMethodReturn", function() {
|
||||
it("should return the mocked value", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
let methodId = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000",0).encodeABI();
|
||||
await mock.givenMethodReturn(methodId, web3.eth.abi.encodeParameter("bool", true))
|
||||
|
||||
// Check transactions and calls
|
||||
complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10)
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, true)
|
||||
|
||||
complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12)
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000001", 12)
|
||||
assert.equal(result, true)
|
||||
|
||||
// Check that we can reset mock
|
||||
await mock.reset()
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, false)
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000001", 12)
|
||||
assert.equal(result, false)
|
||||
|
||||
// Check convenience methods
|
||||
await mock.givenMethodReturnBool(methodId, true)
|
||||
result = await complex.acceptAdressUintReturnBool.call("0x0000000000000000000000000000000000000000", 10)
|
||||
assert.equal(result, true)
|
||||
|
||||
methodId = await complex.contract.methods.acceptUintReturnUint(0).encodeABI();
|
||||
await mock.givenMethodReturnUint(methodId, 42)
|
||||
result = await complex.acceptUintReturnUint.call(0);
|
||||
assert.equal(result, 42)
|
||||
|
||||
methodId = await complex.contract.methods.acceptUintReturnAddress(0).encodeABI();
|
||||
await mock.givenMethodReturnAddress(methodId, accounts[0])
|
||||
result = await complex.acceptUintReturnAddress.call(0);
|
||||
assert.equal(result, accounts[0])
|
||||
});
|
||||
|
||||
it("should mock method returning an address which can be used in `contract under test`", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
const exampleContract = await ExampleContractUnderTest.new(mock.address);
|
||||
|
||||
const methodId = await complex.contract.methods.acceptUintReturnAddress(0).encodeABI();
|
||||
await mock.givenMethodReturnAddress(methodId, accounts[0]);
|
||||
|
||||
await exampleContract.callMethodThatReturnsAddress();
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenMethodRevert", function() {
|
||||
it("should revert if mocked", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
const methodId = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000",0).encodeABI();
|
||||
await mock.givenMethodRevert(methodId);
|
||||
|
||||
// On error it should return the error message for a call
|
||||
var encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10).encodeABI();
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "")
|
||||
encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12).encodeABI();
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "")
|
||||
|
||||
await utils.assertRevert(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10))
|
||||
await utils.assertRevert(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12))
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
// Transactions should be successful
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10)
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12)
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenMethodRevertWithMessage", function() {
|
||||
it("should revert if mocked and return message", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
const methodId = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000",0).encodeABI();
|
||||
await mock.givenMethodRevertWithMessage(methodId, "This is Sparta!!!");
|
||||
|
||||
// On error it should return the error message for a call
|
||||
var encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10).encodeABI();
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "This is Sparta!!!")
|
||||
encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12).encodeABI();
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "This is Sparta!!!")
|
||||
|
||||
await utils.assertRevert(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10))
|
||||
await utils.assertRevert(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12))
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
// Transactions should be successful
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10)
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12)
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenMethodRunOutOfGas", function() {
|
||||
it("should run out of gas if mocked", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
const methodId = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000",0).encodeABI();
|
||||
await mock.givenMethodRunOutOfGas(methodId);
|
||||
|
||||
// On error it should return the error message for a call
|
||||
var encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10).encodeABI();
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "")
|
||||
encoded = await complex.contract.methods.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12).encodeABI();
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "")
|
||||
|
||||
await utils.assertOutOfGas(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10))
|
||||
await utils.assertOutOfGas(complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12))
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
// Transactions should be successful
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000000", 10)
|
||||
await complex.acceptAdressUintReturnBool("0x0000000000000000000000000000000000000001", 12)
|
||||
});
|
||||
});
|
||||
|
||||
describe("test mock priority", function() {
|
||||
|
||||
const methodId = web3.eth.abi.encodeFunctionSignature("acceptUintReturnString(uint256)");
|
||||
const testSpecificMocks = async function (mock, complex) {
|
||||
const encoded = await complex.contract.methods.acceptUintReturnString(42).encodeABI()
|
||||
await mock.givenCalldataReturn(encoded, web3.eth.abi.encodeParameter("string","return specific"));
|
||||
result = await complex.acceptUintReturnString.call(42);
|
||||
// Specific mock should be prioritized over any mock
|
||||
assert.equal(result, "return specific")
|
||||
|
||||
await mock.givenCalldataRevert(encoded);
|
||||
await utils.assertRevert(complex.acceptUintReturnString(42))
|
||||
|
||||
await mock.givenCalldataRevertWithMessage(encoded, "revert specific");
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "revert specific")
|
||||
|
||||
await mock.givenCalldataRunOutOfGas(encoded);
|
||||
await utils.assertOutOfGas(complex.acceptUintReturnString(42))
|
||||
|
||||
// Check that we can reset revert
|
||||
await mock.reset()
|
||||
// Transactions should be successful
|
||||
const response = await complex.acceptUintReturnString.call(42)
|
||||
assert.equal(response, "")
|
||||
}
|
||||
|
||||
it("all specific mocks should be prioritized over return any mock", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
// No mock set
|
||||
const response = await complex.acceptUintReturnString.call(42)
|
||||
assert.equal(response, "")
|
||||
|
||||
// Fallback mock set
|
||||
await mock.givenAnyReturn(web3.eth.abi.encodeParameter("string", "fallback"))
|
||||
let result = await complex.acceptUintReturnString.call(42)
|
||||
assert.equal(result, "fallback")
|
||||
|
||||
// MethodId mock set
|
||||
await mock.givenMethodReturn(methodId, web3.eth.abi.encodeParameter("string", "methodId"));
|
||||
result = await complex.acceptUintReturnString.call(42);
|
||||
assert.equal(result, "methodId")
|
||||
|
||||
await testSpecificMocks(mock, complex)
|
||||
});
|
||||
|
||||
it("all specific mocks should be prioritized over revert any mock", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
// No mock set
|
||||
const response = await complex.acceptUintReturnString.call(42)
|
||||
assert.equal(response, "")
|
||||
|
||||
const encoded = await complex.contract.methods.acceptUintReturnString(42).encodeABI()
|
||||
|
||||
// Fallback mock set
|
||||
await mock.givenAnyRevertWithMessage('revert fallback')
|
||||
await utils.assertRevert(complex.acceptUintReturnString(42))
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "revert fallback")
|
||||
|
||||
// MethodId mock set
|
||||
await mock.givenMethodRevertWithMessage(methodId, "revert method");
|
||||
await utils.assertRevert(complex.acceptUintReturnString(42))
|
||||
error = await utils.getErrorMessage(complex.address, 0, encoded)
|
||||
assert.equal(error, "revert method")
|
||||
|
||||
await testSpecificMocks(mock, complex)
|
||||
});
|
||||
|
||||
it("all specific mocks should be prioritized over out of gas any mock", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
// No mock set
|
||||
const response = await complex.acceptUintReturnString.call(42)
|
||||
assert.equal(response, "")
|
||||
|
||||
// Fallback mock set
|
||||
await mock.givenAnyReturn(web3.eth.abi.encodeParameter("string", "fallback"))
|
||||
result = await complex.acceptUintReturnString.call(42);
|
||||
assert.equal(result, "fallback")
|
||||
|
||||
// MethodId mock set
|
||||
await mock.givenMethodRunOutOfGas(methodId);
|
||||
await utils.assertOutOfGas(complex.acceptUintReturnString(42))
|
||||
|
||||
await testSpecificMocks(mock, complex)
|
||||
});
|
||||
});
|
||||
|
||||
describe("invocationCount", function() {
|
||||
it("returns the correct invocation count", async function() {
|
||||
const mock = await MockContract.new()
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
const calldata = await complex.contract.methods.acceptUintReturnString(42).encodeABI()
|
||||
|
||||
// Initially everything at 0
|
||||
let count = await mock.invocationCount.call()
|
||||
assert.equal(count, 0)
|
||||
|
||||
count = await mock.invocationCountForMethod.call(calldata)
|
||||
assert.equal(count, 0)
|
||||
|
||||
count = await mock.invocationCountForCalldata.call(calldata)
|
||||
assert.equal(count, 0)
|
||||
|
||||
// Make a few calls and assert count
|
||||
await complex.methodA();
|
||||
await complex.acceptUintReturnString(42);
|
||||
await complex.acceptUintReturnString(-1);
|
||||
|
||||
count = await mock.invocationCount.call()
|
||||
assert.equal(count, 3)
|
||||
|
||||
count = await mock.invocationCountForMethod.call(calldata)
|
||||
assert.equal(count, 2)
|
||||
|
||||
count = await mock.invocationCountForCalldata.call(calldata)
|
||||
assert.equal(count, 1)
|
||||
|
||||
// After reset everything at 0 again
|
||||
await mock.reset()
|
||||
count = await mock.invocationCount.call()
|
||||
assert.equal(count, 0)
|
||||
count = await mock.invocationCountForMethod.call(calldata)
|
||||
assert.equal(count, 0)
|
||||
count = await mock.invocationCountForCalldata.call(calldata)
|
||||
assert.equal(count, 0)
|
||||
});
|
||||
});
|
||||
|
||||
describe("givenMethodReturn for view functions", function() {
|
||||
it("should return the mocked value", async function() {
|
||||
const mock = await MockContract.new();
|
||||
const complex = await ComplexInterface.at(mock.address)
|
||||
|
||||
let methodId = await complex.contract.methods.acceptUintReturnUintView(0).encodeABI();
|
||||
await mock.givenMethodReturn(methodId, web3.eth.abi.encodeParameter("uint", 7))
|
||||
|
||||
result = await complex.acceptUintReturnUintView(0)
|
||||
assert.equal(result.toNumber(), 7)
|
||||
});
|
||||
});
|
||||
});
|
||||
45
account-abstraction/contracts/mock-contract/test/utils.js
Normal file
45
account-abstraction/contracts/mock-contract/test/utils.js
Normal file
@@ -0,0 +1,45 @@
|
||||
async function assertRejects(q, msg, errorPredicate) {
|
||||
let res, error = false
|
||||
try {
|
||||
res = await q
|
||||
} catch(e) {
|
||||
error = e
|
||||
} finally {
|
||||
if(!error)
|
||||
assert.fail(res, null, msg)
|
||||
else if (errorPredicate)
|
||||
errorPredicate(error)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
async function assertRevert(q) {
|
||||
const msg = "Should have reverted"
|
||||
await assertRejects(q, msg, (err) => {
|
||||
assert.ok(!err.message.includes("after consuming all gas"), msg)
|
||||
})
|
||||
}
|
||||
|
||||
async function assertOutOfGas(q) {
|
||||
const msg = "Should have run out of gas"
|
||||
await assertRejects(q, msg, (err) => {
|
||||
assert.ok(err.message.includes("after consuming all gas"), msg)
|
||||
})
|
||||
}
|
||||
|
||||
async function getErrorMessage(to, value, data, from) {
|
||||
let returnData = await web3.eth.call({to: to, from: from, value: value, data: data})
|
||||
let returnBuffer = Buffer.from(returnData.slice(2), "hex")
|
||||
if (returnBuffer.length > 4) {
|
||||
return web3.eth.abi.decodeParameter("string", returnBuffer.slice(4).toString("hex"));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(exports, {
|
||||
assertRejects,
|
||||
getErrorMessage,
|
||||
assertOutOfGas,
|
||||
assertRevert,
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
module.exports = {
|
||||
networks: {
|
||||
development: {
|
||||
host: "localhost",
|
||||
port: 8545,
|
||||
network_id: "*"
|
||||
}
|
||||
},
|
||||
compilers: {
|
||||
solc: {
|
||||
version: "^0.6.0",
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -24,7 +24,7 @@
|
||||
"url": "https://github.com/eth-infinitism/account-abstraction/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gnosis.pm/safe-contracts": "^1.3.0",
|
||||
// "@gnosis.pm/safe-contracts": "^1.3.0",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1"
|
||||
}
|
||||
|
||||
1
account-abstraction/contracts/safe-contracts
Submodule
1
account-abstraction/contracts/safe-contracts
Submodule
Submodule account-abstraction/contracts/safe-contracts added at 4b9c46fcfe
@@ -3,8 +3,8 @@ pragma solidity ^0.8.7;
|
||||
|
||||
/* solhint-disable no-inline-assembly */
|
||||
|
||||
import "@gnosis.pm/safe-contracts/contracts/handler/DefaultCallbackHandler.sol";
|
||||
import "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
|
||||
import "../../safe-contracts/contracts/handler/DefaultCallbackHandler.sol";
|
||||
import "../../safe-contracts/contracts/Safe.sol";
|
||||
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
|
||||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
import "../../interfaces/IAccount.sol";
|
||||
@@ -13,8 +13,8 @@ import "./EIP4337Manager.sol";
|
||||
using ECDSA for bytes32;
|
||||
|
||||
/**
|
||||
* The GnosisSafe enables adding custom functions implementation to the Safe by setting a 'fallbackHandler'.
|
||||
* This 'fallbackHandler' adds an implementation of 'validateUserOp' to the GnosisSafe.
|
||||
* The Safe enables adding custom functions implementation to the Safe by setting a 'fallbackHandler'.
|
||||
* This 'fallbackHandler' adds an implementation of 'validateUserOp' to the Safe.
|
||||
* Note that the implementation of the 'validateUserOp' method is located in the EIP4337Manager.
|
||||
* Upon receiving the 'validateUserOp', a Safe with EIP4337Fallback enabled makes a 'delegatecall' to EIP4337Manager.
|
||||
*/
|
||||
@@ -31,8 +31,8 @@ contract EIP4337Fallback is DefaultCallbackHandler, IAccount, IERC1271 {
|
||||
*/
|
||||
function delegateToManager() internal returns (bytes memory) {
|
||||
// delegate entire msg.data (including the appended "msg.sender") to the EIP4337Manager
|
||||
// will work only for GnosisSafe contracts
|
||||
GnosisSafe safe = GnosisSafe(payable(msg.sender));
|
||||
// will work only for Safe contracts
|
||||
Safe safe = Safe(payable(msg.sender));
|
||||
(bool success, bytes memory ret) = safe.execTransactionFromModuleReturnData(eip4337manager, 0, msg.data, Enum.Operation.DelegateCall);
|
||||
if (!success) {
|
||||
assembly {
|
||||
@@ -73,11 +73,11 @@ contract EIP4337Fallback is DefaultCallbackHandler, IAccount, IERC1271 {
|
||||
function isValidSignature(
|
||||
bytes32 _hash,
|
||||
bytes memory _signature
|
||||
) external override view returns (bytes4) {
|
||||
) external override (IERC1271) view returns (bytes4) {
|
||||
bytes32 hash = _hash.toEthSignedMessageHash();
|
||||
address recovered = hash.recover(_signature);
|
||||
|
||||
GnosisSafe safe = GnosisSafe(payable(address(msg.sender)));
|
||||
Safe safe = Safe(payable(address(msg.sender)));
|
||||
|
||||
// Validate signatures
|
||||
if (safe.isOwner(recovered)) {
|
||||
|
||||
@@ -6,9 +6,9 @@ pragma solidity ^0.8.7;
|
||||
/* solhint-disable reason-string */
|
||||
|
||||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
import "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
|
||||
import "@gnosis.pm/safe-contracts/contracts/base/Executor.sol";
|
||||
import "@gnosis.pm/safe-contracts/contracts/examples/libraries/GnosisSafeStorage.sol";
|
||||
import "../../safe-contracts/contracts/Safe.sol";
|
||||
import "../../safe-contracts/contracts/base/Executor.sol";
|
||||
import "../../safe-contracts/contracts/examples/libraries/Migrate_1_3_0_to_1_2_0.sol";
|
||||
import "./EIP4337Fallback.sol";
|
||||
import "../../interfaces/IAccount.sol";
|
||||
import "../../interfaces/IEntryPoint.sol";
|
||||
@@ -24,7 +24,7 @@ import "./Verifiers.sol";
|
||||
* holds an immutable reference to the EntryPoint
|
||||
* Inherits GnosisSafe so that it can reference the memory storage
|
||||
*/
|
||||
contract EIP4337Manager is IAccount, GnosisSafeStorage, Executor {
|
||||
contract EIP4337Manager is IAccount, SafeStorage, Executor {
|
||||
|
||||
address public immutable eip4337Fallback;
|
||||
address public immutable entryPoint;
|
||||
@@ -96,7 +96,7 @@ contract EIP4337Manager is IAccount, GnosisSafeStorage, Executor {
|
||||
require(threshold == 1, "account: only threshold 1");
|
||||
bytes calldata ecdsaSignature = verificationData[1:];
|
||||
if (!ecdsaVerifier.verify(
|
||||
GnosisSafe(payable(address(this))),
|
||||
Safe(payable(address(this))),
|
||||
userOpHash,
|
||||
ecdsaSignature
|
||||
)) {
|
||||
@@ -160,7 +160,7 @@ contract EIP4337Manager is IAccount, GnosisSafeStorage, Executor {
|
||||
function setup4337Modules(
|
||||
EIP4337Manager manager //the manager (this contract)
|
||||
) external {
|
||||
GnosisSafe safe = GnosisSafe(payable(address(this)));
|
||||
Safe safe = Safe(payable(address(this)));
|
||||
require(!safe.isModuleEnabled(manager.entryPoint()), "setup4337Modules: entrypoint already enabled");
|
||||
require(!safe.isModuleEnabled(manager.eip4337Fallback()), "setup4337Modules: eip4337Fallback already enabled");
|
||||
safe.enableModule(manager.entryPoint());
|
||||
@@ -175,7 +175,7 @@ contract EIP4337Manager is IAccount, GnosisSafeStorage, Executor {
|
||||
* @param newManager the new EIP4337Manager, usually with a new EntryPoint
|
||||
*/
|
||||
function replaceEIP4337Manager(address prevModule, EIP4337Manager oldManager, EIP4337Manager newManager) public {
|
||||
GnosisSafe pThis = GnosisSafe(payable(address(this)));
|
||||
Safe pThis = Safe(payable(address(this)));
|
||||
address oldFallback = oldManager.eip4337Fallback();
|
||||
require(pThis.isModuleEnabled(oldFallback), "replaceEIP4337Manager: oldManager is not active");
|
||||
pThis.disableModule(oldFallback, oldManager.entryPoint());
|
||||
@@ -195,7 +195,7 @@ contract EIP4337Manager is IAccount, GnosisSafeStorage, Executor {
|
||||
* the test is might be incomplete: we check that we reach our validateUserOp and fail on signature.
|
||||
* we don't test full transaction
|
||||
*/
|
||||
function validateEip4337(GnosisSafe safe, EIP4337Manager manager) public {
|
||||
function validateEip4337(Safe safe, EIP4337Manager manager) public {
|
||||
|
||||
// this prevents mistaken replaceEIP4337Manager to disable the module completely.
|
||||
// minimal signature that pass "recover"
|
||||
@@ -222,7 +222,7 @@ contract EIP4337Manager is IAccount, GnosisSafeStorage, Executor {
|
||||
* @return prev prev module, needed by replaceEIP4337Manager
|
||||
* @return manager the current active EIP4337Manager
|
||||
*/
|
||||
function getCurrentEIP4337Manager(GnosisSafe safe) public view returns (address prev, address manager) {
|
||||
function getCurrentEIP4337Manager(Safe safe) public view returns (address prev, address manager) {
|
||||
prev = address(SENTINEL_MODULES);
|
||||
(address[] memory modules,) = safe.getModulesPaginated(SENTINEL_MODULES, 100);
|
||||
for (uint i = 0; i < modules.length; i++) {
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
pragma solidity ^0.8.12;
|
||||
|
||||
import "@openzeppelin/contracts/utils/Create2.sol";
|
||||
import "@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol";
|
||||
import "../../safe-contracts/contracts/proxies/SafeProxyFactory.sol";
|
||||
import "./EIP4337Manager.sol";
|
||||
|
||||
/**
|
||||
* A wrapper factory contract to deploy GnosisSafe as an ERC-4337 account contract.
|
||||
*/
|
||||
contract GnosisSafeAccountFactory {
|
||||
contract SafeAccountFactory {
|
||||
|
||||
GnosisSafeProxyFactory public immutable proxyFactory;
|
||||
SafeProxyFactory public immutable proxyFactory;
|
||||
address public immutable safeSingleton;
|
||||
EIP4337Manager public immutable eip4337Manager;
|
||||
|
||||
constructor(GnosisSafeProxyFactory _proxyFactory, address _safeSingleton, EIP4337Manager _eip4337Manager) {
|
||||
constructor(SafeProxyFactory _proxyFactory, address _safeSingleton, EIP4337Manager _eip4337Manager) {
|
||||
proxyFactory = _proxyFactory;
|
||||
safeSingleton = _safeSingleton;
|
||||
eip4337Manager = _eip4337Manager;
|
||||
@@ -39,7 +39,7 @@ contract GnosisSafeAccountFactory {
|
||||
bytes memory setup4337Modules = abi.encodeCall(
|
||||
EIP4337Manager.setup4337Modules, (eip4337Manager));
|
||||
|
||||
return abi.encodeCall(GnosisSafe.setup, (
|
||||
return abi.encodeCall(Safe.setup, (
|
||||
owners, threshold,
|
||||
address (eip4337Manager), setup4337Modules,
|
||||
eip4337fallback,
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
//SPDX-License-Identifier: GPL
|
||||
pragma solidity ^0.8.15;
|
||||
|
||||
import "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
|
||||
import "@gnosis.pm/safe-contracts/contracts/examples/libraries/GnosisSafeStorage.sol";
|
||||
import "../../safe-contracts/contracts/Safe.sol";
|
||||
import "../../safe-contracts/contracts/examples/libraries/Migrate_1_3_0_to_1_2_0.sol";
|
||||
|
||||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
import "./EIP4337Manager.sol";
|
||||
|
||||
interface IVerifier {
|
||||
function verify(
|
||||
GnosisSafe safe,
|
||||
Safe safe,
|
||||
bytes32 hash,
|
||||
bytes calldata verificationData
|
||||
) external view returns (bool);
|
||||
}
|
||||
|
||||
contract ECDSAVerifier is IVerifier, GnosisSafeStorage {
|
||||
contract ECDSAVerifier is IVerifier, SafeStorage {
|
||||
|
||||
using ECDSA for bytes32;
|
||||
|
||||
function verify(
|
||||
GnosisSafe safe,
|
||||
Safe safe,
|
||||
bytes32 hash,
|
||||
bytes calldata ecdsaSignature
|
||||
) public view returns (bool) {
|
||||
@@ -30,7 +30,7 @@ contract ECDSAVerifier is IVerifier, GnosisSafeStorage {
|
||||
}
|
||||
}
|
||||
|
||||
contract BLSGroupVerifier is IVerifier, GnosisSafeStorage {
|
||||
contract BLSGroupVerifier is IVerifier, SafeStorage {
|
||||
uint8 public constant BLS_KEY_LEN = 4;
|
||||
uint256[BLS_KEY_LEN][] public groupMembers;
|
||||
|
||||
@@ -43,7 +43,7 @@ contract BLSGroupVerifier is IVerifier, GnosisSafeStorage {
|
||||
}
|
||||
|
||||
function verify(
|
||||
GnosisSafe safe,
|
||||
Safe safe,
|
||||
bytes32 hash,
|
||||
bytes calldata ecdsaSignature
|
||||
) public pure returns (bool) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -38,6 +38,12 @@ const optimizedComilerSettings = {
|
||||
const config: HardhatUserConfig = {
|
||||
solidity: {
|
||||
compilers: [{
|
||||
version: '0.7.6',
|
||||
settings: {
|
||||
optimizer: { enabled: true, runs: 1000000 }
|
||||
}
|
||||
},
|
||||
{
|
||||
version: '0.8.15',
|
||||
settings: {
|
||||
optimizer: { enabled: true, runs: 1000000 }
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"typechain": "^8.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gnosis.pm/safe-contracts": "^1.3.0",
|
||||
"@gnosis.pm/safe-singleton-factory": "^1.0.3",
|
||||
"@nomiclabs/hardhat-etherscan": "^2.1.6",
|
||||
"@openzeppelin/contracts": "^4.2.0",
|
||||
"@thehubbleproject/bls": "^0.5.1",
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
EIP4337Manager__factory,
|
||||
EntryPoint,
|
||||
EntryPoint__factory,
|
||||
GnosisSafe,
|
||||
GnosisSafeAccountFactory,
|
||||
GnosisSafeAccountFactory__factory,
|
||||
GnosisSafeProxy,
|
||||
GnosisSafeProxyFactory__factory,
|
||||
GnosisSafe__factory,
|
||||
Safe,
|
||||
SafeAccountFactory,
|
||||
SafeAccountFactory__factory,
|
||||
SafeProxy,
|
||||
SafeProxyFactory__factory,
|
||||
Safe__factory,
|
||||
TestCounter,
|
||||
TestCounter__factory
|
||||
} from '../typechain'
|
||||
@@ -33,17 +33,17 @@ describe.only('Gnosis Proxy', function () {
|
||||
this.timeout(30000)
|
||||
|
||||
let ethersSigner: Signer
|
||||
let safeSingleton: GnosisSafe
|
||||
let safeSingleton: Safe
|
||||
let owner: Signer
|
||||
let ownerAddress: string
|
||||
let proxy: GnosisSafeProxy
|
||||
let proxy: SafeProxy
|
||||
let manager: EIP4337Manager
|
||||
let entryPoint: EntryPoint
|
||||
let counter: TestCounter
|
||||
let proxySafe: GnosisSafe
|
||||
let proxySafe: Safe
|
||||
let safe_execTxCallData: string
|
||||
|
||||
let accountFactory: GnosisSafeAccountFactory
|
||||
let accountFactory: SafeAccountFactory
|
||||
|
||||
before('before', async function () {
|
||||
// EIP4337Manager fails to compile with solc-coverage
|
||||
@@ -55,16 +55,16 @@ describe.only('Gnosis Proxy', function () {
|
||||
ethersSigner = provider.getSigner()
|
||||
|
||||
// standard safe singleton contract (implementation)
|
||||
safeSingleton = await new GnosisSafe__factory(ethersSigner).deploy()
|
||||
safeSingleton = await new Safe__factory(ethersSigner).deploy()
|
||||
// standard safe proxy factory
|
||||
const proxyFactory = await new GnosisSafeProxyFactory__factory(ethersSigner).deploy()
|
||||
const proxyFactory = await new SafeProxyFactory__factory(ethersSigner).deploy()
|
||||
entryPoint = await deployEntryPoint()
|
||||
manager = await new EIP4337Manager__factory(ethersSigner).deploy(entryPoint.address)
|
||||
owner = createAccountOwner()
|
||||
ownerAddress = await owner.getAddress()
|
||||
counter = await new TestCounter__factory(ethersSigner).deploy()
|
||||
|
||||
accountFactory = await new GnosisSafeAccountFactory__factory(ethersSigner)
|
||||
accountFactory = await new SafeAccountFactory__factory(ethersSigner)
|
||||
.deploy(proxyFactory.address, safeSingleton.address, manager.address)
|
||||
|
||||
await accountFactory.createAccount(ownerAddress, 0)
|
||||
@@ -74,7 +74,7 @@ describe.only('Gnosis Proxy', function () {
|
||||
const addr = ev[0].args.proxy
|
||||
|
||||
proxy =
|
||||
proxySafe = GnosisSafe__factory.connect(addr, owner)
|
||||
proxySafe = Safe__factory.connect(addr, owner)
|
||||
|
||||
await ethersSigner.sendTransaction({
|
||||
to: proxy.address,
|
||||
|
||||
@@ -471,10 +471,10 @@
|
||||
"@ethersproject/properties" "^5.7.0"
|
||||
"@ethersproject/strings" "^5.7.0"
|
||||
|
||||
"@gnosis.pm/safe-contracts@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-contracts/-/safe-contracts-1.3.0.tgz#316741a7690d8751a1f701538cfc9ec80866eedc"
|
||||
integrity sha512-1p+1HwGvxGUVzVkFjNzglwHrLNA67U/axP0Ct85FzzH8yhGJb4t9jDjPYocVMzLorDoWAfKicGy1akPY9jXRVw==
|
||||
"@gnosis.pm/safe-singleton-factory@^1.0.3":
|
||||
version "1.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-singleton-factory/-/safe-singleton-factory-1.0.14.tgz#42dae9a91fda21b605f94bfe310a7fccc6a4d738"
|
||||
integrity sha512-xZ26c9uKzpd5Sm8ux0sZHt5QC8n+Q2z1/X5xjPnd8aT5EcKH5t1GgLbAqjrMFmXVIOkiWSc7wi2Bj4XfgtiyaQ==
|
||||
|
||||
"@humanwhocodes/config-array@^0.11.8":
|
||||
version "0.11.8"
|
||||
|
||||
Reference in New Issue
Block a user