Skip to content

Commit

Permalink
Merge branch 'master' into feat/optimize-gas-usage
Browse files Browse the repository at this point in the history
  • Loading branch information
itzmeanjan authored Feb 23, 2021
2 parents e993d44 + 9277e74 commit 938fb01
Show file tree
Hide file tree
Showing 17 changed files with 417 additions and 89 deletions.
2 changes: 1 addition & 1 deletion artifacts/ChildMintableERC721.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion artifacts/DummyMintableERC721.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions contracts/Migrations.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pragma solidity 0.6.6;


contract Migrations {
address public owner;
uint256 public last_completed_migration;
Expand Down
47 changes: 24 additions & 23 deletions contracts/child/ChildChainManager/ChildChainManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {Initializable} from "../../common/Initializable.sol";
import {AccessControlMixin} from "../../common/AccessControlMixin.sol";
import {IStateReceiver} from "../IStateReceiver.sol";


contract ChildChainManager is
IChildChainManager,
Initializable,
Expand Down Expand Up @@ -76,16 +77,19 @@ contract ChildChainManager is
}
}

function _syncDeposit(bytes memory syncData) private {
(address user, address rootToken, bytes memory depositData) = abi
.decode(syncData, (address, address, bytes));
address childTokenAddress = rootToChildToken[rootToken];
require(
childTokenAddress != address(0x0),
"ChildChainManager: TOKEN_NOT_MAPPED"
);
IChildToken childTokenContract = IChildToken(childTokenAddress);
childTokenContract.deposit(user, depositData);
/**
* @notice Clean polluted token mapping
* @param rootToken address of token on root chain. Since rename token was introduced later stage,
* clean method is used to clean pollulated mapping
*/
function cleanMapToken(
address rootToken,
address childToken
) external override only(MAPPER_ROLE) {
rootToChildToken[rootToken] = address(0);
childToRootToken[childToken] = address(0);

emit TokenMapped(rootToken, childToken);
}

function _mapToken(address rootToken, address childToken) private {
Expand All @@ -106,18 +110,15 @@ contract ChildChainManager is
emit TokenMapped(rootToken, childToken);
}

/**
* @notice Clean polluted token mapping
* @param rootToken address of token on root chain. Since rename token was introduced later stage,
* clean method is used to clean pollulated mapping
*/
function cleanMapToken(
address rootToken,
address childToken
) external override only(MAPPER_ROLE) {
rootToChildToken[rootToken] = address(0);
childToRootToken[childToken] = address(0);

emit TokenMapped(rootToken, childToken);
function _syncDeposit(bytes memory syncData) private {
(address user, address rootToken, bytes memory depositData) = abi
.decode(syncData, (address, address, bytes));
address childTokenAddress = rootToChildToken[rootToken];
require(
childTokenAddress != address(0x0),
"ChildChainManager: TOKEN_NOT_MAPPED"
);
IChildToken childTokenContract = IChildToken(childTokenAddress);
childTokenContract.deposit(user, depositData);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pragma solidity 0.6.6;

import {UpgradableProxy} from "../../common/Proxy/UpgradableProxy.sol";


contract ChildChainManagerProxy is UpgradableProxy {
constructor(address _proxyTo)
public
Expand Down
43 changes: 43 additions & 0 deletions contracts/child/ChildToken/ChildMintableERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ contract ChildMintableERC721 is
bytes32 public constant DEPOSITOR_ROLE = keccak256("DEPOSITOR_ROLE");
mapping (uint256 => bool) public withdrawnTokens;

event TransferWithMetadata(address indexed from, address indexed to, uint256 indexed tokenId, bytes metaData);

constructor(
string memory name_,
string memory symbol_,
Expand Down Expand Up @@ -71,6 +73,47 @@ contract ChildMintableERC721 is
_burn(tokenId);
}

/**
* @notice called when user wants to withdraw token back to root chain with token URI
* @dev Should handle withraw by burning user's token.
* Should set `withdrawnTokens` mapping to `true` for the tokenId being withdrawn
* This transaction will be verified when exiting on root chain
*
* Before calling this function, you may want calling `encodeTokenMetadata`
* and get metadata to be transferred from L2 to L1 during exit
*
* @param tokenId tokenId to withdraw
*/
function withdrawWithMetadata(uint256 tokenId) external {

require(_msgSender() == ownerOf(tokenId), "ChildMintableERC721: INVALID_TOKEN_OWNER");
withdrawnTokens[tokenId] = true;

// Encoding metadata associated with tokenId & emitting event
emit TransferWithMetadata(ownerOf(tokenId), address(0), tokenId, this.encodeTokenMetadata(tokenId));

_burn(tokenId);

}

/**
* @notice This method is supposed to be called by client when withdrawing token with metadata
* and pass return value of this function as second paramter of `withdrawWithMetadata` method
*
* It can be overridden by clients to encode data in a different form, which needs to
* be decoded back by them correctly during exiting
*
* @param tokenId Token for which URI to be fetched
*/
function encodeTokenMetadata(uint256 tokenId) external view virtual returns (bytes memory) {

// You're always free to change this default implementation
// and pack more data in byte array which can be decoded back
// in L1
return abi.encode(tokenURI(tokenId));

}

/**
* @notice Example function to handle minting tokens on matic chain
* @dev Minting can be done as per requirement,
Expand Down
30 changes: 30 additions & 0 deletions contracts/root/RootToken/DummyMintableERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,36 @@ contract DummyMintableERC721 is
_mint(user, tokenId);
}

/**
* If you're attempting to bring metadata associated with token
* from L2 to L1, you must implement this method, to be invoked
* when minting token back on L1, during exit
*/
function setTokenMetadata(uint256 tokenId, bytes memory data) internal virtual {
// This function should decode metadata obtained from L2
// and attempt to set it for this `tokenId`
//
// Following is just a default implementation, feel
// free to define your own encoding/ decoding scheme
// for L2 -> L1 token metadata transfer
string memory uri = abi.decode(data, (string));

_setTokenURI(tokenId, uri);
}

/**
* @dev See {IMintableERC721-mint}.
*
* If you're attempting to bring metadata associated with token
* from L2 to L1, you must implement this method
*/
function mint(address user, uint256 tokenId, bytes calldata metaData) external override only(PREDICATE_ROLE) {
_mint(user, tokenId);

setTokenMetadata(tokenId, metaData);
}


/**
* @dev See {IMintableERC721-exists}.
*/
Expand Down
13 changes: 13 additions & 0 deletions contracts/root/RootToken/IMintableERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ interface IMintableERC721 is IERC721 {
*/
function mint(address user, uint256 tokenId) external;

/**
* @notice called by predicate contract to mint tokens while withdrawing with metadata from L2
* @dev Should be callable only by MintableERC721Predicate
* Make sure minting is only done either by this function/ 👆
* @param user user address for whom token is being minted
* @param tokenId tokenId being minted
* @param metaData Associated token metadata, to be decoded & set using `setTokenMetadata`
*
* Note : If you're interested in taking token metadata from L2 to L1 during exit, you must
* implement this method
*/
function mint(address user, uint256 tokenId, bytes calldata metaData) external;

/**
* @notice check if token already exists, return true if it does exist
* @dev this check will be used by the predicate to determine if the token needs to be minted or transfered
Expand Down
85 changes: 64 additions & 21 deletions contracts/root/TokenPredicates/MintableERC721Predicate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ contract MintableERC721Predicate is ITokenPredicate, AccessControlMixin, Initial
bytes32 public constant TOKEN_TYPE = 0xd4392723c111fcb98b073fe55873efb447bcd23cd3e49ec9ea2581930cd01ddc;
// keccak("Transfer(address,address,uint256)")
bytes32 public constant TRANSFER_EVENT_SIG = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
// keccak("TransferWithMetadata(address,address,uint256,string)")
bytes32 public constant TRANSFER_WITH_METADATA_EVENT_SIG = 0xf3c6803764de9a0fc1c2acb6a71f53407c5e2b9a3e04973e6586da23d64ecaa5;

event LockedMintableERC721(
address indexed depositor,
Expand Down Expand Up @@ -92,28 +94,69 @@ contract MintableERC721Predicate is ITokenPredicate, AccessControlMixin, Initial
RLPReader.RLPItem[] memory logRLPList = log.toRlpItem().toList();
RLPReader.RLPItem[] memory logTopicRLPList = logRLPList[1].toList(); // topics

require(
bytes32(logTopicRLPList[0].toUint()) == TRANSFER_EVENT_SIG, // topic0 is event sig
"MintableERC721Predicate: INVALID_SIGNATURE"
);

address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address

require(
address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address
"MintableERC721Predicate: INVALID_RECEIVER"
);

IMintableERC721 token = IMintableERC721(rootToken);
uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field
if (token.exists(tokenId)) {
token.safeTransferFrom(
address(this),
withdrawer,
tokenId
);
// If it's a simple exit ( with out metadata coming from L2 to L1 )
if(bytes32(logTopicRLPList[0].toUint()) == TRANSFER_EVENT_SIG) {

address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address

require(
address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address
"MintableERC721Predicate: INVALID_RECEIVER"
);

IMintableERC721 token = IMintableERC721(rootToken);

uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field
if (token.exists(tokenId)) {
token.safeTransferFrom(
address(this),
withdrawer,
tokenId
);
} else {
token.mint(withdrawer, tokenId);
}

} else if (bytes32(logTopicRLPList[0].toUint()) == TRANSFER_WITH_METADATA_EVENT_SIG) {
// If this is NFT exit with metadata i.e. URI 👆
//
// Note: If your token is only minted in L2, you can exit
// it with metadata. But if it was minted on L1, it'll be
// simply transferred to withdrawer address. And in that case,
// it's lot better to exit with `Transfer(address,address,uint256)`
// i.e. calling `withdraw` method on L2 contract
// event signature proof, which is defined under first `if` clause
//
// If you've called `withdrawWithMetadata`, you should submit
// proof of event signature `TransferWithMetadata(address,address,uint256,bytes)`

address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address

require(
address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address
"MintableERC721Predicate: INVALID_RECEIVER"
);

IMintableERC721 token = IMintableERC721(rootToken);

uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field
if (token.exists(tokenId)) {
token.safeTransferFrom(
address(this),
withdrawer,
tokenId
);
} else {
// Minting with metadata received from L2 i.e. emitted
// by event `TransferWithMetadata` during burning
token.mint(withdrawer, tokenId, logRLPList[2].toBytes());
}

} else {
token.mint(withdrawer, tokenId);
// Attempting to exit with some event signature from L2, which is
// not ( yet ) supported by L1 exit manager
revert("MintableERC721Predicate: INVALID_SIGNATURE");
}

}
}
47 changes: 24 additions & 23 deletions flat/ChildChainManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,7 @@ pragma solidity 0.6.6;




contract ChildChainManager is
IChildChainManager,
Initializable,
Expand Down Expand Up @@ -855,16 +856,19 @@ contract ChildChainManager is
}
}

function _syncDeposit(bytes memory syncData) private {
(address user, address rootToken, bytes memory depositData) = abi
.decode(syncData, (address, address, bytes));
address childTokenAddress = rootToChildToken[rootToken];
require(
childTokenAddress != address(0x0),
"ChildChainManager: TOKEN_NOT_MAPPED"
);
IChildToken childTokenContract = IChildToken(childTokenAddress);
childTokenContract.deposit(user, depositData);
/**
* @notice Clean polluted token mapping
* @param rootToken address of token on root chain. Since rename token was introduced later stage,
* clean method is used to clean pollulated mapping
*/
function cleanMapToken(
address rootToken,
address childToken
) external override only(MAPPER_ROLE) {
rootToChildToken[rootToken] = address(0);
childToRootToken[childToken] = address(0);

emit TokenMapped(rootToken, childToken);
}

function _mapToken(address rootToken, address childToken) private {
Expand All @@ -885,18 +889,15 @@ contract ChildChainManager is
emit TokenMapped(rootToken, childToken);
}

/**
* @notice Clean polluted token mapping
* @param rootToken address of token on root chain. Since rename token was introduced later stage,
* clean method is used to clean pollulated mapping
*/
function cleanMapToken(
address rootToken,
address childToken
) external override only(MAPPER_ROLE) {
rootToChildToken[rootToken] = address(0);
childToRootToken[childToken] = address(0);

emit TokenMapped(rootToken, childToken);
function _syncDeposit(bytes memory syncData) private {
(address user, address rootToken, bytes memory depositData) = abi
.decode(syncData, (address, address, bytes));
address childTokenAddress = rootToChildToken[rootToken];
require(
childTokenAddress != address(0x0),
"ChildChainManager: TOKEN_NOT_MAPPED"
);
IChildToken childTokenContract = IChildToken(childTokenAddress);
childTokenContract.deposit(user, depositData);
}
}
1 change: 1 addition & 0 deletions flat/ChildChainManagerProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ contract UpgradableProxy is Proxy {
pragma solidity 0.6.6;



contract ChildChainManagerProxy is UpgradableProxy {
constructor(address _proxyTo)
public
Expand Down
Loading

0 comments on commit 938fb01

Please sign in to comment.