Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eip712 signtypeddata do not support Array? #77

Open
yonghumeijj opened this issue Nov 4, 2024 · 4 comments
Open

eip712 signtypeddata do not support Array? #77

yonghumeijj opened this issue Nov 4, 2024 · 4 comments

Comments

@yonghumeijj
Copy link

like this

export default {
  domain: {
    // Defining the chain aka Rinkeby testnet or Ethereum Main Net
    chainId: 1,
    // Give a user friendly name to the specific contract you are signing for.
    name: "Ether Mail",
    // If name isn't enough add verifying contract to make sure you are establishing contracts with the proper entity
    verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
    // Just let's you know the latest version. Definitely make sure the field name is correct.
    version: "1"
  },

  // Defining the message signing data content.
  message: {
    /*
     - Anything you want. Just a JSON Blob that encodes the data you want to send
     - No required fields
     - This is DApp Specific
     - Be as explicit as possible when building out the message schema.
    */
    contents: "Hello, Bob!",
    attachedMoneyInEth: 4.2,
    from: {
      name: "Cow",
      wallets: [
        "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
        "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
      ]
    },
    to: [
      {
        name: "Bob",
        wallets: [
          "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
          "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57",
          "0xB0B0b0b0b0b0B000000000000000000000000000"
        ]
      }
    ]
  },
  // Refers to the keys of the *types* object below.
  primaryType: "Mail",
  types: {
    // TODO: Clarify if EIP712Domain refers to the domain the contract is hosted on
    EIP712Domain: [
      { name: "name", type: "string" },
      { name: "version", type: "string" },
      { name: "chainId", type: "uint256" },
      { name: "verifyingContract", type: "address" }
    ],
    // Not an EIP712Domain definition
    Group: [
      { name: "name", type: "string" },
      { name: "members", type: "Person[]" }
    ],
    // Refer to PrimaryType
    Mail: [
      { name: "from", type: "Person" },
      { name: "to", type: "Person[]" },
      { name: "contents", type: "string" }
    ],
    // Not an EIP712Domain definition
    Person: [
      { name: "name", type: "string" },
      { name: "wallets", type: "address[]" }
    ]
  }
};

online test https://codesandbox.io/p/sandbox/eth-signtypeddata-v4-335wo?file=%2Fsrc%2FtypedDataV4.js%3A1%2C1-69%2C1

MyTestCode:

procedure TForm12.Button6Click(Sender: TObject);
begin
  const typedData = TTypedData.Create;
  try
    typedData.types.Add('Person',
    [
    TType.Create('name', 'string'),
    TType.Create('wallet', 'address[]')
    ]);

    typedData.types.Add('Group',
    [
    TType.Create('name', 'string'),
    TType.Create('members', 'Person[]')
    ]);

    typedData.types.Add('Mail',
    [
    TType.Create('from', 'Person'),
    TType.Create('to', 'Person[]'),
    TType.Create('contents', 'string')
    ]);

    typedData.PrimaryType := 'Mail';

    typedData.Domain.Name := 'Ether Mail';
    typedData.Domain.Version := '1';
    typedData.Domain.ChainId := 1;
    typedData.Domain.VerifyingContract := '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC';

    typedData.Message.Add('from', newTypedMessage.Add('name', 'Cow').Add('wallet', VarArrayOf(['0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF'])));
    typedData.Message.Add('to', newTypedMessage.Add('name', 'Bob').Add('wallet', VarArrayOf(['0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', '0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57', '0xB0B0b0b0b0b0B000000000000000000000000000'])));
    typedData.Message.Add('contents', 'Hello, Bob!');
    typedData.Message.Add('attachedMoneyInEth', 4.2);

    const challengeHash = typedData.challengeHash;
    if challengeHash.isErr then
      memo1.Lines.Add(challengeHash.Error.Message)
    else
    begin
      if web3.utils.toHex(challengeHash.Value) = '0xBE609AEE343FB3C4B28E1DF9E632FCA64FCFAEDE20F02E86244EFDDF30957BD2' then
      begin
        memo1.Lines.Add('Equal');
      end
      else
      begin
        memo1.Lines.Add(web3.utils.toHex(challengeHash.Value));
      end;
    end;

    const signature = web3.eth.eip712.sign(TPrivateKey('8994250F00DB3D85A260D8FDCF2152063938C1F57005928AA7B964197BCC8830'), challengeHash.Value);
    if signature.isErr then
      memo1.Lines.Add(signature.Error.Message)
    else
    begin
      if '0xF714D2CD123498A5551CAFEE538D073C139C5C237C2D0A98937A5CCE109BFEFB7C6585FED974543C649B0CAE34AC8763EE0AC536A56A82980C14470F0029907B1B' = signature.Value.toHex then
      begin
        memo1.Lines.Add('Equal');
      end
      else
      begin
        memo1.Lines.Add(web3.utils.toHex(challengeHash.Value));
      end;
    end;

  finally
    typedData.Free;
  end;
end;

will couse error

there is extra data provided in the message (3 < 4)

comment typedData.Message.Add('attachedMoneyInEth', 4.2); can work but the result is wrong

@PieterValentijn
Copy link
Contributor

Ok i reviewed this and have the folowing conclusion.
im not sure why attachedMoneyInEth is not part of the EIP712 type but if we want it in the message it will needs tobe defined. Now maby we dont want to encode it as the typedata.
We could edit
function TTypedData.EncodeType(const primaryType: string): TBytes;
....
for var obj in Self.Types[dep] do
begin
if obj.Name <> 'attachedMoneyInEth' then
begin
buffer.Append(obj.&Type);
buffer.Append(' ');
buffer.Append(obj.Name);
buffer.Append(',')
end;
end;

so that is not encoded in the TypeHash.
The bigger problem is in
function TTypedData.EncodeData(const messagelist :TStrings;

where if encType.EndsWith(']') then

  Result := TResult<TBytes>.Err([], TNotImplemented.Create) // ToDo: implement

To bad it did not raise an exception.
So yes at the moment its unsuported.

i gave it a little try but that does not give the right result.
if encType.EndsWith(']') then
begin
if VarIsArray(encValue) then
begin
for var i := VarArrayLowBound(encValue,1) to VarArrayHighBound(encValue,1) do
begin
const byteValue = Self.EncodePrimitiveValue(copy(encType,1,length(encType)-2), encValue[i], depth);
if byteValue.IsErr then
begin
Result := TResult.Err([], byteValue.Error);
EXIT;
end;
buffer := buffer + byteValue.Value;
end;
end;

end

@yonghumeijj
Copy link
Author

Ok i reviewed this and have the folowing conclusion. im not sure why attachedMoneyInEth is not part of the EIP712 type but if we want it in the message it will needs tobe defined. Now maby we dont want to encode it as the typedata. We could edit function TTypedData.EncodeType(const primaryType: string): TBytes; .... for var obj in Self.Types[dep] do begin if obj.Name <> 'attachedMoneyInEth' then begin buffer.Append(obj.&Type); buffer.Append(' '); buffer.Append(obj.Name); buffer.Append(',') end; end;

so that is not encoded in the TypeHash. The bigger problem is in function TTypedData.EncodeData(const messagelist :TStrings;

where if encType.EndsWith(']') then

  Result := TResult<TBytes>.Err([], TNotImplemented.Create) // ToDo: implement

To bad it did not raise an exception. So yes at the moment its unsuported.

i gave it a little try but that does not give the right result. if encType.EndsWith(']') then begin if VarIsArray(encValue) then begin for var i := VarArrayLowBound(encValue,1) to VarArrayHighBound(encValue,1) do begin const byteValue = Self.EncodePrimitiveValue(copy(encType,1,length(encType)-2), encValue[i], depth); if byteValue.IsErr then begin Result := TResult.Err([], byteValue.Error); EXIT; end; buffer := buffer + byteValue.Value; end; end;

end

can update to support?

@PieterValentijn
Copy link
Contributor

i am trying this is my solution
if encType.EndsWith(']') then
begin
if VarIsArray(encValue) then
begin
for var i := VarArrayLowBound(encValue,1) to VarArrayHighBound(encValue,1) do
begin

        if vartype(encValue[i]) = varUnknown then
          begin
          CONST AMSG = IInterface(encValue[i]) as ITypedMessage ;
          const encodedData = Self.EncodeData(messagelist, copy(encType,1,length(encType)-2) , AMSG , depth + 1, Validate);
          if encodedData.IsErr then
          begin
            Result := TResult<TBytes>.Err([], encodedData.error);
            EXIT;
          end;
          buffer := buffer + web3.utils.sha3(encodedData.Value);
        end else
        begin
          const byteValue = Self.EncodePrimitiveValue(messagelist,copy(encType,1,length(encType)-2), encValue[i], depth);
          if byteValue.IsErr then
          begin
            Result := TResult<TBytes>.Err([], byteValue.Error);
            EXIT;
          end;
            buffer := buffer + byteValue.Value;
        end;
    end;

   end ;
end

But your test case is not usable due to the fact your attachedMoneyInEth has no type nor can i find documentation.

If used this test cases
https://github.com/ethereum/go-ethereum/blob/master/signer/core/apitypes/testdata/typed-data.json

But it does not work for arrays im doing something wrong but im not sure what.

@PieterValentijn
Copy link
Contributor

Right i got it working for examples with Arrays. (Bytes )

`if encType.EndsWith(']') then
begin
var Arraybuffer: TBytes ;
if VarIsArray(encValue) then
begin
for var i := VarArrayLowBound(encValue, 1) to VarArrayHighBound(encValue, 1) do
begin

        if VarType(encValue[i]) = varUnknown then
        begin
          CONST
            AMSG = IInterface(encValue[i]) as ITypedMessage;
          const
            encodedData = Self.EncodeData(messagelist, copy(encType, 1, Length(encType) - 2), AMSG, depth + 1,
              Validate);
          if encodedData.IsErr then
          begin
            Result := TResult<TBytes>.Err([], encodedData.error);
            EXIT;
          end;
          Arraybuffer := Arraybuffer + web3.utils.sha3(encodedData.Value);
        end
        else
        begin
          const
            byteValue = Self.EncodePrimitiveValue(messagelist, copy(encType, 1, Length(encType) - 2),
              encValue[i], depth);
          if byteValue.IsErr then
          begin
            Result := TResult<TBytes>.Err([], byteValue.error);
            EXIT;
          end;
          Arraybuffer := Arraybuffer + byteValue.Value;
        end;
      end;

    end;
    buffer := buffer +  web3.utils.sha3(Arraybuffer);
  end`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants