Jelajahi Sumber

Merge branch 'master' of https://git.ichaoj.com/MorningWhale/ams-backend

cjwen 1 tahun lalu
induk
melakukan
48bd9f6662
36 mengubah file dengan 1562 tambahan dan 302 penghapusan
  1. 15 0
      front/admin-front/src/api/task.js
  2. 25 1
      front/admin-front/src/views/address/index.vue
  3. 414 231
      front/admin-front/src/views/autoplay/index.vue
  4. 2 1
      front/admin-front/src/views/dashboard/index.vue
  5. 1 1
      pom.xml
  6. 12 0
      src/main/java/com/ichaoj/ams/common/util/WalletUtil.java
  7. 28 0
      src/main/java/com/ichaoj/ams/controller/AirdropTaskController.java
  8. 6 0
      src/main/java/com/ichaoj/ams/entity/AmsAirdropTask.java
  9. 2 0
      src/main/java/com/ichaoj/ams/entity/AmsExecuteRecord.java
  10. 5 0
      src/main/java/com/ichaoj/ams/request/execute/CreateExecute.java
  11. 4 0
      src/main/java/com/ichaoj/ams/request/task/CreateAirdropTask.java
  12. 5 0
      src/main/java/com/ichaoj/ams/response/execute/ExecuteResponse.java
  13. 3 1
      src/main/java/com/ichaoj/ams/response/task/TaskResponse.java
  14. 60 0
      src/main/java/com/ichaoj/ams/script/IScript.java
  15. 17 0
      src/main/java/com/ichaoj/ams/script/SResult.java
  16. 30 0
      src/main/java/com/ichaoj/ams/script/ScriptContext.java
  17. 23 0
      src/main/java/com/ichaoj/ams/script/annotation/Script.java
  18. 20 0
      src/main/java/com/ichaoj/ams/script/annotation/ScriptParam.java
  19. 26 0
      src/main/java/com/ichaoj/ams/script/model/AirdropParam.java
  20. 24 0
      src/main/java/com/ichaoj/ams/script/model/AirdropWallet.java
  21. 58 0
      src/main/java/com/ichaoj/ams/script/test/nft/Erc21TestScript.java
  22. 45 0
      src/main/java/com/ichaoj/ams/script/transfer/TransferScript.java
  23. 92 0
      src/main/java/com/ichaoj/ams/script/util/Web3Util.java
  24. 27 0
      src/main/java/com/ichaoj/ams/script/zksync/era/SwapPath.java
  25. 25 0
      src/main/java/com/ichaoj/ams/script/zksync/era/SwapStep.java
  26. 16 0
      src/main/java/com/ichaoj/ams/script/zksync/era/TokenAmount.java
  27. 133 0
      src/main/java/com/ichaoj/ams/script/zksync/era/ZkSyncCrossScript.java
  28. 159 0
      src/main/java/com/ichaoj/ams/script/zksync/era/ZkSyncEraScript.java
  29. 18 2
      src/main/java/com/ichaoj/ams/service/IAmsAddressAccountService.java
  30. 9 0
      src/main/java/com/ichaoj/ams/service/impl/AmsAddressAccountServiceImpl.java
  31. 130 39
      src/main/java/com/ichaoj/ams/service/impl/AmsExecuteRecordServiceImpl.java
  32. 8 8
      src/main/java/com/ichaoj/ams/service/impl/AmsTradeRecordServiceImpl.java
  33. 84 0
      src/main/java/org/web3j/abi/datatypes/DynamicArray.java
  34. 15 16
      src/main/resources/mapper/AmsAirdropTaskMapper.xml
  35. 1 1
      src/main/resources/mapper/AmsExcuteRecordMapper.xml
  36. 20 1
      src/test/java/com/ichaoj/ams/AmsBackendApplicationTests.java

+ 15 - 0
front/admin-front/src/api/task.js

@@ -30,3 +30,18 @@ export function modifyTaskInfo(data) {
     data
   })
 }
+
+export function addTaskInfo(data) {
+  return request({
+    url: '/ams/airdrop-task',
+    method: 'post',
+    data
+  })
+}
+
+export function parameterSearch(data) {
+  return request({
+    url: '/ams/airdrop-task/params/' + data,
+    method: 'get'
+  })
+}

+ 25 - 1
front/admin-front/src/views/address/index.vue

@@ -439,7 +439,7 @@
                 <div class="name">
                   Address-{{index+1}}
                 </div>
-                <div class="address">
+                <div class="address" @click="copyAddress(item)">
                   {{item}}
                   <img src="../../assets/address/content_copy.svg" alt="content_copy">
                 </div>
@@ -703,6 +703,29 @@ export default {
         })
       })
     },
+    copyAddress(el){
+      let address = el
+      if (navigator.clipboard) {
+        navigator.clipboard.writeText(address);
+        this.success('复制成功')
+      } else {
+        let textarea = document.createElement('textarea');
+        document.body.appendChild(textarea);
+        // 隐藏此输入框
+        textarea.style.position = 'fixed';
+        textarea.style.clip = 'rect(0 0 0 0)';
+        textarea.style.top = '10px';
+        // 赋值
+        textarea.value = address;
+        // 选中
+        textarea.select();
+        // 复制
+        document.execCommand('copy', true);
+        // 移除输入框
+        document.body.removeChild(textarea);
+        this.success('复制成功')
+      }
+    },
     success(message) {
       this.$notify({
         title: '成功',
@@ -1395,6 +1418,7 @@ export default {
               font-size: 12px;
               line-height: 16px;
               color: #212121;
+              cursor: pointer;
             }
           }
           .pagination{

File diff ditekan karena terlalu besar
+ 414 - 231
front/admin-front/src/views/autoplay/index.vue


+ 2 - 1
front/admin-front/src/views/dashboard/index.vue

@@ -490,6 +490,7 @@ export default {
             type: 'bar',
             stack: 'Total',
             color: '#2980FF',
+            barMaxWidth: '25px',
             emphasis: {
               focus: 'series'
             },
@@ -500,7 +501,7 @@ export default {
             type: 'bar',
             stack: 'Total',
             color: '#FFD428',
-            barWidth: '25%',
+            barMaxWidth: '25px',
             itemStyle: {
               borderRadius: [100, 100, 0, 0]
             },

+ 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>

+ 12 - 0
src/main/java/com/ichaoj/ams/common/util/WalletUtil.java

@@ -3,6 +3,8 @@ package com.ichaoj.ams.common.util;
 import cn.hutool.crypto.symmetric.AES;
 import com.alibaba.fastjson.JSON;
 import com.ichaoj.ams.common.bean.Wallet;
+import com.ichaoj.ams.script.model.AirdropParam;
+import com.ichaoj.ams.script.model.AirdropWallet;
 import com.ichaoj.common.model.PublicUserInfo;
 import com.ichaoj.web.context.SuperWhaleContext;
 import lombok.SneakyThrows;
@@ -11,6 +13,7 @@ import org.web3j.crypto.Credentials;
 import org.web3j.crypto.WalletUtils;
 
 import java.io.File;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
@@ -80,4 +83,13 @@ public class WalletUtil {
     }
 
 
+    @SneakyThrows
+    public static AirdropWallet getWalletByKeystore(String keystore, String userId, String groupName, String password) {
+        Credentials credentials = WalletUtils.loadCredentials(password, WALLET_FILE_PATH + userId + File.separator + getEncryptPwd(groupName) + File.separator + keystore);
+        String privateKey = credentials.getEcKeyPair().getPrivateKey().toString(16);
+        AirdropWallet wallet = new AirdropWallet();
+        wallet.setPrivateKey(privateKey);
+        wallet.setAddress(credentials.getAddress());
+        return wallet;
+    }
 }

+ 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());
+    }
 }

+ 6 - 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;
 
@@ -36,11 +37,16 @@ public class AmsAirdropTask implements Serializable {
      */
     private String airdropProjectId;
 
+    private String chainId;
+
     /**
      * 任务名称
      */
     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;
+
     /**
      * 地址组名称
      */

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

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

+ 5 - 0
src/main/java/com/ichaoj/ams/response/execute/ExecuteResponse.java

@@ -1,6 +1,7 @@
 package com.ichaoj.ams.response.execute;
 
 import com.ichaoj.ams.response.task.TaskResponse;
+import com.ichaoj.ams.script.model.AirdropParam;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
@@ -92,4 +93,8 @@ public class ExecuteResponse {
      */
     private LocalDateTime updateTime;
 
+    private List<AirdropParam> airdropParams;
+
+    private String paramsStr;
+
 }

+ 3 - 1
src/main/java/com/ichaoj/ams/response/task/TaskResponse.java

@@ -19,6 +19,9 @@ public class TaskResponse {
      */
     private String taskName;
 
+
+    private String taskCode;
+
     /**
      * 合约地址
      */
@@ -81,5 +84,4 @@ public class TaskResponse {
     private Integer intervalMax;
 
 
-
 }

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

@@ -0,0 +1,60 @@
+package com.ichaoj.ams.script;
+
+
+import com.ichaoj.ams.script.model.AirdropParam;
+import com.ichaoj.ams.script.model.AirdropWallet;
+import com.ichaoj.ams.script.util.Web3Util;
+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.DefaultBlockParameterName;
+import org.web3j.protocol.core.Response;
+import org.web3j.protocol.core.methods.response.EthSendTransaction;
+import org.web3j.utils.Numeric;
+
+import java.math.BigInteger;
+import java.util.Map;
+
+/**
+ * 脚本接口
+ *
+ * @author wren
+ */
+public abstract class IScript {
+
+
+    /**
+     * 运行脚本
+     *
+     * @param params 参数
+     * @return txId
+     */
+    public abstract SResult run(Map<String, AirdropParam> params, AirdropWallet airdropWallet);
+
+    protected SResult sendTx(AirdropWallet airdropWallet, Web3j web3j, RawTransaction etherTransaction) throws java.io.IOException {
+        Credentials credentials = Web3Util.getCredentials(airdropWallet);
+        byte[] signMessage = TransactionEncoder.signMessage(etherTransaction, credentials);
+        String hexValue = Numeric.toHexString(signMessage);
+        EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
+        Response.Error error = ethSendTransaction.getError();
+        if (error != null) {
+            throw new RuntimeException(error.getMessage());
+        }
+        org.web3j.protocol.core.methods.response.Transaction transaction = web3j
+                .ethGetTransactionByHash(ethSendTransaction.getTransactionHash())
+                .send()
+                .getTransaction()
+                .get();
+        BigInteger gasAmount = transaction.getGas().multiply(transaction.getGasPrice());
+        SResult sResult = new SResult();
+        BigInteger balance = web3j.ethGetBalance(airdropWallet.getAddress(), DefaultBlockParameterName.LATEST).send().getBalance();
+        sResult.setGas(gasAmount.toString());
+        sResult.setTxId(transaction.getHash());
+        sResult.setCurrentBalance(balance.toString());
+        return sResult;
+    }
+
+
+
+}

+ 17 - 0
src/main/java/com/ichaoj/ams/script/SResult.java

@@ -0,0 +1,17 @@
+package com.ichaoj.ams.script;
+
+import lombok.Data;
+
+/**
+ * @author wrenj
+ */
+@Data
+public class SResult {
+
+    private String txId;
+
+    private String gas;
+
+    private String currentBalance;
+
+}

+ 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 "";
+}

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

@@ -0,0 +1,26 @@
+package com.ichaoj.ams.script.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * airdrop script param
+ *
+ * @author wren
+ */
+@Data
+@NoArgsConstructor
+public class AirdropParam {
+
+
+    private String name;
+    private String note;
+    private String value;
+
+
+    public AirdropParam(String name, String note, String value) {
+        this.name = name;
+        this.note = note;
+        this.value = 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;
+
+
+}

+ 58 - 0
src/main/java/com/ichaoj/ams/script/test/nft/Erc21TestScript.java

@@ -0,0 +1,58 @@
+package com.ichaoj.ams.script.test.nft;
+
+import cn.hutool.core.collection.ListUtil;
+import com.ichaoj.ams.script.IScript;
+import com.ichaoj.ams.script.SResult;
+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.datatypes.Function;
+import org.web3j.abi.datatypes.generated.Uint256;
+import org.web3j.crypto.RawTransaction;
+import org.web3j.protocol.Web3j;
+import org.web3j.protocol.core.methods.request.Transaction;
+import org.web3j.protocol.http.HttpService;
+
+import java.math.BigInteger;
+import java.util.Map;
+
+/**
+ * @author wren
+ */
+@Script(name = "test-script",
+        params = {
+                @ScriptParam(name = "quantity", note = "铸币量", defaultValue = "1")
+        }
+)
+public class Erc21TestScript extends IScript {
+    @SneakyThrows
+    @Override
+    public SResult run(Map<String, AirdropParam> params, AirdropWallet airdropWallet) {
+        Web3j web3j = Web3j.build(new HttpService("https://eth-sepolia.g.alchemy.com/v2/QR461hv0kEkKn91VlTAlIx6RhnnfPl-m"));
+        Function mint = new Function("mint", ListUtil.of(new Uint256(Long.parseLong(params.get("quantity").getValue()))), ListUtil.empty());
+        BigInteger nonce = Web3Util.getNonce(web3j, airdropWallet.getAddress());
+        BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice();
+        String mintEncoder = FunctionEncoder.encode(mint);
+        Transaction callTransaction = Transaction.createFunctionCallTransaction(
+                airdropWallet.getAddress(),
+                nonce,
+                gasPrice,
+                BigInteger.valueOf(0),
+                "0xA786221716dd8892003B9e8e029D7bF602e429b1",
+                mintEncoder);
+        BigInteger gasLimit = Web3Util.getTransactionGasLimit(web3j, callTransaction);
+        RawTransaction rawTransaction = RawTransaction.createTransaction(
+                nonce,
+                gasPrice,
+                gasLimit,
+                "0xA786221716dd8892003B9e8e029D7bF602e429b1",
+                BigInteger.valueOf(0),
+                mintEncoder);
+
+        return sendTx(airdropWallet, web3j, rawTransaction);
+    }
+}

+ 45 - 0
src/main/java/com/ichaoj/ams/script/transfer/TransferScript.java

@@ -0,0 +1,45 @@
+package com.ichaoj.ams.script.transfer;
+
+import cn.hutool.core.util.RandomUtil;
+import com.ichaoj.ams.script.IScript;
+import com.ichaoj.ams.script.SResult;
+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.crypto.RawTransaction;
+import org.web3j.protocol.Web3j;
+import org.web3j.protocol.http.HttpService;
+import org.web3j.utils.Convert;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.util.Map;
+
+/**
+ * @author wren
+ */
+@Script(name = "transfer-test",
+        params = {
+                @ScriptParam(name = "minAmount", note = "转账最小金额", defaultValue = "0.001"),
+                @ScriptParam(name = "maxAmount", note = "转账最大金额", defaultValue = "0.002"),
+                @ScriptParam(name = "accuracy", note = "转账精度(精确到小数点后几位)", defaultValue = "5")
+        }
+)
+public class TransferScript extends IScript {
+    @SneakyThrows
+    @Override
+    public SResult run(Map<String, AirdropParam> params, AirdropWallet airdropWallet) {
+        Web3j web3j = Web3j.build(new HttpService("https://eth-sepolia.g.alchemy.com/v2/QR461hv0kEkKn91VlTAlIx6RhnnfPl-m"));
+        BigInteger nonce = Web3Util.getNonce(web3j, airdropWallet.getAddress());
+        BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice();
+        BigDecimal randomed = RandomUtil.randomBigDecimal(new BigDecimal(params.get("minAmount").getValue()), new BigDecimal(params.get("maxAmount").getValue()));
+        randomed = randomed.setScale(Integer.parseInt(params.get("accuracy").getValue()), RoundingMode.DOWN);
+        RawTransaction etherTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, BigInteger.valueOf(21000), "0x0870682e56A9069E7655afD728D91f3653Bff0bE", Convert.toWei(randomed.toString(), Convert.Unit.ETHER).toBigInteger());
+        return sendTx(airdropWallet, web3j, etherTransaction);
+    }
+
+}

+ 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;
+    }
+}

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

@@ -0,0 +1,133 @@
+package com.ichaoj.ams.script.zksync.era;
+
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.util.IdUtil;
+import com.ichaoj.ams.script.IScript;
+import com.ichaoj.ams.script.SResult;
+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.DefaultBlockParameterName;
+import org.web3j.protocol.core.Response;
+import org.web3j.protocol.core.methods.request.Transaction;
+import org.web3j.protocol.core.methods.response.EthGetBalance;
+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.BigDecimal;
+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 SResult run(Map<String, AirdropParam> params, AirdropWallet airdropWallet) {
+        AirdropParam amount = params.get("amount");
+        AirdropParam value = params.get("value");
+        AirdropParam l2GasLimit = params.get("l2GasLimit");
+        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(amount.getValue(), Convert.Unit.ETHER).toBigInteger()));
+        //bytes
+        inputParameters.add(DynamicBytes.DEFAULT);
+        //l2 gas limit
+        inputParameters.add(new Uint256(new BigInteger(l2GasLimit.getValue())));
+        //_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(value.getValue(), Convert.Unit.ETHER).toBigInteger(),
+                l2Encoder);
+        BigInteger gasLimit = Web3Util.getTransactionGasLimit(web3j, callTransaction);
+
+//        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,
+//                gasPrice,
+//                gasLimit,
+//                mailBox,
+//                Convert.toWei(value.getValue(), 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).send();
+//        Response.Error error = ethSendTransaction.getError();
+//        if (error != null) {
+//            throw new RuntimeException(error.getMessage());
+//        }
+//        org.web3j.protocol.core.methods.response.Transaction transaction = web3j
+//                .ethGetTransactionByHash(ethSendTransaction.getTransactionHash())
+//                .send()
+//                .getTransaction()
+//                .get();
+
+//        BigInteger gasAmount = transaction.getGas().multiply(transaction.getGasPrice());
+//        SResult sResult = new SResult();
+//        sResult.setGas(gasAmount.toString());
+//        sResult.setTxId(transaction.getHash());
+
+        BigInteger balance = web3j.ethGetBalance(airdropWallet.getAddress(), DefaultBlockParameterName.LATEST).send().getBalance();
+
+        SResult sResult = new SResult();
+        sResult.setGas(gasLimit.multiply(gasPrice).toString());
+        sResult.setTxId(IdUtil.simpleUUID());
+        sResult.setCurrentBalance(balance.toString());
+        return sResult;
+//        return ethSendTransaction.getTransactionHash();
+//        return gasLimit.toString();
+    }
+
+
+}

+ 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
+//        org.web3j.abi.datatypes.DynamicArray<StaticStruct> steps = new org.web3j.abi.datatypes.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
+//        org.web3j.abi.datatypes.DynamicArray<StaticStruct> paths = new org.web3j.abi.datatypes.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();
+//
+//    }
+//}

+ 18 - 2
src/main/java/com/ichaoj/ams/service/IAmsAddressAccountService.java

@@ -7,6 +7,7 @@ import com.ichaoj.ams.request.address.PageAddressRequest;
 import com.ichaoj.ams.request.address.PremiumAddressRequest;
 import com.ichaoj.ams.response.address.AddressResponse;
 import com.ichaoj.ams.response.address.CountAddressResponse;
+import com.ichaoj.ams.script.model.AirdropWallet;
 import com.ichaoj.common.model.PublicPage;
 
 import javax.servlet.http.HttpServletResponse;
@@ -55,6 +56,7 @@ public interface IAmsAddressAccountService extends IService<AmsAddressAccount> {
 
     /**
      * 分页查询地址信息
+     *
      * @param pageAddressRequest 分页参数
      * @return 分页结果
      */
@@ -62,13 +64,15 @@ public interface IAmsAddressAccountService extends IService<AmsAddressAccount> {
 
     /**
      * 查询地址组列表
-     * @return 地址组列表
+     *
      * @param addressType 地址类型
+     * @return 地址组列表
      */
     List<String> queryGroupList(int addressType);
 
     /**
      * 根据地址查找
+     *
      * @param address 地址
      * @return 地址账户
      */
@@ -76,6 +80,7 @@ public interface IAmsAddressAccountService extends IService<AmsAddressAccount> {
 
     /**
      * 统计用户批量地址的数量(按地址组名称分组)
+     *
      * @param userId 用户id
      * @return 统计结果
      */
@@ -83,8 +88,19 @@ public interface IAmsAddressAccountService extends IService<AmsAddressAccount> {
 
     /**
      * 查询地址组列表
-     * @return 地址组列表
+     *
      * @param addressType 地址类型
+     * @return 地址组列表
      */
     List<String> queryBatchAddress(int addressType);
+
+    /**
+     * 根据账户账户地址以及私钥
+     *
+     * @param accountList 地址列表
+     * @param userId      用户id
+     * @param groupName   地址组名称
+     * @param password    密码
+     */
+    List<AirdropWallet> getAirdropAddressByAccount(List<AmsAddressAccount> accountList, String userId, String groupName, String password);
 }

+ 9 - 0
src/main/java/com/ichaoj/ams/service/impl/AmsAddressAccountServiceImpl.java

@@ -16,6 +16,7 @@ import com.ichaoj.ams.request.address.PageAddressRequest;
 import com.ichaoj.ams.request.address.PremiumAddressRequest;
 import com.ichaoj.ams.response.address.AddressResponse;
 import com.ichaoj.ams.response.address.CountAddressResponse;
+import com.ichaoj.ams.script.model.AirdropWallet;
 import com.ichaoj.ams.service.IAmsAddressAccountService;
 import com.ichaoj.ams.service.IAmsTradeRecordService;
 import com.ichaoj.common.exception.ErrorServiceException;
@@ -137,6 +138,14 @@ public class AmsAddressAccountServiceImpl extends SuperWhaleServiceImpl<AmsAddre
         return this.list(wrapper).stream().map(AmsAddressAccount::getAddress).distinct().collect(Collectors.toList());
     }
 
+    @Override
+    public List<AirdropWallet> getAirdropAddressByAccount(List<AmsAddressAccount> accountList, String userId, String groupName, String password) {
+        return accountList
+                .stream()
+                .map(account -> WalletUtil.getWalletByKeystore(account.getKeystore(), userId, groupName, password))
+                .collect(Collectors.toList());
+    }
+
     /**
      * 添加精品号
      */

+ 130 - 39
src/main/java/com/ichaoj/ams/service/impl/AmsExecuteRecordServiceImpl.java

@@ -2,8 +2,13 @@ package com.ichaoj.ams.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+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 +19,11 @@ 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.SResult;
+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 +36,10 @@ 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.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -41,6 +52,19 @@ import java.util.stream.Collectors;
  */
 @Service
 public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecuteRecordMapper, AmsExecuteRecord> implements IAmsExecuteRecordService {
+    public static ThreadPoolExecutor taskThreadPool =
+            new ThreadPoolExecutor(2 * Runtime.getRuntime().availableProcessors(),
+                    2 * Runtime.getRuntime().availableProcessors(),
+                    10,
+                    TimeUnit.SECONDS,
+                    new LinkedBlockingDeque<>(500),
+                    r -> {
+                        Thread thread = new Thread(r);
+                        thread.setName("task-thread-pool");
+                        return thread;
+                    }
+            );
+
     @Resource
     private IAmsAirdropTaskService taskService;
 
@@ -63,7 +87,13 @@ public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecut
 //                .orderByDesc(AmsExecuteRecord::getCreateTime);
 
         Page<ExecuteResponse> result = this.baseMapper.pageExecute(this.buildPageObj(executeRequest), executeRequest, SuperWhaleContext.getContext(PublicUserInfo.class).getUserId());
-        return this.convertPublicPage(result, s -> BeanUtil.copyProperties(s, ExecuteResponse.class));
+        return this.convertPublicPage(result, s -> {
+            ExecuteResponse executeResponse = BeanUtil.copyProperties(s, ExecuteResponse.class);
+            if (StrUtil.isNotBlank(s.getParamsStr())) {
+                executeResponse.setAirdropParams(JSON.parseArray(s.getParamsStr(), AirdropParam.class));
+            }
+            return executeResponse;
+        });
     }
 
     @Override
@@ -81,25 +111,66 @@ public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecut
             throw new ErrorServiceException(createExecute.getGroupName() + "为非批量号地址组,不允许交易");
         }
         AmsAirdropProject project = verifyProject(createExecute.getProjectId());
+
         AmsAirdropTask task = verifyTask(createExecute.getTaskId(), project.getAmsProjectId());
         AmsExecuteRecord amsExecute = BeanUtil.copyProperties(createExecute, AmsExecuteRecord.class);
         amsExecute.setUserId(userId);
         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())) {
             throw new ErrorServiceException("密码不能为空!");
         }
-        // todo 验证密码是否正确
+        List<AirdropWallet> wallets = accountService.getAirdropAddressByAccount(accountList, userId, amsExecute.getGroupName(), createExecute.getPassword());
+
+        runTask(createExecute.getIntervalMin(),
+                createExecute.getIntervalMax(),
+                amsExecute.getExecuteId(),
+                task.getTaskCode(),
+                createExecute.getAirdropParams(),
+                wallets);
+
+
+    }
+
+
+    public void runTask(long intervalMin,
+                        long intervalMax,
+                        String execId,
+                        String taskCode,
+                        List<AirdropParam> airdropParams,
+                        List<AirdropWallet> wallets) {
+
+        taskThreadPool.execute(() -> {
+            Map<String, AirdropParam> airdropParamMap = new HashMap<>(airdropParams.size());
+            for (AirdropParam airdropParam : airdropParams) {
+                airdropParamMap.put(airdropParam.getName(), airdropParam);
+            }
+            for (AirdropWallet wallet : wallets) {
+                long sleepTime = RandomUtil.randomLong(intervalMin, intervalMax);
+                ThreadUtil.sleep(sleepTime, TimeUnit.MINUTES);
+                IScript script = ScriptContext.getScriptByCode(taskCode);
+                SResult sResult = script.run(airdropParamMap, wallet);
+                AmsTradeRecord tradeRecord = new AmsTradeRecord();
+                tradeRecord.setExecuteId(execId);
+                tradeRecord.setStatus(1);
+                tradeRecord.setAddress(wallet.getAddress());
+                tradeRecord.setGas(sResult.getGas());
+                tradeRecord.setTxId(sResult.getTxId());
+                tradeRecord.setCreateTime(LocalDateTime.now());
+                tradeRecord.setUpdateTime(LocalDateTime.now());
+                tradeRecord.setCurrentBalance(sResult.getCurrentBalance());
+                tradeService.save(tradeRecord);
+            }
+
+            AmsExecuteRecord executeRecord = this.getById(execId);
+            executeRecord.setExecuteStatus(1);
+            this.updateById(executeRecord);
+        });
 
-        ThreadUtil.execute(() -> tradeService.randomTrans(accountList
-                , createExecute.getIntervalMin()
-                , createExecute.getIntervalMax()
-                , createExecute.getAmount()
-                , createExecute.getMaxGas()
-                , amsExecute.getExecuteId()));
 
     }
 
@@ -153,32 +224,52 @@ public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecut
         return task;
     }
 
-    @Scheduled(cron = "0/59 * * * * ? ")
-    public void scanExecuteStatus() {
-        List<AmsExecuteRecord> list = this.list();
-        for (AmsExecuteRecord executeRecord : list) {
-            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);
-                }
-            }
-        }
-    }
+//    @Scheduled(cron = "0/59 * * * * ? ")
+//    public void scanExecuteStatus() {
+//        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);
+//            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

@@ -47,7 +47,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
@@ -120,13 +120,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++;
                 }
             }

+ 84 - 0
src/main/java/org/web3j/abi/datatypes/DynamicArray.java

@@ -0,0 +1,84 @@
+package org.web3j.abi.datatypes;/*
+ * Copyright 2019 Web3 Labs Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+import lombok.SneakyThrows;
+
+import java.util.List;
+
+/**
+ * Dynamic array type.
+ * @author wrenj
+ */
+public class DynamicArray<T extends Type> extends Array<T> {
+
+    @Deprecated
+    @SafeVarargs
+    @SuppressWarnings({"unchecked"})
+    public DynamicArray(T... values) {
+        super(
+                StructType.class.isAssignableFrom(values[0].getClass())
+                        ? (Class<T>) values[0].getClass()
+                        : (Class<T>) AbiTypes.getType(values[0].getTypeAsString()),
+                values);
+    }
+
+    @Deprecated
+    @SuppressWarnings("unchecked")
+    public DynamicArray(List<T> values) {
+        super(
+                StructType.class.isAssignableFrom(values.get(0).getClass())
+                        ? (Class<T>) values.get(0).getClass()
+                        : (Class<T>) AbiTypes.getType(values.get(0).getTypeAsString()),
+                values);
+    }
+
+    @Deprecated
+    @SuppressWarnings("unchecked")
+    private DynamicArray(String type) {
+        super((Class<T>) AbiTypes.getType(type));
+    }
+
+    @Deprecated
+    public static DynamicArray empty(String type) {
+        return new DynamicArray(type);
+    }
+
+    public DynamicArray(Class<T> type, List<T> values) {
+        super(type, values);
+    }
+
+    @Override
+    public int bytes32PaddedLength() {
+        return super.bytes32PaddedLength() + MAX_BYTE_LENGTH;
+    }
+
+    @SafeVarargs
+    public DynamicArray(Class<T> type, T... values) {
+        super(type, values);
+    }
+
+    @SneakyThrows
+    @Override
+    public String getTypeAsString() {
+        String type;
+        if (value == null || value.size() == 0) {
+            return "bytes" + "[]";
+        }
+        if (StructType.class.isAssignableFrom(value.get(0).getClass())) {
+            type = value.get(0).getTypeAsString();
+        } else {
+            type = AbiTypes.getTypeAString(getComponentType());
+        }
+        return type + "[]";
+    }
+}

+ 15 - 16
src/main/resources/mapper/AmsAirdropTaskMapper.xml

@@ -6,6 +6,7 @@
                a.ams_task_id,
                a.airdrop_project_id              amsProjectId,
                a.task_name,
+               a.task_code,
                a.contract_address,
                a.task_type,
                a.task_status,
@@ -24,22 +25,20 @@
         FROM ams_airdrop_task a
                  LEFT JOIN ams_airdrop_project b ON a.airdrop_project_id = b.ams_project_id
             AND b.flag = 0
-                 LEFT JOIN (
-            SELECT aer.execute_status,
-                   aer.execute_id,
-                   aer.create_time AS lastExecuteTime,
-                   aer.group_name,
-                   aer.interval_max,
-                   aer.interval_min,
-                   aer.max_gas,
-                   aer.task_id,
-                   aer.project_id
-            FROM ams_execute_record aer
-            WHERE aer.flag = 0
-              AND aer.user_id = #{userId}
-            ORDER BY aer.create_time DESC
-            LIMIT 1
-        ) er ON er.task_id = a.ams_task_id
+                 LEFT JOIN (SELECT aer.execute_status,
+                                   aer.execute_id,
+                                   aer.create_time AS lastExecuteTime,
+                                   aer.group_name,
+                                   aer.interval_max,
+                                   aer.interval_min,
+                                   aer.max_gas,
+                                   aer.task_id,
+                                   aer.project_id
+                            FROM ams_execute_record aer
+                            WHERE aer.flag = 0
+                              AND aer.user_id = #{userId}
+                            ORDER BY aer.create_time DESC
+                            LIMIT 1) er ON er.task_id = a.ams_task_id
             AND er.project_id = b.ams_project_id
         WHERE a.flag = 0
     </select>

+ 1 - 1
src/main/resources/mapper/AmsExcuteRecordMapper.xml

@@ -2,7 +2,7 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ichaoj.ams.mapper.AmsExecuteRecordMapper">
     <select id="pageExecute" resultType="com.ichaoj.ams.response.execute.ExecuteResponse">
-        SELECT count(*) totalCount,
+        SELECT er.airdrop_params params_str,
         er.execute_id, er.execute_times, er.project_id, er.task_id, er.user_id, er.group_name, er.from_asset,
         er.to_asset, er.interval_min,
         er.interval_max, er.amount, er.max_gas, er.execute_status, er.create_time, er.update_time,

+ 20 - 1
src/test/java/com/ichaoj/ams/AmsBackendApplicationTests.java

@@ -1,13 +1,32 @@
 package com.ichaoj.ams;
 
+import com.ichaoj.ams.script.IScript;
+import com.ichaoj.ams.script.SResult;
+import com.ichaoj.ams.script.model.AirdropParam;
+import com.ichaoj.ams.script.model.AirdropWallet;
+import com.ichaoj.ams.script.test.nft.Erc21TestScript;
+import com.ichaoj.ams.script.transfer.TransferScript;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 
-@SpringBootTest
+import java.util.HashMap;
+import java.util.Map;
+
+
 class AmsBackendApplicationTests {
 
     @Test
     void contextLoads() {
+        IScript script = new TransferScript();
+        Map<String, AirdropParam> params = new HashMap<>();
+        params.put("minAmount", new AirdropParam("minAmount", null, "0.001"));
+        params.put("maxAmount", new AirdropParam("minAmount", null, "0.002"));
+        params.put("accuracy", new AirdropParam("accuracy", null, "5"));
+        AirdropWallet airdropWallet = new AirdropWallet();
+        airdropWallet.setAddress("0x368715F09C1AB5E0B55bF5bA19cD887189A28DBE");
+        airdropWallet.setPrivateKey("f9eac039449802f20cf0a38bc0b6f4064508fa61897d9c4fcb6cb66bd1311674");
+        SResult sResult = script.run(params, airdropWallet);
+        System.out.println(sResult);
     }
 
 }

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini