Skip to content

Commit

Permalink
Add missing user op validation function
Browse files Browse the repository at this point in the history
  • Loading branch information
dphilipson committed Mar 5, 2024
1 parent 7ba613c commit e996d58
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 36 deletions.
39 changes: 24 additions & 15 deletions app/nfts/[address]/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function Page({ params: { address, id } }: { params: { address: Address; id: str
const handleTransferAddressChange = (event: ChangeEvent<HTMLInputElement>) => setTransferAddress(event.target.value);

const locks = useQuery({
queryKey: ['all-locks', address, id],
queryKey: ['all-locks'],
queryFn: async () => {
if (!account) {
throw new Error('Account is null');
Expand Down Expand Up @@ -186,33 +186,42 @@ function Page({ params: { address, id } }: { params: { address: Address; id: str
<Box maxW={500} mx="auto">
<Image src={imageSrc} />
<Text>{name}</Text>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Input placeholder="Enter address…" value={transferAddress} onChange={handleTransferAddressChange} />
<Input placeholder="Enter address…" value={transferAddress} onChange={handleTransferAddressChange} />
<Box mt={1}>
<Button
isDisabled={transfer.isPending && !transferAddress.match(/^0x[0-9a-fA-F]{40}$/)}
onClick={() => transfer.mutate()}
>
{transfer.isPending ? 'Transferring…' : 'Transfer'}
</Button>
<Button
ml={1}
isDisabled={transferWithStorageKey.isPending && !transferAddress.match(/^0x[0-9a-fA-F]{40}$/)}
onClick={() => transferWithStorageKey.mutate()}
>
{transferWithStorageKey.isPending ? 'Transferring…' : 'Transfer with storage key'}
</Button>
</Box>
<Button isDisabled={lockNft.isPending || isTokenLocked} onClick={() => lockNft.mutate()}>
{lockNft.isPending ? 'Locking…' : 'Lock'}
</Button>
<Button isDisabled={unlockNft.isPending || !isTokenLocked} onClick={() => unlockNft.mutate()}>
{unlockNft.isPending ? 'Unlocking…' : 'Unlock'}
</Button>
<Button isDisabled={lockCollection.isPending || isCollectionLocked} onClick={() => lockCollection.mutate()}>
{lockCollection.isPending ? 'Locking…' : 'Lock collection'}
</Button>
<Button isDisabled={unlockCollection.isPending || !isCollectionLocked} onClick={() => unlockCollection.mutate()}>
{unlockCollection.isPending ? 'Unlocking…' : 'Unlock collection'}
</Button>
<Box mt={4}>
<Button isDisabled={lockNft.isPending || isTokenLocked} onClick={() => lockNft.mutate()}>
{lockNft.isPending ? 'Locking…' : 'Lock'}
</Button>
<Button ml={1} isDisabled={unlockNft.isPending || !isTokenLocked} onClick={() => unlockNft.mutate()}>
{unlockNft.isPending ? 'Unlocking…' : 'Unlock'}
</Button>
</Box>
<Box mt={4}>
<Button isDisabled={lockCollection.isPending || isCollectionLocked} onClick={() => lockCollection.mutate()}>
{lockCollection.isPending ? 'Locking…' : 'Lock collection'}
</Button>
<Button
ml={1}
isDisabled={unlockCollection.isPending || !isCollectionLocked}
onClick={() => unlockCollection.mutate()}
>
{unlockCollection.isPending ? 'Unlocking…' : 'Unlock collection'}
</Button>
</Box>
{privateKeyModal}
</Box>
);
Expand Down
49 changes: 49 additions & 0 deletions contracts/broadcast/DeployForReal.s.sol/421614/run-1709624622.json

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions contracts/broadcast/DeployForReal.s.sol/421614/run-latest.json

Large diffs are not rendered by default.

34 changes: 30 additions & 4 deletions contracts/src/ColdStoragePlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ pragma solidity ^0.8.22;

import {BasePlugin} from "modular-account/plugins/BasePlugin.sol";
import {PluginManifest, PluginMetadata} from "modular-account/interfaces/IPlugin.sol";
import {UserOperation} from "modular-account/interfaces/erc4337/UserOperation.sol";
import {IColdStoragePlugin} from "./IColdStoragePlugin.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {Call, IStandardExecutor} from "modular-account/interfaces/IStandardExecutor.sol";
import {IPluginExecutor} from "modular-account/interfaces/IPluginExecutor.sol";
Expand All @@ -19,6 +21,7 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

contract ColdStoragePlugin is IColdStoragePlugin, BasePlugin {
using ERC721LockMapLib for ERC721LockMapLib.ERC721LockMap;
using ECDSA for bytes32;

string internal constant _NAME = "Cold Storage Plugin";
string internal constant _VERSION = "0.1.0";
Expand Down Expand Up @@ -149,7 +152,10 @@ contract ColdStoragePlugin is IColdStoragePlugin, BasePlugin {
override
returns (uint48)
{
uint256 longestLock = Math.max(Math.max(erc721AllLocks[account], erc721Locks[msg.sender].get(true, collection, 0).lockEndTime), erc721Locks[msg.sender].get(false, collection, tokenId).lockEndTime);
uint256 longestLock = Math.max(
Math.max(erc721AllLocks[account], erc721Locks[msg.sender].get(true, collection, 0).lockEndTime),
erc721Locks[msg.sender].get(false, collection, tokenId).lockEndTime
);
return longestLock > block.timestamp ? uint48(longestLock - block.timestamp) : 0;
}

Expand Down Expand Up @@ -222,6 +228,24 @@ contract ColdStoragePlugin is IColdStoragePlugin, BasePlugin {
}
}

/// @inheritdoc BasePlugin
function userOpValidationFunction(uint8 functionId, UserOperation calldata userOp, bytes32 userOpHash)
external
view
override
returns (uint256)
{
if (functionId == uint8(FunctionId.USER_OP_VALIDATION_STORAGE_KEY)) {
(address signer, ECDSA.RecoverError error) =
userOpHash.toEthSignedMessageHash().tryRecover(userOp.signature);
if (error == ECDSA.RecoverError.NoError && signer == storageKeys[msg.sender]) {
return 0;
}
return 1;
}
revert NotImplemented(msg.sig, functionId);
}

/// @inheritdoc BasePlugin
function runtimeValidationFunction(uint8 functionId, address sender, uint256, bytes calldata)
external
Expand All @@ -237,7 +261,7 @@ contract ColdStoragePlugin is IColdStoragePlugin, BasePlugin {
}

/// @inheritdoc BasePlugin
function preExecutionHook(uint8 functionId, address sender, uint256 /* value */, bytes calldata data)
function preExecutionHook(uint8 functionId, address sender, uint256, /* value */ bytes calldata data)
external
view
override
Expand All @@ -249,7 +273,9 @@ contract ColdStoragePlugin is IColdStoragePlugin, BasePlugin {
selector == IStandardExecutor.execute.selector
|| selector == IPluginExecutor.executeFromPluginExternal.selector
) {
if (sender == address(this)) { // Allow for any executeFromPluginExternal calls from the plugin itself since it has already passed validation
if (sender == address(this)) {
// Allow for any executeFromPluginExternal calls from the plugin itself since it has already passed
// validation
return bytes("");
}
(address executeTarget, /* uint256 executeValue */, bytes memory executeData) =
Expand Down Expand Up @@ -476,7 +502,7 @@ contract ColdStoragePlugin is IColdStoragePlugin, BasePlugin {
return interfaceId == type(IColdStoragePlugin).interfaceId || super.supportsInterface(interfaceId);
}

function isErc721LockedFunction(bytes4 selector) internal view returns (bool) {
function isErc721LockedFunction(bytes4 selector) internal pure returns (bool) {
return selector == IERC721.approve.selector || selector == SAFE_TRANSFER_FROM
|| selector == SAFE_TRANSFER_FROM_WITH_DATA || selector == IERC721.transferFrom.selector;
}
Expand Down
3 changes: 1 addition & 2 deletions contracts/src/IColdStoragePlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ interface IColdStoragePlugin {
function getERC721TokenLockedDuration(address account, address collection, uint256 tokenId)
external
view
override
returns (uint48)
returns (uint48);

/// @notice Returns all the active locks for the account.
/// @param account SCA to get all the locks for
Expand Down
2 changes: 1 addition & 1 deletion plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export type ColdStoragePluginActions<
> = ExecutionActions<TAccount> & ManagementActions<TAccount> & ReadAndEncodeActions;

const addresses = {
421614: '0x63008C1f1a179C4109C8c176C174106328Bd3521' as Address
421614: '0xC29f899065e4e35BB82FB0645F0FEC68C7DD8840' as Address
} as Record<number, Address>;

export const ColdStoragePlugin: Plugin<typeof ColdStoragePluginAbi> = {
Expand Down
2 changes: 1 addition & 1 deletion utils/deployed.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Generated by forge deploy script
export const DEPLOYED_COLD_STORAGE_PLUGIN_ADDRESS = '0x63008C1f1a179C4109C8c176C174106328Bd3521';
export const DEPLOYED_COLD_STORAGE_PLUGIN_ADDRESS = '0xC29f899065e4e35BB82FB0645F0FEC68C7DD8840';
export const DEPLOYED_NFT1_ADDRESS = '0xf3b314434B087095dd6A8AA5c0747524AaFE2167';
export const DEPLOYED_NFT2_ADDRESS = '0x85923Ba605EE77181daEb6B435F31c12eD28Cd11';

0 comments on commit e996d58

Please sign in to comment.