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

Agent: add ERC721Receiver support #1058

Merged
merged 5 commits into from
Jan 14, 2020
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
37 changes: 23 additions & 14 deletions apps/agent/contracts/Agent.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ pragma solidity 0.4.24;
import "./SignatureValidator.sol";
import "./standards/IERC165.sol";
import "./standards/ERC1271.sol";
import "./standards/IERC721Receiver.sol";

import "@aragon/apps-vault/contracts/Vault.sol";

import "@aragon/os/contracts/common/IForwarder.sol";


contract Agent is IERC165, ERC1271Bytes, IForwarder, IsContract, Vault {
contract Agent is IERC165, IERC721Receiver, ERC1271Bytes, IForwarder, IsContract, Vault {
/* Hardcoded constants to save gas
bytes32 public constant EXECUTE_ROLE = keccak256("EXECUTE_ROLE");
bytes32 public constant SAFE_EXECUTE_ROLE = keccak256("SAFE_EXECUTE_ROLE");
Expand All @@ -35,6 +36,7 @@ contract Agent is IERC165, ERC1271Bytes, IForwarder, IsContract, Vault {
uint256 public constant PROTECTED_TOKENS_CAP = 10;
sohkai marked this conversation as resolved.
Show resolved Hide resolved

bytes4 private constant ERC165_INTERFACE_ID = 0x01ffc9a7;
bytes4 private constant ERC721_RECEIVED_INTERFACE_ID = 0x150b7a02; // bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))

string private constant ERROR_TARGET_PROTECTED = "AGENT_TARGET_PROTECTED";
string private constant ERROR_PROTECTED_TOKENS_MODIFIED = "AGENT_PROTECTED_TOKENS_MODIFIED";
Expand All @@ -56,6 +58,7 @@ contract Agent is IERC165, ERC1271Bytes, IForwarder, IsContract, Vault {
event RemoveProtectedToken(address indexed token);
event PresignHash(address indexed sender, bytes32 indexed hash);
event SetDesignatedSigner(address indexed sender, address indexed oldSigner, address indexed newSigner);
event ReceiveERC721(address indexed token, address indexed operator, address indexed from, uint256 tokenId, bytes data);

/**
* @notice Execute '`@radspec(_target, _data)`' on `_target``_ethValue == 0 ? '' : ' (Sending ' + @tokenAmount(0x0000000000000000000000000000000000000000, _ethValue) + ')'`
Expand Down Expand Up @@ -197,6 +200,12 @@ contract Agent is IERC165, ERC1271Bytes, IForwarder, IsContract, Vault {
emit SetDesignatedSigner(msg.sender, oldDesignatedSigner, _designatedSigner);
}

function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns (bytes4) {
emit ReceiveERC721(msg.sender, _operator, _from, _tokenId, _data);
sohkai marked this conversation as resolved.
Show resolved Hide resolved

return ERC721_RECEIVED_INTERFACE_ID;
}

// Forwarding fns

/**
Expand All @@ -208,6 +217,19 @@ contract Agent is IERC165, ERC1271Bytes, IForwarder, IsContract, Vault {
return true;
}

/**
* @notice Tells whether this contract supports a given ERC-165 interface
* @dev Implements conformance to ERC-165
* @param _interfaceId Interface bytes to check
* @return True if this contract supports the interface
*/
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
return
_interfaceId == ERC1271_INTERFACE_ID ||
_interfaceId == ERC721_RECEIVED_INTERFACE_ID ||
_interfaceId == ERC165_INTERFACE_ID;
}

/**
* @notice Execute the script as the Agent app
* @dev IForwarder interface conformance. Forwards any token holder action.
Expand All @@ -233,19 +255,6 @@ contract Agent is IERC165, ERC1271Bytes, IForwarder, IsContract, Vault {
return canPerform(_sender, RUN_SCRIPT_ROLE, arr(_getScriptACLParam(_evmScript)));
}

// ERC-165 conformance

/**
* @notice Tells whether this contract supports a given ERC-165 interface
* @param _interfaceId Interface bytes to check
* @return True if this contract supports the interface
*/
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
return
_interfaceId == ERC1271_INTERFACE_ID ||
_interfaceId == ERC165_INTERFACE_ID;
}

// ERC-1271 conformance

/**
Expand Down
20 changes: 20 additions & 0 deletions apps/agent/contracts/standards/IERC721Receiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma solidity 0.4.24;


interface IERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the ERC721 contract address is always the message sender.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes data) external returns (bytes4);
}
5 changes: 5 additions & 0 deletions apps/agent/test/agent_shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module.exports = (
const NO_DATA = '0x'
const ERC165_SUPPORT_INVALID_ID = '0xffffffff'
const ERC165_SUPPORT_INTERFACE_ID = '0x01ffc9a7'
const ERC721_RECEIVED_INTERFACE_ID = '0x150b7a02'

const AgentLike = artifacts.require(agentName)

Expand Down Expand Up @@ -759,6 +760,10 @@ module.exports = (
assert.isTrue(await agent.supportsInterface(ERC1271_INTERFACE_ID))
})

it('supports ERC721Receiver interface', async () => {
assert.isTrue(await agent.supportsInterface(ERC721_RECEIVED_INTERFACE_ID))
})

it('doesn\'t support any other interface', async () => {
assert.isFalse(await agent.supportsInterface('0x12345678'))
assert.isFalse(await agent.supportsInterface('0x'))
Expand Down