Skip to content

Commit

Permalink
Merge pull request #168 from bosonprotocol/meta_tx_erc1155nontransfer…
Browse files Browse the repository at this point in the history
…able
  • Loading branch information
mischat committed Oct 13, 2021
2 parents 76f65a1 + de2f2f3 commit 5319bbb
Show file tree
Hide file tree
Showing 6 changed files with 532 additions and 18 deletions.
28 changes: 19 additions & 9 deletions contracts/ERC1155NonTransferable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ pragma solidity 0.7.6;
import "@openzeppelin/contracts/token/ERC1155/ERC1155Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IERC1155NonTransferable.sol";
import "./MetaTransactionReceiver.sol";

/**
* @title Non transferable token contract, implementing ERC-1155, but preventing transfers
*/
contract ERC1155NonTransferable is
IERC1155NonTransferable,
ERC1155Pausable,
Ownable
Ownable,
MetaTransactionReceiver
{

event LogUriSet(string _newUri, address _triggeredBy);
Expand All @@ -36,7 +38,7 @@ contract ERC1155NonTransferable is
uint256 _tokenId,
uint256 _value,
bytes memory _data
) external override onlyOwner {
) external override onlyOwnerOrSelf {
_mint(_to, _tokenId, _value, _data);
}

Expand All @@ -53,7 +55,7 @@ contract ERC1155NonTransferable is
uint256[] memory _tokenIds,
uint256[] memory _values,
bytes memory _data
) external onlyOwner {
) external onlyOwnerOrSelf {
_mintBatch(_to, _tokenIds, _values, _data);
}

Expand All @@ -68,7 +70,7 @@ contract ERC1155NonTransferable is
address _account,
uint256 _tokenId,
uint256 _value
) external override onlyOwner {
) external override onlyOwnerOrSelf {
_burn(_account, _tokenId, _value);
}

Expand All @@ -83,7 +85,7 @@ contract ERC1155NonTransferable is
address _account,
uint256[] memory _tokenIds,
uint256[] memory _values
) external onlyOwner {
) external onlyOwnerOrSelf {
_burnBatch(_account, _tokenIds, _values);
}

Expand Down Expand Up @@ -115,24 +117,32 @@ contract ERC1155NonTransferable is
/**
* @notice Pause all token mint, transfer, burn
*/
function pause() external override onlyOwner {
function pause() external override onlyOwnerOrSelf {
_pause();
}

/**
* @notice Unpause the contract and allows mint, transfer, burn
*/
function unpause() external override onlyOwner {
function unpause() external override onlyOwnerOrSelf {
_unpause();
}

/**
* @notice Setting the metadata uri
* @param _newUri New uri to be used
*/
function setUri(string memory _newUri) external onlyOwner {
function setUri(string memory _newUri) external onlyOwnerOrSelf {
require(bytes(_newUri).length != 0, "INVALID_VALUE");
_setURI(_newUri);
emit LogUriSet(_newUri, msg.sender);
emit LogUriSet(_newUri, _msgSender());
}

/**
* @notice When functions are invoked via metatransactions, it is already checked there that signer is the owner of transaction
* and we can declare it as a message sender
*/
function _msgSender() internal view virtual override returns (address payable) {
return msg.sender == address(this) ? payable(owner()) : msg.sender;
}
}
78 changes: 78 additions & 0 deletions contracts/MetaTransactionReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: LGPL-3.0-or-later

pragma solidity 0.7.6;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/cryptography/ECDSA.sol";

/*
* This contract accepts metatransactions signed by the Owner.
* The signature of the Owner is verfied on chain and as a result
* the metatransactions can be sent to the network by any EOA
*/
contract MetaTransactionReceiver is Ownable {

using ECDSA for bytes32;

mapping(uint256 => bool) private usedNonce;

event ExecutedMetaTransaction(bytes _data, bytes _returnData);
event UsedNonce(uint256 _nonce);

/// @dev Checks if the caller of the method is the contract itself
modifier onlyOwnerOrSelf() {
require(msg.sender == owner() || msg.sender == address(this), "UNAUTHORIZED_O_SELF");
_;
}

/// @dev This function allows for anyone to relay transactions on the owner's behalf. The owner's signature is verified onchain.
/// @param _nonce only used for relayed transactions. This is used as an idempotency key
/// @param _data abi encoded data payload.
/// @param _signature signed prefix + data.
function executeMetaTransaction(
uint256 _nonce,
bytes calldata _data,
bytes calldata _signature
) external {
// Expecting prefixed data ("boson:") indicating relayed transaction...
// ...and an Ethereum Signed Message to protect user from signing an actual Tx
require(!usedNonce[_nonce], "METATX_NONCE");

uint256 id;
assembly {
id := chainid() //1 for Ethereum mainnet, > 1 for public testnets.
}
bytes32 dataHash = keccak256(abi.encodePacked("boson:", id, address(this), _nonce, _data)).toEthSignedMessageHash();
// Verify signature validity i.e. signer == owner
isValidOwnerSignature(dataHash, _signature);
// Verify that the nonce hasn't been used before


// Store the nonce provided to avoid playback of the same tx
usedNonce[_nonce] = true;

emit UsedNonce(_nonce);

// invoke local function with an external call
(bool success, bytes memory returnData) = address(this).call(_data);
require(success, string(returnData));

emit ExecutedMetaTransaction(_data, returnData);
}

/// @dev Tells if nonce was used already
/// @param _nonce only used for relayed transactions. This is used as an idempotency key
/// @return true if used already, otherwise false
function isUsedNonce(uint256 _nonce) external view returns(bool) {
return usedNonce[_nonce];
}

/// @dev This method ensures that the signature belongs to the owner
/// @param _hashedData Hashed data signed on the behalf of address(this)
/// @param _signature Signature byte array associated with _dataHash
function isValidOwnerSignature(bytes32 _hashedData, bytes memory _signature) public view {
address from = _hashedData.recover(_signature);
require(owner() == from, "METATX_UNAUTHORIZED");
}

}
Loading

0 comments on commit 5319bbb

Please sign in to comment.