Skip to content

Commit

Permalink
Merge pull request #44 from dojoengine/new-typed-data
Browse files Browse the repository at this point in the history
refactor: typed data for new layout
  • Loading branch information
Larkooo authored Jun 6, 2024
2 parents 143367e + b35d0cf commit 912cd02
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 161 deletions.
45 changes: 13 additions & 32 deletions Assets/Dojo/Runtime/ModelInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,20 @@ public virtual void Initialize(Model model)
// Handles the initialization of a field
// of a model instance. Uses reflection to set the field
// to the value of the model member.
private static object HandleField(Type type, object value)
private static object HandleField(Type type, Model.Ty ty)
{
// if the field is a primitive, we can just set it
// fieldelement is included as a primitive because its a class
// but its already instantiated
if (type.IsPrimitive || type == typeof(FieldElement) || type == typeof(BigInteger) || type == typeof(string))
{
return Convert.ChangeType(value, type);
return Convert.ChangeType(ty.value, type);
}
// handle array
else if (type.IsArray)
{
var elementType = type.GetElementType();
var array = (IList)value;
var array = (IList<Model.Ty>)ty.value;
var instance = Array.CreateInstance(elementType, array.Count);
for (var i = 0; i < array.Count; i++)
{
Expand All @@ -87,18 +87,17 @@ private static object HandleField(Type type, object value)
var fields = type.GetFields();
for (var i = 0; i < fields.Length; i++)
{
fields[i].SetValue(instance, HandleField(tupleTypes[i], ((IList)value)[i]));
fields[i].SetValue(instance, HandleField(tupleTypes[i], ((IList<Model.Ty>)ty.value)[i]));
}
return instance;
}
// dynamic types
// handle record (rust-like) enums
else if (value is (string, object)) {
var (variant, member) = ((string, object))value;

var variantType = type.GetNestedType(variant);
else if (ty.value is Model.Enum enumVariant) {
var variantType = type.GetNestedType(enumVariant.option);
if (variantType == null)
{
throw new Exception($"Could not find variant {variant} in enum {type}");
throw new Exception($"Could not find variant {enumVariant.option} in enum {type}");
}

if (type.GenericTypeArguments.Length > 0) {
Expand All @@ -107,7 +106,7 @@ private static object HandleField(Type type, object value)

List<object> args = new List<object>();
if (variantType.GetProperty("value") is PropertyInfo prop) {
args.Add(HandleField(prop.PropertyType, member));
args.Add(HandleField(prop.PropertyType, enumVariant.value));
}

return Activator.CreateInstance(variantType, args.ToArray());
Expand All @@ -116,13 +115,13 @@ private static object HandleField(Type type, object value)
// if the field is a struct/class. we check if our member is a dictionary
// and we go through each of its keys and values and set them to the fields
// of the instantiated struct/class
else if (value is Dictionary<string, object> dict) {
else if (ty.value is Dictionary<string, Model.Ty> children) {
var instance = Activator.CreateInstance(type);
var fields = type.GetFields();

foreach (var field in fields)
{
field.SetValue(instance, HandleField(field.FieldType, dict[field.Name]));
field.SetValue(instance, HandleField(field.FieldType, children[field.Name]));
}

return instance;
Expand All @@ -131,27 +130,9 @@ private static object HandleField(Type type, object value)
}
}

public static Model ToModel<T>(T model) where T : ModelInstance
public TypedData ToTypedData()
{
var members = new Dictionary<string, object>();
foreach (var field in model.GetType().GetFields())
{
var attribute = field.GetCustomAttributes(typeof(ModelField), false);
if (attribute.Length == 0)
{
continue;
}

var modelField = (ModelField)attribute[0];
members.Add(modelField.Name, field.GetValue(model));
}

return new Model(model.GetType().Name, members);
}

public Model ToModel()
{
return ToModel(this);
return new TypedData(Model);
}

// Called when the model is updated
Expand Down
113 changes: 77 additions & 36 deletions Assets/Dojo/Runtime/Starknet/TypedData.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using bottlenoselabs.C2CS.Runtime;
using Codice.CM.SEIDInfo;
using Dojo.Torii;
using dojo_bindings;
using Newtonsoft.Json;
using PlasticGui;

namespace Dojo.Starknet
{
Expand All @@ -25,20 +28,20 @@ public Domain(string name, string version, string chainId)

public class TypedData
{
public Dictionary<string, object[]> types;
public Dictionary<string, TypedDataType[]> types;
public string primaryType;
public Domain domain;
public Dictionary<string, object> message;

public TypedData(Dictionary<string, object[]> types, string primaryType, Domain domain, Dictionary<string, object> message)
public TypedData(Dictionary<string, TypedDataType[]> types, string primaryType, Domain domain, Dictionary<string, object> message)
{
this.types = types;
this.primaryType = primaryType;
this.domain = domain;
this.message = message;
}

struct TypedDataType
public struct TypedDataType
{
public string name;
public string type;
Expand All @@ -52,12 +55,12 @@ public TypedDataType(string name, string type)

public TypedData(Model model)
{
types = new Dictionary<string, object[]>
types = new Dictionary<string, TypedDataType[]>
{
// starknet domain type
{
"StarknetDomain",
new object[] {
new TypedDataType[] {
new TypedDataType("name", "shortstring"),
new TypedDataType("version", "shortstring"),
new TypedDataType("chainId", "shortstring"),
Expand All @@ -67,7 +70,7 @@ public TypedData(Model model)
// primary type
{
"OffchainMessage",
new object[] {
new TypedDataType[] {
// model name
new TypedDataType("model", "shortstring"),
// model type
Expand All @@ -92,51 +95,89 @@ public TypedData(Model model)
var members = new Dictionary<string, object>();
foreach (var member in model.Members)
{
members.Add(member.Key, member.Value);
members.Add(member.Key, mapMember(member.Value));
}

message.Add(model.Name, members);
}

object[] getMembersTypes(ref Dictionary<string, object[]> types, Dictionary<string, object> members)
object mapMember(Model.Ty member)
{
var result = new List<object>();
return member.value switch
{
Model.Enum enum_ => new Dictionary<string, object>
{
{ "option", enum_.option },
{ "value", mapMember(enum_.value) }
},
Dictionary<string, Model.Ty> struct_ => struct_.Select(child => new KeyValuePair<string, object>(child.Key, mapMember(child.Value))).ToDictionary(k => k.Key, v => v.Value),
Model.Ty[] tuple => tuple.Select(mapMember).ToArray(),
List<Model.Ty> array => array.Select(mapMember).ToList(),
_ => member.value
};
}

TypedDataType[] getMembersTypes(ref Dictionary<string, TypedDataType[]> types, Dictionary<string, Model.Ty> members)
{
var result = new List<TypedDataType>();

foreach (var member in members)
{
switch (member.Value)
switch (member.Value.value)
{
case byte _:
result.Add(new TypedDataType(member.Key, "u8"));
break;
case bool _:
result.Add(new TypedDataType(member.Key, "bool"));
break;
case ushort _:
result.Add(new TypedDataType(member.Key, "u16"));
break;
case uint _:
result.Add(new TypedDataType(member.Key, "u32"));
break;
case ulong _:
result.Add(new TypedDataType(member.Key, "u64"));
break;
case BigInteger _:
result.Add(new TypedDataType(member.Key, "u128"));
case string _:
result.Add(new TypedDataType(member.Key, "string"));
break;
case FieldElement _:
result.Add(new TypedDataType(member.Key, "felt"));
var type_name = member.Value.name switch
{
"felt252" => "felt",
"contract_address" => "ContractAddress",
"class_hash" => "ClassHash",
_ => member.Value.name
};
result.Add(new TypedDataType(member.Key, type_name));
break;
case Enum _:
result.Add(new TypedDataType(member.Key, "u8"));
// Enum
// (variantName, variantValue)
case Model.Enum enum_:
var enumMembers = getMembersTypes(ref types, new Dictionary<string, Model.Ty>
{
// enum option is a bytearray / string
{ "option", new Model.Ty(dojo.Ty_Tag.ByteArray, "string", enum_.option, false) },
{ "value", enum_.value }
});
types.TryAdd(member.Value.name, enumMembers);
result.Add(new TypedDataType(member.Key, member.Value.name));
break;
case Dictionary<string, object> struct_:
case Dictionary<string, Model.Ty> struct_:
var structMembers = getMembersTypes(ref types, struct_);
types.Add(member.Key, structMembers);
result.Add(new TypedDataType(member.Key, member.Key));
// add the struct members to the types. it might already be added from
// other members
types.TryAdd(member.Value.name, structMembers);
result.Add(new TypedDataType(member.Key, member.Value.name));
break;
// tuples are arrays
case Model.Ty[] tuple:
var tupleMembers = tuple.Select((v, i) => new KeyValuePair<string, Model.Ty>(i.ToString(), v)).ToDictionary(k => k.Key, v => v.Value);
var tupleMembersTypes = getMembersTypes(ref types, tupleMembers);
var formattedTupleName = $"({string.Join(",", tupleMembersTypes.Select(t => t.type))})";

types.Add(formattedTupleName, tupleMembersTypes);
result.Add(new TypedDataType(member.Key, formattedTupleName));
break;
// should be encoded as TypeObject* for arrays
case List<Model.Ty> array:
var inner = getMembersTypes(ref types, new Dictionary<string, Model.Ty>
{
{ "inner", array[0] }
}).First();

result.Add(new TypedDataType(member.Key, member.Value.name));
break;
default:
throw new System.Exception($"Unknown type {member.Value.GetType()}");
result.Add(new TypedDataType(member.Key, member.Value.name));
break;
}

}
Expand All @@ -161,7 +202,7 @@ public FieldElement encode(FieldElement address)

public static TypedData From<T>(T model) where T : ModelInstance
{
return new TypedData(model.ToModel());
}
return new TypedData(model.Model);
}
}
}
Loading

0 comments on commit 912cd02

Please sign in to comment.