From 91c3cb1a49807a236d77c7e4eb3a6479af840255 Mon Sep 17 00:00:00 2001 From: gldeng Date: Tue, 12 Dec 2023 23:02:04 +0800 Subject: [PATCH] feat(inscription): add inscription test --- all.sln | 7 + src/AElf.Client/AElf.Client.csproj | 1 + src/AElf.Inscription/AElf.Inscription.csproj | 14 + src/AElf.Inscription/Inscription.cs | 404 ++++++++++++++++++ src/AElf.Inscription/Protobuf/aelf/core.proto | 157 +++++++ .../Protobuf/inscription.proto | 18 + test/AElf.Client.Test/InscriptionTest.cs | 75 ++++ 7 files changed, 676 insertions(+) create mode 100644 src/AElf.Inscription/AElf.Inscription.csproj create mode 100644 src/AElf.Inscription/Inscription.cs create mode 100644 src/AElf.Inscription/Protobuf/aelf/core.proto create mode 100644 src/AElf.Inscription/Protobuf/inscription.proto create mode 100644 test/AElf.Client.Test/InscriptionTest.cs diff --git a/all.sln b/all.sln index 3e3a5d1..dcebc5a 100644 --- a/all.sln +++ b/all.sln @@ -20,6 +20,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Test", "test\AE EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Abp", "src\AElf.Client.Abp\AElf.Client.Abp.csproj", "{BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Inscription", "src\AElf.Inscription\AElf.Inscription.csproj", "{D5CA0435-4C95-4A97-8BE3-CD972E1CE1E7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,7 @@ Global {BE0E29FA-A9C6-4E03-9E0C-297CCF7C5729} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} {4D019C99-8B1C-42B0-8630-F1E3661F414B} = {7CD2B508-C765-4720-B430-94E79135797A} {BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F} = {940361AC-2167-4D30-A4F6-9A543C608196} + {D5CA0435-4C95-4A97-8BE3-CD972E1CE1E7} = {940361AC-2167-4D30-A4F6-9A543C608196} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1AE7844D-B4D9-4F5A-9D25-7E551B61138E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -63,5 +66,9 @@ Global {BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}.Debug|Any CPU.Build.0 = Debug|Any CPU {BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}.Release|Any CPU.ActiveCfg = Release|Any CPU {BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}.Release|Any CPU.Build.0 = Release|Any CPU + {D5CA0435-4C95-4A97-8BE3-CD972E1CE1E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5CA0435-4C95-4A97-8BE3-CD972E1CE1E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5CA0435-4C95-4A97-8BE3-CD972E1CE1E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5CA0435-4C95-4A97-8BE3-CD972E1CE1E7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/AElf.Client/AElf.Client.csproj b/src/AElf.Client/AElf.Client.csproj index 259e45a..320df69 100644 --- a/src/AElf.Client/AElf.Client.csproj +++ b/src/AElf.Client/AElf.Client.csproj @@ -11,6 +11,7 @@ + diff --git a/src/AElf.Inscription/AElf.Inscription.csproj b/src/AElf.Inscription/AElf.Inscription.csproj new file mode 100644 index 0000000..b9d9d3f --- /dev/null +++ b/src/AElf.Inscription/AElf.Inscription.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.1 + enable + + + + + + + + + diff --git a/src/AElf.Inscription/Inscription.cs b/src/AElf.Inscription/Inscription.cs new file mode 100644 index 0000000..fdf75f6 --- /dev/null +++ b/src/AElf.Inscription/Inscription.cs @@ -0,0 +1,404 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: inscription.proto +// +#pragma warning disable 1591, 0612, 3021, 8981 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace AElf.Inscription { + + /// Holder for reflection information generated from inscription.proto + public static partial class InscriptionReflection { + + #region Descriptor + /// File descriptor for inscription.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static InscriptionReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChFpbnNjcmlwdGlvbi5wcm90bxIQYWVsZi5pbnNjcmlwdGlvbhoPYWVsZi9j", + "b3JlLnByb3RvInwKHFRyYW5zZmVySW5wdXRXaXRoSW5zY3JpcHRpb24SGQoC", + "dG8YASABKAsyDS5hZWxmLkFkZHJlc3MSDgoGc3ltYm9sGAIgASgJEg4KBmFt", + "b3VudBgDIAEoAxIMCgRtZW1vGAQgASgJEhMKC2luc2NyaXB0aW9uGCogASgM", + "QhOqAhBBRWxmLkluc2NyaXB0aW9uYgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { global::AElf.Types.CoreReflection.Descriptor, }, + new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::AElf.Inscription.TransferInputWithInscription), global::AElf.Inscription.TransferInputWithInscription.Parser, new[]{ "To", "Symbol", "Amount", "Memo", "Inscription" }, null, null, null, null) + })); + } + #endregion + + } + #region Messages + public sealed partial class TransferInputWithInscription : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new TransferInputWithInscription()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::AElf.Inscription.InscriptionReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public TransferInputWithInscription() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public TransferInputWithInscription(TransferInputWithInscription other) : this() { + to_ = other.to_ != null ? other.to_.Clone() : null; + symbol_ = other.symbol_; + amount_ = other.amount_; + memo_ = other.memo_; + inscription_ = other.inscription_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public TransferInputWithInscription Clone() { + return new TransferInputWithInscription(this); + } + + /// Field number for the "to" field. + public const int ToFieldNumber = 1; + private global::AElf.Types.Address to_; + /// + /// The receiver of the token. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::AElf.Types.Address To { + get { return to_; } + set { + to_ = value; + } + } + + /// Field number for the "symbol" field. + public const int SymbolFieldNumber = 2; + private string symbol_ = ""; + /// + /// The token symbol to transfer. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string Symbol { + get { return symbol_; } + set { + symbol_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "amount" field. + public const int AmountFieldNumber = 3; + private long amount_; + /// + /// The amount to to transfer. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public long Amount { + get { return amount_; } + set { + amount_ = value; + } + } + + /// Field number for the "memo" field. + public const int MemoFieldNumber = 4; + private string memo_ = ""; + /// + /// The memo. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string Memo { + get { return memo_; } + set { + memo_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "inscription" field. + public const int InscriptionFieldNumber = 42; + private pb::ByteString inscription_ = pb::ByteString.Empty; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pb::ByteString Inscription { + get { return inscription_; } + set { + inscription_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as TransferInputWithInscription); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(TransferInputWithInscription other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(To, other.To)) return false; + if (Symbol != other.Symbol) return false; + if (Amount != other.Amount) return false; + if (Memo != other.Memo) return false; + if (Inscription != other.Inscription) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (to_ != null) hash ^= To.GetHashCode(); + if (Symbol.Length != 0) hash ^= Symbol.GetHashCode(); + if (Amount != 0L) hash ^= Amount.GetHashCode(); + if (Memo.Length != 0) hash ^= Memo.GetHashCode(); + if (Inscription.Length != 0) hash ^= Inscription.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (to_ != null) { + output.WriteRawTag(10); + output.WriteMessage(To); + } + if (Symbol.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Symbol); + } + if (Amount != 0L) { + output.WriteRawTag(24); + output.WriteInt64(Amount); + } + if (Memo.Length != 0) { + output.WriteRawTag(34); + output.WriteString(Memo); + } + if (Inscription.Length != 0) { + output.WriteRawTag(210, 2); + output.WriteBytes(Inscription); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (to_ != null) { + output.WriteRawTag(10); + output.WriteMessage(To); + } + if (Symbol.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Symbol); + } + if (Amount != 0L) { + output.WriteRawTag(24); + output.WriteInt64(Amount); + } + if (Memo.Length != 0) { + output.WriteRawTag(34); + output.WriteString(Memo); + } + if (Inscription.Length != 0) { + output.WriteRawTag(210, 2); + output.WriteBytes(Inscription); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (to_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(To); + } + if (Symbol.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Symbol); + } + if (Amount != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Amount); + } + if (Memo.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Memo); + } + if (Inscription.Length != 0) { + size += 2 + pb::CodedOutputStream.ComputeBytesSize(Inscription); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(TransferInputWithInscription other) { + if (other == null) { + return; + } + if (other.to_ != null) { + if (to_ == null) { + To = new global::AElf.Types.Address(); + } + To.MergeFrom(other.To); + } + if (other.Symbol.Length != 0) { + Symbol = other.Symbol; + } + if (other.Amount != 0L) { + Amount = other.Amount; + } + if (other.Memo.Length != 0) { + Memo = other.Memo; + } + if (other.Inscription.Length != 0) { + Inscription = other.Inscription; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + if (to_ == null) { + To = new global::AElf.Types.Address(); + } + input.ReadMessage(To); + break; + } + case 18: { + Symbol = input.ReadString(); + break; + } + case 24: { + Amount = input.ReadInt64(); + break; + } + case 34: { + Memo = input.ReadString(); + break; + } + case 338: { + Inscription = input.ReadBytes(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + if (to_ == null) { + To = new global::AElf.Types.Address(); + } + input.ReadMessage(To); + break; + } + case 18: { + Symbol = input.ReadString(); + break; + } + case 24: { + Amount = input.ReadInt64(); + break; + } + case 34: { + Memo = input.ReadString(); + break; + } + case 338: { + Inscription = input.ReadBytes(); + break; + } + } + } + } + #endif + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/AElf.Inscription/Protobuf/aelf/core.proto b/src/AElf.Inscription/Protobuf/aelf/core.proto new file mode 100644 index 0000000..c990aa9 --- /dev/null +++ b/src/AElf.Inscription/Protobuf/aelf/core.proto @@ -0,0 +1,157 @@ +syntax = "proto3"; + +package aelf; + +option csharp_namespace = "AElf.Types"; + +message Transaction { + // The address of the sender of the transaction. + Address from = 1; + // The address of the contract when calling a contract. + Address to = 2; + // The height of the referenced block hash. + int64 ref_block_number = 3; + // The first four bytes of the referenced block hash. + bytes ref_block_prefix = 4; + // The name of a method in the smart contract at the To address. + string method_name = 5; + // The parameters to pass to the smart contract method. + bytes params = 6; + // When signing a transaction it’s actually a subset of the fields: from/to and the target method as well as + // the parameter that were given. It also contains the reference block number and prefix. + bytes signature = 10000; +} + +message StatePath { + // The partial path of the state path. + repeated string parts = 1; +} + +message ScopedStatePath { + // The scope address, which will be the contract address. + Address address = 1; + // The path of contract state. + StatePath path = 2; +} + +enum TransactionResultStatus { + // The execution result of the transaction does not exist. + NOT_EXISTED = 0; + // The transaction is in the transaction pool waiting to be packaged. + PENDING = 1; + // Transaction execution failed. + FAILED = 2; + // The transaction was successfully executed and successfully packaged into a block. + MINED = 3; + // When executed in parallel, there are conflicts with other transactions. + CONFLICT = 4; + // The transaction is waiting for validation. + PENDING_VALIDATION = 5; + // Transaction validation failed. + NODE_VALIDATION_FAILED = 6; +} + +message TransactionResult { + // The transaction id. + Hash transaction_id = 1; + // The transaction result status. + TransactionResultStatus status = 2; + // The log events. + repeated LogEvent logs = 3; + // Bloom filter for transaction logs. A transaction log event can be defined in the contract and stored + // in the bloom filter after the transaction is executed. Through this filter, we can quickly search for + // and determine whether a log exists in the transaction result. + bytes bloom = 4; + // The return value of the transaction execution. + bytes return_value = 5; + // The height of the block hat packages the transaction. + int64 block_number = 6; + // The hash of the block hat packages the transaction. + Hash block_hash = 7; + // Failed execution error message. + string error = 10; +} + +message LogEvent { + // The contract address. + Address address = 1; + // The name of the log event. + string name = 2; + // The indexed data, used to calculate bloom. + repeated bytes indexed = 3; + // The non indexed data. + bytes non_indexed = 4; +} + +message SmartContractRegistration { + // The category of contract code(0: C#). + sint32 category = 1; + // The byte array of the contract code. + bytes code = 2; + // The hash of the contract code. + Hash code_hash = 3; + // Whether it is a system contract. + bool is_system_contract = 4; + // The version of the current contract. + int32 version = 5; + // The version of the contract. + string contract_version = 6; + // The address of the current contract. + Address contract_address = 7; + // Indicates if the contract is the user contract. + bool is_user_contract = 8; +} + +message TransactionExecutingStateSet { + // The changed states. + map writes = 1; + // The read states. + map reads = 2; + // The deleted states. + map deletes = 3; +} + +message Address +{ + bytes value = 1; +} + +message Hash +{ + bytes value = 1; +} + +message SInt32Value +{ + sint32 value = 1; +} + +message SInt64Value +{ + sint64 value = 1; +} + +message MerklePath { + // The merkle path nodes. + repeated MerklePathNode merkle_path_nodes = 1; +} + +message MerklePathNode{ + // The node hash. + Hash hash = 1; + // Whether it is a left child node. + bool is_left_child_node = 2; +} + +message BinaryMerkleTree { + // The leaf nodes. + repeated Hash nodes = 1; + // The root node hash. + Hash root = 2; + // The count of leaf node. + int32 leaf_count = 3; +} + +message BigIntValue { + string value = 1; +} \ No newline at end of file diff --git a/src/AElf.Inscription/Protobuf/inscription.proto b/src/AElf.Inscription/Protobuf/inscription.proto new file mode 100644 index 0000000..f35ea88 --- /dev/null +++ b/src/AElf.Inscription/Protobuf/inscription.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package aelf.inscription; +import "aelf/core.proto"; + +option csharp_namespace = "AElf.Inscription"; + +message TransferInputWithInscription { + // The receiver of the token. + aelf.Address to = 1; + // The token symbol to transfer. + string symbol = 2; + // The amount to to transfer. + int64 amount = 3; + // The memo. + string memo = 4; + bytes inscription = 42; +} diff --git a/test/AElf.Client.Test/InscriptionTest.cs b/test/AElf.Client.Test/InscriptionTest.cs new file mode 100644 index 0000000..1c7bc8b --- /dev/null +++ b/test/AElf.Client.Test/InscriptionTest.cs @@ -0,0 +1,75 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using AElf.Types; +using AElf.Client.Dto; +using AElf.Inscription; +using Google.Protobuf; +using Xunit; +using Xunit.Abstractions; + +namespace AElf.Client.Test; + +public class InscriptionTest +{ + private const string BaseUrl = "https://aelf-test-node.aelf.io"; + + // example contract-method-name + private string ContractMethodName => "GetContractAddressByName"; + + // Address and privateKey of a node. + private readonly string _address; + private const string PrivateKey = "345af4fbb6d6142f15ec0f5ebae680195bb655f88f22573ac01222bbf537e9ca"; + + private AElfClient Client { get; } + private readonly ITestOutputHelper _testOutputHelper; + + private const string UserName = "test1"; + private const string Password = "test"; + + public InscriptionTest(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + Client = new AElfClient(BaseUrl, userName: UserName, password: Password); + + // To get address from privateKey.s + _address = Client.GetAddressFromPrivateKey(PrivateKey); + } + + [Fact] + public async Task Test_Inscribe() + { + var tokenContractAddress = + await Client.GetContractAddressByNameAsync(HashHelper.ComputeFrom("AElf.ContractNames.Token")); + + // Using example https://ethscriptions.com/ethscriptions/0x6317e5bcc0b2d41f8de41d30b77e8e49e465766ca634f024c8ae913898620e94 + + var dataUrl = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAACXBIWXMAAAsSAAALEgHS3X78AAAFUmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLycgeDp4bXB0az0nSW1hZ2U6OkV4aWZUb29sIDEyLjY0Jz4KPHJkZjpSREYgeG1sbnM6cmRmPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjJz4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOmRjPSdodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyc+CiAgPGRjOmRlc2NyaXB0aW9uPgogICA8cmRmOkFsdD4KICAgIDxyZGY6bGkgeG1sOmxhbmc9J3gtZGVmYXVsdCc+eyYjMzk7bmFtZSYjMzk7OiYjMzk7QmV2JiMzOTssJiMzOTtudW1iZXImIzM5OzozMDUwLCYjMzk7ZGVzY3JpcHRpb24mIzM5OzomIzM5O0h5cHBvY3JpdGV6IGlzIG9uZSBvZiB0aGUgZmlyc3Qgb3JpZ2luYWwgY29sbGVjdGlvbnMgbWFkZSBzcGVjaWZpY2FsbHkgZm9yIEV0aHNjcmlwdGlvbnMuIEEgY29sbGVjdGlvbiBvZiBkZWdlbiBoeXBwb3MgZm9yIFdlYjMgcGVvcGxlIHdobyBoYXRlIHRvIGxvdmUgV2ViMy4mIzM5OywmIzM5O2V4dGVybmFsX3VybCYjMzk7OiYjMzk7aHR0cHM6Ly93d3cuaHlwcG9jcml0ZXouY29tJiMzOTssJiMzOTthdHRyaWJ1dGVzJiMzOTs6W3smIzM5O3RyYWl0X3R5cGUmIzM5OzomIzM5O1RlYW0mIzM5OywmIzM5O3ZhbHVlJiMzOTs6JiMzOTtCbHVlJiMzOTt9LHsmIzM5O3RyYWl0X3R5cGUmIzM5OzomIzM5O0hhbmQmIzM5OywmIzM5O3ZhbHVlJiMzOTs6JiMzOTtIb21lLVJ1bm5lciYjMzk7fSx7JiMzOTt0cmFpdF90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"; + + var methodName = "Transfer"; + var param = new TransferInputWithInscription + { + To = new Address { Value = Address.FromBase58("hvydyxP4xbM85oe2ipneoMFFtxzF9xi2u6h2MxZxLPTxbnRQL").Value }, + Symbol = "ELF", + Amount = 1, + Memo = "", + Inscription = ByteString.CopyFrom(Encoding.UTF8.GetBytes(dataUrl)) + }; + var ownerAddress = Client.GetAddressFromPrivateKey(PrivateKey); + var transaction = + await Client.GenerateTransactionAsync(ownerAddress, tokenContractAddress.ToBase58(), methodName, param); + var txWithSign = Client.SignTransaction(PrivateKey, transaction); + + var result = await Client.SendTransactionAsync(new SendTransactionInput + { + RawTransaction = txWithSign.ToByteArray().ToHex() + }); + + await Task.Delay(4000); + // After the transaction is mined, query the execution results. + var transactionResult = await Client.GetTransactionResultAsync(result.TransactionId); + _testOutputHelper.WriteLine($"TransactionId is: {result.TransactionId}"); + _testOutputHelper.WriteLine(transactionResult.Status); + } +} \ No newline at end of file