소스 검색

feat: script

Wren 1 년 전
부모
커밋
749b7394f2

+ 1 - 1
pom.xml

@@ -36,7 +36,7 @@
         <dependency>
             <groupId>org.web3j</groupId>
             <artifactId>core</artifactId>
-            <version>5.0.0</version>
+            <version>4.8.7</version>
         </dependency>
         <dependency>
             <groupId>cn.hutool</groupId>

+ 28 - 0
src/main/java/com/ichaoj/ams/controller/AirdropTaskController.java

@@ -1,6 +1,7 @@
 package com.ichaoj.ams.controller;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.ListUtil;
 import com.ichaoj.ams.entity.AmsAirdropTask;
 import com.ichaoj.ams.request.airdrop.PageProjectRequest;
 import com.ichaoj.ams.request.task.CreateAirdropTask;
@@ -9,6 +10,11 @@ import com.ichaoj.ams.request.task.UpdateAirdropTask;
 import com.ichaoj.ams.request.task.UpdateTaskStatusRequest;
 import com.ichaoj.ams.response.airdrop.AirdropProjectResponse;
 import com.ichaoj.ams.response.task.TaskResponse;
+import com.ichaoj.ams.script.IScript;
+import com.ichaoj.ams.script.ScriptContext;
+import com.ichaoj.ams.script.annotation.Script;
+import com.ichaoj.ams.script.annotation.ScriptParam;
+import com.ichaoj.ams.script.model.AirdropParam;
 import com.ichaoj.ams.service.IAmsAirdropTaskService;
 import com.ichaoj.common.annotation.AuthResource;
 import com.ichaoj.common.exception.ErrorServiceException;
@@ -21,6 +27,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -92,4 +99,25 @@ public class AirdropTaskController {
         List<TaskResponse> result = taskService.listTask();
         return PublicResult.success(result);
     }
+
+    @GetMapping("/params/{task-code}")
+    @AuthResource
+    @Operation(summary = "查询任务参数")
+    public PublicResult<List<AirdropParam>> getAirdropParam(@PathVariable("task-code") String taskCode) {
+        IScript script = ScriptContext.getScriptByCode(taskCode);
+        Script annotation = script.getClass().getAnnotation(Script.class);
+        ScriptParam[] params = annotation.params();
+        if (params != null && params.length > 0) {
+            List<AirdropParam> airdropParams = new ArrayList<>();
+            for (ScriptParam param : params) {
+                AirdropParam airdropParam = new AirdropParam();
+                airdropParam.setName(param.name());
+                airdropParam.setNote(param.note());
+                airdropParam.setValue(param.defaultValue());
+                airdropParams.add(airdropParam);
+            }
+            return PublicResult.success(airdropParams);
+        }
+        return PublicResult.success(ListUtil.empty());
+    }
 }

+ 4 - 0
src/main/java/com/ichaoj/ams/entity/AmsAirdropTask.java

@@ -3,6 +3,7 @@ package com.ichaoj.ams.entity;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+
 import java.io.Serializable;
 import java.time.LocalDateTime;
 
@@ -41,6 +42,9 @@ public class AmsAirdropTask implements Serializable {
      */
     private String taskName;
 
+
+    private String taskCode;
+
     /**
      * 合约地址
      */

+ 2 - 0
src/main/java/com/ichaoj/ams/entity/AmsExecuteRecord.java

@@ -44,6 +44,8 @@ public class AmsExecuteRecord implements Serializable {
      */
     private String taskId;
 
+    private String airdropParams;
+
     /**
      * 用户id
      */

+ 5 - 0
src/main/java/com/ichaoj/ams/request/execute/CreateExecute.java

@@ -1,8 +1,11 @@
 package com.ichaoj.ams.request.execute;
 
+import com.ichaoj.ams.script.model.AirdropParam;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * @author : cjwen
  * @date : 2023/04/26 14:25
@@ -31,6 +34,8 @@ public class CreateExecute {
      */
     private String userId;
 
+    private List<AirdropParam> airdropParams;
+
     /**
      * 地址组名称
      */

+ 2 - 0
src/main/java/com/ichaoj/ams/request/task/CreateAirdropTask.java

@@ -24,6 +24,8 @@ public class CreateAirdropTask {
      */
     private String taskName;
 
+    private String taskCode;
+
     /**
      * 计划任务执行次数
      */

+ 26 - 0
src/main/java/com/ichaoj/ams/script/IScript.java

@@ -0,0 +1,26 @@
+package com.ichaoj.ams.script;
+
+
+import com.ichaoj.ams.script.model.AirdropParam;
+import com.ichaoj.ams.script.model.AirdropWallet;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 脚本接口
+ *
+ * @author wren
+ */
+public abstract class IScript {
+
+
+    /**
+     * 运行脚本
+     *
+     * @param params 参数
+     * @return txId
+     */
+    public abstract String run(Map<String, AirdropParam> params, AirdropWallet airdropWallet);
+
+}

+ 30 - 0
src/main/java/com/ichaoj/ams/script/ScriptContext.java

@@ -0,0 +1,30 @@
+package com.ichaoj.ams.script;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.ichaoj.ams.script.annotation.Script;
+import com.ichaoj.common.exception.ErrorServiceException;
+
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * @author wren
+ */
+public class ScriptContext {
+
+
+    public static IScript getScriptByCode(String code) {
+        Map<String, IScript> beansOfType = SpringUtil.getBeansOfType(IScript.class);
+        Optional<IScript> optional = beansOfType
+                .values()
+                .stream()
+                .filter(script -> {
+                    Script annotation = script.getClass().getAnnotation(Script.class);
+                    return annotation != null && annotation.name().equals(code);
+                })
+                .findFirst();
+        return optional.orElseThrow(() -> new ErrorServiceException("脚本不存在"));
+    }
+
+
+}

+ 23 - 0
src/main/java/com/ichaoj/ams/script/annotation/Script.java

@@ -0,0 +1,23 @@
+package com.ichaoj.ams.script.annotation;
+
+import org.springframework.stereotype.Component;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author wren
+ * @since 2022-06-07
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Component
+public @interface Script {
+    String name() default "";
+
+    ScriptParam[] params() default {};
+
+
+}

+ 20 - 0
src/main/java/com/ichaoj/ams/script/annotation/ScriptParam.java

@@ -0,0 +1,20 @@
+package com.ichaoj.ams.script.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author wren
+ * @since 2022-06-07
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ScriptParam {
+    String name() default "";
+
+    String note() default "";
+
+    String defaultValue() default "";
+}

+ 19 - 0
src/main/java/com/ichaoj/ams/script/model/AirdropParam.java

@@ -0,0 +1,19 @@
+package com.ichaoj.ams.script.model;
+
+import lombok.Data;
+
+/**
+ * airdrop script param
+ *
+ * @author wren
+ */
+@Data
+public class AirdropParam {
+
+
+    private String name;
+    private String note;
+    private String value;
+
+
+}

+ 24 - 0
src/main/java/com/ichaoj/ams/script/model/AirdropWallet.java

@@ -0,0 +1,24 @@
+package com.ichaoj.ams.script.model;
+
+import lombok.Data;
+
+import java.math.BigInteger;
+
+/**
+ * airdrop wallet
+ *
+ * @author wren
+ */
+@Data
+public class AirdropWallet {
+
+    private String address;
+
+    private String privateKey;
+    /**
+     * 公链原生币余额
+     */
+    private BigInteger nativeCoinBalance;
+
+
+}

+ 92 - 0
src/main/java/com/ichaoj/ams/script/util/Web3Util.java

@@ -0,0 +1,92 @@
+package com.ichaoj.ams.script.util;
+
+import com.ichaoj.ams.script.model.AirdropWallet;
+import org.web3j.abi.FunctionEncoder;
+import org.web3j.abi.FunctionReturnDecoder;
+import org.web3j.abi.TypeReference;
+import org.web3j.abi.datatypes.Address;
+import org.web3j.abi.datatypes.Function;
+import org.web3j.abi.datatypes.Type;
+import org.web3j.abi.datatypes.generated.Uint256;
+import org.web3j.crypto.Credentials;
+import org.web3j.protocol.Web3j;
+import org.web3j.protocol.core.DefaultBlockParameterName;
+import org.web3j.protocol.core.methods.request.Transaction;
+import org.web3j.protocol.core.methods.response.EthCall;
+import org.web3j.protocol.core.methods.response.EthEstimateGas;
+import org.web3j.protocol.core.methods.response.EthGetBalance;
+import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
+import org.web3j.utils.Convert;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Web3Util {
+
+    public static BigInteger getNonce(Web3j web3j, String addr) {
+        try {
+            EthGetTransactionCount getNonce = web3j.ethGetTransactionCount(addr, DefaultBlockParameterName.PENDING).send();
+            if (getNonce == null) {
+                throw new RuntimeException("net error");
+            }
+            return getNonce.getTransactionCount();
+        } catch (IOException e) {
+            throw new RuntimeException("net error");
+        }
+    }
+
+    public static BigDecimal getBalance(Web3j web3j, String address) {
+        try {
+            EthGetBalance ethGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
+            return Convert.fromWei(new BigDecimal(ethGetBalance.getBalance()), Convert.Unit.ETHER);
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static BigInteger getTokenBalance(Web3j web3j, String fromAddress, String contractAddress) {
+        String methodName = "balanceOf";
+        List<Type> inputParameters = new ArrayList<>();
+        List<TypeReference<?>> outputParameters = new ArrayList<>();
+        Address address = new Address(fromAddress);
+        inputParameters.add(address);
+
+        TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {
+        };
+        outputParameters.add(typeReference);
+        Function function = new Function(methodName, inputParameters, outputParameters);
+        String data = FunctionEncoder.encode(function);
+        Transaction transaction = Transaction.createEthCallTransaction(fromAddress, contractAddress, data);
+
+        EthCall ethCall;
+        BigInteger balanceValue = BigInteger.ZERO;
+        try {
+            ethCall = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send();
+            List<Type> results = FunctionReturnDecoder.decode(ethCall.getValue(), function.getOutputParameters());
+            balanceValue = (BigInteger) results.get(0).getValue();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return balanceValue;
+    }
+
+    public static BigInteger getTransactionGasLimit(Web3j web3j, Transaction transaction) {
+        try {
+            EthEstimateGas ethEstimateGas = web3j.ethEstimateGas(transaction).send();
+            if (ethEstimateGas.hasError()) {
+                throw new RuntimeException(ethEstimateGas.getError().getMessage());
+            }
+            return ethEstimateGas.getAmountUsed();
+        } catch (IOException e) {
+            throw new RuntimeException("net error");
+        }
+    }
+
+    public static Credentials getCredentials(AirdropWallet wallet) {
+        return Credentials.create(wallet.getPrivateKey());
+    }
+}

+ 27 - 0
src/main/java/com/ichaoj/ams/script/zksync/era/SwapPath.java

@@ -0,0 +1,27 @@
+package com.ichaoj.ams.script.zksync.era;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.web3j.abi.datatypes.Address;
+import org.web3j.abi.datatypes.DynamicArray;
+import org.web3j.abi.datatypes.StaticStruct;
+import org.web3j.abi.datatypes.Uint;
+/**
+ * @author wrenj
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SwapPath extends StaticStruct {
+    public DynamicArray<SwapStep> steps;
+    public Address tokenIn;
+
+    public Uint amountIn;
+
+    public SwapPath(DynamicArray<SwapStep> steps, Address tokenIn, Uint amountIn) {
+        super(steps, tokenIn, amountIn);
+        this.steps = steps;
+        this.tokenIn = tokenIn;
+        this.amountIn = amountIn;
+    }
+
+}

+ 25 - 0
src/main/java/com/ichaoj/ams/script/zksync/era/SwapStep.java

@@ -0,0 +1,25 @@
+package com.ichaoj.ams.script.zksync.era;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.web3j.abi.datatypes.Address;
+import org.web3j.abi.datatypes.DynamicBytes;
+import org.web3j.abi.datatypes.StaticStruct;
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SwapStep extends StaticStruct {
+
+    public Address pool;
+    public DynamicBytes data;
+    public Address callback;
+
+    public DynamicBytes callbackData;
+
+    public SwapStep(Address pool, DynamicBytes data, Address callback, DynamicBytes callbackData) {
+        super(pool, data, callback, callbackData);
+        this.pool = pool;
+        this.data = data;
+        this.callback = callback;
+        this.callbackData = callbackData;
+    }
+}

+ 16 - 0
src/main/java/com/ichaoj/ams/script/zksync/era/TokenAmount.java

@@ -0,0 +1,16 @@
+package com.ichaoj.ams.script.zksync.era;
+
+import org.web3j.abi.datatypes.Address;
+import org.web3j.abi.datatypes.StaticStruct;
+import org.web3j.abi.datatypes.Uint;
+
+public class TokenAmount extends StaticStruct {
+    public Address token;
+   public Uint amount;
+
+
+    public TokenAmount(Address token, Uint amount) {
+        this.token = token;
+        this.amount = amount;
+    }
+}

+ 102 - 0
src/main/java/com/ichaoj/ams/script/zksync/era/ZkSyncCrossScript.java

@@ -0,0 +1,102 @@
+package com.ichaoj.ams.script.zksync.era;
+
+import cn.hutool.core.collection.ListUtil;
+import com.ichaoj.ams.script.IScript;
+import com.ichaoj.ams.script.annotation.Script;
+import com.ichaoj.ams.script.annotation.ScriptParam;
+import com.ichaoj.ams.script.model.AirdropParam;
+import com.ichaoj.ams.script.model.AirdropWallet;
+import com.ichaoj.ams.script.util.Web3Util;
+import lombok.SneakyThrows;
+import org.jetbrains.annotations.NotNull;
+import org.web3j.abi.FunctionEncoder;
+import org.web3j.abi.TypeReference;
+import org.web3j.abi.datatypes.*;
+import org.web3j.abi.datatypes.generated.Bytes32;
+import org.web3j.abi.datatypes.generated.Uint256;
+import org.web3j.crypto.Credentials;
+import org.web3j.crypto.RawTransaction;
+import org.web3j.crypto.TransactionEncoder;
+import org.web3j.protocol.Web3j;
+import org.web3j.protocol.core.Response;
+import org.web3j.protocol.core.methods.request.Transaction;
+import org.web3j.protocol.core.methods.response.EthSendTransaction;
+import org.web3j.protocol.http.HttpService;
+import org.web3j.utils.Convert;
+import org.web3j.utils.Numeric;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author wrenj
+ */
+@Script(name = "zksync-cross",
+        params = {
+                @ScriptParam(name = "amount", note = "跨链金额"),
+                @ScriptParam(name = "value", note = "转账value,要求覆盖L2的手续费"),
+                @ScriptParam(name = "l2GasLimit", note = "L2 gas limit", defaultValue = "901876"),
+
+        })
+public class ZkSyncCrossScript extends IScript {
+
+    @SneakyThrows
+    @Override
+    public String run(Map<String, AirdropParam> params, AirdropWallet airdropWallet) {
+        String mailBox = "0x32400084c286cf3e17e7b677ea9583e60a000324";
+        Web3j web3j = Web3j.build(new HttpService("https://eth-mainnet.g.alchemy.com/v2/zS4UDTUziif2jL82eFGoP_sddA8aVgEV"));
+        List<Type> inputParameters = new ArrayList<>();
+        //recipient
+        inputParameters.add(new Address(airdropWallet.getAddress()));
+        //amount
+        inputParameters.add(new Uint256(Convert.toWei("0.001", Convert.Unit.ETHER).toBigInteger()));
+        //bytes
+        inputParameters.add(DynamicBytes.DEFAULT);
+        //l2 gas limit
+        inputParameters.add(new Uint256(701876));
+        //_l2GasPerPub-dataByteLimit
+        inputParameters.add(new Uint256(800));
+        //data
+        inputParameters.add(new DynamicArray<>(DynamicArray.class));
+        //_refundRecipient
+        inputParameters.add(new Address(airdropWallet.getAddress()));
+        Function requestL2Transaction = new Function("requestL2Transaction", inputParameters, ListUtil.of(new TypeReference<Bytes32>() {
+        }));
+        BigInteger nonce = Web3Util.getNonce(web3j, airdropWallet.getAddress());
+        BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice();
+        String l2Encoder = FunctionEncoder.encode(requestL2Transaction);
+
+        Transaction callTransaction = Transaction.createFunctionCallTransaction(
+                airdropWallet.getAddress(),
+                nonce,
+                gasPrice,
+                BigInteger.valueOf(0),
+                mailBox,
+                Convert.toWei("0.002", Convert.Unit.ETHER).toBigInteger(),
+                l2Encoder);
+        BigInteger gasLimit = Web3Util.getTransactionGasLimit(web3j, callTransaction);
+
+        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,
+                gasPrice,
+                gasLimit,
+                mailBox,
+                Convert.toWei("0.002", Convert.Unit.ETHER).toBigInteger(),
+                l2Encoder);
+        //签名
+        Credentials credentials = Web3Util.getCredentials(airdropWallet);
+        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
+        String hexValue = Numeric.toHexString(signMessage);
+        //发送交易
+        EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
+        Response.Error error = ethSendTransaction.getError();
+        if (error != null) {
+            throw new RuntimeException(error.getMessage());
+        }
+        return ethSendTransaction.getTransactionHash();
+    }
+
+
+}

+ 159 - 0
src/main/java/com/ichaoj/ams/script/zksync/era/ZkSyncEraScript.java

@@ -0,0 +1,159 @@
+package com.ichaoj.ams.script.zksync.era;
+
+import cn.hutool.core.collection.ListUtil;
+import com.ichaoj.ams.script.IScript;
+import com.ichaoj.ams.script.annotation.Script;
+import com.ichaoj.ams.script.annotation.ScriptParam;
+import com.ichaoj.ams.script.model.AirdropParam;
+import com.ichaoj.ams.script.model.AirdropWallet;
+import com.ichaoj.ams.script.util.Web3Util;
+import lombok.SneakyThrows;
+import org.web3j.abi.FunctionEncoder;
+import org.web3j.abi.TypeReference;
+import org.web3j.abi.datatypes.*;
+import org.web3j.abi.datatypes.generated.Uint8;
+import org.web3j.protocol.Web3j;
+import org.web3j.protocol.core.DefaultBlockParameterName;
+import org.web3j.protocol.core.methods.request.Transaction;
+import org.web3j.protocol.core.methods.response.EthGasPrice;
+import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
+import org.web3j.protocol.http.HttpService;
+import org.web3j.utils.Convert;
+import org.web3j.utils.Numeric;
+
+import java.math.BigInteger;
+import java.util.Map;
+
+/**
+ * zkSyncEra script
+ * //todo 假设原生代币已经转入了子钱包
+ *
+ * @author wren
+ */
+@Script(name = "zkSyncEra",
+        params = {
+
+                @ScriptParam(name = "swapAmount", note = "swap金额")
+        }
+)
+public class ZkSyncEraScript extends IScript {
+    private final String POOL_FACTORY = "0xf2DAd89f2788a8CD54625C60b55cD3d2D0ACa7Cb";
+    private final String W_ETH = "0x5aea5775959fbc2557cc8789bc1bf90a239d9a91";
+    private final String USDC = "0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4";
+
+    private final String ROUTER = "0x2da10A1e27bF85cEdD8FFb1AbBe97e53391C0295";
+
+    private final String ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
+
+// 0x2cc4081e
+// 0000000000000000000000000000000000000000000000000000000000000060
+// 00000000000000000000000000000000000000000000000000000000001b6171
+// 00000000000000000000000000000000000000000000000000000000647084d2
+// 0000000000000000000000000000000000000000000000000000000000000001
+// 0000000000000000000000000000000000000000000000000000000000000020
+// 0000000000000000000000000000000000000000000000000000000000000060
+// 0000000000000000000000000000000000000000000000000000000000000000
+// 00000000000000000000000000000000000000000000000000038d7ea4c68000
+// 0000000000000000000000000000000000000000000000000000000000000001
+// 0000000000000000000000000000000000000000000000000000000000000020
+// 00000000000000000000000080115c708e12edd42e504c1cd52aea96c547c05c
+// 0000000000000000000000000000000000000000000000000000000000000080
+// 0000000000000000000000000000000000000000000000000000000000000000
+// 0000000000000000000000000000000000000000000000000000000000000100
+// 0000000000000000000000000000000000000000000000000000000000000060
+// 0000000000000000000000005aea5775959fbc2557cc8789bc1bf90a239d9a91
+// 000000000000000000000000368715f09c1ab5e0b55bf5ba19cd887189a28dbe  address
+// 0000000000000000000000000000000000000000000000000000000000000002
+// 0000000000000000000000000000000000000000000000000000000000000000
+
+// 0x0054ed6a
+// 0000000000000000000000000000000000000000000000000000000000000060
+// 0000000000000000000000000000000000000000000000000000000000000000
+// 00000000000000000000000000000000000000000000000000000000647570f3
+// 0000000000000000000000000000000000000000000000000000000000000001
+// 0000000000000000000000000000000000000000000000000000000000000001
+// 00000000000000000000000080115c708e12edd42e504c1cd52aea96c547c05c
+// 0000000000000000000000000000000000000000000000000000000000000060
+// 0000000000000000000000005aea5775959fbc2557cc8789bc1bf90a239d9a91
+// 000000000000000000000000368715f09c1ab5e0b55bf5ba19cd887189a28dbe
+// 0000000000000000000000000000000000000000000000000000000000000001
+// 0000000000000000000000000000000000000000000000000000000000000000
+// 0000000000000000000000000000000000000000000000000000000000000000
+// 0000000000000000000000000000000000000000000000000000000000000000
+// 00000000000000000000000000000000000000000000000000005af3107a4000
+
+    @SneakyThrows
+    @Override
+    public String run(Map<String, AirdropParam> params, AirdropWallet airdropWallet) {
+
+
+        Web3j web3j = Web3j.build(new HttpService("https://mainnet.era.zksync.io"));
+        Address poolAddress = new Address("0x80115c708E12eDd42E504c1cD52Aea96C547c05c");
+
+//        0x0000000000000000000000005aea5775959fbc2557cc8789bc1bf90a239d9a91000000000000000000000000368715f09c1ab5e0b55bf5ba19cd887189a28dbe0000000000000000000000000000000000000000000000000000000000000001
+//        0x0000000000000000000000005aea5775959fbc2557cc8789bc1bf90a239d9a91000000000000000000000000368715f09c1ab5e0b55bf5ba19cd887189a28dbe0000000000000000000000000000000000000000000000000000000000000001
+        String data = "0x" + FunctionEncoder.encodeConstructor(ListUtil.of(
+                new Address(W_ETH),
+                new Address(airdropWallet.getAddress()),
+                new Uint8(1)));
+        // build SwapSteps  --step1
+        DynamicArray<StaticStruct> steps = new DynamicArray<>(StaticStruct.class, ListUtil.of(new StaticStruct(
+                poolAddress,
+                new DynamicBytes(Numeric.hexStringToByteArray(data)),
+                new Address(ZERO_ADDRESS),
+                new DynamicBytes(Numeric.hexStringToByteArray("0x")))));
+        BigInteger swapAmount = Convert.toWei(params.get("swapAmount").getValue(), Convert.Unit.ETHER).toBigInteger();
+        // build SwapPaths --step2
+        DynamicArray<StaticStruct> paths = new DynamicArray<>(StaticStruct.class, ListUtil.of(new StaticStruct(
+                steps,
+                new Address(ZERO_ADDRESS),
+                new Uint(swapAmount))));
+
+        Function swap = new Function("swap",
+                ListUtil.of(
+                        paths,
+                        new Uint(BigInteger.ZERO),
+                        new Uint(BigInteger.valueOf(System.currentTimeMillis() / 1000 + 1800))),
+                ListUtil.of(new TypeReference<TokenAmount>() {
+                }));
+        String swapEncoder = FunctionEncoder.encode(swap);
+
+        BigInteger nonce;
+        EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(airdropWallet.getAddress(), DefaultBlockParameterName.PENDING).send();
+        if (transactionCount == null) {
+            throw new RuntimeException("Failed to get nonce");
+        }
+        nonce = transactionCount.getTransactionCount();
+        BigInteger gasPrice;
+        EthGasPrice ethGasPrice = web3j.ethGasPrice().sendAsync().get();
+        if (ethGasPrice == null) {
+            throw new RuntimeException("Failed to get GasPrice");
+        }
+        gasPrice = ethGasPrice.getGasPrice();
+        Transaction callTransaction = Transaction.createFunctionCallTransaction(
+                airdropWallet.getAddress(),
+                nonce,
+                gasPrice,
+                BigInteger.valueOf(0),
+                ROUTER,
+                swapAmount,
+                swapEncoder);
+
+
+        BigInteger gasLimit = Web3Util.getTransactionGasLimit(web3j, callTransaction);
+
+//        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, ROUTER, swapEncoder);
+//        //签名
+//        Credentials credentials = Web3Util.getCredentials(airdropWallet);
+//        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
+//        String hexValue = Numeric.toHexString(signMessage);
+//        //发送交易
+//        EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
+//        Response.Error error = ethSendTransaction.getError();
+//        if (error != null) {
+//            throw new RuntimeException(error.getMessage());
+//        }
+        return gasLimit.toString();
+
+    }
+}

+ 52 - 28
src/main/java/com/ichaoj/ams/service/impl/AmsExecuteRecordServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -14,6 +15,10 @@ import com.ichaoj.ams.request.execute.CreateExecute;
 import com.ichaoj.ams.request.execute.PageExecuteRequest;
 import com.ichaoj.ams.request.execute.UpdateExecute;
 import com.ichaoj.ams.response.execute.ExecuteResponse;
+import com.ichaoj.ams.script.IScript;
+import com.ichaoj.ams.script.ScriptContext;
+import com.ichaoj.ams.script.model.AirdropParam;
+import com.ichaoj.ams.script.model.AirdropWallet;
 import com.ichaoj.ams.service.*;
 import com.ichaoj.common.exception.ErrorServiceException;
 import com.ichaoj.common.model.PublicPage;
@@ -26,9 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -87,6 +90,7 @@ public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecut
         amsExecute.setCreateTime(LocalDateTime.now());
         amsExecute.setTaskId(task.getAmsTaskId());
         amsExecute.setProjectId(project.getAmsProjectId());
+        amsExecute.setAirdropParams(JSON.toJSONString(createExecute.getAirdropParams()));
         // todo 查询是第几次执行
         this.save(amsExecute);
         if (StrUtil.isBlank(createExecute.getPassword())) {
@@ -94,12 +98,12 @@ public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecut
         }
         // todo 验证密码是否正确
 
-        ThreadUtil.execute(() -> tradeService.randomTrans(accountList
-                , createExecute.getIntervalMin()
-                , createExecute.getIntervalMax()
-                , createExecute.getAmount()
-                , createExecute.getMaxGas()
-                , amsExecute.getExecuteId()));
+//        ThreadUtil.execute(() -> tradeService.randomTrans(accountList
+//                , createExecute.getIntervalMin()
+//                , createExecute.getIntervalMax()
+//                , createExecute.getAmount()
+//                , createExecute.getMaxGas()
+//                , amsExecute.getExecuteId()));
 
     }
 
@@ -155,30 +159,50 @@ public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecut
 
     @Scheduled(cron = "0/59 * * * * ? ")
     public void scanExecuteStatus() {
-        List<AmsExecuteRecord> list = this.list();
-        for (AmsExecuteRecord executeRecord : list) {
+        LambdaQueryWrapper<AmsExecuteRecord> eq = Wrappers.lambdaQuery(AmsExecuteRecord.class)
+                .eq(AmsExecuteRecord::getExecuteStatus, 0);
+        List<AmsExecuteRecord> amsExecuteRecords = this.list(eq);
+//        List<AmsExecuteRecord> list = this.list();
+        for (AmsExecuteRecord executeRecord : amsExecuteRecords) {
             String executeId = executeRecord.getExecuteId();
             String userId = executeRecord.getUserId();
             String groupName = executeRecord.getGroupName();
             List<AmsAddressAccount> accounts = accountService.getByGroupNameAndUserId(groupName, userId);
-            Set<String> predictSet = accounts.stream().map(AmsAddressAccount::getAddress).collect(Collectors.toSet());
-            List<AmsTradeRecord> tradeRecords = tradeService.list(new LambdaQueryWrapper<AmsTradeRecord>()
-                    .eq(AmsTradeRecord::getExecuteId, executeId));
-            Set<String> actualSet = tradeRecords.stream().map(AmsTradeRecord::getAddress).collect(Collectors.toSet());
-            // 判断交易记录的地址是否
-            if (CollectionUtil.containsAll(predictSet, actualSet) && CollectionUtil.containsAll(actualSet, predictSet)) {
-                Set<String> confirmSet = new HashSet<>();
-                for (AmsTradeRecord tradeRecord : tradeRecords) {
-                    if (tradeRecord.getStatus() == 1) {
-                        confirmSet.add(tradeRecord.getAddress());
-                    }
-                }
-                // 修改状态
-                if (CollectionUtil.containsAll(confirmSet, actualSet) && CollectionUtil.containsAll(actualSet, confirmSet)) {
-                    executeRecord.setExecuteStatus(1);
-                    this.updateById(executeRecord);
-                }
+            AmsAirdropTask task = taskService.getById(executeRecord.getTaskId());
+            String taskCode = task.getTaskCode();
+            IScript script = ScriptContext.getScriptByCode(taskCode);
+            String airdropParams = executeRecord.getAirdropParams();
+            List<AirdropParam> params = JSON.parseArray(airdropParams, AirdropParam.class);
+            Map<String, AirdropParam> paramMap = new HashMap<>();
+            for (AirdropParam param : params) {
+                paramMap.put(param.getName(), param);
             }
+//            accounts.stream().map(account->{
+//                AirdropWallet wallet = new AirdropWallet();
+//                wallet.setAddress(account.getAddress());
+//                String keystore = account.getKeystore();
+//                accountService.getPrivateKeyByKeystore(keystore, executeId);
+//            })
+
+
+//            Set<String> predictSet = accounts.stream().map(AmsAddressAccount::getAddress).collect(Collectors.toSet());
+//            List<AmsTradeRecord> tradeRecords = tradeService.list(new LambdaQueryWrapper<AmsTradeRecord>()
+//                    .eq(AmsTradeRecord::getExecuteId, executeId));
+//            Set<String> actualSet = tradeRecords.stream().map(AmsTradeRecord::getAddress).collect(Collectors.toSet());
+//            // 判断交易记录的地址是否
+//            if (CollectionUtil.containsAll(predictSet, actualSet) && CollectionUtil.containsAll(actualSet, predictSet)) {
+//                Set<String> confirmSet = new HashSet<>();
+//                for (AmsTradeRecord tradeRecord : tradeRecords) {
+//                    if (tradeRecord.getStatus() == 1) {
+//                        confirmSet.add(tradeRecord.getAddress());
+//                    }
+//                }
+//                // 修改状态
+//                if (CollectionUtil.containsAll(confirmSet, actualSet) && CollectionUtil.containsAll(actualSet, confirmSet)) {
+//                    executeRecord.setExecuteStatus(1);
+//                    this.updateById(executeRecord);
+//                }
+//            }
         }
     }
 }

+ 8 - 8
src/main/java/com/ichaoj/ams/service/impl/AmsTradeRecordServiceImpl.java

@@ -50,7 +50,7 @@ import java.util.stream.Collectors;
 @Slf4j
 public class AmsTradeRecordServiceImpl extends SuperWhaleServiceImpl<AmsTradeRecordMapper, AmsTradeRecord> implements IAmsTradeRecordService {
 
-    private final static ScheduledExecutorService AMS_SCHEDULER = Executors.newScheduledThreadPool(5);
+//    private final static ScheduledExecutorService AMS_SCHEDULER = Executors.newScheduledThreadPool(5);
 
     @Resource
     @Lazy
@@ -123,13 +123,13 @@ public class AmsTradeRecordServiceImpl extends SuperWhaleServiceImpl<AmsTradeRec
                             sleep / 60);
                     log.info("当前gas: {}", gas);
                     log.info("amount: {}", currenAmount);
-                    AMS_SCHEDULER.schedule(() -> {
-                        // todo 调用链上交易
-
-                        //交易入库
-                        executeTrans(executeId, address, gas, currenAmount);
-
-                    }, sleep, TimeUnit.SECONDS);
+//                    AMS_SCHEDULER.schedule(() -> {
+//                        // todo 调用链上交易
+//
+//                        //交易入库
+//                        executeTrans(executeId, address, gas, currenAmount);
+//
+//                    }, sleep, TimeUnit.SECONDS);
                     i++;
                 }
             }