completium / completium-cli

MIT License
6 stars 3 forks source link

Binding generated for events with more than two outputs causes a type error #48

Open grum-tez opened 2 weeks ago

grum-tez commented 2 weeks ago

This .arl:

entry register_challenger(fighter_id_requested : nat) {
  require {
    register_challenger_r1: fighters_map.contains(fighter_id_requested) otherwise "There is no fighter with the requested id.";
  }
  effect {
  challengers_big_map.add_update( caller, {
    chosen_fighter_id = fighter_id_requested
  });
  const this_new_challenger_record_option = challengers_big_map[caller];
  const this_new_challenger_record ?=  this_new_challenger_record_option : ("That challenger has not been registered");

  const f_id: nat = this_new_challenger_record.chosen_fighter_id;
  const c_fh: list<fight_record> = this_new_challenger_record.fightHistory;
  const c_fc: nat =  this_new_challenger_record.fightCount;

  emit<new_challenger_registered>({
    caller
    ;f_id
    ;c_fh
    ;c_fc
    })
  }

}
event new_challenger_registered {
  new_challenger_address: address
  ;new_challenger_fighter_id: nat
  ;new_challenger_fight_history: list<fight_record>
  ;new_challenger_fight_count: nat
}

Produces this class which fails with a type error testing in mockup or in the browser:

export class new_challenger_registered implements att.ArchetypeType {
    constructor(public new_challenger_address: att.Address, public new_challenger_fighter_id: att.Nat, public new_challenger_fight_history: Array<fight_record>, public new_challenger_fight_count: att.Nat) { }
    toString(): string {
        return JSON.stringify(this, null, 2);
    }
    to_mich(): att.Micheline {
        return att.pair_to_mich([this.new_challenger_address.to_mich(), this.new_challenger_fighter_id.to_mich(), att.list_to_mich(this.new_challenger_fight_history, x => {
                return x.to_mich();
            }), this.new_challenger_fight_count.to_mich()]);
    }
    equals(v: new_challenger_registered): boolean {
        return att.micheline_equals(this.to_mich(), v.to_mich());
    }
    static from_mich(input: att.Micheline): new_challenger_registered {
        return new new_challenger_registered(att.Address.from_mich((input as att.Mpair).args[0]), att.Nat.from_mich((input as att.Mpair).args[1]), att.mich_to_list((input as att.Mpair).args[2], x => { return fight_record.from_mich(x); }), att.Nat.from_mich((input as att.Mpair).args[3]));
    }
}

Updating the from_mich static to this code (generated with LLM) resolves the error:

 static from_mich(input: att.Micheline): new_challenger_registered {
        console.log("from_mich input: ", input);

        // Ensure the input is an array
        if (!Array.isArray(input)) {
            throw new Error("Invalid Micheline input, expected an array: " + JSON.stringify(input));
        }

        // Ensure the array has the expected length
        if (input.length !== 4) {
            throw new Error("Invalid Micheline input, expected array length of 4: " + JSON.stringify(input));
        }

        const new_challenger_address = att.Address.from_mich(input[0]);
        const new_challenger_fighter_id = att.Nat.from_mich(input[1]);
        const new_challenger_fight_history = att.mich_to_list(input[2], x => fight_record.from_mich(x));
        const new_challenger_fight_count = att.Nat.from_mich(input[3]);

        return new new_challenger_registered(
            new_challenger_address, 
            new_challenger_fighter_id, 
            new_challenger_fight_history, 
            new_challenger_fight_count
        );
    }

The error does not appear if only one or two variables are emitted.

When investigating this previously I noticed that the format of the emitted event data changes when more than 3 variables are output.

Error in browser:

battlemaster.ts:18 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '0')
    at new_challenger_registered.from_mich (battlemaster.ts:18:89)
    at battlemaster.ts:235:58
    at Object.process (battlemaster.ts:236:21)
    at @completium_event-listener.js?v=cf415e32:2513:15
    at Array.forEach (<anonymous>)
    at @completium_event-listener.js?v=cf415e32:2512:14
    at Generator.next (<anonymous>)
    at fulfilled (@completium_event-listener.js?v=cf415e32:2417:28)

Line 18 is the static from_mich

grum-tez commented 1 week ago

from https://tezos.stackexchange.com/questions/6383/events-and-emit-archetype-issue-typeerror-cannot-read-properties-of-undefined

EDIT + HACKY FIX: The problem, as best I can tell, is that the from_mich static in the new_challenger_registered class in the dapp bindings expects an Mpair but recieves an Array of micheline objects in the form of Mstring, Mint etc (see the achetype types library). My current workaround is to edit the event classes directly in the bindings so if they receieve an array of this type the process it correctly, until this is addressed.

Example of a working class in the bindings:

export class new_challenger_registered implements att.ArchetypeType {
    constructor(public new_challenger_address: att.Address, public new_challenger_fighter_id: att.Nat, public new_challenger_fight_history: Array<fight_record>, public new_challenger_fight_count: att.Nat, public new_challenger_c_mode: att.Nat) { }
    toString(): string {
        return JSON.stringify(this, null, 2);
    }
    to_mich(): att.Micheline {
        return att.pair_to_mich([this.new_challenger_address.to_mich(), this.new_challenger_fighter_id.to_mich(), att.list_to_mich(this.new_challenger_fight_history, x => {
                return x.to_mich();
            }), this.new_challenger_fight_count.to_mich(), this.new_challenger_c_mode.to_mich()]);
    }
    equals(v: new_challenger_registered): boolean {
        return att.micheline_equals(this.to_mich(), v.to_mich());
    }
    static from_mich(input: att.Micheline): new_challenger_registered {
        let args: att.Micheline[];
        if (Array.isArray(input)) {
            args = input;
        } else {
            args = (input as att.Mpair).args;
        }

        const new_challenger_address = att.Address.from_mich(args[0]);
        const new_challenger_fighter_id = att.Nat.from_mich(args[1]);
        const new_challenger_fight_history = att.mich_to_list(args[2], x => fight_record.from_mich(x));
        const new_challenger_fight_count = att.Nat.from_mich(args[3]);
        const new_challenger_c_mode = att.Nat.from_mich(args[4]);

        return new new_challenger_registered(
            new_challenger_address,
            new_challenger_fighter_id,
            new_challenger_fight_history,
            new_challenger_fight_count,
            new_challenger_c_mode
        );
    }
}