Skip to content

Commit

Permalink
Merge pull request #200 from cheng521521/main
Browse files Browse the repository at this point in the history
feat: get token apy
  • Loading branch information
liberhe authored Mar 10, 2024
2 parents df42f91 + b0ab446 commit 7e0c6e8
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 78 deletions.
161 changes: 99 additions & 62 deletions src/main/java/com/dl/officialsite/aave/AaveTokenAPYService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
import static org.web3j.tx.gas.DefaultGasProvider.GAS_LIMIT;
import static org.web3j.tx.gas.DefaultGasProvider.GAS_PRICE;

import com.dl.officialsite.config.ChainInfo;
import com.dl.officialsite.config.Web3jAutoConfiguration;
import com.dl.officialsite.contract.ipool.IPool;
import com.dl.officialsite.contract.ipooladdressesprovider.IPoolAddressesProvider;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
Expand All @@ -35,50 +40,107 @@ public class AaveTokenAPYService extends AbstractTokenAPY {
BigInteger e8 = new BigInteger("100000000");

BigInteger E23 = new BigInteger("100000000000000000000000");
static Map<String, Map<String, String>> chainWithToken = new HashMap<>();
static {
Map<String, String> optimismTokenMap = new HashMap<>();
optimismTokenMap.put("usdc", "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174");
optimismTokenMap.put("usdt", "0xc2132D05D31c914a87C6611C10748AEb04B58e8F");
optimismTokenMap.put("dai", "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063");
optimismTokenMap.put("eth", "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619");
optimismTokenMap.put("btc", "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6");
chainWithToken.put("optimism", optimismTokenMap);
}
static ConcurrentHashMap<ChainInfo, Web3j> chainWithWeb3j = Web3jAutoConfiguration.web3jMap;

private final TokenAPYInfoRepository tokenAPYInfoRepository;

private Web3j web3j;
public AaveTokenAPYService(TokenAPYInfoRepository tokenAPYInfoRepository) {
this.tokenAPYInfoRepository = tokenAPYInfoRepository;
}

private final Credentials credentials;
@Override
public List<TokenAPYInfo> queryTokenApy() {
return tokenAPYInfoRepository.findAll();
}

private final IPoolAddressesProvider poolAddressesProvider;
@Override
public HealthInfo getHealthInfo(ChainInfo chainInfo, String address) {
try {
AtomicReference<HealthInfo> healthInfo = new AtomicReference<>();
chainWithWeb3j.forEach((chain, web3j) -> {
if (chain.getId().equals(chainInfo.getId())) {
log.info("chain: {}", chain.getName());
IPoolAddressesProvider addressesProvider = IPoolAddressesProvider.load(
chain.getLendingPoolAddressProviderV3Address(), web3j, getCredentials(), GAS_PROVIDER);
String poolAddress = null;
try {
poolAddress = addressesProvider.getPool().send();
} catch (Exception e) {
log.error("get pool address error: {}, IPoolAddressesProviderAddress{}",
chain.getLendingPoolAddressProviderV3Address(), e.getMessage());
throw new RuntimeException(e);
}
log.info("poolAddress is : {}", poolAddress);
IPool pool = IPool.load(poolAddress, web3j, getCredentials(), GAS_PROVIDER);
try {
Tuple6<BigInteger, BigInteger, BigInteger, BigInteger, BigInteger, BigInteger> info =
pool.getUserAccountData(address).send();
BigInteger totalCollateralBase = info.component1();
BigInteger totalDebtBase = info.component2();
BigInteger ltv = info.component5();
BigInteger healthFactor = info.component6();
healthInfo.set(HealthInfo.builder()
.healthFactor(healthFactor)
.totalBorrows(totalDebtBase.toString())
.totalCollateralETH(totalCollateralBase.toString())
.totalLiquidity(ltv.toString())
.build());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
return healthInfo.get();
} catch (Exception e) {
log.error("getHealthInfo error: " + e.getMessage());
throw new RuntimeException("get health Info error");
}
}

public AaveTokenAPYService(Credentials credentials,
IPoolAddressesProvider poolAddressesProvider) {
// this.web3j = web3j;
this.credentials = credentials;
this.poolAddressesProvider = poolAddressesProvider;
public List<ChainInfo> queryChainList() {
return new ArrayList<>(Web3jAutoConfiguration.web3jMap.keySet());
}

@Override
public List<TokenAPYInfo> queryTokenApy() {
@Scheduled(cron = "${jobs.defi.corn: 0 30 * * * * ?}")
public void updateTokenAPYInfo() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
log.info("update token info task begin --------------------- ");
log.info("now date {}", LocalDateTime.now().format(formatter));
tokenAPYInfoRepository.deleteAll();
List<TokenAPYInfo> tokenAPYInfoList = queryTokenApyOnChain();
tokenAPYInfoRepository.saveAll(tokenAPYInfoList);
}

public List<TokenAPYInfo> queryTokenApyOnChain() {
List<TokenAPYInfo> tokenAPYInfoList = new ArrayList<>();
try {
String poolAddress = poolAddressesProvider.getPool().send();
log.info("poolAddress is : {}", poolAddress);
IPool pool = IPool.load(poolAddress, web3j, credentials, GAS_PROVIDER);
chainWithToken.forEach((chain, tokenMap) -> {
tokenMap.forEach((tokenName, tokenAddress) -> {
log.info("chain: {} tokenName: {} tokenAddress: {}", chain, tokenName, tokenAddress);
chainWithWeb3j.forEach((chain, web3j) -> {
log.info("chain: {}", chain.getName());
IPoolAddressesProvider addressesProvider =
IPoolAddressesProvider.load(chain.getLendingPoolAddressProviderV3Address(), web3j,
getCredentials(), GAS_PROVIDER);
String poolAddress = null;
try {
poolAddress = addressesProvider.getPool().send();
} catch (Exception e) {
log.error("get pool address error: {}, IPoolAddressesProviderAddress{}",
chain.getLendingPoolAddressProviderV3Address(), e.getMessage());
throw new RuntimeException(e);
}
log.info("poolAddress is : {}", poolAddress);
IPool pool = IPool.load(poolAddress, web3j, getCredentials(), GAS_PROVIDER);
chain.getTokens().forEach(token -> {
log.info("tokenName: {} tokenAddress: {}", token.getName(), token.getAddress());
try {
IPool.ReserveData reserveData = pool.getReserveData(tokenAddress).send();
log.info("{} variable deposit interest: {}", tokenName, reserveData.currentVariableBorrowRate.divide(E23).floatValue() / 100);
IPool.ReserveData reserveData = pool.getReserveData(token.getAddress()).send();
log.info("{} variable deposit interest: {}", token.getName(), reserveData.currentVariableBorrowRate.divide(E23).floatValue() / 100);
String tokenApy = reserveData.currentLiquidityRate.divide(E23).floatValue() / 100 + "%";
TokenAPYInfo tokenAPYInfo = new TokenAPYInfo();
tokenAPYInfo.setTokenName(tokenName);
tokenAPYInfo.setTokenAddress(tokenAddress);
tokenAPYInfo.setChainName(chain);
tokenAPYInfo.setTokenName(token.getName());
tokenAPYInfo.setTokenAddress(token.getAddress());
tokenAPYInfo.setChainName(chain.getName());
tokenAPYInfo.setTokenApy(tokenApy);
tokenAPYInfo.setProtocol("Aave");
tokenAPYInfoList.add(tokenAPYInfo);
} catch (Exception e) {
log.error("get reserve data error: {}", e.getMessage());
Expand All @@ -87,38 +149,13 @@ public List<TokenAPYInfo> queryTokenApy() {
});
});
} catch (Exception e) {
log.error("get pool address error: {}", e.getMessage());
throw new RuntimeException("get token apy error", e);
log.error("queryTokenApyOnChain error: {}", e.getMessage());
throw new RuntimeException("query token apy error", e);
}
return tokenAPYInfoList;
}

@Override
public HealthInfo getHealthInfo(String address) {
try {
String poolAddress = poolAddressesProvider.getPool().send();
log.info("poolAddress is : " + poolAddress);
IPool pool = IPool.load(poolAddress, web3j, credentials, GAS_PROVIDER);
Tuple6<BigInteger, BigInteger, BigInteger, BigInteger, BigInteger, BigInteger> info = pool.getUserAccountData(
address).send();
BigInteger totalCollateralBase = info.component1();
BigInteger totalDebtBase = info.component2();
BigInteger ltv = info.component5();
BigInteger healthFactor = info.component6();
HealthInfo healthInfo = HealthInfo.builder()
.healthFactor(healthFactor)
.totalBorrows(totalDebtBase.toString())
.totalCollateralETH(totalCollateralBase.toString())
.totalLiquidity(ltv.toString())
.build();
return healthInfo;
} catch (Exception e) {
log.error("getHealthInfo error: " + e.getMessage());
throw new RuntimeException("get health Info error");
}
}

public List<String> queryChainList() {
return new ArrayList<>(chainWithToken.keySet());
private Credentials getCredentials() {
return Credentials.create("1");
}
}
3 changes: 2 additions & 1 deletion src/main/java/com/dl/officialsite/aave/AbstractTokenAPY.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dl.officialsite.aave;

import com.dl.officialsite.config.ChainInfo;
import java.util.List;

/**
Expand All @@ -15,7 +16,7 @@ public abstract class AbstractTokenAPY {
*/
public abstract List<TokenAPYInfo> queryTokenApy();

public abstract HealthInfo getHealthInfo(String address);
public abstract HealthInfo getHealthInfo(ChainInfo chainInfo, String address);


}
12 changes: 6 additions & 6 deletions src/main/java/com/dl/officialsite/aave/DeFiController.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.dl.officialsite.aave;

import com.dl.officialsite.common.base.BaseResponse;
import com.dl.officialsite.config.ChainInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -24,9 +27,6 @@ public DeFiController(AaveTokenAPYService aaveService) {
this.aaveService = aaveService;
}

/**
* 查询币种列表
*/

/**
* get all chainName
Expand All @@ -47,9 +47,9 @@ public BaseResponse tokenApy() {
/**
* get healthInfo by wallet address
*/
@GetMapping("/healthInfo")
public BaseResponse detail(@RequestParam String address) {
HealthInfo healthInfo = aaveService.getHealthInfo(address);
@PostMapping("/healthInfo")
public BaseResponse detail(@RequestParam String address, @RequestBody ChainInfo chainInfo) {
HealthInfo healthInfo = aaveService.getHealthInfo(chainInfo, address);
return BaseResponse.successWithData(healthInfo);
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/dl/officialsite/aave/TokenAPYInfo.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package com.dl.officialsite.aave;

import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

/**
* @ClassName TokenAPYInfo
Expand All @@ -9,13 +16,24 @@
* @Description TokenAPYInfo
**/
@Data
@Entity
@Table(name = "token_apy_info")
@EntityListeners(AuditingEntityListener.class)
public class TokenAPYInfo {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

String tokenName;

String tokenAddress;

String chainName;

String tokenApy;

String current;

String protocol;
}
15 changes: 15 additions & 0 deletions src/main/java/com/dl/officialsite/aave/TokenAPYInfoRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.dl.officialsite.aave;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
* @ClassName TokenAPYInfoRepository
* @Author jackchen
* @Date 2024/3/9 16:39
* @Description TokenAPYInfoRepository
**/
public interface TokenAPYInfoRepository extends JpaRepository<TokenAPYInfo, Long>,
JpaSpecificationExecutor<TokenAPYInfo> {

}
15 changes: 14 additions & 1 deletion src/main/java/com/dl/officialsite/bounty/BountyService.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

/**
* @ClassName BountyService
Expand Down Expand Up @@ -80,7 +81,11 @@ public Page<BountyVo> search(BountySearchVo bountySearchVo, Pageable pageable) {
.and(hasStatus(bountySearchVo.getStatus()))
.and(hasDeadLineBefore(bountySearchVo.getDeadLine()))
.and(isNotStatus(5));

if (!ObjectUtils.isEmpty(bountySearchVo.getLinkStream())) {
if (bountySearchVo.getLinkStream().equals(2)) {
spec = spec.and(notLinkStream());
}
}
Page<Bounty> bountyPage = bountyRepository.findAll(spec, pageable);
return bountyPage.map(this::mapToBountyVo);
}
Expand All @@ -105,6 +110,11 @@ private Specification<Bounty> hasDeadLineBefore(LocalDateTime deadline) {
criteriaBuilder.lessThan(root.get("deadLine"), deadline) : null;
}

private Specification<Bounty> notLinkStream() {
return (root, query, criteriaBuilder) ->
criteriaBuilder.isNull(root.get("streamId"));
}

private Specification<Bounty> isNotStatus(Integer status) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.notEqual(root.get("status"), status);
Expand Down Expand Up @@ -222,6 +232,9 @@ public void link(BountyVo bountyVo, String address) {
if (!bounty.getCreator().equals(address)) {
throw new BizException("2003", "not link bounty by creator");
}
if (!ObjectUtils.isEmpty(bounty.getStreamStart())) {
throw new BizException("2005", "bounty not apply");
}
bounty.setStreamId(bountyVo.getStreamId());
bounty.setStreamEnd(bountyVo.getStreamStart());
bounty.setStreamEnd(bountyVo.getStreamEnd());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public class BountySearchVo {
//状态
private Integer status;

//0: is linked 2:not linked
private Integer linkStream;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime deadLine;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public enum CodeEnums {

APPLY_REPEAT("1024", "apply repeat"),

NOT_FOUND_BOUNTY("2001", "not found bounty"),
NOT_FOUND_BOUNTY("1025", "not found bounty"),

// distribute
ID_NEED_EMPTY("6000", "Id need empty"),
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/dl/officialsite/config/ChainInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.dl.officialsite.config;

import java.util.List;
import lombok.Data;

/**
* @ClassName ChainInfo
* @Author jackchen
* @Date 2024/3/10 11:35
* @Description ChainInfo
**/
@Data
public class ChainInfo {

private String name;

private String id;

private String lendingPoolAddressProviderV3Address;

private List<TokenConfig> tokens;
}
Loading

0 comments on commit 7e0c6e8

Please sign in to comment.