فهرست منبع

Merge remote-tracking branch 'origin/master'

million 1 سال پیش
والد
کامیت
825056aec2

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

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

@@ -37,6 +37,8 @@ public class AmsAirdropTask implements Serializable {
      */
     private String airdropProjectId;
 
+    private String chainId;
+
     /**
      * 任务名称
      */

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

@@ -46,6 +46,8 @@ public class CreateAirdropTask {
      */
     private String taskUrl;
 
+    private String chainId;
+
     /**
      * 任务类型
      */

+ 3 - 1
src/main/java/com/ichaoj/ams/script/IScript.java

@@ -21,6 +21,8 @@ public abstract class IScript {
      * @param params 参数
      * @return txId
      */
-    public abstract String run(Map<String, AirdropParam> params, AirdropWallet airdropWallet);
+    public abstract SResult run(Map<String, AirdropParam> params, AirdropWallet airdropWallet);
+
+
 
 }

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

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

@@ -1,6 +1,7 @@
 package com.ichaoj.ams.script.model;
 
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 /**
  * airdrop script param
@@ -8,6 +9,7 @@ import lombok.Data;
  * @author wren
  */
 @Data
+@NoArgsConstructor
 public class AirdropParam {
 
 
@@ -16,4 +18,9 @@ public class AirdropParam {
     private String value;
 
 
+    public AirdropParam(String name, String note, String value) {
+        this.name = name;
+        this.note = note;
+        this.value = value;
+    }
 }

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

@@ -0,0 +1,85 @@
+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.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.EthSendTransaction;
+import org.web3j.protocol.http.HttpService;
+import org.web3j.utils.Convert;
+import org.web3j.utils.Numeric;
+
+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);
+
+        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();
+        BigInteger balance = web3j.ethGetBalance(airdropWallet.getAddress(), DefaultBlockParameterName.LATEST).send().getBalance();
+        sResult.setGas(gasAmount.toString());
+        sResult.setTxId(transaction.getHash());
+        sResult.setCurrentBalance(balance.toString());
+        return sResult;
+    }
+}

+ 54 - 23
src/main/java/com/ichaoj/ams/script/zksync/era/ZkSyncCrossScript.java

@@ -1,7 +1,9 @@
 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;
@@ -18,14 +20,17 @@ 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;
@@ -45,26 +50,31 @@ public class ZkSyncCrossScript extends IScript {
 
     @SneakyThrows
     @Override
-    public String run(Map<String, AirdropParam> params, AirdropWallet airdropWallet) {
+    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("0.001", Convert.Unit.ETHER).toBigInteger()));
+        inputParameters.add(new Uint256(Convert.toWei(amount.getValue(), Convert.Unit.ETHER).toBigInteger()));
         //bytes
         inputParameters.add(DynamicBytes.DEFAULT);
         //l2 gas limit
-        inputParameters.add(new Uint256(701876));
+        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>() {
-        }));
+        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);
@@ -75,27 +85,48 @@ public class ZkSyncCrossScript extends IScript {
                 gasPrice,
                 BigInteger.valueOf(0),
                 mailBox,
-                Convert.toWei("0.002", Convert.Unit.ETHER).toBigInteger(),
+                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("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();
+//        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();
     }
 
 

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

@@ -1,159 +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());
+//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金额")
 //        }
-        return gasLimit.toString();
-
-    }
-}
+//)
+//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());
+    }
+
     /**
      * 添加精品号
      */

+ 114 - 54
src/main/java/com/ichaoj/ams/service/impl/AmsExecuteRecordServiceImpl.java

@@ -2,7 +2,10 @@ 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 com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -16,6 +19,7 @@ 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;
@@ -32,6 +36,9 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -44,6 +51,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;
 
@@ -84,6 +104,7 @@ 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);
@@ -96,14 +117,53 @@ public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecut
         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.safeSleep(sleepTime);
+                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()));
 
     }
 
@@ -157,52 +217,52 @@ public class AmsExecuteRecordServiceImpl extends SuperWhaleServiceImpl<AmsExecut
         return task;
     }
 
-    @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);
-//                }
+//    @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);
+////                }
+////            }
+//        }
+//    }
 }

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

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

@@ -1,13 +1,28 @@
 package com.ichaoj.ams;
 
+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 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() {
+        Erc21TestScript script = new Erc21TestScript();
+        Map<String, AirdropParam> params = new HashMap<>();
+        params.put("quantity", new AirdropParam("quantity", null, "1"));
+        AirdropWallet airdropWallet = new AirdropWallet();
+        airdropWallet.setAddress("0x368715F09C1AB5E0B55bF5bA19cD887189A28DBE");
+        airdropWallet.setPrivateKey("f9eac039449802f20cf0a38bc0b6f4064508fa61897d9c4fcb6cb66bd1311674");
+        SResult sResult = script.run(params, airdropWallet);
+        System.out.println(sResult);
     }
 
 }