Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 101 additions & 38 deletions public/templates/macros.njk
Original file line number Diff line number Diff line change
Expand Up @@ -38,115 +38,178 @@

{# Recursive Borsh Reader, responsible for handling collections and any nested levels #}
{% macro borshReader(field) %}
{{ recursiveReader(field.nesting, field.baseType, field.isStruct) }}
{{ recursiveReader(field.nesting, field.baseType, field.isStruct, field.size, field.format) }}
{% endmacro %}

{% macro recursiveReader(depth, baseType, isStruct) %}
{% if depth == 0 %}
{% macro recursiveReader(depth, baseType, isStruct, size, format) %}
{% if depth > 5 %}
// ERROR: Nesting depth exceeds supported limit (5)
throw Exception('Nesting depth exceeds supported limit(5)');
{% elif depth == 0 %}
{% if isStruct %}
{{ baseType }}Borsh.fromBorsh(reader)
{% else %}
{{ baseTypeReader(baseType) }}
{{ baseTypeReader(baseType, false, size, format) }}
{% endif %}
{% else %}
reader.readArray(() {
{% if isStruct %}
// item is a struct, call fromBorsh per item
return {{ baseType }}Borsh.fromBorsh(reader);
{% else %}
return {{ recursiveReader(depth - 1, baseType, false) }};
return {{ recursiveReader(depth - 1, baseType, false, size, format) }};
{% endif %}
})
{% endif %}
{% endmacro %}

{% macro intFormatUtil(format, isWrite, varName='value') %}
{% if format == 'u8' %}
{% if isWrite %}
writer.writeU8({{ varName }})
{% else %}
reader.readU8()
{% endif %}
{% elif format == 'i8' %}
{% if isWrite %}
writer.writeI8({{ varName }})
{% else %}
reader.readI8()
{% endif %}
{% elif format == 'u16' %}
{% if isWrite %}
writer.writeU16({{ varName }})
{% else %}
reader.readU16()
{% endif %}
{% elif format == 'i16' %}
{% if isWrite %}
writer.writeI16({{ varName }})
{% else %}
reader.readI16()
{% endif %}
{% elif format == 'u32' %}
{% if isWrite %}
writer.writeU32({{ varName }})
{% else %}
reader.readU32()
{% endif %}
{% elif format == 'i32' %}
{% if isWrite %}
writer.writeI32({{ varName }})
{% else %}
reader.readI32()
{% endif %}
{% elif format == 'u64' %}
{% if isWrite %}
writer.writeU64({{ varName }})
{% else %}
reader.readU64()
{% endif %}
{% elif format == 'i64' %}
{% if isWrite %}
writer.writeI64({{ varName }})
{% else %}
reader.readI64()
{% endif %}
{% elif format == 'u128' or format == 'i128' %}
{% if isWrite %}
writer.writeBigInt({{ varName }})
{% else %}
reader.readBigInt()
{% endif %}
{% else %}
throw Exception('Unsupported number format: {{ format }}');
{% endif %}
{% endmacro %}

{# Reader for the base types (no nesting) #}
{% macro baseTypeReader(baseType, isStruct) %}
{% if baseType == 'int' %}
reader.readInt()
{% elif baseType == 'BigInt' %}
reader.readBigInt()
{% macro baseTypeReader(baseType, isStruct, size, format) %}
{% if baseType == 'int' or baseType == 'BigInt' %}
{{ intFormatUtil(format, false) }}
{% elif baseType == 'String' %}
reader.readString()
{% elif baseType == 'Uint8List' %}
reader.readFixedU8Array({{ field.size or 8}})
reader.readU8Array({{ size if size is defined else '' }})
{% elif baseType == 'Int8List' %}
reader.readFixedI8Array({{ field.size or 8}})
reader.readI8Array({{ size if size is defined else '' }})
{% elif baseType == 'Uint16List' %}
reader.readFixedU16Array({{ field.size or 16 }})
reader.readU16Array({{ size if size is defined else '' }})
{% elif baseType == 'Int16List' %}
reader.readFixedI16Array({{ field.size or 16}})
reader.readI16Array({{ size if size is defined else '' }})
{% elif baseType == 'Uint32List' %}
reader.readFixedU32Array({{ field.size or 32}})
reader.readU32Array({{ size if size is defined else '' }})
{% elif baseType == 'Int32List' %}
reader.readFixedI32Array({{ field.size or 32}})
reader.readI32Array({{ size if size is defined else '' }})
{% elif baseType == 'Uint64List' %}
reader.readFixedU64Array({{ field.size or 64}})
reader.readU64Array({{ size if size is defined else '' }})
{% elif baseType == 'Int64List' %}
reader.readFixedI64Array({{ field.size or 64 }})
reader.readI64Array({{ size if size is defined else '' }})
{% elif baseType == 'bool' %}
reader.readBool()
{% elif baseType == 'Ed25519HDPublicKey' %}
Ed25519HDPublicKey(Uint8List.fromList(reader.readFixedU8Array(32)))
Ed25519HDPublicKey(reader.readPubkey())
{% else %}
/// TODO: I need to provide panic or error guard to indicate that user is doing something that is not supported
reader.readUnknownType('{{ baseType }}')
{% endif %}
{% endmacro %}

{# Recursive Borsh Writer, responsible to handle nested type of collections #}
{% macro borshWriter(field, overrideFieldName="") %}
{% set name = overrideFieldName if overrideFieldName else field.name %}
{{ recursiveWriter(name, field.nesting, field.baseType, field.isStruct) }}
{{ recursiveWriter(name, field.nesting, field.baseType, field.isStruct, field.size, field.format) }}
{% endmacro %}

{% macro recursiveWriter(varName, depth, baseType, isStruct) %}
{% if depth == 0 %}
{% macro recursiveWriter(varName, depth, baseType, isStruct, size, format) %}
{% if depth > 5 %}
// ERROR: Nesting depth exceeds supported limit (5)
throw Exception('Nesting depth exceeds supported limit(5)');
{% elif depth == 0 %}
{% if isStruct %}
{{ varName }}.toBorsh(writer);
{% else %}
{# TODO: I have problem here because of these recursions i put ';' twice because twice is iterated here first trough writeArray and on the recursion i go inside here and i put ';' again #}
{{ baseTypeWriter(baseType, varName) }};
{{ baseTypeWriter(baseType, varName, size, format) }};
{% endif %}
{% else %}
writer.writeArray<{{ baseType }}>({{ varName }}, ({{ baseType }} item) {
{% if isStruct %}
// Each item is a struct
item.toBorsh(writer);
{% else %}
{{ recursiveWriter("item", depth - 1, baseType, false) }};
{{ recursiveWriter("item", depth - 1, baseType, false, size, format) }};
{% endif %}
});
{% endif %}
{% endmacro %}

{# Base Writer, no nested types inside #}
{% macro baseTypeWriter(baseType, varName, isStruct) %}
{% if baseType == 'int' %}
writer.writeInt({{ varName }})
{% elif baseType == 'BigInt' %}
writer.writeBigInt({{ varName }})
{% macro baseTypeWriter(baseType, varName, size, format) %}
{% if baseType == 'int' or baseType == 'BigInt' %}
{{ intFormatUtil(format, true, varName) }}
{% elif baseType == 'String' %}
writer.writeString({{ varName }})
{% elif baseType == 'Uint8List' %}
writer.writeFixedU8Array({{ varName }})
writer.writeU8Array({{ varName }}, {{ size if size is defined else 'null' }})
{% elif baseType == 'Int8List' %}
writer.writeFixedI8Array({{ varName }})
writer.writeI8Array({{ varName }}, {{ size if size is defined else 'null' }})
{% elif baseType == 'Uint16List' %}
writer.writeFixedU16Array({{ varName }})
writer.writeU16Array({{ varName }}, {{ size if size is defined else 'null' }})
{% elif baseType == 'Int16List' %}
writer.writeFixedI16Array({{ varName }})
writer.writeI16Array({{ varName }}, {{ size if size is defined else 'null' }})
{% elif baseType == 'Uint32List' %}
writer.writeFixedU32Array({{ varName }})
writer.writeU32Array({{ varName }}, {{ size if size is defined else 'null' }})
{% elif baseType == 'Int32List' %}
writer.writeFixedI32Array({{ varName }})
writer.writeI32Array({{ varName }}, {{ size if size is defined else 'null' }})
{% elif baseType == 'Uint64List' %}
writer.writeFixedU64Array({{ varName }})
writer.writeU64Array({{ varName }}, {{ size if size is defined else 'null' }})
{% elif baseType == 'Int64List' %}
writer.writeFixedI64Array({{ varName }})
writer.writeI64Array({{ varName }}, {{ size if size is defined else 'null' }})
{% elif baseType == 'bool' %}
writer.writeBool({{ varName }})
{% elif baseType == 'Ed25519HDPublicKey' %}
writer.writeFixedU8Array(Uint8List.fromList({{ varName }}.bytes))
writer.writePubkey(Uint8List.fromList({{ varName }}.bytes))
{% else %}
writer.writeUnknownType('{{ baseType }}')
{% endif %}
Expand Down
10 changes: 7 additions & 3 deletions public/templates/pages/accountsPage.njk
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void toBorsh(BinaryWriter writer) {

{% if account.discriminator %}
// Validate the discriminator
final discriminator = reader.readFixedU8Array(8);
final discriminator = reader.readDiscriminator();
if (!const ListEquality().equals(discriminator, {{ account.name | snakeCase | upper }}_DISCRIMINATOR)) {
throw FormatException('Invalid account discriminator');
}
Expand All @@ -82,7 +82,7 @@ void toBorsh(BinaryWriter writer) {

{% if account.discriminator %}
// Write discriminator
writer.writeFixedU8Array(Uint8List.fromList({{ account.name | snakeCase | upper }}_DISCRIMINATOR));
writer.writeDiscriminator(Uint8List.fromList({{ account.name | snakeCase | upper }}_DISCRIMINATOR));
{% endif %}

// Write account data
Expand Down Expand Up @@ -129,7 +129,11 @@ void toBorsh(BinaryWriter writer) {
RpcClient client,
Ed25519HDPublicKey address,
) async {
final accountInfo = await client.getAccountInfo(address.toBase58());
{# Always use encoding: Encoding.base64 when fetching account data. #}
{# Base58 is meant for public keys, not raw binary, and will produce the wrong bytes. #}
{# Base64 ensures we get the exact raw on-chain bytes (including the correct discriminator). #}

final accountInfo = await client.getAccountInfo(address.toBase58(), encoding: Encoding.base64);
final data = accountInfo.value?.data;

if (data == null) {
Expand Down
Loading