/// <summary> /// Check whether any of the destination accounts require a memo. /// /// This method implements the checks defined in /// <a href="https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0029.md">SEP0029</a>. /// It will sequantially load each destination account and check if it has the data field /// <c>config.memo_required</c> set to <c>"MQ=="</c>. /// </summary> /// <param name="transaction"></param> /// <returns></returns> /// <exception cref="AccountRequiresMemoException"></exception> public async Task CheckMemoRequired(TransactionBase transaction) { var tx = GetTransactionToCheck(transaction); if (tx.Memo != null && !Equals(tx.Memo, Memo.None())) { return; } var destinations = new HashSet <string>(); foreach (var operation in tx.Operations) { if (!IsPaymentOperation(operation)) { continue; } // If it's a muxed account it already contains the memo. var destinationKey = PaymentOperationDestination(operation); if (destinationKey.IsMuxedAccount) { continue; } var destination = destinationKey.Address; if (destinations.Contains(destination)) { continue; } destinations.Add(destination); try { var account = await Accounts.Account(destination); if (!account.Data.ContainsKey(AccountRequiresMemoKey)) { continue; } if (account.Data[AccountRequiresMemoKey] == AccountRequiresMemo) { throw new AccountRequiresMemoException("Account requires memo", destination, operation); } } catch (HttpResponseException ex) { if (ex.StatusCode != 404) { throw; } } } }
public Transaction(IAccountId sourceAccount, uint fee, long sequenceNumber, Operation[] operations, Memo memo, TimeBounds timeBounds) { SourceAccount = sourceAccount ?? throw new ArgumentNullException(nameof(sourceAccount), "sourceAccount cannot be null"); Fee = fee; SequenceNumber = sequenceNumber; Operations = operations ?? throw new ArgumentNullException(nameof(operations), "operations cannot be null"); if (operations.Length == 0) { throw new ArgumentNullException(nameof(operations), "At least one operation required"); } Memo = memo ?? Memo.None(); TimeBounds = timeBounds; }
private Transaction(KeyPair sourceAccount, long sequenceNumber, Operation[] operations, Memo memo, TimeBounds timeBounds) { SourceAccount = sourceAccount ?? throw new ArgumentNullException(nameof(sourceAccount), "sourceAccount cannot be null"); SequenceNumber = sequenceNumber; Operations = operations ?? throw new ArgumentNullException(nameof(operations), "operations cannot be null"); if (operations.Length == 0) { throw new ArgumentNullException(nameof(operations), "At least one operation required"); } Fee = operations.Length * BaseFee; Signatures = new List <DecoratedSignature>(); Memo = memo ?? Memo.None(); TimeBounds = timeBounds; }
public void TestTryingToAddMemoTwice() { // GBPMKIRA2OQW2XZZQUCQILI5TMVZ6JNRKM423BSAISDM7ZFWQ6KWEBC4 var source = KeyPair.FromSecretSeed("SCH27VUZZ6UAKB67BDNF6FA42YMBMQCBKXWGMFD5TZ6S5ZZCZFLRXKHS"); var destination = KeyPair.FromAccountId("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR"); try { var account = new Account(source, 2908908335136768L); new Transaction.Builder(account) .AddOperation(new CreateAccountOperation.Builder(destination, "2000").Build()) .AddMemo(Memo.None()) .AddMemo(Memo.None()); Assert.Fail(); } catch (Exception exception) { Assert.IsTrue(exception.Message.Contains("Memo has been already added.")); } }
public void TestMemoNone() { var memo = Memo.None(); Assert.AreEqual(sdkxdr.MemoType.MemoTypeEnum.MEMO_NONE, memo.ToXdr().Discriminant.InnerValue); }