ljoaquim пре 2 недеља
родитељ
комит
34b30f0cc7

+ 1 - 1
README.md

@@ -16,7 +16,7 @@ Na pasta `sdk/`:
 
 ```bash
 go mod tidy
-go build -o easycli .
+CGO_ENABLED=0 go build -o easycli .
 ./easycli --help
 ```
 

+ 21 - 0
contracts/EasyTokenDocument.sol

@@ -17,11 +17,13 @@ contract EasyTokenDocument is ERC721URIStorage, ERC721Burnable, ERC721Pausable,
     mapping(address => bool) private _blacklist;
     mapping(uint256 => bytes32) private _documentHash;
     mapping(uint256 => uint256) private _appraisalValue;
+    mapping(uint256 => string) private _tokenContent;
 
     uint256 private _tokenIdCounter;
 
     event BlacklistUpdated(address indexed account, bool isBlacklisted);
     event DocumentMinted(uint256 indexed tokenId, address indexed to, string uri, bytes32 documentHash, uint256 appraisalValue);
+    event ContentMinted(uint256 indexed tokenId, address indexed to, string content);
     event AppraisalUpdated(uint256 indexed tokenId, uint256 oldValue, uint256 newValue);
     event TokenURIUpdated(uint256 indexed tokenId, string uri);
 
@@ -63,6 +65,11 @@ contract EasyTokenDocument is ERC721URIStorage, ERC721Burnable, ERC721Pausable,
         return _appraisalValue[tokenId];
     }
 
+    function contentOf(uint256 tokenId) external view returns (string memory) {
+        require(_ownerOf(tokenId) != address(0), "Invalid token");
+        return _tokenContent[tokenId];
+    }
+
     function safeMint(address to, string memory uri, bytes32 documentHash, uint256 appraisalValue) external onlyRole(MINTER_ROLE) returns (uint256) {
         require(!_blacklist[to], "Blacklisted");
         uint256 tokenId = ++_tokenIdCounter;
@@ -74,6 +81,19 @@ contract EasyTokenDocument is ERC721URIStorage, ERC721Burnable, ERC721Pausable,
         return tokenId;
     }
 
+    function mintContent(address to, string calldata content) external onlyRole(MINTER_ROLE) returns (uint256) {
+        require(to != address(0), "Invalid recipient");
+        require(!_blacklist[to], "Blacklisted");
+        bytes memory raw = bytes(content);
+        require(raw.length > 0, "Content required");
+        require(raw.length <= 20, "Content too long");
+        uint256 tokenId = ++_tokenIdCounter;
+        _safeMint(to, tokenId);
+        _tokenContent[tokenId] = content;
+        emit ContentMinted(tokenId, to, content);
+        return tokenId;
+    }
+
     function setTokenURI(uint256 tokenId, string memory uri) external onlyRole(METADATA_ROLE) {
         require(_ownerOf(tokenId) != address(0), "Invalid token");
         _setTokenURI(tokenId, uri);
@@ -116,6 +136,7 @@ contract EasyTokenDocument is ERC721URIStorage, ERC721Burnable, ERC721Pausable,
         if (to == address(0)) {
             delete _documentHash[tokenId];
             delete _appraisalValue[tokenId];
+            delete _tokenContent[tokenId];
         }
         return previousOwner;
     }

Разлика између датотеке није приказан због своје велике величине
+ 68 - 0
ignition/deployments/chain-137/artifacts/EasyTokensModule#EasyTokenDocument.json


Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
ignition/deployments/chain-137/build-info/solc-0_8_28-3c1fd04852d45635fdc94cd1db24408c9378c555.json


+ 2 - 2
ignition/deployments/chain-137/deployed_addresses.json

@@ -1,4 +1,4 @@
 {
-  "EasyTokensModule#EasyBRLStable": "0x0D674e9384220bf9146fccD70E422B0480CC0a12",
-  "EasyTokensModule#EasyTokenDocument": "0xA325731B277424f4157B734729C733c46591bfE6"
+  "EasyTokensModule#EasyBRLStable": "0x065FB961Df42F2fdE1705007AFA24b8697d5C899",
+  "EasyTokensModule#EasyTokenDocument": "0x5F5776584b69d4C61590b9911EdcE2aBA0f0BA93"
 }

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
ignition/deployments/chain-137/journal.jsonl


+ 1 - 1
package-lock.json

@@ -1,5 +1,5 @@
 {
-  "name": "sc",
+  "name": "easyBlockchain",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {

+ 2 - 0
sdk/abi.go

@@ -36,6 +36,7 @@ const easyTokenDocumentABI = `[
   {"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},
   {"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},
   {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},
+  {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"contentOf","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},
   {"inputs":[],"name":"valueDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},
   {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"documentHashOf","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},
   {"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"appraisalOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
@@ -49,5 +50,6 @@ const easyTokenDocumentABI = `[
   {"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},
   {"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
   {"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"bytes32","name":"documentHash","type":"bytes32"},{"internalType":"uint256","name":"appraisalValue","type":"uint256"}],"name":"safeMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},
+  {"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"string","name":"content","type":"string"}],"name":"mintContent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},
   {"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"grantComplianceRole","outputs":[],"stateMutability":"nonpayable","type":"function"}
 ]`


+ 37 - 12
sdk/main.go

@@ -535,6 +535,22 @@ func cmdToken() *cobra.Command {
 		tokCmd.AddCommand(cmd)
 	}
 
+	{
+		cmd := &cobra.Command{Use: "get-info [token-id]", Short: "Show owner and content", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) {
+			loadEnv()
+			id, ok := new(big.Int).SetString(args[0], 10); if !ok { log.Fatal("invalid token-id") }
+			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second); defer cancel()
+			c, _ := mustClient(ctx)
+			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
+			con := boundContract(addr, easyTokenDocumentABI, c)
+			owner, err := callAddress(ctx, con, "ownerOf", id); if err != nil { log.Fatal(err) }
+			content, err := callString(ctx, con, "contentOf", id); if err != nil { log.Fatal(err) }
+			fmt.Printf("owner=%s\n", owner.Hex())
+			fmt.Printf("content=%s\n", content)
+		}}
+		tokCmd.AddCommand(cmd)
+	}
+
 	// balance
 	var tbAddr string
 	{
@@ -639,24 +655,33 @@ func cmdToken() *cobra.Command {
 		tokCmd.AddCommand(cmd)
 	}
 
-	// safe-mint
-	var smTo, smURI, smHash, smVal string
+	// mint content
 	{
-		cmd := &cobra.Command{Use: "safe-mint", Short: "Mint new document NFT", Run: func(cmd *cobra.Command, args []string) {
-			loadEnv(); if smTo==""||smURI==""||smHash==""||smVal=="" { log.Fatal("--to --uri --hash --value required") }
-			h, err := fromHex32(smHash); if err != nil { log.Fatal(err) }
-			v, err := toWei(smVal); if err != nil { log.Fatal(err) }
+		cmd := &cobra.Command{Use: "mint [content]", Short: "Mint new token content (<=20 chars)", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) {
+			content := args[0]
+			if content == "" { log.Fatal("content required") }
+			if len([]byte(content)) > 20 { log.Fatal("content must be at most 20 bytes") }
+			loadEnv()
 			ctx, cancel := context.WithTimeout(context.Background(), 240*time.Second); defer cancel()
 			c, chain := mustClient(ctx); auth := txOpts(ctx, c, chain)
 			addr := mustAddress(tokenAddr, "EASY_TOKEN_ADDR")
 			con := boundContract(addr, easyTokenDocumentABI, c)
-			tx, err := con.Transact(auth, "safeMint", common.HexToAddress(smTo), smURI, h, v); if err != nil { log.Fatal(err) }
-			fmt.Println(tx.Hash().Hex())
+			sender := crypto.PubkeyToAddress(mustPrivKey().PublicKey)
+			tx, err := con.Transact(auth, "mintContent", sender, content); if err != nil { log.Fatal(err) }
+			receipt, err := bind.WaitMined(ctx, c, tx); if err != nil { log.Fatal(err) }
+			mintSig := crypto.Keccak256Hash([]byte("ContentMinted(uint256,address,string)"))
+			tokenID := new(big.Int)
+			for _, lg := range receipt.Logs {
+				if lg.Address == addr && len(lg.Topics) >= 3 && lg.Topics[0] == mintSig {
+					tokenID.SetBytes(lg.Topics[1].Bytes())
+					break
+				}
+			}
+			fmt.Printf("tx=%s\n", tx.Hash().Hex())
+			if tokenID.Sign() > 0 {
+				fmt.Printf("tokenId=%s\n", tokenID.String())
+			}
 		}}
-		cmd.Flags().StringVar(&smTo, "to", "", "Recipient address")
-		cmd.Flags().StringVar(&smURI, "uri", "", "Document URL")
-		cmd.Flags().StringVar(&smHash, "hash", "", "Document hash (0x + 64 hex)")
-		cmd.Flags().StringVar(&smVal, "value", "", "Appraisal in base units")
 		tokCmd.AddCommand(cmd)
 	}
 

Неке датотеке нису приказане због велике количине промена