Skip to content

Commit a03c7b0

Browse files
Implementing review: most aspects corrected, tests working
1 parent ca192b6 commit a03c7b0

File tree

79 files changed

+637
-1067
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+637
-1067
lines changed

packages/cactus-plugin-satp-hermes/src/main/solidity/contracts/SATPWrapperContract.sol

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// SPDX-License-Identifier: GPL-3.0
2-
pragma solidity 0.8.20;
2+
pragma solidity ^0.8.20;
33

44
import "@openzeppelin/contracts/access/Ownable.sol";
55
import "./ITraceableContract.sol";
66
import "@openzeppelin/contracts/utils/Strings.sol";
7+
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
78

89
/**
910
* @dev Enum for the supported token types.
@@ -12,11 +13,11 @@ enum TokenType { UNSPECIFIED, ERC20, ERC721, ERC1155, NONSTANDARD_FUNGIBLE, NONS
1213
/**
1314
* @dev Enum for the supported interaction types.
1415
*/
15-
enum InteractionType { MINT, BURN, ASSIGN, CHECKPERMITION, LOCK, UNLOCK }
16+
enum InteractionType { MINT, BURN, ASSIGN, CHECKPERMITION, LOCK, UNLOCK, APPROVE }
1617
/**
1718
* @dev Enum representing the supported variable types used for contract-to-contract calls.
1819
*/
19-
enum VarType {CONTRACTADDRESS, TOKENTYPE, TOKENID, OWNER, AMOUNT, BRIDGE, RECEIVER, UNIQUEDESCRIPTOR}
20+
enum AssetParameterIdentifier {CONTRACTADDRESS, TOKENTYPE, TOKENID, OWNER, AMOUNT, BRIDGE, RECEIVER, UNIQUE_DESCRIPTOR}
2021

2122

2223
/**
@@ -50,7 +51,7 @@ struct Token {
5051
struct InteractionSignature {
5152
InteractionType interactionType;
5253
string[] functionsSignature;
53-
VarType[][] variables;
54+
AssetParameterIdentifier[][] variables;
5455
bool available;
5556
}
5657

@@ -66,7 +67,9 @@ error TokenNotUnlocked(string tokenId);
6667

6768
error InsuficientAmountLocked(string tokenId, uint256 amount);
6869

69-
error TokenTypeNotSupported(string tokenId);
70+
error TokenNotSupported(string tokenId);
71+
72+
error TokenAlreadyLocked(string tokenId);
7073

7174

7275
/**
@@ -78,7 +81,7 @@ error TokenTypeNotSupported(string tokenId);
7881
*
7982
* @notice Ensure that the contract is deployed and configured correctly before interacting with it.
8083
*/
81-
contract SATPWrapperContract is Ownable, ITraceableContract{
84+
contract SATPWrapperContract is Ownable, ITraceableContract, IERC721Receiver{
8285

8386
/**
8487
* Maping of token IDs to Token structs.
@@ -105,6 +108,7 @@ contract SATPWrapperContract is Ownable, ITraceableContract{
105108
event Mint(string indexed tokenId, uint256 amount);
106109
event Burn(string indexed tokenId, uint256 amount);
107110
event Assign(string indexed tokenId, address receiver_account, uint256 amount);
111+
event Approve(string indexed tokenId, address spender, uint256 amount);
108112

109113

110114
/**
@@ -174,7 +178,7 @@ contract SATPWrapperContract is Ownable, ITraceableContract{
174178
}
175179
}
176180
else {
177-
revert TokenTypeNotSupported(tokenId);
181+
revert TokenNotSupported(tokenId);
178182
}
179183
deleteFromArray(tokens[tokenId].tokenId);
180184
delete tokens[tokenId];
@@ -203,8 +207,14 @@ contract SATPWrapperContract is Ownable, ITraceableContract{
203207
}
204208
else if (tt == TokenType.ERC721 || tt == TokenType.NONSTANDARD_NONFUNGIBLE) {
205209
// When dealing with non-fungible tokens, the "amount" is interpreted as the token unique descriptor.
210+
if(tokens[tokenId].amount != 0) {
211+
revert TokenAlreadyLocked(tokenId);
212+
}
206213
tokens[tokenId].amount = assetAttribute;
207214
}
215+
else {
216+
revert TokenNotSupported(tokenId);
217+
}
208218
emit Lock(tokenId, assetAttribute);
209219
return true;
210220
}
@@ -239,9 +249,10 @@ contract SATPWrapperContract is Ownable, ITraceableContract{
239249
tokens[tokenId].amount = 0;
240250
emit Unlock(tokenId, assetAttribute);
241251
return true;
242-
}
243-
244-
revert TokenNotUnlocked(tokenId);
252+
}
253+
else {
254+
revert TokenNotSupported(tokenId);
255+
}
245256
}
246257

247258
/**
@@ -292,7 +303,7 @@ contract SATPWrapperContract is Ownable, ITraceableContract{
292303
return true;
293304
}
294305
else {
295-
revert TokenTypeNotSupported(tokenId);
306+
revert TokenNotSupported(tokenId);
296307
}
297308
}
298309

@@ -325,9 +336,23 @@ contract SATPWrapperContract is Ownable, ITraceableContract{
325336
return true;
326337
}
327338
else {
328-
revert TokenTypeNotSupported(tokenId);
339+
revert TokenNotSupported(tokenId);
329340
}
330-
}
341+
}
342+
343+
/**
344+
* @notice REQUIRED by OpenZeppelin: Supports the use of safe functions for ERC721 tokens.
345+
* @return success A boolean indicating if the account has the bridge role.
346+
*/
347+
function onERC721Received(
348+
address,
349+
address,
350+
uint256,
351+
bytes calldata
352+
) external pure override returns (bytes4) {
353+
return this.onERC721Received.selector;
354+
}
355+
331356

332357
/**
333358
* Gets all the token IDs.
@@ -407,7 +432,7 @@ contract SATPWrapperContract is Ownable, ITraceableContract{
407432
for (uint i = 0; i < tokensInteractions[tokenId][interactionType].functionsSignature.length; i++) {
408433
bytes4 functionSelector = bytes4(keccak256(abi.encodePacked(tokensInteractions[tokenId][interactionType].functionsSignature[i])));
409434

410-
bytes memory encodedParams = encodeDynamicParams(functionSelector, encodeParams(tokensInteractions[tokenId][interactionType].variables[i], tokenId, receiver, assetAttribute));
435+
bytes memory encodedParams = encodeDynamicParams(functionSelector, AssetParameterIdentifierEncoder(tokensInteractions[tokenId][interactionType].variables[i], tokenId, receiver, assetAttribute));
411436

412437
(bool callSuccess, ) = tokens[tokenId].contractAddress.call(encodedParams);
413438
if (!callSuccess) {
@@ -438,22 +463,22 @@ contract SATPWrapperContract is Ownable, ITraceableContract{
438463
* @param receiver The address of the receiver account.
439464
* @param assetAttribute The asset attribute of tokens to be encoded.
440465
*/
441-
function encodeParams(VarType[] memory variables, string memory tokenId, address receiver, uint256 assetAttribute) internal view returns (bytes[] memory){
466+
function AssetParameterIdentifierEncoder(AssetParameterIdentifier[] memory variables, string memory tokenId, address receiver, uint256 assetAttribute) internal view returns (bytes[] memory){
442467
bytes[] memory dynamicParams = new bytes[](variables.length);
443468
for (uint i = 0; i < variables.length; i++) {
444-
if (variables[i] == VarType.BRIDGE) {
469+
if (variables[i] == AssetParameterIdentifier.BRIDGE) {
445470
dynamicParams[i] = abi.encode(address(this));
446-
} else if (variables[i] == VarType.TOKENID) {
471+
} else if (variables[i] == AssetParameterIdentifier.TOKENID) {
447472
dynamicParams[i] = abi.encode(tokenId);
448-
} else if (variables[i] == VarType.AMOUNT) {
473+
} else if (variables[i] == AssetParameterIdentifier.AMOUNT) {
449474
dynamicParams[i] = abi.encode(assetAttribute);
450-
} else if (variables[i] == VarType.OWNER) {
475+
} else if (variables[i] == AssetParameterIdentifier.OWNER) {
451476
dynamicParams[i] = abi.encode(tokens[tokenId].owner);
452-
} else if (variables[i] == VarType.CONTRACTADDRESS) {
477+
} else if (variables[i] == AssetParameterIdentifier.CONTRACTADDRESS) {
453478
dynamicParams[i] = abi.encode(tokens[tokenId].contractAddress);
454-
} else if (variables[i] == VarType.RECEIVER) {
479+
} else if (variables[i] == AssetParameterIdentifier.RECEIVER) {
455480
dynamicParams[i] = abi.encode(receiver);
456-
} else if (variables[i] == VarType.UNIQUEDESCRIPTOR) {
481+
} else if (variables[i] == AssetParameterIdentifier.UNIQUE_DESCRIPTOR) {
457482
dynamicParams[i] = abi.encode(assetAttribute);
458483
} else {
459484
revert("Variable not supported");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"abi":[],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"details\":\"Provides information about the current execution context, including the sender of the transaction and its data. While these are generally available via msg.sender and msg.data, they should not be accessed in such a direct manner, since when dealing with meta-transactions the account sending and paying for execution may not be the actual sender (as far as an application is concerned). This contract is only required for intermediate, library-like contracts.\",\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"node_modules/@openzeppelin/contracts/utils/Context.sol\":\"Context\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000},\"remappings\":[\":@openzeppelin/=node_modules/@openzeppelin/\",\":ds-test/=node_modules/ds-test/src/\",\":forge-std/=node_modules/forge-std/src/\",\":hardhat/=node_modules/hardhat/\",\":openzeppelin-solidity/=node_modules/openzeppelin-solidity/\"]},\"sources\":{\"node_modules/@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12\",\"dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.20+commit.a1b79de6"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin/=node_modules/@openzeppelin/","ds-test/=node_modules/ds-test/src/","forge-std/=node_modules/forge-std/src/","hardhat/=node_modules/hardhat/","openzeppelin-solidity/=node_modules/openzeppelin-solidity/"],"optimizer":{"enabled":true,"runs":1000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"node_modules/@openzeppelin/contracts/utils/Context.sol":"Context"},"evmVersion":"paris","libraries":{}},"sources":{"node_modules/@openzeppelin/contracts/utils/Context.sol":{"keccak256":"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2","urls":["bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12","dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF"],"license":"MIT"}},"version":1},"id":1}
1+
{"abi":[],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"details\":\"Provides information about the current execution context, including the sender of the transaction and its data. While these are generally available via msg.sender and msg.data, they should not be accessed in such a direct manner, since when dealing with meta-transactions the account sending and paying for execution may not be the actual sender (as far as an application is concerned). This contract is only required for intermediate, library-like contracts.\",\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"node_modules/@openzeppelin/contracts/utils/Context.sol\":\"Context\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000},\"remappings\":[\":@openzeppelin/=node_modules/@openzeppelin/\",\":ds-test/=node_modules/ds-test/src/\",\":forge-std/=node_modules/forge-std/src/\",\":hardhat/=node_modules/hardhat/\",\":openzeppelin-solidity/=node_modules/openzeppelin-solidity/\"]},\"sources\":{\"node_modules/@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12\",\"dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.20+commit.a1b79de6"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin/=node_modules/@openzeppelin/","ds-test/=node_modules/ds-test/src/","forge-std/=node_modules/forge-std/src/","hardhat/=node_modules/hardhat/","openzeppelin-solidity/=node_modules/openzeppelin-solidity/"],"optimizer":{"enabled":true,"runs":1000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"node_modules/@openzeppelin/contracts/utils/Context.sol":"Context"},"evmVersion":"paris","libraries":{}},"sources":{"node_modules/@openzeppelin/contracts/utils/Context.sol":{"keccak256":"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2","urls":["bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12","dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF"],"license":"MIT"}},"version":1},"id":2}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"abi":[{"type":"function","name":"onERC721Received","inputs":[{"name":"operator","type":"address","internalType":"address"},{"name":"from","type":"address","internalType":"address"},{"name":"tokenId","type":"uint256","internalType":"uint256"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bytes4","internalType":"bytes4"}],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"onERC721Received(address,address,uint256,bytes)":"150b7a02"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onERC721Received\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Interface for any contract that wants to support safeTransfers from ERC721 asset contracts.\",\"kind\":\"dev\",\"methods\":{\"onERC721Received(address,address,uint256,bytes)\":{\"details\":\"Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} by `operator` from `from`, this function is called. It must return its Solidity selector to confirm the token transfer. If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\"}},\"title\":\"ERC721 token receiver interface\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\":\"IERC721Receiver\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000},\"remappings\":[\":@openzeppelin/=node_modules/@openzeppelin/\",\":ds-test/=node_modules/ds-test/src/\",\":forge-std/=node_modules/forge-std/src/\",\":hardhat/=node_modules/hardhat/\",\":openzeppelin-solidity/=node_modules/openzeppelin-solidity/\"]},\"sources\":{\"node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\":{\"keccak256\":\"0x7f7a26306c79a65fb8b3b6c757cd74660c532cd8a02e165488e30027dd34ca49\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d01e0b2b837ee2f628545e54d8715b49c7ef2befd08356c2e7f6c50dde8a1c22\",\"dweb:/ipfs/QmWBAn6y2D1xgftci97Z3qR9tQnkvwQpYwFwkTvDMvqU4i\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.20+commit.a1b79de6"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}]}],"devdoc":{"kind":"dev","methods":{"onERC721Received(address,address,uint256,bytes)":{"details":"Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} by `operator` from `from`, this function is called. It must return its Solidity selector to confirm the token transfer. If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`."}},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin/=node_modules/@openzeppelin/","ds-test/=node_modules/ds-test/src/","forge-std/=node_modules/forge-std/src/","hardhat/=node_modules/hardhat/","openzeppelin-solidity/=node_modules/openzeppelin-solidity/"],"optimizer":{"enabled":true,"runs":1000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol":"IERC721Receiver"},"evmVersion":"paris","libraries":{}},"sources":{"node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol":{"keccak256":"0x7f7a26306c79a65fb8b3b6c757cd74660c532cd8a02e165488e30027dd34ca49","urls":["bzz-raw://d01e0b2b837ee2f628545e54d8715b49c7ef2befd08356c2e7f6c50dde8a1c22","dweb:/ipfs/QmWBAn6y2D1xgftci97Z3qR9tQnkvwQpYwFwkTvDMvqU4i"],"license":"MIT"}},"version":1},"id":1}

0 commit comments

Comments
 (0)