1+ pragma solidity ^ 0.6.0 ;
2+
3+ import "./../../libs/ownership/Ownable.sol " ;
4+ import "./../../libs/common/ZeroCopySink.sol " ;
5+ import "./../../libs/common/ZeroCopySource.sol " ;
6+ import "./../../libs/utils/Utils.sol " ;
7+ import "./../../libs/utils/Address.sol " ;
8+ import "./../../libs/token/ERC721/IERC721Metadata.sol " ;
9+ import "./../../libs/token/ERC721/IERC721Receiver.sol " ;
10+ import "./../../libs/math/SafeMath.sol " ;
11+ import "./../cross_chain_manager/interface/IEthCrossChainManager.sol " ;
12+ import "./../cross_chain_manager/interface/IEthCrossChainManagerProxy.sol " ;
13+
14+
15+ contract NFTLockProxy is IERC721Receiver , Ownable {
16+ using SafeMath for uint ;
17+ using Address for address ;
18+
19+ struct TxArgs {
20+ bytes toAssetHash;
21+ bytes toAddress;
22+ uint256 tokenId;
23+ bytes tokenURI;
24+ }
25+
26+ address public managerProxyContract;
27+ mapping (uint64 => bytes ) public proxyHashMap;
28+ mapping (address => mapping (uint64 => bytes )) public assetHashMap;
29+ mapping (address => bool ) safeTransfer;
30+
31+ event SetManagerProxyEvent (address manager );
32+ event BindProxyEvent (uint64 toChainId , bytes targetProxyHash );
33+ event BindAssetEvent (address fromAssetHash , uint64 toChainId , bytes targetProxyHash );
34+ event UnlockEvent (address toAssetHash , address toAddress , uint256 tokenId );
35+ event LockEvent (address fromAssetHash , address fromAddress , bytes toAssetHash , bytes toAddress , uint64 toChainId , uint256 tokenId );
36+
37+ modifier onlyManagerContract () {
38+ IEthCrossChainManagerProxy ieccmp = IEthCrossChainManagerProxy (managerProxyContract);
39+ require (_msgSender () == ieccmp.getEthCrossChainManager (), "msgSender is not EthCrossChainManagerContract " );
40+ _;
41+ }
42+
43+ function setManagerProxy (address ethCCMProxyAddr ) onlyOwner public {
44+ managerProxyContract = ethCCMProxyAddr;
45+ emit SetManagerProxyEvent (managerProxyContract);
46+ }
47+
48+ function bindProxyHash (uint64 toChainId , bytes memory targetProxyHash ) onlyOwner public returns (bool ) {
49+ proxyHashMap[toChainId] = targetProxyHash;
50+ emit BindProxyEvent (toChainId, targetProxyHash);
51+ return true ;
52+ }
53+
54+ function bindAssetHash (address fromAssetHash , uint64 toChainId , bytes memory toAssetHash ) onlyOwner public returns (bool ) {
55+ assetHashMap[fromAssetHash][toChainId] = toAssetHash;
56+ emit BindAssetEvent (fromAssetHash, toChainId, toAssetHash);
57+ return true ;
58+ }
59+
60+ // /* @notice This function is meant to be invoked by the ETH crosschain management contract,
61+ // * then mint a certin amount of tokens to the designated address since a certain amount
62+ // * was burnt from the source chain invoker.
63+ // * @param argsBs The argument bytes recevied by the ethereum lock proxy contract, need to be deserialized.
64+ // * based on the way of serialization in the source chain proxy contract.
65+ // * @param fromContractAddr The source chain contract address
66+ // * @param fromChainId The source chain id
67+ // */
68+ function unlock (bytes memory argsBs , bytes memory fromContractAddr , uint64 fromChainId ) public onlyManagerContract returns (bool ) {
69+ TxArgs memory args = _deserializeTxArgs (argsBs);
70+
71+ require (fromContractAddr.length != 0 , "from proxy contract address cannot be empty " );
72+ require (Utils.equalStorage (proxyHashMap[fromChainId], fromContractAddr), "From Proxy contract address error! " );
73+
74+ require (args.toAssetHash.length != 0 , "toAssetHash cannot be empty " );
75+ address toAssetHash = Utils.bytesToAddress (args.toAssetHash);
76+
77+ require (args.toAddress.length != 0 , "toAddress cannot be empty " );
78+ address toAddress = Utils.bytesToAddress (args.toAddress);
79+
80+ bool success;
81+ bytes memory res;
82+ address owner;
83+ bytes memory raw = abi.encodeWithSignature ("ownerOf(uint256) " , args.tokenId);
84+ (success, res) = toAssetHash.call (raw);
85+ if (success) {
86+ owner = abi.decode (res, (address ));
87+ require (owner == address (this ) || owner == address (0 ), "your token ID is not hold by lockproxy. " );
88+ if (owner == address (this )) {
89+ raw = abi.encodeWithSignature ("safeTransferFrom(address,address,uint256) " , address (this ), toAddress, args.tokenId);
90+ (success, ) = toAssetHash.call (raw);
91+ require (success, "failed to call safeTransferFrom " );
92+ }
93+ }
94+ if (! success || owner == address (0 )) {
95+ raw = abi.encodeWithSignature ("mintWithURI(address,uint256,string) " , toAddress, args.tokenId, string (args.tokenURI));
96+ (success, ) = toAssetHash.call (raw);
97+ require (success, "failed to call mintWithURI to mint a new mapping NFT " );
98+ }
99+
100+ emit UnlockEvent (toAssetHash, toAddress, args.tokenId);
101+ return true ;
102+ }
103+
104+ function onERC721Received (address operator , address from , uint256 tokenId , bytes calldata data ) public override returns (bytes4 ) {
105+ address fromAssetHash = _msgSender ();
106+ require (data.length > 0 , "length of toAddress can't be zero. " );
107+ require (fromAssetHash.isContract (), "caller must be a contract. " );
108+
109+ bytes memory toAddress;
110+ uint64 toChainId;
111+ bytes memory toAssetHash;
112+ {
113+ (toAddress, toChainId) = _deserializeCallData (data);
114+ toAssetHash = assetHashMap[fromAssetHash][toChainId];
115+ require (toAssetHash.length != 0 , "empty illegal toAssetHash " );
116+
117+ IERC721Metadata nft = IERC721Metadata (fromAssetHash);
118+ require (nft.ownerOf (tokenId) == address (this ), "wrong owner for this token ID " );
119+
120+ string memory uri = nft.tokenURI (tokenId);
121+ TxArgs memory txArgs = TxArgs ({
122+ toAssetHash: toAssetHash,
123+ toAddress: toAddress,
124+ tokenId: tokenId,
125+ tokenURI: bytes (uri)
126+ });
127+ bytes memory txData = _serializeTxArgs (txArgs);
128+ IEthCrossChainManager eccm = IEthCrossChainManager (IEthCrossChainManagerProxy (managerProxyContract).getEthCrossChainManager ());
129+
130+ bytes memory toProxyHash = proxyHashMap[toChainId];
131+ require (toProxyHash.length != 0 , "empty illegal toProxyHash " );
132+ require (eccm.crossChain (toChainId, toProxyHash, "unlock " , txData), "EthCrossChainManager crossChain executed error! " );
133+ }
134+ {
135+ emit LockEvent (fromAssetHash, from, toAssetHash, toAddress, toChainId, tokenId);
136+ }
137+
138+ return this .onERC721Received.selector ;
139+ }
140+
141+ function _serializeTxArgs (TxArgs memory args ) internal pure returns (bytes memory ) {
142+ bytes memory buff;
143+ buff = abi.encodePacked (
144+ ZeroCopySink.WriteVarBytes (args.toAssetHash),
145+ ZeroCopySink.WriteVarBytes (args.toAddress),
146+ ZeroCopySink.WriteUint256 (args.tokenId),
147+ ZeroCopySink.WriteVarBytes (args.tokenURI)
148+ );
149+ return buff;
150+ }
151+
152+ function _deserializeTxArgs (bytes memory valueBs ) internal pure returns (TxArgs memory ) {
153+ TxArgs memory args;
154+ uint256 off = 0 ;
155+ (args.toAssetHash, off) = ZeroCopySource.NextVarBytes (valueBs, off);
156+ (args.toAddress, off) = ZeroCopySource.NextVarBytes (valueBs, off);
157+ (args.tokenId, off) = ZeroCopySource.NextUint256 (valueBs, off);
158+ (args.tokenURI, off) = ZeroCopySource.NextVarBytes (valueBs, off);
159+ return args;
160+ }
161+
162+ function _deserializeCallData (bytes memory valueBs ) internal pure returns (bytes memory , uint64 ) {
163+ bytes memory toAddress;
164+ uint64 chainId;
165+ uint256 off = 0 ;
166+ (toAddress, off) = ZeroCopySource.NextVarBytes (valueBs, off);
167+ (chainId, off) = ZeroCopySource.NextUint64 (valueBs, off);
168+ return (toAddress, chainId);
169+ }
170+ }
0 commit comments