public Message(MessageArgs args) { this.header = args.header; this.accountKeys = args.accountKeys.ToList().Select(account => new PublicKey(account)).ToArray(); this.recentBlockhash = args.recentBlockhash; this.instructions = args.instructions; }
// /** // * Construct an empty Transaction // */ // constructor(opts?: TransactionCtorFields) // { // opts && Object.assign(this, opts); // } // /** // * Add one or more instructions to this Transaction // */ // add( // ...items: Array< // Transaction | TransactionInstruction | TransactionInstructionCtorFields // > // ) : Transaction { // if (items.length === 0) { // throw new Error('No instructions'); // } // items.forEach((item: any) => { // if ('instructions' in item) { // this.instructions = this.instructions.concat(item.instructions); // } else if ('data' in item && 'programId' in item && 'keys' in item) { // this.instructions.push(item); // } else { // this.instructions.push(new TransactionInstruction(item)); // } // }); //return this; // } /// <summary> /// Compile transaction data /// </summary> /// <returns></returns> public Message compileMessage() { var nonceInfo = this.nonceInfo; if (nonceInfo != null && this.instructions[0] != nonceInfo.nonceInstruction) { this.recentBlockhash = nonceInfo.nonce; this.instructions.Insert(0, nonceInfo.nonceInstruction); } var recentBlockhash = this.recentBlockhash; if (recentBlockhash.Length < 1) { throw new Exception("Transaction recentBlockhash required"); } if (this.instructions.Count < 1) { throw new Exception("No instructions provided"); } PublicKey feePayer; if (this.feePayer != null) { feePayer = this.feePayer; } else if (signatures.Count > 0 && signatures[0].publicKey != null) { // Use implicit fee payer feePayer = this.signatures[0].publicKey; } else { throw new Exception("Transaction fee payer required"); } for (var i = 0; i < this.instructions.Count(); i++) { if (this.instructions[i].programId == null) { throw new Exception(string.Format("Transaction instruction index {0} has undefined program id", i)); } } List <string> programIds = new List <string>(); List <AccountMeta> accountMetas = new List <AccountMeta>(); this.instructions.ForEach(instruction => { instruction.keys.ForEach(accountMeta => { accountMetas.Add(accountMeta); }); var programId = instruction.programId.ToString(); if (!programIds.Contains(programId)) { programIds.Add(programId); } }); // Append programID account metas programIds.ForEach(programId => { accountMetas.Add(new AccountMeta() { pubkey = new PublicKey(programId), isSigner = false, isWritable = false, }); }); // Sort. Prioritizing first by signer, then by writable accountMetas.Sort((x, y) => { var checkSigner = x.isSigner == y.isSigner ? 0 : x.isSigner ? -1 : 1; var checkWritable = x.isWritable == y.isWritable ? 0 : x.isWritable ? -1 : 1; return(Convert.ToBoolean(checkSigner) ? checkSigner : checkWritable); }); // Cull duplicate account metas List <AccountMeta> uniqueMetas = new List <AccountMeta>(); accountMetas.ForEach(accountMeta => { var pubkeyString = accountMeta.pubkey.ToString(); var uniqueIndex = uniqueMetas.FindIndex(x => { return(x.pubkey.ToString() == pubkeyString); }); if (uniqueIndex > -1) { var meta = uniqueMetas[uniqueIndex]; meta.isWritable = meta.isWritable || accountMeta.isWritable; uniqueMetas[uniqueIndex] = meta; } else { uniqueMetas.Add(accountMeta); } }); // Move fee payer to the front var feePayerIndex = uniqueMetas.FindIndex(x => { return(x.pubkey.Equals(feePayer)); }); if (feePayerIndex > -1) { var payerMeta = uniqueMetas[feePayerIndex]; payerMeta.isSigner = true; payerMeta.isWritable = true; uniqueMetas.Insert(0, payerMeta); } else { uniqueMetas.Insert(0, new AccountMeta() { pubkey = feePayer, isSigner = true, isWritable = true, }); } // Disallow unknown signers foreach (var signature in this.signatures) { var uniqueIndex = uniqueMetas.FindIndex(x => { return(x.pubkey.Equals(signature.publicKey)); }); if (uniqueIndex > -1) { if (!uniqueMetas[uniqueIndex].isSigner) { var meta = uniqueMetas[uniqueIndex]; meta.isSigner = true; uniqueMetas[uniqueIndex] = meta; //console.warn( // 'Transaction references a signature that is unnecessary, ' + // 'only the fee payer and instruction signer accounts should sign a transaction. ' + // 'This behavior is deprecated and will throw an error in the next major version release.', //); } } else { throw new Exception(string.Format("unknown signer: {0}", signature.publicKey.ToString())); } } var numRequiredSignatures = 0; var numReadonlySignedAccounts = 0; var numReadonlyUnsignedAccounts = 0; // Split out signing from non-signing keys and count header values List <string> signedKeys = new List <string>(); List <string> unsignedKeys = new List <string>(); uniqueMetas.ForEach(meta => { if (meta.isSigner) { signedKeys.Add(meta.pubkey.ToString()); numRequiredSignatures += 1; if (!meta.isWritable) { numReadonlySignedAccounts += 1; } } else { unsignedKeys.Add(meta.pubkey.ToString()); if (!meta.isWritable) { numReadonlyUnsignedAccounts += 1; } } }); signedKeys.AddRange(unsignedKeys); var accountKeys = signedKeys; var instructions = this.instructions.Select( instruction => { var data = instruction.data; var programId = instruction.programId; //const { data, programId} = instruction; var cin = new CompiledInstruction(); cin.programIdIndex = accountKeys.IndexOf(programId.ToString()); cin.accounts = instruction.keys.Select(meta => accountKeys.IndexOf(meta.pubkey.ToString())).ToArray(); cin.data = Base58CheckEncoding.EncodePlain(data); return(cin); }).ToList(); instructions.ForEach(instruction => { if (!(instruction.programIdIndex >= 0)) { throw new Exception("instruction.programIdIndex error"); } foreach (var keyIndex in instruction.accounts) { if (keyIndex < 0) { throw new Exception("instruction.accounts error"); } } }); var args = new MessageArgs() { header = new MessageHeader() { numRequiredSignatures = numRequiredSignatures, numReadonlySignedAccounts = numReadonlySignedAccounts, numReadonlyUnsignedAccounts = numReadonlyUnsignedAccounts }, accountKeys = accountKeys.ToArray(), recentBlockhash = recentBlockhash, instructions = instructions.ToArray() }; return(new Message(args)); }