Skip to content

Commit

Permalink
feat(abi): add Decode methods to ABI
Browse files Browse the repository at this point in the history
also includes new AbiTypeCode
  • Loading branch information
jasonboukheir committed Jul 11, 2022
1 parent 2623f43 commit a83f1fe
Show file tree
Hide file tree
Showing 30 changed files with 636 additions and 121 deletions.
9 changes: 6 additions & 3 deletions Runtime/CareBoo.AlgoSdk/Experimental/Abi/AbiReferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public byte Encode(Address value)
/// <returns>An array of account addresses.</returns>
public Address[] GetForeignAccounts()
{
return accounts.Slice(1).ToArray();
var result = accounts.Slice(1);
return result.Count > 0 ? result.ToArray() : null;
}

/// <summary>
Expand All @@ -78,7 +79,8 @@ public Address[] GetForeignAccounts()
/// <returns>An array of app indices.</returns>
public ulong[] GetForeignApps()
{
return apps.Slice(1).ToArray();
var result = apps.Slice(1);
return result.Count > 0 ? result.ToArray() : null;
}

/// <summary>
Expand All @@ -87,7 +89,8 @@ public ulong[] GetForeignApps()
/// <returns>An array of asset indices.</returns>
public ulong[] GetForeignAssets()
{
return assets.Slice(0).ToArray();
var result = assets.Slice(0);
return result.Count > 0 ? result.ToArray() : null;
}

/// <inheritdoc />
Expand Down
73 changes: 73 additions & 0 deletions Runtime/CareBoo.AlgoSdk/Experimental/Abi/AbiTypeCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace AlgoSdk.Experimental.Abi
{
/// <summary>
/// The untagged part of the tagged <see cref="AbiType"/> union.
/// </summary>
public enum AbiTypeCode
{
/// <summary>
/// Default code
/// </summary>
None,

/// <summary>
/// A reference to an account, asset, or application represented as a uint8.
/// </summary>
Reference,

/// <summary>
/// A transaction type that represents a previous transaction in the group.
/// </summary>
Transaction,

/// <summary>
/// An account address
/// </summary>
Address,

/// <summary>
/// A boolean value, true or false
/// </summary>
Bool,

/// <summary>
/// Alias of uint8.
/// </summary>
Byte,

/// <summary>
/// Fixed sized array of any other type, T[N].
/// </summary>
FixedArray,

/// <summary>
/// Variable sized array of any other type, T[].
/// </summary>
VariableArray,

/// <summary>
/// An alias of byte[] (variable sized array of bytes).
/// </summary>
String,

/// <summary>
/// A group of ordered abi types, (T1, T2, T3, ..., TN)
/// </summary>
Tuple,

/// <summary>
/// A fixed-length, decimal precision number, ufixedNxM.
/// </summary>
UFixed,

/// <summary>
/// A fixed-length integer, uintN.
/// </summary>
UInt,

/// <summary>
/// Used to find the count of AbiTypes - 1 (because this enum's starting index is 1).
/// </summary>
AbiTypeCount
}
}
11 changes: 11 additions & 0 deletions Runtime/CareBoo.AlgoSdk/Experimental/Abi/AbiTypeCode.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Runtime/CareBoo.AlgoSdk/Experimental/Abi/Args/ArgsArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public readonly struct ArgsArray
/// <inheritdoc />
public int Count => values?.Length ?? 0;

public IAbiValue[] Values => values;

public ArgsArray(IAbiValue[] values, int current)
{
this.values = values;
Expand Down
107 changes: 0 additions & 107 deletions Runtime/CareBoo.AlgoSdk/Experimental/Abi/AtomicTxnExtensions.Abi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,112 +5,5 @@ namespace AlgoSdk.Experimental.Abi
{
public static partial class AtomicTxnExtensions
{
/// <summary>
/// Encode and apply ABI Method arguments to an <see cref="AppCallTxn"/> then add the transaction to this group.
/// </summary>
/// <param name="atomicTxn">The atomic transaction to add a method to.</param>
/// <param name="sender">The address of the account that pays the fee and amount.</param>
/// <param name="txnParams">See <see cref="TransactionParams"/></param>
/// <param name="applicationId">ID of the application being configured.</param>
/// <param name="method">The ABI method definition.</param>
/// <param name="onComplete">Defines what additional actions occur with the transaction.</param>
/// <param name="methodArgs">The list of arguments to encode.</param>
/// <typeparam name="T">The type of arg enumerator.</typeparam>
/// <returns>An Atomic Transaction in the Building state, ready to add more transactions or build.</returns>
public static Building AddMethodCall<T>(
this Building atomicTxn,
Address sender,
TransactionParams txnParams,
AppIndex applicationId,
OnCompletion onComplete,
Abi.Method method,
in T methodArgs
)
where T : struct, IArgEnumerator<T>
{
using var methodCallBuilder = new MethodCallBuilder<T>(
sender,
txnParams,
applicationId,
method,
in methodArgs,
onComplete,
Allocator.Persistent
);
methodCallBuilder.ValidateTxnArgs(atomicTxn.Txns);
var txn = methodCallBuilder.BuildTxn();
return atomicTxn.AddTxn(txn);
}

/// <summary>
/// Encode and apply ABI Method arguments to an <see cref="AppCallTxn"/> then add the transaction to this group.
/// </summary>
/// <param name="atomicTxn">The atomic transaction to add a method to.</param>
/// <param name="sender">The address of the account that pays the fee and amount.</param>
/// <param name="txnParams">See <see cref="TransactionParams"/></param>
/// <param name="applicationId">ID of the application being configured.</param>
/// <param name="method">The ABI method definition.</param>
/// <param name="methodArgs">The list of arguments to encode.</param>
/// <typeparam name="T">The type of arg enumerator.</typeparam>
/// <returns>An Atomic Transaction in the Building state, ready to add more transactions or build.</returns>
public static Building AddMethodCall<T>(
this Building atomicTxn,
Address sender,
TransactionParams txnParams,
AppIndex applicationId,
Abi.Method method,
in T methodArgs
)
where T : struct, IArgEnumerator<T>
{
return atomicTxn.AddMethodCall(sender, txnParams, applicationId, OnCompletion.NoOp, method, in methodArgs);
}

/// <summary>
/// Encode and apply ABI Method arguments to an <see cref="AppCallTxn"/> then add the transaction to this group.
/// </summary>
/// <param name="atomicTxn">The atomic transaction to add a method to.</param>
/// <param name="sender">The address of the account that pays the fee and amount.</param>
/// <param name="txnParams">See <see cref="TransactionParams"/></param>
/// <param name="applicationId">ID of the application being configured.</param>
/// <param name="method">The ABI method definition.</param>
/// <param name="onComplete">Defines what additional actions occur with the transaction.</param>
/// <param name="methodArgs">The list of arguments to encode.</param>
/// <returns>An Atomic Transaction in the Building state, ready to add more transactions or build.</returns>
public static Building AddMethodCall(
this Building atomicTxn,
Address sender,
TransactionParams txnParams,
AppIndex applicationId,
OnCompletion onComplete,
Abi.Method method,
params IAbiValue[] methodArgsParams
)
{
var methodArgs = new ArgsArray(methodArgsParams, 0);
return atomicTxn.AddMethodCall(sender, txnParams, applicationId, onComplete, method, methodArgs);
}

/// <summary>
/// Encode and apply ABI Method arguments to an <see cref="AppCallTxn"/> then add the transaction to this group.
/// </summary>
/// <param name="atomicTxn">The atomic transaction to add a method to.</param>
/// <param name="sender">The address of the account that pays the fee and amount.</param>
/// <param name="txnParams">See <see cref="TransactionParams"/></param>
/// <param name="applicationId">ID of the application being configured.</param>
/// <param name="method">The ABI method definition.</param>
/// <param name="methodArgs">The list of arguments to encode.</param>
/// <returns>An Atomic Transaction in the Building state, ready to add more transactions or build.</returns>
public static Building AddMethodCall(
this Building atomicTxn,
Address sender,
TransactionParams txnParams,
AppIndex applicationId,
Abi.Method method,
params IAbiValue[] methodArgsParams
)
{
return atomicTxn.AddMethodCall(sender, txnParams, applicationId, OnCompletion.NoOp, method, methodArgsParams);
}
}
}
86 changes: 86 additions & 0 deletions Runtime/CareBoo.AlgoSdk/Experimental/Abi/MethodCallResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using AlgoSdk.Algod;

namespace AlgoSdk.Experimental.Abi
{
public struct MethodCallResult
{
private static readonly byte[] ReturnHash = new byte[] { 0x15, 0x1f, 0x7c, 0x75 };
public string TxnId { get; set; }

public byte[] RawValue { get; set; }

public IAbiValue ReturnValue { get; set; }

public string DecodeError { get; set; }

public PendingTransactionResponse TxnInfo { get; set; }

public Method Method { get; set; }

public MethodCallResult(
string txnId,
PendingTransactionResponse txnInfo,
Method method
)
{
TxnId = txnId;
Method = method;
TxnInfo = txnInfo;
(DecodeError, RawValue, ReturnValue) = ParseResult(txnInfo, method);
}

private static (string, byte[], IAbiValue) ParseResult(
PendingTransactionResponse txnInfo,
Method method
)
{
if (method.Returns.Type == null)
{
return (null, null, null);
}

if (txnInfo.Logs == null || txnInfo.Logs.Length == 0)
{
return ("app call transaction did not log a return value", null, null);
}

var result = txnInfo.Logs[txnInfo.Logs.Length - 1];
if (!CheckReturnHash(result))
{
return ("app call transaction did not log a return value", null, null);
}

var rawValue = new byte[result.Length - ReturnHash.Length];
Array.Copy(result, ReturnHash.Length, rawValue, 0, rawValue.Length);

try
{
var (decodeError, returnValue) = method.Returns.Type.Decode(rawValue);
return (decodeError, rawValue, returnValue);
}
catch (Exception ex)
{
return (ex.Message, rawValue, null);
}
}

private static bool CheckReturnHash(byte[] result)
{
if (result == null || result.Length < ReturnHash.Length)
{
return false;
}

for (var i = 0; i < ReturnHash.Length; i++)
{
if (result[i] != ReturnHash[i])
{
return false;
}
}

return true;
}
}
}
11 changes: 11 additions & 0 deletions Runtime/CareBoo.AlgoSdk/Experimental/Abi/MethodCallResult.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions Runtime/CareBoo.AlgoSdk/Experimental/Abi/PackedBooleans.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
namespace AlgoSdk.Experimental.Abi
{
/// <summary>
/// Utility struct to handle booleans packed in a single byte.
/// </summary>
public struct PackedBooleans
{
byte b;

public bool this[int index]
{
get
{
if (index < 0 || index > 7)
{
throw new System.ArgumentOutOfRangeException(nameof(index));
}

return (b & (1 << index)) > 0 ? true : false;
}
set
{
if (index < 0 || index > 7)
{
throw new System.ArgumentOutOfRangeException(nameof(index));
}

if (value)
{
b = (byte)(b & ~(1 << index));
}
else
{
b = (byte)(b | (1 << index));
}
}
}

public PackedBooleans(byte b)
{
this.b = b;
}

public static explicit operator byte(PackedBooleans packedBooleans)
{
return packedBooleans.b;
}

public static explicit operator PackedBooleans(byte b)
{
return new PackedBooleans(b);
}
}
}
Loading

0 comments on commit a83f1fe

Please sign in to comment.