🎯 내가 만들고 싶은 기능
- 특정 유저가 이용한 dApp들의 통계를 보여주고 싶다.
- 근데 그 유저가 이용한 시기(분기)를 설정해서 분기마다의 이용 통계값을 반환하길 원한다.
- 유저가 이용하는 dApp들은 1️⃣Defi 2️⃣NFT 3️⃣Others 로 분류한다.
- 분기별 사용자의 이용 dApp 통계를 DTO로 반환합니다.
🏹 기능을 구현하는데 필요한 요소는?
특정 유저가 이용한 dApp들의 통계를 보여주고 싶다.
- https://doc.pikespeak.ai/
- /account/transactions/{contract_address} API 호출하면 아래의 json을 반환 받는다.(최근 50개의 tnxs정보)
- Key 값 중 "first_action_type" : "functionCall" 인 부분을 추출할 것이고
- "signer" 과 "receiver" 중 contract_address와 일치하지 않는 부분이 바로 사용자가 사용한 dApp의 이름이다.
[
{
"block_height": "92607293",
"chunk_hash": "5bEscxzE1Nb6Q3cJs5d7uBnLQcxTRUR99FMSuydCJiDj",
"block_hash": "4gkV7ULRZhK6MRrPbUivHqW7HAYXRPVTtstPPugGk9X5",
"signer": "sehir.near",
"receiver": "wrap.near",
"method_name": "near_withdraw",
"transaction_timestamp": "1684939010149657052",
"id": "HHg4cDuhXetT9n2Ee92tEYypdqPJrher8Kf8RVWPP4U7",
"first_action_type": "functionCall",
"all_action_type": "functionCall",
"has_error": false
},
{
"block_height": "92607244",
"chunk_hash": "F6FFxWei3fHs5x58HmjboUUBsEBidmRbNZTD9bHBd8QT",
"block_hash": "FZ6wY6Xijc5MQVkYE1CvWFvdrCqergSZN6BmFoxQCDk3",
"signer": "sehir.near",
"receiver": "token.bocachica_mars.near",
"method_name": "ft_transfer_call",
"transaction_timestamp": "1684938954141871765",
"id": "3Z5jEQTvNfJBxmMLyYRfhvb2GZUS32AE3NCBzRU6nx2X",
"first_action_type": "functionCall",
"all_action_type": "functionCall",
"has_error": false
}
]
근데 그 유저가 이용한 시기(분기)를 설정해서 분기마다의 이용 통계값을 반환하길 원한다.
- 분기별로 제시를 한다 = txns의 데이터를 생성날짜를 기준으로 분류를 한다.
- "transaction_timestamp": "1684834722800094638" 부분을 연월일로 치환후 저장합니다.
- NEAR TimeStamp 환경설정 : number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC
- 1684834722800094638 = 2023년 6월 20일 8시 9분 5.472800094638초
public static List<String> convertTimestamp(String timestampStr) {
long timestamp = Long.parseLong(timestampStr);
Instant instant = Instant.ofEpochSecond(0, timestamp);
LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
int year = dateTime.getYear();
int month = dateTime.getMonthValue();
List<String> yearMonth = new ArrayList<>();
yearMonth.add(String.valueOf(year));
yearMonth.add(String.valueOf(month));
return yearMonth;
}
유저가 이용하는 dApp들은 1️⃣Defi 2️⃣NFT 3️⃣Others 로 분류한다.
- 유저가 이용한 dApp의 name으로 어떤 종류의 dApp을 이용했는지 알기위해서 mapping할 자료들이 필요하다.
- NEAR 네트워크에 존재하는 NFT 서비스들을 크롤링을 통해 NFT_data.json 을 추출했음
- 자료구조 중, 집합(set)을 활용해서 NFT_set, Defi_set을 준비한다.
- 사용자가 사용한 dApp의 이름 이 NFT집합 or Defi집합에 contain 하는지 확인 후, 각각 appCategory컬럼에 맞는값으로 분류해서 저장한다.
public class NftUtil {
public static void addContractNamesFromJson(Nft nft, String filePath) {
ObjectMapper objectMapper = new ObjectMapper();
try {
List<ContractDTO> contracts = objectMapper.readValue(new File(filePath), new TypeReference<List<ContractDTO>>() {});
for (ContractDTO contract : contracts) {
nft.addContractName(contract.getContract_name());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
[
{ "id": 0, "contract_name": "x.paras.near", "category": "nft" },
{ "id": 1, "contract_name": "marketplace.paras.near", "category": "nft" },
{ "id": 2, "contract_name": "token.paras.near", "category": "nft" },
{ "id": 3, "contract_name": "staking.paras.near", "category": "nft" },
{ "id": 4, "contract_name": "market.fewandfar.near", "category": "nft" },
{
"id": 5,
"contract_name": "pack.promotional.nfl.playible.near",
"category": "nft"
},
{
"id": 6,
"contract_name": "pack.promotional.basketball.playible.near",
"category": "nft"
},
{ "id": 7, "contract_name": "spin-nft-contract.near", "category": "nft" },
{ "id": 8, "contract_name": "dragonnation.near", "category": "nft" },
{
"id": 9,
"contract_name": "simple.market.mintbase1.near",
"category": "nft"
},
{ "id": 10, "contract_name": "mintbase1.near", "category": "nft" },
{ "id": 11, "contract_name": "market.tradeport.near", "category": "nft" },
{ "id": 12, "contract_name": "serumnft.near", "category": "nft" },
{ "id": 13, "contract_name": "nearnautsnft.near", "category": "nft" },
{ "id": 14, "contract_name": "nft.alienbearcrew.near", "category": "nft" },
{ "id": 15, "contract_name": "nft.uniqart.near", "category": "nft" },
{
"id": 16,
"contract_name": "game.basketball.playible.near",
"category": "nft"
},
{ "id": 17, "contract_name": "game.fnl.playible.near", "category": "nft" },
{
"id": 18,
"contract_name": "laboratory.secretskelliessociety.near",
"category": "nft"
},
{ "id": 19, "contract_name": "nativo-minter.near", "category": "nft" },
{ "id": 20, "contract_name": "proxy2.minista.near", "category": "nft" }
]
분기별 사용자의 이용 dApp 통계를 DTO로 반환합니다.
- 유저의 사용 dApp의 이름 상위 5위를 반환합니다. dApp은 오늘을 포함하는 분기에 해당하는 정보들이 반환됩니다.
public List<UsedAppDTO> getTopUsedAppsByUser(String userAddress) {
Defi defi = new Defi();
DefiUtil.addContractNamesFromJson(defi, "src/main/java/ni/co/nico/set/defi/defiData.json");
Set<String> defiSet = defi.getContractNames();
Nft nft = new Nft();
NftUtil.addContractNamesFromJson(nft, "src/main/java/ni/co/nico/set/nft/nftData.json");
Set<String> nftSet = nft.getContractNames();
LocalDate currentDate = LocalDate.now();
int currentYear = currentDate.getYear();
int currentQuarter = (currentDate.getMonthValue() - 1) / 3 + 1;
LocalDate quarterStart = LocalDate.of(currentYear, (currentQuarter - 1) * 3 + 1, 1);
LocalDate quarterEnd = quarterStart.plusMonths(3).minusDays(1);
List<UsedApp> usedApps = usedAppRepository.findByUserAddress(userAddress);
List<UsedApp> usedAppsInQuarter = usedApps.stream()
.filter(usedApp -> {
LocalDate appDate = LocalDate.parse(usedApp.getCreatedDate()); // Assuming createdDate is the field representing the date
return !appDate.isBefore(quarterStart) && !appDate.isAfter(quarterEnd);
})
.collect(Collectors.toList());
// 현재 분기 내 개수로 매핑 앱 이름 매핑
Map<String, Integer> appNameCountMapInQuarter = new HashMap<>();
// 현재 분기 내에서 앱 이름당 사용 횟수 계산
for (UsedApp usedApp : usedAppsInQuarter) {
String appName = usedApp.getAppName();
appNameCountMapInQuarter.put(appName, appNameCountMapInQuarter.getOrDefault(appName, 0) + 1);
}
// 개수별로 내림차순으로 정렬하고 상위 5개만 추출
List<Map.Entry<String, Integer>> sortedEntries = appNameCountMapInQuarter.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(5)
.collect(Collectors.toList());
// 결과를 담을 리스트
List<UsedAppDTO> usedAppDTOs = new ArrayList<>();
// 정렬된 엔트리를 순회하며 DTO로 변환하여 리스트에 추가
for (Map.Entry<String, Integer> entry : sortedEntries) {
String appName = entry.getKey();
int count = entry.getValue();
UsedAppDTO usedAppDTO = new UsedAppDTO();
usedAppDTO.setUserAddress(userAddress);
usedAppDTO.setAppName(appName);
usedAppDTO.setCount(count);
usedAppDTO.setYear(currentYear);
usedAppDTO.setQuarter(currentQuarter);
// appCategory 설정
if (defiSet.contains(appName)) {
usedAppDTO.setAppCategory("defi");
} else if (nftSet.contains(appName)) {
usedAppDTO.setAppCategory("nft");
} else {
usedAppDTO.setAppCategory("others");
}
usedAppDTOs.add(usedAppDTO);
}
return usedAppDTOs;
}
'블록체인' 카테고리의 다른 글
[Hackathon](후기: 대회 1등을 했습니다😉) KYC dApp Project ( Multichain API, Webhook, Json RPC) (0) | 2023.06.05 |
---|---|
[Polygon Hackathon] Backend Dashboard (5.30 ~ 6.4) (0) | 2023.05.30 |
[Digital Signiture] 암호화의 원리🔐 (0) | 2023.05.24 |
[Hackathon] NICO dApp Project (feat. Block Explorer, SBT, Community) (0) | 2023.05.22 |
[Hackathon] Backend DashBoard is underway👾 (2) | 2023.05.14 |