From e032a190d7ee26e5b36af27fe1db016b996a3fe9 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 2 Jul 2024 15:05:18 +0200 Subject: [PATCH 1/3] Added KnowledgeCollection early demo PoC --- abi/ERC1155Delta.json | 404 ++++++++++++++ abi/IERC1155.json | 295 ++++++++++ abi/IERC1155Delta.json | 81 +++ abi/IERC1155MetadataURI.json | 314 +++++++++++ abi/IERC1155Receiver.json | 99 ++++ abi/KnowledgeCollection.json | 571 ++++++++++++++++++++ contracts/tokens/ERC1155Delta.sol | 528 ++++++++++++++++++ contracts/tokens/IERC1155Delta.sol | 63 +++ contracts/v2/assets/KnowledgeCollection.sol | 36 ++ deploy/047_deploy_knowledge_collection.ts | 12 + package-lock.json | 12 + package.json | 1 + 12 files changed, 2416 insertions(+) create mode 100644 abi/ERC1155Delta.json create mode 100644 abi/IERC1155.json create mode 100644 abi/IERC1155Delta.json create mode 100644 abi/IERC1155MetadataURI.json create mode 100644 abi/IERC1155Receiver.json create mode 100644 abi/KnowledgeCollection.json create mode 100644 contracts/tokens/ERC1155Delta.sol create mode 100644 contracts/tokens/IERC1155Delta.sol create mode 100644 contracts/v2/assets/KnowledgeCollection.sol create mode 100644 deploy/047_deploy_knowledge_collection.ts diff --git a/abi/ERC1155Delta.json b/abi/ERC1155Delta.json new file mode 100644 index 00000000..19256814 --- /dev/null +++ b/abi/ERC1155Delta.json @@ -0,0 +1,404 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "uri_", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceQueryForZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "BurnFromNonOnwerAddress", + "type": "error" + }, + { + "inputs": [], + "name": "BurnFromZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InputLengthMistmatch", + "type": "error" + }, + { + "inputs": [], + "name": "MintToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintZeroQuantity", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFromIncorrectOwnerOrInvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToNonERC1155ReceiverImplementer", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "isOwnerOf", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/IERC1155.json b/abi/IERC1155.json new file mode 100644 index 00000000..f8391d5b --- /dev/null +++ b/abi/IERC1155.json @@ -0,0 +1,295 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/IERC1155Delta.json b/abi/IERC1155Delta.json new file mode 100644 index 00000000..14f45158 --- /dev/null +++ b/abi/IERC1155Delta.json @@ -0,0 +1,81 @@ +[ + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceQueryForZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "BurnFromNonOnwerAddress", + "type": "error" + }, + { + "inputs": [], + "name": "BurnFromZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InputLengthMistmatch", + "type": "error" + }, + { + "inputs": [], + "name": "MintToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintZeroQuantity", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFromIncorrectOwnerOrInvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToNonERC1155ReceiverImplementer", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToZeroAddress", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "isOwnerOf", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/IERC1155MetadataURI.json b/abi/IERC1155MetadataURI.json new file mode 100644 index 00000000..a4d8e9cf --- /dev/null +++ b/abi/IERC1155MetadataURI.json @@ -0,0 +1,314 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/IERC1155Receiver.json b/abi/IERC1155Receiver.json new file mode 100644 index 00000000..66694c4e --- /dev/null +++ b/abi/IERC1155Receiver.json @@ -0,0 +1,99 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/KnowledgeCollection.json b/abi/KnowledgeCollection.json new file mode 100644 index 00000000..effd7039 --- /dev/null +++ b/abi/KnowledgeCollection.json @@ -0,0 +1,571 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "uri", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceQueryForZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "BurnFromNonOnwerAddress", + "type": "error" + }, + { + "inputs": [], + "name": "BurnFromZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InputLengthMistmatch", + "type": "error" + }, + { + "inputs": [], + "name": "MintToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintZeroQuantity", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFromIncorrectOwnerOrInvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToNonERC1155ReceiverImplementer", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "custodian", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "MisplacedOTPWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "custodian", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "tokenContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "MisplacedTokensWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "custodian", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "TokenTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "newMerkleRoot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "quantity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "size", + "type": "uint256" + }, + { + "internalType": "uint160", + "name": "chunksNumber", + "type": "uint160" + }, + { + "internalType": "uint96", + "name": "tokenAmount", + "type": "uint96" + } + ], + "name": "batchMint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "isOwnerOf", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenContract", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "custodian", + "type": "address" + } + ], + "name": "transferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawMisplacedOTP", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenContractAddress", + "type": "address" + } + ], + "name": "withdrawMisplacedTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/contracts/tokens/ERC1155Delta.sol b/contracts/tokens/ERC1155Delta.sol new file mode 100644 index 00000000..3d60f2d0 --- /dev/null +++ b/contracts/tokens/ERC1155Delta.sol @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; + +import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol"; + +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts/utils/Context.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +import "solady/src/utils/LibBitmap.sol"; + +import "./IERC1155Delta.sol"; + +contract ERC1155Delta is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Delta { + using Address for address; + using LibBitmap for LibBitmap.Bitmap; + + // Mapping from accout to owned tokens + mapping(address => LibBitmap.Bitmap) internal _owned; + + // Mapping from account to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json + string private _uri; + + // The next token ID to be minted. + uint256 private _currentIndex; + + /** + * @dev See {_setURI}. + */ + constructor(string memory uri_) { + _setURI(uri_); + _currentIndex = _startTokenId(); + } + + /** + * @dev Returns the starting token ID. + * To change the starting token ID, please override this function. + */ + function _startTokenId() internal pure virtual returns (uint256) { + return 0; + } + + /** + * @dev Returns the next token ID to be minted. + */ + function _nextTokenId() internal view returns (uint256) { + return _currentIndex; + } + + /** + * @dev Returns the total amount of tokens minted in the contract. + */ + function _totalMinted() internal view returns (uint256) { + return _nextTokenId() - _startTokenId(); + } + + /** + * @dev Returns true if the account owns the `id` token. + */ + function isOwnerOf(address account, uint256 id) public view virtual override returns (bool) { + return _owned[account].get(id); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC1155).interfaceId || + interfaceId == type(IERC1155MetadataURI).interfaceId || + interfaceId == type(IERC1155Delta).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC1155MetadataURI-uri}. + * + * This implementation returns the same URI for *all* token types. It relies + * on the token type ID substitution mechanism + * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. + * + * Clients calling this function must replace the `\{id\}` substring with the + * actual token type ID. + */ + function uri(uint256) public view virtual override returns (string memory) { + return _uri; + } + + /** + * @dev See {IERC1155-balanceOf}. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { + if (account == address(0)) { + revert BalanceQueryForZeroAddress(); + } + if (_owned[account].get(id)) { + return 1; + } else { + return 0; + } + } + + /** + * @dev See {IERC1155-balanceOfBatch}. + * + * Requirements: + * + * - `accounts` and `ids` must have the same length. + */ + function balanceOfBatch( + address[] memory accounts, + uint256[] memory ids + ) public view virtual override returns (uint256[] memory) { + if (accounts.length != ids.length) { + revert InputLengthMistmatch(); + } + + uint256[] memory batchBalances = new uint256[](accounts.length); + + for (uint256 i = 0; i < accounts.length; ++i) { + batchBalances[i] = balanceOf(accounts[i], ids[i]); + } + + return batchBalances; + } + + /** + * @dev See {IERC1155-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC1155-isApprovedForAll}. + */ + function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { + return _operatorApprovals[account][operator]; + } + + /** + * @dev See {IERC1155-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual override { + if (from == _msgSender() || isApprovedForAll(from, _msgSender())) { + _safeTransferFrom(from, to, id, amount, data); + } else { + revert TransferCallerNotOwnerNorApproved(); + } + } + + /** + * @dev See {IERC1155-safeBatchTransferFrom}. + */ + function safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual override { + if (!(from == _msgSender() || isApprovedForAll(from, _msgSender()))) { + revert TransferCallerNotOwnerNorApproved(); + } + _safeBatchTransferFrom(from, to, ids, amounts, data); + } + + /** + * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `amount` cannot be zero. + * - `from` must have a balance of tokens of type `id` of at least `amount`. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) internal virtual { + if (to == address(0)) { + revert TransferToZeroAddress(); + } + + address operator = _msgSender(); + uint256[] memory ids = _asSingletonArray(id); + + _beforeTokenTransfer(operator, from, to, ids); + + if (amount == 1 && _owned[from].get(id)) { + _owned[from].unset(id); + _owned[to].set(id); + } else { + revert TransferFromIncorrectOwnerOrInvalidAmount(); + } + + emit TransferSingle(operator, from, to, id, amount); + + _afterTokenTransfer(operator, from, to, ids); + + _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function _safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual { + if (ids.length != amounts.length) { + revert InputLengthMistmatch(); + } + + if (to == address(0)) { + revert TransferToZeroAddress(); + } + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, to, ids); + + for (uint256 i = 0; i < ids.length; ++i) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + + if (amount == 1 && _owned[from].get(id)) { + _owned[from].unset(id); + _owned[to].set(id); + } else { + revert TransferFromIncorrectOwnerOrInvalidAmount(); + } + } + + emit TransferBatch(operator, from, to, ids, amounts); + + _afterTokenTransfer(operator, from, to, ids); + + _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); + } + + /** + * @dev Sets a new URI for all token types, by relying on the token type ID + * substitution mechanism + * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. + * + * By this mechanism, any occurrence of the `\{id\}` substring in either the + * URI or any of the amounts in the JSON file at said URI will be replaced by + * clients with the token type ID. + * + * For example, the `https://token-cdn-domain/\{id\}.json` URI would be + * interpreted by clients as + * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` + * for token type ID 0x4cce0. + * + * See {uri}. + * + * Because these URIs cannot be meaningfully represented by the {URI} event, + * this function emits no events. + */ + function _setURI(string memory newuri) internal virtual { + _uri = newuri; + } + + function _mint(address to, uint256 amount) internal virtual { + _mint(to, amount, ""); + } + + /** + * @dev Creates `amount` tokens, and assigns them to `to`. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `amount` cannot be zero. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _mint(address to, uint256 amount, bytes memory data) internal virtual { + (uint256[] memory ids, uint256[] memory amounts) = _mintWithoutCheck(to, amount); + + uint256 end = _currentIndex; + _doSafeBatchTransferAcceptanceCheck(_msgSender(), address(0), to, ids, amounts, data); + if (_currentIndex != end) revert(); + } + + function _mintWithoutCheck( + address to, + uint256 amount + ) internal virtual returns (uint256[] memory ids, uint256[] memory amounts) { + if (to == address(0)) { + revert MintToZeroAddress(); + } + if (amount == 0) { + revert MintZeroQuantity(); + } + + address operator = _msgSender(); + + ids = new uint256[](amount); + amounts = new uint256[](amount); + uint256 startTokenId = _nextTokenId(); + + unchecked { + require(type(uint256).max - amount >= startTokenId); + for (uint256 i = 0; i < amount; i++) { + ids[i] = startTokenId + i; + amounts[i] = 1; + } + } + + _beforeTokenTransfer(operator, address(0), to, ids); + + _owned[to].setBatch(startTokenId, amount); + _currentIndex += amount; + + emit TransferBatch(operator, address(0), to, ids, amounts); + + _afterTokenTransfer(operator, address(0), to, ids); + } + + /** + * @dev Destroys token of token type `id` from `from` + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `from` must have the token of token type `id`. + */ + function _burn(address from, uint256 id) internal virtual { + if (from == address(0)) { + revert BurnFromZeroAddress(); + } + + address operator = _msgSender(); + uint256[] memory ids = _asSingletonArray(id); + + _beforeTokenTransfer(operator, from, address(0), ids); + + if (!_owned[from].get(id)) { + revert BurnFromNonOnwerAddress(); + } + + _owned[from].unset(id); + + emit TransferSingle(operator, from, address(0), id, 1); + + _afterTokenTransfer(operator, from, address(0), ids); + } + + /** + * @dev Destroys tokens of token types in `ids` from `from` + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `from` must have the token of token types in `ids`. + */ + function _burnBatch(address from, uint256[] memory ids) internal virtual { + if (from == address(0)) { + revert BurnFromZeroAddress(); + } + + address operator = _msgSender(); + + uint256[] memory amounts = new uint256[](ids.length); + + _beforeTokenTransfer(operator, from, address(0), ids); + + unchecked { + for (uint256 i = 0; i < ids.length; i++) { + amounts[i] = 1; + uint256 id = ids[i]; + if (!_owned[from].get(id)) { + revert BurnFromNonOnwerAddress(); + } + _owned[from].unset(id); + } + } + + emit TransferBatch(operator, from, address(0), ids, amounts); + + _afterTokenTransfer(operator, from, address(0), ids); + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits an {ApprovalForAll} event. + */ + function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { + require(owner != operator, "ERC1155: setting approval status for self"); + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning, as well as batched variants. + * + * The same hook is called on both single and batched variants. For single + * transfers, the length of the `ids` and `amounts` arrays will be 1. + * + * Calling conditions (for each `id` and `amount` pair): + * + * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * of token type `id` will be transferred to `to`. + * - When `from` is zero, `amount` tokens of token type `id` will be minted + * for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` + * will be burned. + * - `from` and `to` are never both zero. + * - `ids` and `amounts` have the same, non-zero length. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address operator, address from, address to, uint256[] memory ids) internal virtual {} + + /** + * @dev Hook that is called after any token transfer. This includes minting + * and burning, as well as batched variants. + * + * The same hook is called on both single and batched variants. For single + * transfers, the length of the `id` and `amount` arrays will be 1. + * + * Calling conditions (for each `id` and `amount` pair): + * + * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * of token type `id` will be transferred to `to`. + * - When `from` is zero, `amount` tokens of token type `id` will be minted + * for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` + * will be burned. + * - `from` and `to` are never both zero. + * - `ids` and `amounts` have the same, non-zero length. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer(address operator, address from, address to, uint256[] memory ids) internal virtual {} + + function _doSafeTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) private { + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { + if (response != IERC1155Receiver.onERC1155Received.selector) { + revert TransferToNonERC1155ReceiverImplementer(); + } + } catch Error(string memory reason) { + revert(reason); + } catch { + revert TransferToNonERC1155ReceiverImplementer(); + } + } + } + + function _doSafeBatchTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) private { + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( + bytes4 response + ) { + if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { + revert TransferToNonERC1155ReceiverImplementer(); + } + } catch Error(string memory reason) { + revert(reason); + } catch { + revert TransferToNonERC1155ReceiverImplementer(); + } + } + } + + function _asSingletonArray(uint256 element) private pure returns (uint256[] memory array) { + array = new uint256[](1); + array[0] = element; + } +} diff --git a/contracts/tokens/IERC1155Delta.sol b/contracts/tokens/IERC1155Delta.sol new file mode 100644 index 00000000..64bd95a6 --- /dev/null +++ b/contracts/tokens/IERC1155Delta.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +interface IERC1155Delta { + /** + * The caller must own the token or be an approved operator. + */ + error ApprovalCallerNotOwnerNorApproved(); + + /** + * Cannot query the balance for the zero address. + */ + error BalanceQueryForZeroAddress(); + + /** + * Cannot mint to the zero address. + */ + error MintToZeroAddress(); + + /** + * The quantity of tokens minted must be more than zero. + */ + error MintZeroQuantity(); + + /** + * Cannot burn from the zero address. + */ + error BurnFromZeroAddress(); + + /** + * Cannot burn from the address that doesn't owne the token. + */ + error BurnFromNonOnwerAddress(); + + /** + * The caller must own the token or be an approved operator. + */ + error TransferCallerNotOwnerNorApproved(); + + /** + * The token must be owned by `from` or the `amount` is not 1. + */ + error TransferFromIncorrectOwnerOrInvalidAmount(); + + /** + * Cannot safely transfer to a contract that does not implement the + * ERC1155Receiver interface. + */ + error TransferToNonERC1155ReceiverImplementer(); + + /** + * Cannot transfer to the zero address. + */ + error TransferToZeroAddress(); + + /** + * The length of input arraies is not matching. + */ + error InputLengthMistmatch(); + + function isOwnerOf(address account, uint256 id) external view returns (bool); +} diff --git a/contracts/v2/assets/KnowledgeCollection.sol b/contracts/v2/assets/KnowledgeCollection.sol new file mode 100644 index 00000000..14ec07b4 --- /dev/null +++ b/contracts/v2/assets/KnowledgeCollection.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {Guardian} from "../../v1/Guardian.sol"; +import {ERC1155Delta} from "../../tokens/ERC1155Delta.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract KnowledgeCollection is ERC1155Delta, Guardian { + bytes32 merkleRoot; + uint256 totalSize; + uint160 totalChunksNumber; + uint96 totalTokenAmount; + + constructor(address hubAddress, string memory uri) ERC1155Delta(uri) Guardian(hubAddress) {} + + function batchMint( + bytes32 newMerkleRoot, + uint256 quantity, + uint256 size, + uint160 chunksNumber, + uint96 tokenAmount + ) external { + merkleRoot = newMerkleRoot; + totalSize += size; + totalChunksNumber += chunksNumber; + totalTokenAmount += tokenAmount; + + _mint(msg.sender, quantity); + + IERC20 tknc = tokenContract; + require(tknc.allowance(msg.sender, address(this)) >= tokenAmount); + require(tknc.balanceOf(msg.sender) >= tokenAmount); + tknc.transferFrom(msg.sender, address(this), tokenAmount); + } +} diff --git a/deploy/047_deploy_knowledge_collection.ts b/deploy/047_deploy_knowledge_collection.ts new file mode 100644 index 00000000..dc34b4c4 --- /dev/null +++ b/deploy/047_deploy_knowledge_collection.ts @@ -0,0 +1,12 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + await hre.helpers.deploy({ + newContractName: 'KnowledgeCollection', + }); +}; + +export default func; +func.tags = ['KnowledgeCollection', 'v2']; +func.dependencies = ['HubV2']; diff --git a/package-lock.json b/package-lock.json index fac7876c..b98311ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "hardhat": "^2.22.5", "hardhat-deploy": "^0.11.25", "hardhat-deploy-ethers": "^0.3.0-beta.13", + "solady": "^0.0.217", "ts-node": "^10.9.1", "typescript": "^4.9.4" }, @@ -13314,6 +13315,12 @@ "no-case": "^2.2.0" } }, + "node_modules/solady": { + "version": "0.0.217", + "resolved": "https://registry.npmjs.org/solady/-/solady-0.0.217.tgz", + "integrity": "sha512-F2SiaUbzWrAabcP3z7hQdujRuv8SEe4sWqPZ9zlvIloV1FyXFCwPw+h3Q5P/jbqVHNx5QzfduncBgX7dVcDDAA==", + "license": "MIT" + }, "node_modules/solc": { "version": "0.4.26", "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz", @@ -26175,6 +26182,11 @@ "no-case": "^2.2.0" } }, + "solady": { + "version": "0.0.217", + "resolved": "https://registry.npmjs.org/solady/-/solady-0.0.217.tgz", + "integrity": "sha512-F2SiaUbzWrAabcP3z7hQdujRuv8SEe4sWqPZ9zlvIloV1FyXFCwPw+h3Q5P/jbqVHNx5QzfduncBgX7dVcDDAA==" + }, "solc": { "version": "0.4.26", "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz", diff --git a/package.json b/package.json index 4ad864c4..c61b4a1f 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "hardhat": "^2.22.5", "hardhat-deploy": "^0.11.25", "hardhat-deploy-ethers": "^0.3.0-beta.13", + "solady": "^0.0.217", "ts-node": "^10.9.1", "typescript": "^4.9.4" }, From 490fb7f580ffc7f9390ffd19ad636eee83a6b860 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 2 Jul 2024 15:50:14 +0200 Subject: [PATCH 2/3] Added missing KnowledgeCollection constructor argument --- deploy/047_deploy_knowledge_collection.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy/047_deploy_knowledge_collection.ts b/deploy/047_deploy_knowledge_collection.ts index dc34b4c4..5352ae1c 100644 --- a/deploy/047_deploy_knowledge_collection.ts +++ b/deploy/047_deploy_knowledge_collection.ts @@ -4,6 +4,7 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { await hre.helpers.deploy({ newContractName: 'KnowledgeCollection', + additionalArgs: [''], }); }; From f5669f7a3e9e88d368b06024ae6836c56391dd52 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 16 Jul 2024 17:30:51 +0700 Subject: [PATCH 3/3] Added possibility to mine Paranet KnowledgeAsset for someone --- abi/ContentAssetV2.json | 7 +- abi/Paranet.json | 76 ++++++++++++++++ contracts/v2/assets/ContentAsset.sol | 34 +++++-- contracts/v2/paranets/Paranet.sol | 127 ++++++++++++++++----------- package-lock.json | 4 +- package.json | 2 +- test/v2/unit/Paranet.test.ts | 90 ++++++++++++++++++- 7 files changed, 274 insertions(+), 66 deletions(-) diff --git a/abi/ContentAssetV2.json b/abi/ContentAssetV2.json index e9f97705..32e116e6 100644 --- a/abi/ContentAssetV2.json +++ b/abi/ContentAssetV2.json @@ -435,7 +435,12 @@ "inputs": [ { "internalType": "address", - "name": "originalSender", + "name": "minter", + "type": "address" + }, + { + "internalType": "address", + "name": "payer", "type": "address" }, { diff --git a/abi/Paranet.json b/abi/Paranet.json index 7eca7ac3..15ce2ba6 100644 --- a/abi/Paranet.json +++ b/abi/Paranet.json @@ -511,6 +511,82 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "paranetKAStorageContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "paranetKATokenId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "assertionId", + "type": "bytes32" + }, + { + "internalType": "uint128", + "name": "size", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "triplesNumber", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "chunksNumber", + "type": "uint96" + }, + { + "internalType": "uint16", + "name": "epochsNumber", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "tokenAmount", + "type": "uint96" + }, + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "immutable_", + "type": "bool" + } + ], + "internalType": "struct ContentAssetStructs.AssetInputArgs", + "name": "knowledgeAssetArgs", + "type": "tuple" + }, + { + "internalType": "address", + "name": "miner", + "type": "address" + } + ], + "name": "mintKnowledgeAssetFor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "name", diff --git a/contracts/v2/assets/ContentAsset.sol b/contracts/v2/assets/ContentAsset.sol index 79b4a527..53b0f1c9 100644 --- a/contracts/v2/assets/ContentAsset.sol +++ b/contracts/v2/assets/ContentAsset.sol @@ -19,6 +19,7 @@ import {Versioned} from "../../v1/interface/Versioned.sol"; import {ContentAssetStructs} from "../../v1/structs/assets/ContentAssetStructs.sol"; import {ServiceAgreementStructsV1} from "../../v1/structs/ServiceAgreementStructsV1.sol"; import {ContentAssetErrors} from "../errors/assets/ContentAssetErrors.sol"; +import {ParanetErrors} from "../errors/paranets/ParanetErrors.sol"; import {HASH_FUNCTION_ID} from "../../v1/constants/assets/ContentAssetConstants.sol"; import {LOG2PLDSF_ID, LINEAR_SUM_ID} from "../../v1/constants/ScoringConstants.sol"; @@ -47,7 +48,7 @@ contract ContentAssetV2 is Named, Versioned, HubDependentV2, Initializable { event AssetUpdatePaymentIncreased(address indexed assetContract, uint256 indexed tokenId, uint96 tokenAmount); string private constant _NAME = "ContentAsset"; - string private constant _VERSION = "2.1.0"; + string private constant _VERSION = "2.2.0"; Assertion public assertionContract; HashingProxy public hashingProxy; @@ -103,6 +104,7 @@ contract ContentAssetV2 is Named, Versioned, HubDependentV2, Initializable { function createAsset(ContentAssetStructs.AssetInputArgs calldata args) external returns (uint256) { return _createAsset( + msg.sender, msg.sender, args.assertionId, args.size, @@ -116,12 +118,14 @@ contract ContentAssetV2 is Named, Versioned, HubDependentV2, Initializable { } function createAssetFromContract( - address originalSender, + address minter, + address payer, ContentAssetStructs.AssetInputArgs calldata args ) external onlyContracts returns (uint256) { return _createAsset( - originalSender, + minter, + payer, args.assertionId, args.size, args.triplesNumber, @@ -145,6 +149,7 @@ contract ContentAssetV2 is Named, Versioned, HubDependentV2, Initializable { ) external returns (uint256) { return _createAsset( + msg.sender, msg.sender, assertionId, size, @@ -455,13 +460,28 @@ contract ContentAssetV2 is Named, Versioned, HubDependentV2, Initializable { revert ContentAssetErrors.AssetExpired(tokenId); } + ParanetKnowledgeAssetsRegistry pkar = paranetKnowledgeAssetsRegistry; + + if (pkar.isParanetKnowledgeAsset(keccak256(abi.encodePacked(contentAssetStorageAddress, tokenId)))) { + bytes32 paranetId = pkar.getParanetId(keccak256(abi.encodePacked(contentAssetStorageAddress, tokenId))); + + // Add additional tokenAmount to the UpdatingKnowledgeAssets in the KnowledgeMinersRegistry + paranetKnowledgeMinersRegistry.addUpdatingKnowledgeAssetUpdateTokenAmount( + msg.sender, + paranetId, + keccak256(abi.encodePacked(contentAssetStorageAddress, tokenId, unfinalizedState)), + tokenAmount + ); + } + sasV1.addUpdateTokens(msg.sender, agreementId, tokenAmount); emit AssetUpdatePaymentIncreased(contentAssetStorageAddress, tokenId, tokenAmount); } function _createAsset( - address originalSender, + address minter, + address payer, bytes32 assertionId, uint128 size, uint32 triplesNumber, @@ -474,10 +494,10 @@ contract ContentAssetV2 is Named, Versioned, HubDependentV2, Initializable { ContentAssetStorage cas = contentAssetStorage; uint256 tokenId = cas.generateTokenId(); - cas.mint(originalSender, tokenId); + cas.mint(minter, tokenId); assertionContract.createAssertion(assertionId, size, triplesNumber, chunksNumber); - cas.setAssertionIssuer(tokenId, assertionId, originalSender); + cas.setAssertionIssuer(tokenId, assertionId, minter); cas.setMutability(tokenId, immutable_); cas.pushAssertionId(tokenId, assertionId); @@ -485,7 +505,7 @@ contract ContentAssetV2 is Named, Versioned, HubDependentV2, Initializable { serviceAgreementV1.createServiceAgreement( ServiceAgreementStructsV1.ServiceAgreementInputArgs({ - assetCreator: originalSender, + assetCreator: payer, assetContract: contentAssetStorageAddress, tokenId: tokenId, keyword: abi.encodePacked(contentAssetStorageAddress, assertionId), diff --git a/contracts/v2/paranets/Paranet.sol b/contracts/v2/paranets/Paranet.sol index 4553c0f6..4f37f84a 100644 --- a/contracts/v2/paranets/Paranet.sol +++ b/contracts/v2/paranets/Paranet.sol @@ -67,7 +67,7 @@ contract Paranet is Named, Versioned, ContractStatusV2, Initializable { ); string private constant _NAME = "Paranet"; - string private constant _VERSION = "2.1.3"; + string private constant _VERSION = "2.2.0"; ParanetsRegistry public paranetsRegistry; ParanetServicesRegistry public paranetServicesRegistry; @@ -284,50 +284,16 @@ contract Paranet is Named, Versioned, ContractStatusV2, Initializable { uint256 paranetKATokenId, ContentAssetStructs.AssetInputArgs calldata knowledgeAssetArgs ) external returns (uint256) { - ParanetsRegistry pr = paranetsRegistry; - - // Check if Paranet exists - // If not: Throw an error - if (!pr.paranetExists(keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)))) { - revert ParanetErrors.ParanetDoesntExist(paranetKAStorageContract, paranetKATokenId); - } - - // Check if Knowledge Miner has profile - // If not: Create a profile - if (!paranetKnowledgeMinersRegistry.knowledgeMinerExists(msg.sender)) { - paranetKnowledgeMinersRegistry.registerKnowledgeMiner(msg.sender); - } - - // Check if Knowledge Miner is registert to paranet - // If not: Register it - if ( - !pr.isKnowledgeMinerRegistered( - keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)), - msg.sender - ) - ) { - pr.addKnowledgeMiner(keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)), msg.sender); - } - - // Mint Knowledge Asset - uint256 knowledgeAssetTokenId = contentAsset.createAssetFromContract(msg.sender, knowledgeAssetArgs); - - _updateSubmittedKnowledgeAssetMetadata( - paranetKAStorageContract, - paranetKATokenId, - address(contentAssetStorage), - knowledgeAssetTokenId, - knowledgeAssetArgs.tokenAmount - ); - - emit KnowledgeAssetSubmittedToParanet( - paranetKAStorageContract, - paranetKATokenId, - address(contentAssetStorage), - knowledgeAssetTokenId - ); + return _mintKnowledgeAsset(paranetKAStorageContract, paranetKATokenId, knowledgeAssetArgs, msg.sender); + } - return knowledgeAssetTokenId; + function mintKnowledgeAssetFor( + address paranetKAStorageContract, + uint256 paranetKATokenId, + ContentAssetStructs.AssetInputArgs calldata knowledgeAssetArgs, + address miner + ) external returns (uint256) { + return _mintKnowledgeAsset(paranetKAStorageContract, paranetKATokenId, knowledgeAssetArgs, miner); } function submitKnowledgeAsset( @@ -376,7 +342,7 @@ contract Paranet is Named, Versioned, ContractStatusV2, Initializable { paranetKnowledgeMinersRegistry.registerKnowledgeMiner(msg.sender); } - // Check if Knowledge Miner is registert to paranet + // Check if Knowledge Miner is registered in the paranet // If not: Register it if ( !pr.isKnowledgeMinerRegistered( @@ -392,7 +358,8 @@ contract Paranet is Named, Versioned, ContractStatusV2, Initializable { paranetKATokenId, knowledgeAssetStorageContract, knowledgeAssetTokenId, - remainingTokenAmount + remainingTokenAmount, + msg.sender ); emit KnowledgeAssetSubmittedToParanet( @@ -420,12 +387,66 @@ contract Paranet is Named, Versioned, ContractStatusV2, Initializable { ); } + function _mintKnowledgeAsset( + address paranetKAStorageContract, + uint256 paranetKATokenId, + ContentAssetStructs.AssetInputArgs calldata knowledgeAssetArgs, + address miner + ) internal returns (uint256) { + ParanetsRegistry pr = paranetsRegistry; + + // Check if Paranet exists + // If not: Throw an error + if (!pr.paranetExists(keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)))) { + revert ParanetErrors.ParanetDoesntExist(paranetKAStorageContract, paranetKATokenId); + } + + // Check if Knowledge Miner has profile + // If not: Create a profile + if (!paranetKnowledgeMinersRegistry.knowledgeMinerExists(miner)) { + paranetKnowledgeMinersRegistry.registerKnowledgeMiner(miner); + } + + // Check if Knowledge Miner is registered in the paranet + // If not: Register it + if ( + !pr.isKnowledgeMinerRegistered( + keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)), + miner + ) + ) { + pr.addKnowledgeMiner(keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)), miner); + } + + // Mint Knowledge Asset + uint256 knowledgeAssetTokenId = contentAsset.createAssetFromContract(miner, msg.sender, knowledgeAssetArgs); + + _updateSubmittedKnowledgeAssetMetadata( + paranetKAStorageContract, + paranetKATokenId, + address(contentAssetStorage), + knowledgeAssetTokenId, + knowledgeAssetArgs.tokenAmount, + miner + ); + + emit KnowledgeAssetSubmittedToParanet( + paranetKAStorageContract, + paranetKATokenId, + address(contentAssetStorage), + knowledgeAssetTokenId + ); + + return knowledgeAssetTokenId; + } + function _updateSubmittedKnowledgeAssetMetadata( address paranetKAStorageContract, uint256 paranetKATokenId, address knowledgeAssetStorageContract, uint256 knowledgeAssetTokenId, - uint96 tokenAmount + uint96 tokenAmount, + address miner ) internal { ParanetsRegistry pr = paranetsRegistry; ParanetKnowledgeMinersRegistry pkmr = paranetKnowledgeMinersRegistry; @@ -435,7 +456,7 @@ contract Paranet is Named, Versioned, ContractStatusV2, Initializable { keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)), knowledgeAssetStorageContract, knowledgeAssetTokenId, - msg.sender + miner ); // Add Knowledge Asset Metadata to the ParanetsRegistry @@ -450,22 +471,22 @@ contract Paranet is Named, Versioned, ContractStatusV2, Initializable { // Add Knowledge Asset Metadata to the KnowledgeMinersRegistry pkmr.addSubmittedKnowledgeAsset( - msg.sender, + miner, keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)), keccak256(abi.encodePacked(knowledgeAssetStorageContract, knowledgeAssetTokenId)) ); pkmr.addCumulativeTracSpent( - msg.sender, + miner, keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)), tokenAmount ); pkmr.addUnrewardedTracSpent( - msg.sender, + miner, keccak256(abi.encodePacked(paranetKAStorageContract, paranetKATokenId)), tokenAmount ); - pkmr.incrementTotalSubmittedKnowledgeAssetsCount(msg.sender); - pkmr.addTotalTracSpent(msg.sender, tokenAmount); + pkmr.incrementTotalSubmittedKnowledgeAssetsCount(miner); + pkmr.addTotalTracSpent(miner, tokenAmount); } function _processUpdatedKnowledgeAssetStatesMetadata( diff --git a/package-lock.json b/package-lock.json index b98311ca..bb1c0faf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dkg-evm-module", - "version": "4.3.3", + "version": "4.3.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dkg-evm-module", - "version": "4.3.3", + "version": "4.3.4", "license": "Apache-2.0", "dependencies": { "@openzeppelin/contracts": "^4.9.3", diff --git a/package.json b/package.json index c61b4a1f..0a601ff9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dkg-evm-module", - "version": "4.3.3", + "version": "4.3.4", "description": "Smart contracts for OriginTrail V6", "main": "index.ts", "files": [ diff --git a/test/v2/unit/Paranet.test.ts b/test/v2/unit/Paranet.test.ts index 9a2b8b4d..4afcd846 100644 --- a/test/v2/unit/Paranet.test.ts +++ b/test/v2/unit/Paranet.test.ts @@ -127,8 +127,8 @@ describe('@v2 @unit ParanetKnowledgeMinersRegistry contract', function () { expect(await Paranet.name()).to.equal('Paranet'); }); - it('The contract is version "2.1.3"', async () => { - expect(await Paranet.version()).to.equal('2.1.3'); + it('The contract is version "2.2.0"', async () => { + expect(await Paranet.version()).to.equal('2.2.0'); }); it('should register paranet', async () => { @@ -925,6 +925,92 @@ describe('@v2 @unit ParanetKnowledgeMinersRegistry contract', function () { .and.to.emit(ServiceAgreementV1, 'ServiceAgreementV1Created'); }); + it('should mint knowledge asset for miner & add it to paranet', async () => { + await Token.connect(accounts[0]).increaseAllowance(ServiceAgreementV1.address, hre.ethers.utils.parseEther('105')); + const { paranetKAStorageContract, paranetKATokenId, paranetId } = await registerParanet(accounts, Paranet, 3); + const assetInputArgs = { + assertionId: getHashFromNumber(500), + size: 3, + triplesNumber: 1, + chunksNumber: 1, + epochsNumber: 5, + tokenAmount: hre.ethers.utils.parseEther('105'), + scoreFunctionId: 2, + immutable_: false, + }; + + const tx = await Paranet.connect(accounts[0]).mintKnowledgeAssetFor( + paranetKAStorageContract, + paranetKATokenId, + assetInputArgs, + accounts[5].address, + ); + const receipt = await tx.wait(); + const tokenId = Number(receipt.logs[0].topics[3]); + + expect(await ContentAssetStorageV2.ownerOf(tokenId)).to.be.equal(accounts[5].address); + + const knowledgeMinerMetadata = await ParanetKnowledgeMinersRegistry.getKnowledgeMinerMetadata(accounts[5].address); + + expect(knowledgeMinerMetadata.addr).to.be.equal(accounts[5].address); + expect(knowledgeMinerMetadata.totalTracSpent).to.be.equal(hre.ethers.utils.parseEther('105')); + expect(knowledgeMinerMetadata.totalSubmittedKnowledgeAssetsCount).to.be.equal(1); + + const submittedKnowledgeAsset = await ParanetKnowledgeMinersRegistry[ + 'getSubmittedKnowledgeAssets(address,bytes32)' + ](accounts[5].address, paranetId); + + expect(submittedKnowledgeAsset.length).to.be.equal(1); + expect(submittedKnowledgeAsset[0]).to.be.equal(getknowledgeAssetId(ContentAssetStorageV2.address, tokenId)); + + const cumulativeTracSpent = await ParanetKnowledgeMinersRegistry.getCumulativeTracSpent( + accounts[5].address, + paranetId, + ); + + expect(cumulativeTracSpent).to.be.equal(hre.ethers.utils.parseEther('105')); + + const unrewardedTracSpent = await ParanetKnowledgeMinersRegistry.getUnrewardedTracSpent( + accounts[5].address, + paranetId, + ); + + expect(unrewardedTracSpent).to.be.equal(hre.ethers.utils.parseEther('105')); + + const knowledgeAssets = await ParanetsRegistry.getKnowledgeAssets(paranetId); + + expect(knowledgeAssets.length).to.be.equal(1); + expect(knowledgeAssets[0]).to.be.equal(getknowledgeAssetId(ContentAssetStorageV2.address, tokenId)); + + const cumulativeKnowledgeValue = await ParanetsRegistry.getCumulativeKnowledgeValue(paranetId); + + expect(cumulativeKnowledgeValue).to.be.equal(hre.ethers.utils.parseEther('105')); + + const isKnowledgeAssetRegistered = await ParanetsRegistry.isKnowledgeAssetRegistered( + paranetId, + getknowledgeAssetId(ContentAssetStorageV2.address, tokenId), + ); + + expect(isKnowledgeAssetRegistered).to.be.equal(true); + + const knowledgeAssetsCount = await ParanetsRegistry.getKnowledgeAssetsCount(paranetId); + + expect(knowledgeAssetsCount).to.be.equal(1); + + const isKnowledgeMinerRegistered = await ParanetsRegistry.isKnowledgeMinerRegistered( + paranetId, + accounts[5].address, + ); + + expect(isKnowledgeMinerRegistered).to.be.equal(true); + + expect( + await ParanetKnowledgeAssetsRegistry.isParanetKnowledgeAsset( + getknowledgeAssetId(ContentAssetStorageV2.address, tokenId), + ), + ).to.be.equal(true); + }); + async function registerParanet(accounts: SignerWithAddress[], Paranet: Paranet, number: number) { const assetInputArgs = { assertionId: getHashFromNumber(number),