public void TransferScriptMethodExtraction() { var source = PhantasmaKeys.Generate(); var dest = PhantasmaKeys.Generate(); var amount = UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals); var script = ScriptUtils.BeginScript().AllowGas(source.Address, Address.Null, 1, 999).TransferTokens(DomainSettings.StakingTokenSymbol, source.Address, dest.Address, amount).SpendGas(source.Address).EndScript(); var table = DisasmUtils.GetDefaultDisasmTable(); var methods = DisasmUtils.ExtractMethodCalls(script, table); Assert.IsTrue(methods != null && methods.Count() == 3); }
public static IEnumerator GetDescription(byte[] script, Action <string, string> callback) { foreach (var entry in methodTable.Keys) { Debug.Log("disam method: " + entry); } if (knownContracts == null) { // Collecting known contract names knownContracts = methodTable.Keys.Select(x => x.IndexOf(".") > 0 ? x.Substring(0, x.IndexOf(".")) : x).Distinct().ToList(); } var accountManager = AccountManager.Instance; var contracts = DisasmUtils.ExtractContractNames(script).Where(x => !knownContracts.Contains(x)); var contractsToLoad = contracts.Count(); var contractsProcessed = 0; foreach (var contract in contracts) { WalletGUI.Instance.StartCoroutine( accountManager.phantasmaApi.GetContract(contract, (contractStruct) => { Log.Write($"Registering {contractStruct.methods.Length} methods for {contract}"); foreach (var method in contractStruct.methods) { Log.Write($"Registering contract method {contract}.{method.name} with {method.parameters.Length} parameters"); DescriptionUtils.RegisterContractMethod($"{contract}.{method.name}", method.parameters.Length); } contractsProcessed++; }, (error, msg) => { Log.WriteWarning("Could not fetch contract info: " + contract); contractsProcessed++; })); } while (contractsProcessed < contractsToLoad) { yield return(null); } IEnumerable <DisasmMethodCall> disasm; try { disasm = DisasmUtils.ExtractMethodCalls(script, methodTable); } catch (Exception e) { callback(null, e.Message); yield break; } // Checking if all calls are "market.SellToken" calls only or "Runtime.TransferToken" only, // and we can group them. int groupSize = 0; DisasmMethodCall?prevCall = null; foreach (var call in disasm) { if (CheckIfCallShouldBeIgnored(call)) { continue; } if (GetCallFullName(call) == "Runtime.TransferToken") { if (prevCall != null && !CompareCalls((DisasmMethodCall)prevCall, call, 0, 1)) { groupSize = 0; break; } groupSize++; } else if (GetCallFullName(call) == "market.SellToken") { if (prevCall != null && !CompareCalls((DisasmMethodCall)prevCall, call, 0, 2, 4, 5)) { groupSize = 0; break; } groupSize++; } else { // Different call, grouping is not supported. groupSize = 0; break; } prevCall = call; } int transferTokenCounter = 0; // Counting "market.TransferToken" calls, needed for grouping. int sellTokenCounter = 0; // Counting "market.SellToken" calls, needed for grouping. var sb = new StringBuilder(); foreach (var entry in disasm) { if (CheckIfCallShouldBeIgnored(entry)) { continue; } // Put it to log so that developer can easily check what PG is receiving. Log.Write("GetDescription(): Contract's description: " + entry.ToString()); switch (GetCallFullName(entry)) { case "Runtime.TransferToken": { var src = GetStringArg(entry, 0); var dst = GetStringArg(entry, 1); var symbol = GetStringArg(entry, 2); var nftNumber = GetStringArg(entry, 3); if (groupSize > 1) { if (transferTokenCounter == 0) { // Desc line #1. sb.AppendLine("\u2605 Transfer:"); } if (transferTokenCounter == groupSize - 1) { // Desc line for token #N. sb.AppendLine($"{symbol} NFT #{ShortenTokenId(nftNumber)}"); sb.AppendLine($"to {dst}."); } else { // Desc line for tokens #1 ... N-1. sb.AppendLine($"{symbol} NFT #{ShortenTokenId(nftNumber)},"); } } else { sb.AppendLine($"\u2605 Transfer {symbol} NFT #{ShortenTokenId(nftNumber)} to {dst}."); } transferTokenCounter++; break; } case "Runtime.TransferTokens": { var src = GetStringArg(entry, 0); var dst = GetStringArg(entry, 1); var symbol = GetStringArg(entry, 2); var amount = GetNumberArg(entry, 3); Token token; accountManager.GetTokenBySymbol(symbol, PlatformKind.Phantasma, out token); var total = UnitConversion.ToDecimal(amount, token.decimals); sb.AppendLine($"\u2605 Transfer {total} {symbol} from {src} to {dst}."); break; } case "market.BuyToken": { var dst = GetStringArg(entry, 0); var symbol = GetStringArg(entry, 1); var nftNumber = GetStringArg(entry, 2); sb.AppendLine($"\u2605 Buy {symbol} NFT #{ShortenTokenId(nftNumber)}."); break; } case "market.CancelSale": { var symbol = GetStringArg(entry, 0); var nftNumber = GetStringArg(entry, 1); sb.AppendLine($"\u2605 Cancel sale of {symbol} NFT #{ShortenTokenId(nftNumber)}."); break; } case "market.SellToken": { var dst = GetStringArg(entry, 0); var tokenSymbol = GetStringArg(entry, 1); var priceSymbol = GetStringArg(entry, 2); var nftNumber = GetStringArg(entry, 3); Token priceToken; accountManager.GetTokenBySymbol(priceSymbol, PlatformKind.Phantasma, out priceToken); var price = UnitConversion.ToDecimal(GetNumberArg(entry, 4), priceToken.decimals); var untilDate = GetTimestampArg(entry, 5); if (groupSize > 1) { if (sellTokenCounter == 0) { // Desc line #1. sb.AppendLine("\u2605 Sell:"); sb.AppendLine($"{tokenSymbol} NFT #{ShortenTokenId(nftNumber)},"); } else if (sellTokenCounter == groupSize - 1) { // Desc line #N. sb.AppendLine($"{tokenSymbol} NFT #{ShortenTokenId(nftNumber)}"); sb.AppendLine($"for {price} {priceSymbol} each, offer valid until {untilDate}."); } else { // Desc lines #2 ... N-1. sb.AppendLine($"{tokenSymbol} NFT #{ShortenTokenId(nftNumber)},"); } } else { sb.AppendLine($"\u2605 Sell {tokenSymbol} NFT #{ShortenTokenId(nftNumber)} for {price} {priceSymbol}, offer valid until {untilDate}."); } sellTokenCounter++; break; } case "market.EditAuction": { var dst = GetStringArg(entry, 0); var tokenSymbol = GetStringArg(entry, 1); var priceSymbol = GetStringArg(entry, 2); var nftNumber = GetStringArg(entry, 3); Token priceToken; accountManager.GetTokenBySymbol(priceSymbol, PlatformKind.Phantasma, out priceToken); var price = UnitConversion.ToDecimal(GetNumberArg(entry, 4), priceToken.decimals); var endPrice = UnitConversion.ToDecimal(GetNumberArg(entry, 5), priceToken.decimals); var startDate = GetTimestampArg(entry, 6); var untilDate = GetTimestampArg(entry, 7); var extensionPeriod = GetStringArg(entry, 8); sb.AppendLine($"\u2605 Edit {tokenSymbol} NFT #{ShortenTokenId(nftNumber)} Auction."); break; } case "market.ListToken": { var dst = GetStringArg(entry, 0); var tokenSymbol = GetStringArg(entry, 1); var priceSymbol = GetStringArg(entry, 2); var nftNumber = GetStringArg(entry, 3); Token priceToken; accountManager.GetTokenBySymbol(priceSymbol, PlatformKind.Phantasma, out priceToken); var price = UnitConversion.ToDecimal(GetNumberArg(entry, 4), priceToken.decimals); var endPrice = UnitConversion.ToDecimal(GetNumberArg(entry, 5), priceToken.decimals); var startDate = GetTimestampArg(entry, 6); var untilDate = GetTimestampArg(entry, 7); var extensionPeriod = GetStringArg(entry, 8); var typeAuction = GetNumberArg(entry, 9); var listingFee = GetStringArg(entry, 10); var listingFeeAddress = GetStringArg(entry, 11); if (typeAuction == 0) { sb.AppendLine($"\u2605 List {tokenSymbol} NFT #{ShortenTokenId(nftNumber)} for a Fixed Auction with a price of {price} {priceSymbol}."); break; } else if (typeAuction == 1) { sb.AppendLine($"\u2605 List {tokenSymbol} NFT #{ShortenTokenId(nftNumber)} for a Classic Auction with a starting price of {price} {priceSymbol}."); break; } else if (typeAuction == 2) { sb.AppendLine($"\u2605 List {tokenSymbol} NFT #{ShortenTokenId(nftNumber)} for a Reserve Auction with a reserve price of {price} {priceSymbol}."); break; } else if (typeAuction == 3) { sb.AppendLine($"\u2605 List {tokenSymbol} NFT #{ShortenTokenId(nftNumber)} for a Dutch Auction with a starting price of {price} {priceSymbol} and an end price of {endPrice} {priceSymbol}."); break; } break; } case "market.BidToken": { var dst = GetStringArg(entry, 0); var tokenSymbol = GetStringArg(entry, 1); var nftNumber = GetStringArg(entry, 2); //Token priceToken; //accountManager.GetTokenBySymbol(priceSymbol, PlatformKind.Phantasma, out priceToken); //var price = UnitConversion.ToDecimal(GetNumberArg(entry, 3), priceToken.decimals); var buyingFee = GetStringArg(entry, 4); var buyingFeeAddress = GetStringArg(entry, 5); sb.AppendLine($"\u2605 Bid or Buy {tokenSymbol} NFT #{ShortenTokenId(nftNumber)}."); break; } case "sale.Purchase": { var saleHash = GetStringArg(entry, 1); var tokenSymbol = GetStringArg(entry, 2); var tokenAmount = GetStringArg(entry, 3); Token priceToken; accountManager.GetTokenBySymbol(tokenSymbol, PlatformKind.Phantasma, out priceToken); var purchase = UnitConversion.ToDecimal(GetNumberArg(entry, 4), priceToken.decimals); sb.AppendLine($"\u2605 Participate to sale {saleHash} with {purchase} {tokenSymbol}."); break; } case "Runtime.MintToken": { var owner = GetStringArg(entry, 0); var recepient = GetStringArg(entry, 1); var symbol = GetStringArg(entry, 2); var bytes = GetByteArrayArg(entry, 3); sb.AppendLine($"\u2605 Mint {symbol} NFT from {owner} with {recepient} as recipient."); break; } case "Runtime.BurnTokens": { var address = GetStringArg(entry, 0); var symbol = GetStringArg(entry, 1); var amount = GetNumberArg(entry, 2); Token token; accountManager.GetTokenBySymbol(symbol, PlatformKind.Phantasma, out token); var total = UnitConversion.ToDecimal(amount, token.decimals); sb.AppendLine($"\u2605 Burn {total} {symbol} from {address}."); break; } case "Runtime.BurnToken": { var address = GetStringArg(entry, 0); var symbol = GetStringArg(entry, 1); var nftNumber = GetStringArg(entry, 2); sb.AppendLine($"\u2605 Burn {symbol} NFT #{ShortenTokenId(nftNumber)} from {address}."); break; } case "Runtime.InfuseToken": { var address = GetStringArg(entry, 0); var targetSymbol = GetStringArg(entry, 1); var tokenID = GetStringArg(entry, 2); var infuseSymbol = GetStringArg(entry, 3); var amount = GetNumberArg(entry, 4); var amountString = amount.ToString(); Token infuseToken; accountManager.GetTokenBySymbol(infuseSymbol, PlatformKind.Phantasma, out infuseToken); sb.AppendLine($"\u2605 Infuse {targetSymbol} NFT #{ShortenTokenId(tokenID)} with " + (infuseToken.flags.Contains("Fungible") ? $"{UnitConversion.ToDecimal(amount, infuseToken.decimals)} {infuseSymbol}." : $"{infuseSymbol} NFT #{ShortenTokenId(amountString)}.")); break; } case "Nexus.CreateToken": { var address = GetStringArg(entry, 0); var symbol = GetStringArg(entry, 1); var name = GetStringArg(entry, 2); var maxSupply = GetNumberArg(entry, 3); var decimals = GetNumberArg(entry, 4); var flags = GetNumberArg(entry, 5); var ctScript = GetByteArrayArg(entry, 6); sb.AppendLine($"\u2605 Create token {symbol} with name '{name}', {maxSupply} max supply, {decimals} decimals from {address}."); break; } case "GHOST.getLockedContent": { var nftSymbol = GetStringArg(entry, 0); var nftID = GetStringArg(entry, 1); sb.AppendLine($"\u2605 Get locked content for {nftSymbol} #{ShortenTokenId(nftID)}."); break; } case "GHOST.mintToken": { var editionId = GetNumberArg(entry, 0); var editionMax = GetNumberArg(entry, 1); var editionMode = GetNumberArg(entry, 2); var creator = GetStringArg(entry, 3); var royalties = GetNumberArg(entry, 4); var mintTicker = GetStringArg(entry, 5); var numOfNfts = GetNumberArg(entry, 6); var name = GetStringArg(entry, 7); var description = GetStringArg(entry, 8); var type = GetStringArg(entry, 9); var imageURL = GetStringArg(entry, 10); var infoURL = GetStringArg(entry, 11); var attributeType1 = GetStringArg(entry, 12); var attributeValue1 = GetStringArg(entry, 13); var attributeType2 = GetStringArg(entry, 14); var attributeValue2 = GetStringArg(entry, 15); var attributeType3 = GetStringArg(entry, 16); var attributeValue3 = GetStringArg(entry, 17); var lockedContent = GetStringArg(entry, 18); var listPrice = GetNumberArg(entry, 19); var listPriceCurrency = GetStringArg(entry, 20); var listLastEndDate = GetTimestampArg(entry, 21); var infusedAsset = GetStringArg(entry, 22); var infusedAmount = GetNumberArg(entry, 23); var hasLocked = GetStringArg(entry, 24); if (editionId > 0) { sb.AppendLine($"\u2605 Mint on existing series #{editionId}, a total of {numOfNfts}x {mintTicker}."); } else { sb.AppendLine($"\u2605 Mint on a new series {numOfNfts}x {mintTicker}, with a {royalties}% royalty and named: {name}."); } if (infusedAmount > 0) { accountManager.GetTokenBySymbol(infusedAsset, PlatformKind.Phantasma, out var infusedToken); var infusedAmountWithDecimals = infusedToken.flags.Contains("Fungible") ? UnitConversion.ToDecimal(infusedAmount, infusedToken.decimals) : 0; sb.AppendLine($"\u2605 Infuse {numOfNfts}x {mintTicker} with {(infusedAmountWithDecimals > 0 ? infusedAmountWithDecimals.ToString() : infusedAmount.ToString())} {infusedAsset} each."); } if (listPrice > 0) { accountManager.GetTokenBySymbol(listPriceCurrency, PlatformKind.Phantasma, out var listPriceToken); var listPriceWithDecimals = (listPrice > 0) ? UnitConversion.ToDecimal(listPrice, listPriceToken.decimals) : 0; sb.AppendLine($"\u2605 Sell {numOfNfts}x {mintTicker}, for {listPriceWithDecimals} {listPriceCurrency}, offer valid until {listLastEndDate}."); } break; } default: sb.AppendLine(entry.ToString()); break; } } if (sb.Length > 0) { callback(sb.ToString(), null); yield break; } callback(null, "Unknown transaction content."); }
public static string GetDescription(byte[] script) { var table = DisasmUtils.GetDefaultDisasmTable(); // adding missing stuff here like table["methodname"] = 5 where 5 is arg count var disasm = DisasmUtils.ExtractMethodCalls(script, table); // Checking if all calls are "market.SellToken" calls only or "Runtime.TransferToken" only, // and we can group them. int groupSize = 0; DisasmMethodCall?prevCall = null; foreach (var call in disasm) { if (CheckIfCallShouldBeIgnored(call)) { continue; } if (GetCallFullName(call) == "Runtime.TransferToken") { if (prevCall != null && !CompareCalls((DisasmMethodCall)prevCall, call, 0, 1)) { groupSize = 0; break; } groupSize++; } else if (GetCallFullName(call) == "market.SellToken") { if (prevCall != null && !CompareCalls((DisasmMethodCall)prevCall, call, 0, 2, 4, 5)) { groupSize = 0; break; } groupSize++; } else { // Different call, grouping is not supported. groupSize = 0; break; } prevCall = call; } int transferTokenCounter = 0; // Counting "market.TransferToken" calls, needed for grouping. int sellTokenCounter = 0; // Counting "market.SellToken" calls, needed for grouping. var sb = new StringBuilder(); foreach (var entry in disasm) { if (CheckIfCallShouldBeIgnored(entry)) { continue; } // Put it to log so that developer can easily check what PG is receiving. Log.Write("GetDescription(): Contract's description: " + entry.ToString()); switch (GetCallFullName(entry)) { case "Runtime.TransferToken": { var src = GetStringArg(entry, 0); var dst = GetStringArg(entry, 1); var symbol = GetStringArg(entry, 2); var nftNumber = GetStringArg(entry, 3); if (groupSize > 1) { if (transferTokenCounter == 0) { // Desc line #1. sb.AppendLine("Transfer:"); } if (transferTokenCounter == groupSize - 1) { // Desc line for token #N. sb.AppendLine($"{symbol} NFT #{nftNumber.Substring(0, 5) + "..." + nftNumber.Substring(nftNumber.Length - 5)}"); sb.AppendLine($"to {dst}."); } else { // Desc line for tokens #1 ... N-1. sb.AppendLine($"{symbol} NFT #{nftNumber.Substring(0, 5) + "..." + nftNumber.Substring(nftNumber.Length - 5)},"); } } else { sb.AppendLine($"Transfer {symbol} NFT #{nftNumber.Substring(0, 5) + "..." + nftNumber.Substring(nftNumber.Length - 5)} to {dst}."); } transferTokenCounter++; break; } case "Runtime.TransferTokens": { var src = GetStringArg(entry, 0); var dst = GetStringArg(entry, 1); var symbol = GetStringArg(entry, 2); var amount = GetNumberArg(entry, 3); Token token; AccountManager.Instance.GetTokenBySymbol(symbol, PlatformKind.Phantasma, out token); var total = UnitConversion.ToDecimal(amount, token.decimals); sb.AppendLine($"Transfer {total} {symbol} from {src} to {dst}."); break; } case "market.BuyToken": { var dst = GetStringArg(entry, 0); var symbol = GetStringArg(entry, 1); var nftNumber = GetStringArg(entry, 2); sb.AppendLine($"Buy {symbol} NFT #{nftNumber.Substring(0 ,5) + "..." + nftNumber.Substring(nftNumber.Length - 5)}."); break; } case "market.SellToken": { var dst = GetStringArg(entry, 0); var tokenSymbol = GetStringArg(entry, 1); var priceSymbol = GetStringArg(entry, 2); var nftNumber = GetStringArg(entry, 3); Token priceToken; AccountManager.Instance.GetTokenBySymbol(priceSymbol, PlatformKind.Phantasma, out priceToken); var price = UnitConversion.ToDecimal(GetNumberArg(entry, 4), priceToken.decimals); var untilDate = GetTimestampArg(entry, 5); if (groupSize > 1) { if (sellTokenCounter == 0) { // Desc line #1. sb.AppendLine("Sell:"); sb.AppendLine($"{tokenSymbol} NFT #{nftNumber.Substring(0, 5) + "..." + nftNumber.Substring(nftNumber.Length - 5)},"); } else if (sellTokenCounter == groupSize - 1) { // Desc line #N. sb.AppendLine($"{tokenSymbol} NFT #{nftNumber.Substring(0, 5) + "..." + nftNumber.Substring(nftNumber.Length - 5)}"); sb.AppendLine($"for {price} {priceSymbol} each, offer valid until {untilDate}."); } else { // Desc lines #2 ... N-1. sb.AppendLine($"{tokenSymbol} NFT #{nftNumber.Substring(0, 5) + "..." + nftNumber.Substring(nftNumber.Length - 5)},"); } } else { sb.AppendLine($"Sell {tokenSymbol} NFT #{nftNumber.Substring(0, 5) + "..." + nftNumber.Substring(nftNumber.Length - 5)} for {price} {priceSymbol}, offer valid until {untilDate}."); } sellTokenCounter++; break; } default: sb.AppendLine(entry.ToString()); break; } } if (sb.Length > 0) { return(sb.ToString()); } return("Unknown transaction content."); }