Skip to content

Commit

Permalink
Add new NFT API endpoints (#258)
Browse files Browse the repository at this point in the history
* 1. NFT meta API returns raw data without BaseReponse wrapper
2. NFT claim result API returns extra tokenId

* replace redpacket and distributor API address

* update return value

* update response structure
  • Loading branch information
yuanmomo authored Jun 29, 2024
1 parent 939d268 commit 069dd52
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 86 deletions.
29 changes: 27 additions & 2 deletions src/main/java/com/dl/officialsite/activity/ActivityController.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -30,7 +31,9 @@
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static com.dl.officialsite.common.enums.CodeEnums.PARAM_ERROR;
Expand Down Expand Up @@ -122,7 +125,7 @@ public BaseResponse verifyAndMint(@NotNull @RequestParam("chainId") String chain
}

/**
* 检查用户是否完成所有任务,如果完成,则生成 NFT mint 签名
* 检查用户的 Mint 结果
*/
@GetMapping("/claim/result")
public BaseResponse claimResult(@NotNull @RequestParam("chainId") String chainIdParam,
Expand All @@ -136,6 +139,28 @@ public BaseResponse claimResult(@NotNull @RequestParam("chainId") String chainId
SessionUserInfo sessionUserInfo = HttpSessionUtils.getMember(session);
final String address = sessionUserInfo != null ? sessionUserInfo.getAddress() : addressForTesting;

return this.warCraftContractService.rank(address, ContractNameEnum.fromValue(contractName), chainId);
return BaseResponse.successWithData(this.warCraftContractService.rank(address, ContractNameEnum.fromValue(contractName),
chainId).getKey());
}

/**
* 检查用户的 Mint 结果
*/
@GetMapping("/claim/result/v2")
public BaseResponse claimResultWithTokenId(@NotNull @RequestParam("chainId") String chainIdParam,
@RequestParam(required = false) String addressForTesting,
@RequestParam(required = false, defaultValue = "WarCraft") String contractName,
HttpSession session) {
String chainId = Arrays.stream(chainConfig.getIds()).filter(id -> StringUtils.equalsIgnoreCase(chainIdParam, id))
.findFirst()
.orElseThrow(() -> new BizException(PARAM_ERROR.getCode(), String.format("Chain id %s not exists", chainIdParam)));

SessionUserInfo sessionUserInfo = HttpSessionUtils.getMember(session);
final String address = sessionUserInfo != null ? sessionUserInfo.getAddress() : addressForTesting;
Pair<Integer, Integer> rank = this.warCraftContractService.rank(address, ContractNameEnum.fromValue(contractName), chainId);
Map<String, Integer> result = new HashMap<>();
result.put("rankId", rank.getKey());
result.put("tokenId", rank.getValue());
return BaseResponse.successWithData(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,6 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
Expand All @@ -60,6 +46,21 @@
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j(topic = "Distribute")
@Configuration
Expand Down Expand Up @@ -228,26 +229,26 @@ private HttpEntity getHttpEntityFromChain(String chainId) throws IOException {
HttpPost request = null;
switch (chainId) {
case Constants.CHAIN_ID_OP: // op
request = new HttpPost("https://api.studio.thegraph.com/query/64403/optimism/version/latest");
request = new HttpPost("https://gateway-arbitrum.network.thegraph.com/api/18ec3dcb6a75917897da2d5798fb98d3/subgraphs/id/Bfrtk6bLRZoULLzAZ99hZ1Ae8kvFxsUWa4e97XzWJYhG");
break;
case Constants.CHAIN_ID_SEPOLIA: // sepolia
request = new HttpPost(
"https://api.studio.thegraph.com/query/64403/sepolia/v0.0.12");
"https://gateway-arbitrum.network.thegraph.com/api/18ec3dcb6a75917897da2d5798fb98d3/subgraphs/id/9p6zFejTBC2bLLJRCdrk1A2PXfZkriRMacM5wiCAyh5Z");
break;
case Constants.CHAIN_ID_SCROLL: // scroll
request = new HttpPost("https://api.studio.thegraph.com/query/64403/scroll/version/latest");
request = new HttpPost("https://gateway-arbitrum.network.thegraph.com/api/18ec3dcb6a75917897da2d5798fb98d3/subgraphs/id/8HhHjdsXrZ1cjq5qNpksoWfAm1NTDDFhH2Yb8ZkBixjo");
break;
case Constants.CHAIN_ID_ARBITRUM: // arbitrum
request = new HttpPost("https://api.studio.thegraph.com/query/64403/arbitrum/version/latest");
request = new HttpPost("https://gateway-arbitrum.network.thegraph.com/api/18ec3dcb6a75917897da2d5798fb98d3/subgraphs/id/BbFUyrr7KeB9VjxrSqWFDWAcsyDsGdbinrteLrPK5MVb");
break;
case Constants.CHAIN_ID_ZKSYNC: // zksync
request = new HttpPost("https://api.studio.thegraph.com/query/64403/zksync/version/latest");
request = new HttpPost("https://gateway-arbitrum.network.thegraph.com/api/18ec3dcb6a75917897da2d5798fb98d3/subgraphs/id/DzTmBT2V5kFQL6LpjC3iubYkusfj4HLVScCrAcT2DKGK");
break;
case Constants.CHAIN_ID_POLYGON_ZKEVM: // polygon zkevm
request = new HttpPost("https://api.studio.thegraph.com/query/64403/polygonzkevm/version/latest");
request = new HttpPost("https://gateway-arbitrum.network.thegraph.com/api/18ec3dcb6a75917897da2d5798fb98d3/subgraphs/id/A46TwCAxDy4upqLuS7bgXd14yLQdTjfS4AVPMNwWkZSR");
break;
case Constants.CHAIN_ID_LINEA: //linea
request = new HttpPost("https://api.studio.thegraph.com/query/64403/linea/version/latest");
request = new HttpPost("https://gateway-arbitrum.network.thegraph.com/api/18ec3dcb6a75917897da2d5798fb98d3/subgraphs/id/DatcM6CoN5u79XPx1wYFFnWXpHYcfDodkbycx2vSp25C");
break;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class LoginFilter extends OncePerRequestFilter {
add("/defi/healthInfo");
add("/bounty/list");
add("/nft/WarCraft");
add("/nft/raw/WarCraft");
}} ;

private Set<String> noAddrCheckApis = new HashSet() {{
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/com/dl/officialsite/nft/NFTController.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class NFTController {
* 获取 NFT 的元数据
*/
@GetMapping("nft/{nftType}")
public BaseResponse verifyAndMint(
public BaseResponse fetchNftMetadata(
@PathVariable String nftType,
@NotNull @RequestParam("rank") Integer rank) {
NFTMetadata nftMetadata = fileLoadConfig.get(nftType, rank);
Expand All @@ -34,4 +34,19 @@ public BaseResponse verifyAndMint(
}
return BaseResponse.successWithData(nftMetadata);
}

/**
* 获取 NFT 的原始元数据
*/
@GetMapping("nft/raw/{nftType}")
public Object fetchRawNftMetadata(
@PathVariable String nftType,
@NotNull @RequestParam("rank") Integer rank) {
NFTMetadata nftMetadata = fileLoadConfig.get(nftType, rank);
if (nftMetadata == null) {
return "";
}

return nftMetadata;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
Expand Down Expand Up @@ -57,6 +58,10 @@ public class MemberNFTMintRecord implements Serializable {
@NotNull
private String chainId;

@ColumnDefault("-1")
private int tokenId = -1;

@ColumnDefault("-1")
private int rankValue = -1;

@CreatedDate
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.dl.officialsite.nft.service;

import com.dl.officialsite.common.base.BaseResponse;
import com.dl.officialsite.common.exception.BizException;
import com.dl.officialsite.nft.bean.MemberNFTMintRecord;
import com.dl.officialsite.nft.bean.MemberNFTMintRecordRepository;
import com.dl.officialsite.nft.config.ContractConfigService;
import com.dl.officialsite.nft.constant.ContractNameEnum;
import com.dl.officialsite.nft.contract.WarCraftContract;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.web3j.protocol.Web3j;
Expand All @@ -32,21 +33,23 @@ public class WarCraftContractService {
@Autowired
private MemberNFTMintRecordRepository memberNFTMintRecordRepository;

public BaseResponse rank(String address, ContractNameEnum contractName, String chainId) {
public Pair<Integer, Integer> rank(String address, ContractNameEnum contractName, String chainId) {
String contractAddress = this.contractConfigService.getContractAddressByName(contractName, chainId);
String rpcAddress = this.contractConfigService.getRpcAddressByName(contractName, chainId);

if (StringUtils.isAnyBlank(contractAddress, rpcAddress)) {
log.error("Fetch blank values of contractAddress:[{}] or rpcAddress:[{}] filtered by contractName:[{}] and chainId:[{}]",
contractAddress, rpcAddress, contractName.getContractName(), chainId);
return BaseResponse.failWithReason("1204", "Fetch rank failed, please try again later.");
throw new BizException("1204", "Fetch rank failed, please try again later.");
}

Optional<MemberNFTMintRecord> memberTaskRecordExists =
memberNFTMintRecordRepository.findByAddressAndContractNameAndChainId(address, contractName.name(), chainId);

if(memberTaskRecordExists.isPresent() && memberTaskRecordExists.get().getRankValue() >= 0){
return BaseResponse.successWithData(memberTaskRecordExists.get().getRankValue());
if(memberTaskRecordExists.isPresent()
&& memberTaskRecordExists.get().getRankValue() >= 0
&& memberTaskRecordExists.get().getTokenId() >= 0){
return Pair.of(memberTaskRecordExists.get().getRankValue(), memberTaskRecordExists.get().getTokenId());
}

Web3j web3j = Web3j.build(new HttpService(rpcAddress));
Expand All @@ -58,25 +61,26 @@ public BaseResponse rank(String address, ContractNameEnum contractName, String c
BigInteger tokenId = contract.claimedTokenIdBy(address).sendAsync().get(TIMEOUT, TimeUnit.SECONDS);
if (tokenId == null || tokenId.intValue() == 0) {
log.error("No claim(tokenId) info found for address:[{}].", address);
return BaseResponse.failWithReason("200", "No claim info found.");
throw new BizException("200", "No claim info found.");
}

String rank = contract.tokenURIs(tokenId).sendAsync().get(TIMEOUT, TimeUnit.SECONDS);
if (StringUtils.isBlank(rank)) {
log.error("No rank info found for address:[{}] and tokenId:[{}].", address, tokenId.intValue());
return BaseResponse.failWithReason("200", "No rank info found.");
throw new BizException("200", "No rank info found.");
}

MemberNFTMintRecord memberNFTMintRecord = new MemberNFTMintRecord();
MemberNFTMintRecord memberNFTMintRecord = memberTaskRecordExists.orElseGet(MemberNFTMintRecord::new);
memberNFTMintRecord.setAddress(address);
memberNFTMintRecord.setContractName(contractName);
memberNFTMintRecord.setChainId(chainId);
memberNFTMintRecord.setTokenId(tokenId.intValue());
memberNFTMintRecord.setRankValue(Integer.parseInt(rank));
this.memberNFTMintRecordRepository.save(memberNFTMintRecord);
return BaseResponse.successWithData(rank);
return Pair.of(memberNFTMintRecord.getRankValue(), memberNFTMintRecord.getTokenId());
} catch (Exception e) {
log.error("Call contract method:[claimedTokenIdBy] remote error.", e);
return BaseResponse.failWithReason("1205", "Fetch rank failed, please try again later.");
throw new BizException("1205", "Fetch rank failed, please try again later.");
}

}
Expand Down
Loading

0 comments on commit 069dd52

Please sign in to comment.