ethers-rs: eip712 compatibility problem between ethers-js and ethers-rs

Version

├── ethers v0.5.4 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   ├── ethers-contract v0.5.3 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   ├── ethers-contract-abigen v0.5.3 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   │   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   ├── ethers-contract-derive v0.5.3 (proc-macro) (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   │   ├── ethers-contract-abigen v0.5.3 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   │   │   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   │   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   │   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   │   ├── ethers-derive-eip712 v0.1.0 (proc-macro) (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   │   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   │   ├── ethers-providers v0.5.4 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   │   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   ├── ethers-middleware v0.5.3 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   ├── ethers-contract v0.5.3 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   │   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   │   ├── ethers-providers v0.5.4 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   │   ├── ethers-signers v0.5.3 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│   │   │   ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   ├── ethers-providers v0.5.4 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   ├── ethers-signers v0.5.3 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)
│   └── ethers-solc v0.1.0 (https://github.com/gakonst/ethers-rs.git#ec00325a)
│       ├── ethers-core v0.5.5 (https://github.com/gakonst/ethers-rs.git#ec00325a) (*)

Platform windows 11

Description use the etheris sign a typed data and then copy the data & signature to rust side, it verified failed

the js code

const {Wallet, ethers} = require("ethers");
const crypto = require('crypto');

async function sign_eip712() {
    const mnemonic = "announce room limb pattern dry unit scale effort smooth jazz weasel alcohol"
    const walletMnemonic = Wallet.fromMnemonic(mnemonic)


    const domain = {
        name: 'Relationship dApp',
        version: '1',
        chainId: 1,
        verifyingContract: '0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc',
        //salt: 'aBrqX434MenBV2yBhAf5oD7VrqbSLW3p'
    };

    const types = {
        Receipt : [
            { name: 'address', type: 'string' },
            { name: 'nonce', type: 'string' },
            { name: 'timestamp', type: 'uint256' }
        ]
    };

    const value = {
        address: walletMnemonic.address,
        nonce: crypto.randomBytes(16).toString('base64'),
        timestamp: Math.floor(Date.now() / 1000)
    };

    const rawSignature = await walletMnemonic._signTypedData(domain, types, value);
    const signature = ethers.utils.splitSignature(rawSignature);
    console.log(JSON.stringify({
        address: value.address,
        nonce: value.nonce,
        timestamp: value.timestamp,
        r: signature.r,
        s: signature.s,
        v: signature.v
    }));
}

sign_eip712().then(_ => {})

rust code:

use chrono::{Duration, TimeZone, Utc};
use ethers::types::U256;
use ethers::{
    contract::{Eip712, EthAbiType},
    core::types::transaction::eip712::Eip712,
    types::{Address, Signature},
    utils::hex,
};
use serde::{Deserialize, Deserializer};

fn deserialize_u256_from_i64<'de, D>(deserializer: D) -> Result<U256, D::Error>
where
    D: Deserializer<'de>,
{
    i64::deserialize(deserializer).map(U256::from)
}

#[derive(Clone, Debug, Deserialize, Eip712, EthAbiType)]
#[eip712(
    name = "Relationship dApp",
    version = "1",
    chain_id = 1,
    verifying_contract = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"
)]
pub struct Receipt {
    pub address: Address,
    pub nonce: String,
    #[serde(deserialize_with = "deserialize_u256_from_i64")]
    pub timestamp: U256,
}

impl Receipt {
    pub fn is_expired(&self, expired: Duration) -> bool {
        let datetime = Utc.timestamp(self.timestamp.try_into().unwrap(), 0);
        (Utc::now() - datetime) > expired
    }

    pub fn verify(&self, signature: &Signature) -> anyhow::Result<()> {
        self.encode_eip712()
            .map_err(anyhow::Error::from)
            .and_then(|receipt_hash| {
                signature
                    .verify(receipt_hash, self.address)
                    .map_err(anyhow::Error::from)
            })
            .map_err(Into::into)
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15 (15 by maintainers)

Most upvoted comments

@Ryanmtate @sebastinez do you folks mind taking a look, given you implemented this feature?

So to make sure we understand, it should be “{ name: ‘address’, type: ‘address’ }”?

Thanks, don’t have time to review deeply right now. Away for the holiday. On the surface everything looks fine. Might suggest testing signature against a third library (e.g. https://www.npmjs.com/package/eip-712) to confirm the problem is on this implementation.

Also might be worth creating a solidity contract to test against.

Will try to find time to dig in deeper over the weekend.

Can you try to uppercase the receipt type to Receipt on the JS code and see if that works? I believe it should be uppercased. i have tried before, it still not worked…,

@Matrix-Zhang Can you check if the timestamps match?

yes, sure, it matches…

@Ryanmtate @sebastinez do you folks mind taking a look, given you implemented this feature?

Hey @gakonst we’ll look into it 👍