github.com/klaytn/klaytn@v1.10.2/datasync/chaindatafetcher/kas/repository_contracts.go (about) 1 // Copyright 2020 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package kas 18 19 import ( 20 "fmt" 21 "strings" 22 "time" 23 24 "github.com/klaytn/klaytn/blockchain" 25 "github.com/klaytn/klaytn/blockchain/types" 26 "github.com/klaytn/klaytn/common" 27 ) 28 29 // filterKIPContracts filters the deployed contracts to KIP7, KIP17 and others. 30 func filterKIPContracts(api BlockchainAPI, event blockchain.ChainEvent) ([]*FT, []*NFT, []*Contract, error) { 31 var ( 32 kip7s []*FT 33 kip17s []*NFT 34 others []*Contract 35 ) 36 caller := newContractCaller(api) 37 for _, receipt := range event.Receipts { 38 if receipt.Status != types.ReceiptStatusSuccessful || receipt.ContractAddress == (common.Address{}) { 39 continue 40 } 41 contract := receipt.ContractAddress 42 isKIP13, err := caller.isKIP13(contract, nil) 43 if err != nil { 44 logger.Error("Failed to call isKIP13", "err", err, "contract", contract.String()) 45 return nil, nil, nil, err 46 } else if !isKIP13 { 47 others = append(others, &Contract{Address: contract.Bytes()}) 48 continue 49 } 50 51 if isKIP7, err := caller.isKIP7(contract, nil); err != nil { 52 logger.Error("Failed to call isKIP7", "err", err, "contract", contract.String()) 53 return nil, nil, nil, err 54 } else if isKIP7 { 55 kip7s = append(kip7s, &FT{Address: contract.Bytes()}) 56 continue 57 } 58 59 if isKIP17, err := caller.isKIP17(contract, nil); err != nil { 60 logger.Error("Failed to call isKIP17", "err", err, "contract", contract.String()) 61 return nil, nil, nil, err 62 } else if isKIP17 { 63 kip17s = append(kip17s, &NFT{Address: contract.Bytes()}) 64 continue 65 } 66 others = append(others, &Contract{Address: contract.Bytes()}) 67 } 68 return kip7s, kip17s, others, nil 69 } 70 71 // InsertContracts inserts deployed contracts in the given chain event into KAS database. 72 func (r *repository) InsertContracts(event blockchain.ChainEvent) error { 73 kip7s, kip17s, others, err := filterKIPContracts(r.blockchainApi, event) 74 if err != nil { 75 logger.Error("Failed to filter KIP contracts", "err", err, "blockNumber", event.Block.NumberU64()) 76 return err 77 } 78 79 if err := r.insertFTs(kip7s); err != nil { 80 logger.Error("Failed to insert KIP7 contracts", "err", err, "blockNumber", event.Block.NumberU64(), "numKIP7s", len(kip7s)) 81 return err 82 } 83 84 if err := r.insertNFTs(kip17s); err != nil { 85 logger.Error("Failed to insert KIP17 contracts", "err", err, "blockNumber", event.Block.NumberU64(), "numKIP17s", len(kip17s)) 86 return err 87 } 88 89 if err := r.insertContracts(others); err != nil { 90 logger.Error("Failed to insert other contracts", "err", err, "blockNumber", event.Block.NumberU64(), "numContracts", len(others)) 91 return err 92 } 93 94 return nil 95 } 96 97 // insertContracts inserts the contracts which are divided into chunkUnit because of max number of placeholders. 98 func (r *repository) insertContracts(contracts []*Contract) error { 99 chunkUnit := maxPlaceholders / placeholdersPerContractItem 100 var chunks []*Contract 101 102 for contracts != nil { 103 if placeholdersPerContractItem*len(contracts) > maxPlaceholders { 104 chunks = contracts[:chunkUnit] 105 contracts = contracts[chunkUnit:] 106 } else { 107 chunks = contracts 108 contracts = nil 109 } 110 111 if err := r.bulkInsertContracts(chunks); err != nil { 112 return err 113 } 114 } 115 116 return nil 117 } 118 119 // bulkInsertTransactions inserts the given contracts in multiple rows at once. 120 func (r *repository) bulkInsertContracts(contracts []*Contract) error { 121 if len(contracts) == 0 { 122 return nil 123 } 124 var valueStrings []string 125 var valueArgs []interface{} 126 127 for _, ft := range contracts { 128 valueStrings = append(valueStrings, "(?)") 129 valueArgs = append(valueArgs, ft.Address) 130 } 131 132 rawQuery := ` 133 INSERT INTO contract(address) 134 VALUES %s 135 ON DUPLICATE KEY 136 UPDATE address=address` 137 query := fmt.Sprintf(rawQuery, strings.Join(valueStrings, ",")) 138 139 if _, err := r.db.DB().Exec(query, valueArgs...); err != nil { 140 return err 141 } 142 return nil 143 } 144 145 // insertFTs inserts the FT contracts which are divided into chunkUnit because of max number of placeholders. 146 func (r *repository) insertFTs(fts []*FT) error { 147 chunkUnit := maxPlaceholders / placeholdersPerFTItem 148 var chunks []*FT 149 150 for fts != nil { 151 if placeholdersPerFTItem*len(fts) > maxPlaceholders { 152 chunks = fts[:chunkUnit] 153 fts = fts[chunkUnit:] 154 } else { 155 chunks = fts 156 fts = nil 157 } 158 159 if err := r.bulkInsertFTs(chunks); err != nil { 160 return err 161 } 162 } 163 164 return nil 165 } 166 167 // bulkInsertFTs inserts the given FT contracts in multiple rows at once. 168 func (r *repository) bulkInsertFTs(fts []*FT) error { 169 if len(fts) == 0 { 170 return nil 171 } 172 var valueStrings []string 173 var valueArgs []interface{} 174 175 now := time.Now() 176 for _, ft := range fts { 177 valueStrings = append(valueStrings, "(?, ?, ?)") 178 valueArgs = append(valueArgs, ft.Address) 179 valueArgs = append(valueArgs, &now) 180 valueArgs = append(valueArgs, &now) 181 } 182 183 rawQuery := ` 184 INSERT INTO kct_ft_metadata(address, createdAt, updatedAt) 185 VALUES %s 186 ON DUPLICATE KEY 187 UPDATE address=address` 188 query := fmt.Sprintf(rawQuery, strings.Join(valueStrings, ",")) 189 190 if _, err := r.db.DB().Exec(query, valueArgs...); err != nil { 191 return err 192 } 193 return nil 194 } 195 196 // insertNFTs inserts the NFT contracts which are divided into chunkUnit because of max number of placeholders. 197 func (r *repository) insertNFTs(nfts []*NFT) error { 198 chunkUnit := maxPlaceholders / placeholdersPerNFTItem 199 var chunks []*NFT 200 201 for nfts != nil { 202 if placeholdersPerNFTItem*len(nfts) > maxPlaceholders { 203 chunks = nfts[:chunkUnit] 204 nfts = nfts[chunkUnit:] 205 } else { 206 chunks = nfts 207 nfts = nil 208 } 209 210 if err := r.bulkInsertNFTs(chunks); err != nil { 211 return err 212 } 213 } 214 215 return nil 216 } 217 218 // bulkInsertNFTs inserts the given NFT contracts in multiple rows at once. 219 func (r *repository) bulkInsertNFTs(nfts []*NFT) error { 220 if len(nfts) == 0 { 221 return nil 222 } 223 var valueStrings []string 224 var valueArgs []interface{} 225 226 now := time.Now() 227 for _, nft := range nfts { 228 valueStrings = append(valueStrings, "(?, ?, ?)") 229 valueArgs = append(valueArgs, nft.Address) 230 valueArgs = append(valueArgs, &now) 231 valueArgs = append(valueArgs, &now) 232 } 233 234 rawQuery := ` 235 INSERT INTO kct_nft_metadata(address, createdAt, updatedAt) 236 VALUES %s 237 ON DUPLICATE KEY 238 UPDATE address=address` 239 query := fmt.Sprintf(rawQuery, strings.Join(valueStrings, ",")) 240 241 if _, err := r.db.DB().Exec(query, valueArgs...); err != nil { 242 return err 243 } 244 return nil 245 }