Skip to content

Commit

Permalink
feat: token deployment (Open-Attestation#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
superical committed Apr 18, 2022
1 parent 6f3788b commit 3cdeafa
Show file tree
Hide file tree
Showing 27 changed files with 7,106 additions and 330 deletions.
48 changes: 22 additions & 26 deletions contracts/TitleEscrow.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/interfaces/IERC165.sol";
import "./interfaces/ITitleEscrow.sol";
import "./interfaces/ITradeTrustERC721.sol";
Expand All @@ -21,29 +19,33 @@ contract TitleEscrow is IERC165, ITitleEscrow, Initializable {

bool public override active;

constructor() {
initialize(address(0), address(0), address(0), 0x00);
}

modifier onlyBeneficiary() {
require(msg.sender == beneficiary, "TitleEscrow: Caller is not beneficiary");
require(msg.sender == beneficiary, "TE: Not beneficiary");
_;
}

modifier onlyHolder() {
require(msg.sender == holder, "TitleEscrow: Caller is not holder");
require(msg.sender == holder, "TE: Not holder");
_;
}

modifier whenHoldingToken() {
require(_isHoldingToken(), "TitleEscrow: Not holding token");
require(_isHoldingToken(), "TE: Not holding token");
_;
}

modifier whenNotPaused() {
bool paused = Pausable(registry).paused();
require(!paused, "TitleEscrow: Token Registry is paused");
require(!paused, "TE: Registry paused");
_;
}

modifier whenActive() {
require(active, "TitleEscrow: Inactive");
require(active, "TE: Inactive");
_;
}

Expand All @@ -70,8 +72,8 @@ contract TitleEscrow is IERC165, ITitleEscrow, Initializable {
uint256 _tokenId,
bytes calldata /* data */
) external override whenNotPaused whenActive returns (bytes4) {
require(tokenId == _tokenId, "TitleEscrow: Unable to accept token");
require(msg.sender == address(registry), "TitleEscrow: Only tokens from predefined token registry can be accepted");
require(tokenId == _tokenId, "TE: Invalid token");
require(msg.sender == address(registry), "TE: Wrong registry");

emit TokenReceived(registry, tokenId);
return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
Expand All @@ -85,8 +87,8 @@ contract TitleEscrow is IERC165, ITitleEscrow, Initializable {
whenHoldingToken
whenActive
{
require(beneficiary != _nominatedBeneficiary, "TitleEscrow: Nominee is already beneficiary");
require(nominatedBeneficiary != _nominatedBeneficiary, "TitleEscrow: Beneficiary nominee is already nominated");
require(beneficiary != _nominatedBeneficiary, "TE: Nominee is beneficiary");
require(nominatedBeneficiary != _nominatedBeneficiary, "TE: Already beneficiary nominee");

nominatedBeneficiary = _nominatedBeneficiary;

Expand All @@ -101,8 +103,8 @@ contract TitleEscrow is IERC165, ITitleEscrow, Initializable {
whenHoldingToken
whenActive
{
require(holder != _nominatedHolder, "TitleEscrow: Nominee is already holder");
require(nominatedHolder != _nominatedHolder, "TitleEscrow: Holder nominee is already nominated");
require(holder != _nominatedHolder, "TE: Nominee is holder");
require(nominatedHolder != _nominatedHolder, "TE: Already holder nominee");

nominatedHolder = _nominatedHolder;

Expand All @@ -122,11 +124,8 @@ contract TitleEscrow is IERC165, ITitleEscrow, Initializable {
whenHoldingToken
whenActive
{
require(_nominatedBeneficiary != address(0), "TitleEscrow: Cannot endorse address");
require(
beneficiary == holder || (nominatedBeneficiary == _nominatedBeneficiary),
"TitleEscrow: Cannot endorse non-nominee"
);
require(_nominatedBeneficiary != address(0), "TE: Endorsing zero");
require(beneficiary == holder || (nominatedBeneficiary == _nominatedBeneficiary), "TE: Endorse non-nominee");

beneficiary = _nominatedBeneficiary;
nominatedBeneficiary = address(0);
Expand All @@ -142,13 +141,10 @@ contract TitleEscrow is IERC165, ITitleEscrow, Initializable {
whenHoldingToken
whenActive
{
require(_nominatedHolder != address(0), "TitleEscrow: Cannot endorse address");
require(holder != _nominatedHolder, "TitleEscrow: Endorsee is already holder");
require(_nominatedHolder != address(0), "TE: Endorsing zero");
require(holder != _nominatedHolder, "TE: Endorsee already holder");
if (nominatedHolder != address(0)) {
require(
beneficiary == holder || (nominatedHolder == _nominatedHolder),
"TitleEscrow: Cannot endorse non-nominee"
);
require(beneficiary == holder || (nominatedHolder == _nominatedHolder), "TE: Endorse non-nominee");
}

holder = _nominatedHolder;
Expand All @@ -170,8 +166,8 @@ contract TitleEscrow is IERC165, ITitleEscrow, Initializable {
}

function shred() external override whenNotPaused whenActive {
require(!_isHoldingToken(), "TitleEscrow: Not surrendered yet");
require(msg.sender == registry, "TitleEscrow: Caller is not registry");
require(!_isHoldingToken(), "TE: Not surrendered");
require(msg.sender == registry, "TE: Invalid registry");

_resetNominees();
beneficiary = address(0);
Expand Down
1 change: 0 additions & 1 deletion contracts/TitleEscrowFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ contract TitleEscrowFactory is ITitleEscrowFactory {

constructor() {
implementation = address(new TitleEscrow());
TitleEscrow(implementation).initialize(address(0), address(0), address(0), 0x00);
}

function create(
Expand Down
146 changes: 19 additions & 127 deletions contracts/TradeTrustERC721.sol
Original file line number Diff line number Diff line change
@@ -1,143 +1,35 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "./access/RegistryAccess.sol";
import "./interfaces/ITitleEscrow.sol";
import "./interfaces/ITradeTrustERC721.sol";
import "./interfaces/ITitleEscrowFactory.sol";
import "./TradeTrustERC721Base.sol";

contract TradeTrustERC721 is ITradeTrustERC721, RegistryAccess, Pausable, ERC721 {
using Address for address;

event TokenBurnt(uint256 indexed tokenId, address indexed titleEscrow, address indexed burner);
event TokenReceived(address indexed operator, address indexed from, uint256 indexed tokenId, bytes data);
event TokenRestored(uint256 indexed tokenId, address indexed newOwner);

address internal constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;

uint256 public genesisBlock;
ITitleEscrowFactory public override titleEscrowFactory;
contract TradeTrustERC721 is TradeTrustERC721Base {
address internal immutable _titleEscrowFactory;
uint256 internal immutable _genesis;

constructor(
string memory name,
string memory symbol,
address _titleEscrowFactory
) ERC721(name, symbol) {
genesisBlock = block.number;
titleEscrowFactory = ITitleEscrowFactory(_titleEscrowFactory);
}

function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC721, IERC165, RegistryAccess)
returns (bool)
{
return
interfaceId == type(ITradeTrustERC721).interfaceId ||
ERC721.supportsInterface(interfaceId) ||
RegistryAccess.supportsInterface(interfaceId);
}

function onERC721Received(
address, /* _operator */
address, /* _from */
uint256, /* _tokenId */
bytes memory /* _data */
) public pure override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}

/**
* @dev Permanently burns a token and does not allow the same ID to be minted again.
* This call is meant for a minter to accept a surrendered token. Token will be transferred to 0xdead address.
*
* Requirements:
*
* - the caller must be a `minter`.
* - the token is surrendered
*
* Emits a {TokenBurnt} event.
*
* @param tokenId Token ID to be burnt
*/
function destroyToken(uint256 tokenId) external override whenNotPaused onlyAccepter {
address titleEscrow = titleEscrowFactory.getAddress(address(this), tokenId);

ITitleEscrow(titleEscrow).shred();

// Burning token to 0xdead instead to show a differentiate state as address(0) is used for unminted tokens
_registryTransferTo(BURN_ADDRESS, tokenId);

emit TokenBurnt(tokenId, titleEscrow, _msgSender());
}

function mintTitle(
address beneficiary,
address holder,
uint256 tokenId
) public virtual override whenNotPaused onlyMinter returns (address) {
require(!_exists(tokenId), "TokenRegistry: Token already exists");

return _mintTitle(beneficiary, holder, tokenId);
address titleEscrowFactory_
) {
_genesis = block.number;
_titleEscrowFactory = titleEscrowFactory_;
initialize(name, symbol, _msgSender());
}

function restoreTitle(uint256 tokenId) external override whenNotPaused onlyRestorer returns (address) {
require(_exists(tokenId), "TokenRegistry: Token does not exist");
require(isSurrendered(tokenId), "TokenRegistry: Token is not surrendered");
require(ownerOf(tokenId) != BURN_ADDRESS, "TokenRegistry: Token is already burnt");

address titleEscrow = titleEscrowFactory.getAddress(address(this), tokenId);

_registryTransferTo(titleEscrow, tokenId);

emit TokenRestored(tokenId, titleEscrow);

return titleEscrow;
}

function isSurrendered(uint256 tokenId) public view returns (bool) {
require(_exists(tokenId), "TokenRegistry: Token does not exist");
address owner = ownerOf(tokenId);
return owner == address(this) || owner == BURN_ADDRESS;
}

function pause() external onlyAdmin {
_pause();
}

function unpause() external onlyAdmin {
_unpause();
}

function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override whenNotPaused {
if (to == BURN_ADDRESS) {
require(isSurrendered(tokenId), "TokenRegistry: Token has not been surrendered");
}
super._beforeTokenTransfer(from, to, tokenId);
function initialize(
string memory name,
string memory symbol,
address admin
) internal initializer {
__TradeTrustERC721Base_init(name, symbol, admin);
}

function _mintTitle(
address beneficiary,
address holder,
uint256 tokenId
) internal virtual returns (address) {
address newTitleEscrow = titleEscrowFactory.create(beneficiary, holder, tokenId);
_safeMint(newTitleEscrow, tokenId);

return newTitleEscrow;
function titleEscrowFactory() public view override returns (ITitleEscrowFactory) {
return ITitleEscrowFactory(_titleEscrowFactory);
}

function _registryTransferTo(address to, uint256 tokenId) internal {
this.safeTransferFrom(address(this), to, tokenId, "");
function genesis() public view override returns (uint256) {
return _genesis;
}
}

0 comments on commit 3cdeafa

Please sign in to comment.