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

EIP-6551 compat #27

Merged
merged 43 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
23c3a86
upgrade to latest account-abstraction contracts
jfschwarz Apr 26, 2023
f76da22
fix package json
jfschwarz May 10, 2023
4170293
adjust tests
jfschwarz May 10, 2023
4e7cf06
trial to fix vercel routes
jfschwarz May 10, 2023
217223d
fix deploy script for gnosis chain
jfschwarz May 11, 2023
6e53bb0
implement IERC6551Account
jfschwarz May 30, 2023
3d6d79d
restructure contract inheritance hierarchy
jfschwarz Jun 7, 2023
203d1d2
add mech factory
jfschwarz Jun 7, 2023
1f67b0c
refactor contracts
jfschwarz Jun 12, 2023
de98dec
add erc20 threshold mech
jfschwarz Jun 12, 2023
c749149
cleanup
jfschwarz Jun 12, 2023
e264243
update docs
jfschwarz Jun 12, 2023
06d0971
adjust all deploy functions and migrate them to viem
jfschwarz Jun 14, 2023
b608faa
add hierarchy graph
jfschwarz Jun 20, 2023
139d356
adding exports
jfschwarz Jul 13, 2023
f8436f9
Merge branch 'main' into erc-6551
jfschwarz Aug 18, 2023
52484d9
fixes after merge
jfschwarz Aug 18, 2023
cdab142
upgrade deps
jfschwarz Aug 18, 2023
98c79c1
6551 updates and prevent ownership cycles
jfschwarz Aug 21, 2023
eb19c39
fine-tune execution interface
jfschwarz Aug 21, 2023
81dbd52
upgrade tooling
jfschwarz Aug 21, 2023
00304e4
ethers v5 -> v6 migration
jfschwarz Aug 21, 2023
8d249e6
erc165
jfschwarz Aug 21, 2023
a5a32fe
tokenbound deterministic deployment test green
jfschwarz Aug 25, 2023
cd7e95d
update 4337 entrypoint address
jfschwarz Aug 25, 2023
4167db5
fix 4337 tests
jfschwarz Aug 25, 2023
f399e1d
fix missing onlyOperator annotation
jfschwarz Aug 25, 2023
ee82d3f
improve test coverage
jfschwarz Aug 25, 2023
c2f8e3c
test fixes
jfschwarz Aug 28, 2023
a39dc16
front-end updates
jfschwarz Aug 28, 2023
5186a9c
fix deploy scripts
jfschwarz Aug 28, 2023
9fe7e05
migrate from n.xyz to sequencer
jfschwarz Sep 1, 2023
9f371eb
Merge pull request #28 from gnosis/sequencer
jfschwarz Sep 1, 2023
c57769b
deploy 6551 factory
jfschwarz Sep 4, 2023
3198123
more solid deploy script
jfschwarz Sep 4, 2023
1381bd4
fix wallet connect
jfschwarz Sep 4, 2023
63edc04
fix a JS error when switching chains
jfschwarz Oct 12, 2023
7354c08
work around issue of tokenID not being set by sequence api
jfschwarz Oct 12, 2023
0c7cc8c
fix walletconnect v2 issues
jfschwarz Oct 12, 2023
3ca7ec0
fix a ux issue
jfschwarz Oct 12, 2023
bf94f2e
upgrade to latest 6551 contract and adjust accordingly
jfschwarz Oct 13, 2023
11e9b4d
fix tests
jfschwarz Oct 13, 2023
39ef6c7
update eip6551 registry address
jfschwarz Oct 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fine-tune execution interface
  • Loading branch information
jfschwarz committed Aug 21, 2023
commit eb19c39c5b9854e78fde16db1f57f60d63e9bf57
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,18 @@ Mech implements the [EIP-4337](https://eips.ethereum.org/EIPS/eip-4337) account
Returns true if `signer` is allowed to operate the Mech.
Sub classes implement this function for defining the specific operator criteria.

### `exec(address to, uint256 value, bytes data, Enum.Operation operation, uint256 txGas)`
### `execute(address to, uint256 value, bytes data, Enum.Operation operation)`

Allows the operator to make the Mech execute a transaction.

- `operation: 0` for a regular call
- `operation: 1` for a delegate call

### `execute(address to, uint256 value, bytes data, Enum.Operation operation, uint256 txGas)`

Allows the operator to make the Mech execute a transaction restricting the gas amount made available to the direct execution of the internal meta transaction.
Any remaining transaction gas must only be spent for surrounding checks of the operator criteria.

## Contribute

The repo is structured as a monorepo with `mech-contracts` as the container package exporting the contract sources and artifacts.
Expand Down Expand Up @@ -91,7 +96,7 @@ Integration tests are run on a mainnet fork and cover the interaction of mech co

Mech implements the EIP-4337 [Account](contracts/base/Account.sol) interface meaning they allow bundlers to execute account-abstracted user operations from the Mech's address.
For this purpose the EIP-4337 entry point contract first calls the Mech's `validateUserOp()` function for checking if a user operation has a valid signature by the mech operator.
The entry point then calls the `exec` function, or any other function using the `onlyOperator` modifier, to trigger execution.
The entry point then calls the `execute` function, or any other function using the `onlyOperator` modifier, to trigger execution.

### EIP-1271 signatures

Expand Down
8 changes: 4 additions & 4 deletions contracts/ERC1155TokenboundMech.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ contract ERC1155TokenboundMech is TokenboundMech {
}

function onERC1155Received(
address operator,
address,
address from,
uint256 receivedTokenId,
uint256 value,
uint256,
bytes calldata
) external view override returns (bytes4) {
(
Expand All @@ -46,10 +46,10 @@ contract ERC1155TokenboundMech is TokenboundMech {
}

function onERC1155BatchReceived(
address operator,
address,
address from,
uint256[] calldata ids,
uint256[] calldata values,
uint256[] calldata,
bytes calldata
) external view override returns (bytes4) {
(
Expand Down
4 changes: 2 additions & 2 deletions contracts/ERC721TokenboundMech.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ contract ERC721TokenboundMech is TokenboundMech {
}

function onERC721Received(
address operator,
address from,
address,
address,
uint256 receivedTokenId,
bytes calldata
) external view override returns (bytes4) {
Expand Down
31 changes: 27 additions & 4 deletions contracts/base/Mech.sol
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ abstract contract Mech is IMech, Account, Receiver {
function _exec(
address to,
uint256 value,
bytes memory data,
bytes calldata data,
Enum.Operation operation,
uint256 txGas
) internal returns (bool success, bytes memory returnData) {
Expand All @@ -111,13 +111,13 @@ abstract contract Mech is IMech, Account, Receiver {
/// @param operation Operation type of transaction.
/// @param txGas Gas to send for executing the meta transaction, if 0 all left will be sent
/// @return returnData Return data of the call
function exec(
function execute(
address to,
uint256 value,
bytes memory data,
bytes calldata data,
Enum.Operation operation,
uint256 txGas
) public onlyOperator returns (bytes memory returnData) {
) public payable onlyOperator returns (bytes memory returnData) {
bool success;
(success, returnData) = _exec(
to,
Expand All @@ -135,6 +135,29 @@ abstract contract Mech is IMech, Account, Receiver {
}
}

/// @dev Allows the mech operator to execute arbitrary transactions
/// @param to Destination address of transaction.
/// @param value Ether value of transaction.
/// @param data Data payload of transaction.
/// @param operation Operation type of transaction.
/// @return returnData Return data of the call
function execute(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation
) external payable returns (bytes memory returnData) {
bool success;
(success, returnData) = _exec(to, value, data, operation, gasleft());

if (!success) {
// solhint-disable-next-line no-inline-assembly
assembly {
revert(add(returnData, 0x20), mload(returnData))
}
}
}

/**
* @dev Divides bytes signature into `uint8 v, bytes32 r, bytes32 s`.
* @param signature The signature bytes
Expand Down
6 changes: 3 additions & 3 deletions contracts/base/Receiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract Receiver is
uint256,
uint256,
bytes calldata
) external view override returns (bytes4) {
) external view virtual override returns (bytes4) {
return 0xf23a6e61;
}

Expand All @@ -31,7 +31,7 @@ contract Receiver is
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external view override returns (bytes4) {
) external view virtual override returns (bytes4) {
return 0xbc197c81;
}

Expand All @@ -40,7 +40,7 @@ contract Receiver is
address,
uint256,
bytes calldata
) external view override returns (bytes4) {
) external view virtual override returns (bytes4) {
return 0x150b7a02;
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/base/TokenboundMech.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ abstract contract TokenboundMech is Mech, IERC6551Account {
receive() external payable override(Receiver, IERC6551Account) {}

/**
* @dev EIP-6551 compatibility: Returns a magic value indicating whether a given signer is authorized to act on behalf
* @dev Returns a magic value indicating whether a given signer is authorized to act on behalf
* of the account
* @param signer The address to check signing authorization for
* @return magicValue Magic value indicating whether the signer is valid
Expand Down
22 changes: 18 additions & 4 deletions contracts/interfaces/IMech.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
pragma solidity ^0.8.12;

import "@openzeppelin/contracts/interfaces/IERC1271.sol";
import "@erc6551/reference/src/interfaces/IERC6551Executable.sol";
import "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import "@account-abstraction/contracts/interfaces/IAccount.sol";

interface IMech is IAccount, IERC1271 {
interface IMech is IAccount, IERC1271 /*, IERC6551Executable */ {
/**
* @dev Return if the passed address is authorized to sign on behalf of the mech, must be implemented by the child contract
* @param signer The address to check
Expand All @@ -17,13 +18,26 @@ interface IMech is IAccount, IERC1271 {
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @return returnData bytes The return data of the call
function execute(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation
) external payable returns (bytes memory returnData);

/// @dev Executes either a delegatecall or a call with provided parameters, with a specified gas limit for the meta transaction
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param txGas Gas to send for executing the meta transaction, if 0 all left will be sent
/// @return returnData bytes The return data of the call
function exec(
function execute(
address to,
uint256 value,
bytes memory data,
bytes calldata data,
Enum.Operation operation,
uint256 txGas
) external returns (bytes memory returnData);
) external payable returns (bytes memory returnData);
}
2 changes: 1 addition & 1 deletion contracts/test/ERC1155Token.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ contract ERC1155Token is ERC1155("ERC1155Token") {
address recipient,
uint256 id,
uint256 amount,
bytes memory data
bytes calldata data
) public {
_mint(recipient, id, amount, data);
}
Expand Down
6 changes: 3 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
"react-dom": "^18.2.0",
"react-router-dom": "^6.15.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"typescript-plugin-css-modules": "^4.2.3",
"viem": "^1.6.0",
"typescript": "^5.1.6",
"typescript-plugin-css-modules": "^5.0.1",
"viem": "^1.6.4",
"wagmi": "^1.3.10"
},
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions integrationTest/SafeMigration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe("Safe migration", () => {

// sanity check making sure the Gnosis DAO Safe is configured as expected
expect(await safe.isModuleEnabled(enabledModule)).to.be.true
await expect(zodiacMech.exec(ZERO_ADDRESS, parseEther("1.0"), "0x", 0, 0))
await expect(zodiacMech.execute(ZERO_ADDRESS, parseEther("1.0"), "0x", 0))
.to.be.reverted

// deploy ZodiacMech mastercopy
Expand Down Expand Up @@ -76,7 +76,7 @@ describe("Safe migration", () => {

// make sure the Safe is now a ZodiacMech
await expect(
zodiacMech.exec(ZERO_ADDRESS, parseEther("1.0"), "0x", 0, 0)
zodiacMech.execute(ZERO_ADDRESS, parseEther("1.0"), "0x", 0)
).to.changeEtherBalance(safeAddress, parseEther("-1.0"))

// the enabled modules did not change
Expand Down
4 changes: 2 additions & 2 deletions sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"homepage": "https://github.com/gnosis/mech#readme",
"devDependencies": {
"rimraf": "^4.4.1",
"typescript": "^4.9.5"
"typescript": "^5.1.6"
},
"dependencies": {
"@gnosis.pm/safe-contracts": "^1.3.0",
Expand All @@ -42,7 +42,7 @@
"@types/chai": "^4.3.5",
"@types/node": "^18.17.5",
"ethers": "^5.7.2",
"viem": "^1.6.0"
"viem": "^1.6.4"
},
"packageManager": "[email protected]"
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface TransactionRequest {
nonce?: number
}

export const makeExecTransaction = (
export const makeExecuteTransaction = (
mechAddress: `0x${string}`,
transaction: TransactionRequest
) => {
Expand All @@ -36,7 +36,7 @@ export const makeExecTransaction = (
// gas for mech's onlyOperator modifier still needs to be calculated (can't be fixed, since it depends on external ERC721 ownerOf() function)
gasLimit: undefined,

data: IMech.encodeFunctionData("exec", [
data: IMech.encodeFunctionData("execute()", [
to || "",
value || 0,
data || "0x",
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./deploy"
export * from "./exec"
export * from "./execute"
export * from "./sign"
4 changes: 2 additions & 2 deletions test/Account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ describe("Account base contract", () => {
}

const BURN_1_ETH = Mech__factory.createInterface().encodeFunctionData(
"exec",
[ZERO_ADDRESS, parseEther("1.0"), "0x", 0, 0]
"execute",
[ZERO_ADDRESS, parseEther("1.0"), "0x", 0]
)

describe("validateUserOp()", () => {
Expand Down
20 changes: 11 additions & 9 deletions test/Mech.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ describe("Mech base contract", () => {
})
})

describe("exec()", () => {
describe("execute()", () => {
it("reverts if called when the connected token ID is not valid", async () => {
const { mech1, testToken, alice } = await loadFixture(deployMech1)

Expand All @@ -173,7 +173,7 @@ describe("Mech base contract", () => {
)

await expect(
mech1.exec(testToken.address, 0, testTx.data as string, 0, 0)
mech1.execute(testToken.address, 0, testTx.data as string, 0)
).to.be.revertedWith("ERC721: invalid token ID")
})

Expand All @@ -193,7 +193,7 @@ describe("Mech base contract", () => {

// call exec() from deployer who is not an operator
await expect(
mech1.exec(testToken.address, 0, testTx.data as string, 0, 0)
mech1.execute(testToken.address, 0, testTx.data as string, 0)
).to.be.revertedWith(
"Only callable by the mech operator or the entry point contract"
)
Expand All @@ -212,7 +212,9 @@ describe("Mech base contract", () => {
)

await expect(
mech1.connect(alice).exec(testToken.address, 0, revertingTxData, 0, 0)
mech1
.connect(alice)
.execute(testToken.address, 0, revertingTxData, 0, 0)
).to.be.revertedWith("ERC721: caller is not token owner or approved")
})

Expand All @@ -227,7 +229,7 @@ describe("Mech base contract", () => {

const result = await mech1
.connect(alice)
.callStatic.exec(testToken.address, 0, callData, 0, 0)
.callStatic.execute(testToken.address, 0, callData, 0)
const decoded = testToken.interface.decodeFunctionResult(
"ownerOf",
result
Expand All @@ -249,12 +251,12 @@ describe("Mech base contract", () => {

// this should revert because the call is not a delegate call
await expect(
mech1.connect(alice).exec(delegateCall.address, 0, callData, 0, 0)
mech1.connect(alice).execute(delegateCall.address, 0, callData, 0)
).to.be.revertedWith("Can only be called via delegatecall")

// this should succeed because the call is a delegate call
await expect(
mech1.connect(alice).exec(delegateCall.address, 0, callData, 1, 0)
mech1.connect(alice).execute(delegateCall.address, 0, callData, 1)
).to.not.be.reverted
})

Expand Down Expand Up @@ -284,7 +286,7 @@ describe("Mech base contract", () => {
// send just enough gas to meta tx -> gas estimation succeed
const mechTxGas = await mech1
.connect(alice)
.estimateGas.exec(
.estimateGas.execute(
testToken.address,
0,
testToken.interface.encodeFunctionData("transferFrom", [
Expand All @@ -298,7 +300,7 @@ describe("Mech base contract", () => {

// send too little gas to the meta tx -> tx fails
await expect(
mech1.connect(alice).exec(
mech1.connect(alice).execute(
testToken.address,
0,
testToken.interface.encodeFunctionData("transferFrom", [
Expand Down
3 changes: 1 addition & 2 deletions test/Receiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,13 @@ describe("Receiver base contract", () => {

await mech1
.connect(alice)
.exec(
.execute(
test20Token.address,
0,
test20Token.interface.encodeFunctionData("transfer", [
alice.address,
500,
]),
0,
0
)

Expand Down
2 changes: 1 addition & 1 deletion test/ZodiacMech.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ describe("ZodiacMech contract", () => {
// value: parseEther("1.0"),
// })

// const BURN_1_ETH = mech1.interface.encodeFunctionData("exec", [
// const BURN_1_ETH = mech1.interface.encodeFunctionData("execute", [
// ZERO_ADDRESS,
// parseEther("1.0"),
// "0x",
Expand Down