/// <summary> /// send asset /// </summary> /// <returns></returns> public async Task <object> SendTo(TransferRequest[] transfers) { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } if (transfers.IsEmpty()) { return(Error(ErrorCode.ParameterIsNull)); } var transferList = new List <TransferRequestModel>(); foreach (var transferRequest in transfers) { var(transfer, error) = ParseTransferRequest(transferRequest); if (error.NotNull()) { return(Error(ErrorCode.InvalidPara, error)); } transferList.Add(transfer); } try { var tx = MakeTransaction(transferList); if (tx == null) { return(Error(ErrorCode.BalanceNotEnough, "Insufficient funds")); } var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } await tx.Broadcast(); return(new TransactionModel(tx)); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } var error = ex.Message; var currentEx = ex; while (currentEx.InnerException != null) { currentEx = currentEx.InnerException; error += $"\r\n{currentEx.Message}"; } return(Error(ErrorCode.TransferError, error)); } }
/// <summary> /// show private key /// </summary> /// <returns></returns> public async Task <object> ClaimGas() { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } var addresses = CurrentWallet.GetAccounts().Where(a => !a.Lock && !a.WatchOnly && a.Contract.Script.IsSignatureContract()).Select(a => a.ScriptHash).ToList(); var balances = addresses.GetBalanceOf(NativeContract.NEO.Hash); balances = balances.Where(b => b.Value > 0).ToList(); if (balances.IsEmpty()) { return(Error(ErrorCode.NoNeedClaimGas)); } var outputs = balances.Select((t, index) => new TransferOutput() { AssetId = NativeContract.NEO.Hash, Value = t, ScriptHash = addresses[index], }).ToArray(); try { Transaction tx = CurrentWallet.MakeTransaction(Helpers.GetDefaultSnapshot(), outputs); if (tx == null) { return(Error(ErrorCode.ClaimGasFail)); } var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } await tx.Broadcast(); return(new TransactionModel(tx)); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } return(Error(ErrorCode.ClaimGasFail, ex.Message)); } }
/// <summary> /// send asset /// </summary> /// <param name="sender"></param> /// <param name="receivers"></param> /// <param name="asset"></param> /// <returns></returns> public async Task <object> SendToMultiAddress(MultiReceiverRequest[] receivers, string asset = "neo", UInt160 sender = null) { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } if (receivers.IsEmpty()) { return(Error(ErrorCode.ParameterIsNull, $"receivers is null!")); } UInt160 assetHash = ConvertToAssetId(asset, out var convertError); if (assetHash == null) { return(Error(ErrorCode.InvalidPara, $"asset is not valid:{convertError}")); } var assetInfo = AssetCache.GetAssetInfo(assetHash); if (assetInfo == null) { return(Error(ErrorCode.InvalidPara, $"asset is not valid:{convertError}")); } if (sender != null) { var account = CurrentWallet.GetAccount(sender); if (account == null) { return(Error(ErrorCode.AddressNotFound)); } } var toes = new List <(UInt160 scriptHash, BigDecimal amount)>(); foreach (var receiver in receivers) { if (!BigDecimal.TryParse(receiver.Amount, assetInfo.Decimals, out BigDecimal sendAmount) || sendAmount.Sign <= 0) { return(Error(ErrorCode.InvalidPara, $"Incorrect Amount Format:{receiver.Amount}")); } toes.Add((receiver.Address, sendAmount)); } var outputs = toes.Select(t => new TransferOutput() { AssetId = assetHash, Value = t.amount, ScriptHash = t.scriptHash, }).ToArray(); try { Transaction tx = CurrentWallet.MakeTransaction(Helpers.GetDefaultSnapshot(), outputs, sender); if (tx == null) { return(Error(ErrorCode.BalanceNotEnough, "Insufficient funds")); } var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } await tx.Broadcast(); return(new TransactionModel(tx)); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } return(Error(ErrorCode.TransferError, ex.Message)); } }
/// <summary> /// send asset /// </summary> /// <param name="sender"></param> /// <param name="receiver"></param> /// <param name="amount"></param> /// <param name="asset"></param> /// <returns></returns> public async Task <object> SendToAddress(UInt160 receiver, string amount, string asset = "neo", UInt160 sender = null) { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } if (receiver == null) { return(Error(ErrorCode.ParameterIsNull, $"receiver address is null!")); } UInt160 assetHash = ConvertToAssetId(asset, out var convertError); if (assetHash == null) { return(Error(ErrorCode.InvalidPara, $"asset is not valid:{convertError}")); } var assetInfo = AssetCache.GetAssetInfo(assetHash); if (assetInfo == null) { return(Error(ErrorCode.InvalidPara, $"asset is not valid:{convertError}")); } if (!BigDecimal.TryParse(amount, assetInfo.Decimals, out BigDecimal sendAmount) || sendAmount.Sign <= 0) { return(Error(ErrorCode.InvalidPara, "Incorrect Amount Format")); } if (sender != null) { var account = CurrentWallet.GetAccount(sender); if (account == null) { return(Error(ErrorCode.AddressNotFound)); } var balance = sender.GetBalanceOf(assetHash); if (balance.Value < sendAmount.Value) { return(Error(ErrorCode.BalanceNotEnough)); } } try { Transaction tx = CurrentWallet.MakeTransaction(Helpers.GetDefaultSnapshot(), new[] { new TransferOutput { AssetId = assetHash, Value = sendAmount, ScriptHash = receiver } }, sender); if (tx == null) { return(Error(ErrorCode.BalanceNotEnough, "Insufficient funds")); } var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } await tx.Broadcast(); return(new TransactionModel(tx)); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } return(Error(ErrorCode.TransferError, ex.Message)); } }
public async Task <object> DeployContract(string nefPath, string manifestPath = null, bool sendTx = false, UInt160 sender = null) { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } if (nefPath.IsNull()) { return(Error(ErrorCode.ParameterIsNull, "nefPath is empty.")); } if (manifestPath.IsNull()) { manifestPath = Path.ChangeExtension(nefPath, ".manifest.json"); } // Read nef NefFile nefFile = ReadNefFile(nefPath); // Read manifest ContractManifest manifest = ReadManifestFile(manifestPath); // Basic script checks await CheckBadOpcode(nefFile.Script); // Build script using ScriptBuilder sb = new ScriptBuilder(); sb.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nefFile.ToArray(), manifest.ToJson().ToString()); //sb.EmitAppCall(NativeContract.Management.Hash, "deploy", nefFile.ToArray(), manifest.ToJson().ToString()); var script = sb.ToArray(); Transaction tx; try { tx = CurrentWallet.MakeTransaction(Helpers.GetDefaultSnapshot(), script, sender); } catch (InvalidOperationException ex) { return(Error(ErrorCode.EngineFault, ex.GetExMessage())); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } throw; } UInt160 hash = SmartContract.Helper.GetContractHash(tx.Sender, nefFile.CheckSum, manifest.Name); var oldContract = hash.GetContract(); if (oldContract != null) { return(Error(ErrorCode.ContractAlreadyExist)); } var result = new DeployResultModel { ContractHash = hash, GasConsumed = new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals) }; if (sendTx) { var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } await tx.Broadcast(); result.TxId = tx.Hash; } return(result); }
public async Task <object> InvokeContract(InvokeContractParameterModel para) { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } if (para.ContractHash == null || para.Method.IsNull()) { return(Error(ErrorCode.ParameterIsNull)); } var contract = para.ContractHash.GetContract(); if (contract == null) { return(Error(ErrorCode.UnknownContract)); } ContractParameter[] contractParameters = null; try { contractParameters = para.Parameters?.Select(JsonToContractParameter).ToArray(); } catch (Exception e) { return(Error(ErrorCode.InvalidPara)); } var signers = new List <Signer>(); if (para.Cosigners.NotEmpty()) { signers.AddRange(para.Cosigners.Select(s => new Signer() { Account = s.Account, Scopes = s.Scopes, AllowedContracts = new UInt160[0] })); } Transaction tx = null; using ScriptBuilder sb = new ScriptBuilder(); sb.EmitDynamicCall(para.ContractHash, para.Method, contractParameters); try { tx = CurrentWallet.InitTransaction(sb.ToArray(), null, signers.ToArray()); } catch (InvalidOperationException ex) { return(Error(ErrorCode.EngineFault, $"{ex.Message}\r\n InnerError:{ex.InnerException}")); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } throw; } var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } var result = new InvokeResultModel(); using ApplicationEngine engine = tx.Script.RunTestMode(null, tx); result.VmState = engine.State; result.GasConsumed = new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals); result.ResultStack = engine.ResultStack.Select(p => JStackItem.FromJson(p.ToContractParameter().ToJson())).ToList(); result.Notifications = engine.Notifications?.Select(ConvertToEventModel).ToList(); if (engine.State.HasFlag(VMState.FAULT)) { return(Error(ErrorCode.EngineFault)); } if (!para.SendTx) { return(result); } await tx.Broadcast(); result.TxId = tx.Hash; return(result); }
public async Task <object> UpdateContract(UInt160 contractHash, string nefPath, string manifestPath = null, bool sendTx = false, UInt160[] cosigners = null) { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } if (nefPath.IsNull()) { return(Error(ErrorCode.ParameterIsNull, "nefPath is empty.")); } if (manifestPath.IsNull()) { manifestPath = Path.ChangeExtension(nefPath, ".manifest.json"); } // Read nef NefFile nefFile = ReadNefFile(nefPath); // Read manifest ContractManifest manifest = ReadManifestFile(manifestPath); // Basic script checks await CheckBadOpcode(nefFile.Script); // Build script using ScriptBuilder sb = new ScriptBuilder(); sb.EmitDynamicCall(contractHash, "update", nefFile.ToArray(), manifest.ToJson().ToString(), null); var script = sb.ToArray(); var singers = new List <Signer> { }; if (cosigners != null) { singers.AddRange(cosigners.Select(s => new Signer() { Account = s, Scopes = WitnessScope.Global })); } Transaction tx; try { tx = CurrentWallet.MakeTransaction(Helpers.GetDefaultSnapshot(), script, null, singers.ToArray()); } catch (InvalidOperationException ex) { return(Error(ErrorCode.EngineFault, ex.GetExMessage())); } catch (Exception ex) { if (ex.Message.Contains("Insufficient GAS")) { return(Error(ErrorCode.GasNotEnough)); } throw; } var result = new DeployResultModel { ContractHash = contractHash, GasConsumed = new BigDecimal((BigInteger)tx.SystemFee, NativeContract.GAS.Decimals) }; if (sendTx) { var(signSuccess, context) = CurrentWallet.TrySignTx(tx); if (!signSuccess) { return(Error(ErrorCode.SignFail, context.SafeSerialize())); } await tx.Broadcast(); result.TxId = tx.Hash; } return(result); }