Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create Extension4337 #735

Merged
merged 17 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/build-lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ jobs:
"lsp11",
"lsp11init",
"lsp17",
"lsp17extensions",
"lsp20",
"lsp20init",
"lsp23",
Expand Down
100 changes: 100 additions & 0 deletions contracts/LSP17Extensions/Extension4337.sol
skimaharvey marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.9;

// interfaces
import {IAccount} from "@account-abstraction/contracts/interfaces/IAccount.sol";
import {
IERC725Y
} from "@erc725/smart-contracts/contracts/interfaces/IERC725Y.sol";
import {
ILSP20CallVerifier
} from "../LSP20CallVerification/ILSP20CallVerifier.sol";

// modules
import {LSP14Ownable2Step} from "../LSP14Ownable2Step/LSP14Ownable2Step.sol";
import {LSP17Extension} from "../LSP17ContractExtension/LSP17Extension.sol";

// librairies
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {LSP6Utils} from "../LSP6KeyManager/LSP6Utils.sol";

// constants
import {
UserOperation
} from "@account-abstraction/contracts/interfaces/UserOperation.sol";

contract Extension4337 is LSP17Extension, IAccount {
using ECDSA for bytes32;
using LSP6Utils for *;

address internal immutable _ENTRY_POINT;

// permission needed to be able to use this extension
bytes32 internal constant _4337_PERMISSION =
0x0000000000000000000000000000000000000000000000000000000000800000;
skimaharvey marked this conversation as resolved.
Show resolved Hide resolved

// error code returned when signature or permission validation fails
uint256 internal constant _SIG_VALIDATION_FAILED = 1;

constructor(address entryPoint_) {
_ENTRY_POINT = entryPoint_;
}

/**
* @inheritdoc IAccount
*/
function validateUserOp(
skimaharvey marked this conversation as resolved.
Show resolved Hide resolved
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 /* missingAccountFunds */
) external returns (uint256) {
require(
_extendableMsgSender() == _ENTRY_POINT,
"Only EntryPoint contract can call this"
);

// recover initiator of the tx from the signature
bytes32 hash = userOpHash.toEthSignedMessageHash();
address recovered = hash.recover(userOp.signature);

// verify that the recovered address has the _4337_PERMISSION
if (
!LSP6Utils.hasPermission(
IERC725Y(msg.sender).getPermissionsFor(recovered),
_4337_PERMISSION
)
) {
return _SIG_VALIDATION_FAILED;
}

// retrieve owner from caller
address owner = LSP14Ownable2Step(msg.sender).owner();

// verify that the recovered address can execute the userOp.callData
bytes4 magicValue = ILSP20CallVerifier(owner).lsp20VerifyCall({
callee: msg.sender,
caller: recovered,
value: 0,
receivedCalldata: userOp.callData
});

// if the call verifier returns a different magic value, return signature validation failed
if (
bytes3(magicValue) !=
bytes3(ILSP20CallVerifier.lsp20VerifyCall.selector)
) {
return _SIG_VALIDATION_FAILED;
}

skimaharvey marked this conversation as resolved.
Show resolved Hide resolved
// if sig validation passed, return 0
return 0;
}

/**
* @dev Get the address of the Entry Point contract that will execute the user operation.
* @return The address of the EntryPoint contract
*/
function entryPoint() public view returns (address) {
return _ENTRY_POINT;
}
}
15 changes: 15 additions & 0 deletions contracts/LSP17Extensions/OnERC721ReceivedExtension.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.4;

import {
ERC721Holder
} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";

/**
* @dev LSP17 Extension that can be attached to a LSP17Extendable contract
* to allow it to receive ERC721 tokens via `safeTransferFrom`.
*/
// solhint-disable-next-line no-empty-blocks
contract OnERC721ReceivedExtension is ERC721Holder {

}
2 changes: 1 addition & 1 deletion contracts/LSP2ERC725YJSONSchema/LSP2Utils.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-3.0
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.4;

// interfaces
Expand Down
16 changes: 0 additions & 16 deletions contracts/Mocks/FallbackExtensions/OnERC721ReceivedExtension.sol

This file was deleted.

2 changes: 1 addition & 1 deletion contracts/Mocks/Tokens/RequireCallbackToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.4;

import {
OnERC721ReceivedExtension
} from "../FallbackExtensions/OnERC721ReceivedExtension.sol";
} from "../../LSP17Extensions/OnERC721ReceivedExtension.sol";

/**
* @dev This contract is used only for testing purposes
Expand Down
173 changes: 173 additions & 0 deletions docs/contracts/LSP17Extensions/Extension4337.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<!-- This file is auto-generated. Do not edit! -->
<!-- Check `@lukso-network/lsp-smart-contracts/CONTRIBUTING.md#solidity-code-comments` for more information. -->

# Extension4337

:::info Standard Specifications

[`LSP-17-Extensions`](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md)

:::
:::info Solidity implementation

[`Extension4337.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/Extension4337.sol)

:::

## Public Methods

Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts.
When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally.

### constructor

:::note References

- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#constructor)
- Solidity implementation: [`Extension4337.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/Extension4337.sol)

:::

```solidity
constructor(address entryPoint_);
```

#### Parameters

| Name | Type | Description |
| ------------- | :-------: | ----------- |
| `entryPoint_` | `address` | - |

<br/>

### entryPoint

:::note References

- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#entrypoint)
- Solidity implementation: [`Extension4337.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/Extension4337.sol)
- Function signature: `entryPoint()`
- Function selector: `0xb0d691fe`

:::

```solidity
function entryPoint() external view returns (address);
```

Get the address of the Entry Point contract that will execute the user operation.

#### Returns

| Name | Type | Description |
| ---- | :-------: | -------------------------------------- |
| `0` | `address` | The address of the EntryPoint contract |

<br/>

### supportsInterface

:::note References

- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#supportsinterface)
- Solidity implementation: [`Extension4337.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/Extension4337.sol)
- Function signature: `supportsInterface(bytes4)`
- Function selector: `0x01ffc9a7`

:::

```solidity
function supportsInterface(bytes4 interfaceId) external view returns (bool);
```

See [`IERC165-supportsInterface`](#ierc165-supportsinterface).

#### Parameters

| Name | Type | Description |
| ------------- | :------: | ----------- |
| `interfaceId` | `bytes4` | - |

#### Returns

| Name | Type | Description |
| ---- | :----: | ----------- |
| `0` | `bool` | - |

<br/>

### validateUserOp

:::note References

- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#validateuserop)
- Solidity implementation: [`Extension4337.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/Extension4337.sol)
- Function signature: `validateUserOp(UserOperation,bytes32,)`
Copy link
Member

Choose a reason for hiding this comment

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

@b00ste small bug in the template of the docs generator for param that don't have named arguments but just the types.

To be fixed in a separate PR, as the function signature is incorrect

- Function selector: `0x68159319`

:::

```solidity
function validateUserOp(
UserOperation userOp,
bytes32 userOpHash,
uint256
) external nonpayable returns (uint256);
```

_Validate user's signature and nonce the entryPoint will make the call to the recipient only if this validation call returns successfully. signature failure should be reported by returning SIG_VALIDATION_FAILED (1). This allows making a "simulation call" without a valid signature Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure._

Must validate caller is the entryPoint. Must validate the signature and nonce

#### Parameters

| Name | Type | Description |
| ------------ | :-------------: | ------------------------------------------------------------------------ |
| `userOp` | `UserOperation` | the operation that is about to be executed. |
| `userOpHash` | `bytes32` | hash of the user's request data. can be used as the basis for signature. |
| `_2` | `uint256` | - |

#### Returns

| Name | Type | Description |
| ---- | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `0` | `uint256` | packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, otherwise, an address of an "authorizer" contract. <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" <6-byte> validAfter - first timestamp this operation is valid If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure. Note that the validation code cannot use block.timestamp (or block.number) directly. |

<br/>

## Internal Methods

Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs.

Internal functions cannot be called externally, whether from other smart contracts, dApp interfaces, or backend services. Their restricted accessibility ensures that they remain exclusively available within the context of the current contract, promoting controlled and encapsulated usage of these internal utilities.

### \_extendableMsgData

```solidity
function _extendableMsgData() internal view returns (bytes);
```

Returns the original `msg.data` passed to the extendable contract
without the appended `msg.sender` and `msg.value`.

<br/>

### \_extendableMsgSender

```solidity
function _extendableMsgSender() internal view returns (address);
```

Returns the original `msg.sender` calling the extendable contract.

<br/>

### \_extendableMsgValue

```solidity
function _extendableMsgValue() internal view returns (uint256);
```

Returns the original `msg.value` sent to the extendable contract.

<br/>
61 changes: 61 additions & 0 deletions docs/contracts/LSP17Extensions/OnERC721ReceivedExtension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<!-- This file is auto-generated. Do not edit! -->
<!-- Check `@lukso-network/lsp-smart-contracts/CONTRIBUTING.md#solidity-code-comments` for more information. -->

# OnERC721ReceivedExtension

:::info Standard Specifications

[`LSP-17-Extensions`](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md)

:::
:::info Solidity implementation

[`OnERC721ReceivedExtension.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/OnERC721ReceivedExtension.sol)

:::

LSP17 Extension that can be attached to a LSP17Extendable contract to allow it to receive ERC721 tokens via `safeTransferFrom`.

## Public Methods

Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts.
When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally.

### onERC721Received

:::note References

- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#,,,))
- Solidity implementation: [`OnERC721ReceivedExtension.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/OnERC721ReceivedExtension.sol)
- Function signature: `,,,)`
- Function selector: `0x940e0af1`

:::

```solidity
function onERC721Received(
address,
address,
uint256,
bytes
) external nonpayable returns (bytes4);
```

See [`IERC721Receiver-onERC721Received`](#ierc721receiver-onerc721received). Always returns `IERC721Receiver.onERC721Received.selector`.

#### Parameters

| Name | Type | Description |
| ---- | :-------: | ----------- |
| `_0` | `address` | - |
| `_1` | `address` | - |
| `_2` | `uint256` | - |
| `_3` | `bytes` | - |

#### Returns

| Name | Type | Description |
| ---- | :------: | ----------- |
| `0` | `bytes4` | - |

<br/>
Loading
Loading