PHP EIP712签名类 和 JS调用EIP712 方法

PHP-EIP712

安装必要的依赖

首先,通过 Composer 安装必要的依赖:

composer require kornrunner/keccak
composer require simplito/elliptic-php

编写 EIP712Signer 类

创建一个新的类 EIP712Signer,用于处理 EIP-712 签名生成。

<?php

namespace app\common\services;

use kornrunner\Keccak;
use Elliptic\EC;

class EIP712Signer
{
    private $domain;
    private $types;
    private $primaryType;

    public function __construct(array $domain, array $types, string $primaryType)
    {
        $this->domain = $domain;
        $this->types = $types;
        $this->primaryType = $primaryType;
    }

    public function sign(array $message, string $privateKey)
    {
        $domainSeparator = $this->hashStruct('EIP712Domain', $this->domain);

        $messageHash = $this->hashStruct($this->primaryType, $message);
        $dataToSign = $this->keccak256(hex2bin('1901' . $domainSeparator . $messageHash));

        $ec = new EC('secp256k1');
        $key = $ec->keyFromPrivate($privateKey);
        $signature = $key->sign($dataToSign, ['canonical' => true]);

        return [
            'r' => '0x' . $signature->r->toString(16),
            's' => '0x' . $signature->s->toString(16),
            'v' => $signature->recoveryParam + 27
        ];
    }

    private function hashStruct(string $type, array $data)
    {
        $typeHash = $this->keccak256($this->encodeType($type));
        $encodedData = $this->encodeData($type, $data);
        return $this->keccak256(hex2bin($typeHash . $encodedData));
    }

    private function encodeType(string $type)
    {
        $types = $this->types[$type];
        $encoded = $type . '(';
        $encoded .= implode(',', array_map(function ($field) {
            return $field['type'] . ' ' . $field['name'];
        }, $types));
        $encoded .= ')';

        return $encoded;
    }

    private function encodeData(string $type, array $data)
    {
        $types = $this->types[$type];
        $encodedValues = '';

        foreach ($types as $field) {
            $fieldType = $field['type'];
            $fieldName = $field['name'];
            $value = $data[$fieldName];

            if (isset($this->types[$fieldType])) {
                $encodedValues .= $this->hashStruct($fieldType, $value);
            } else {
                $encodedValues .= $this->encodeValue($fieldType, $value);
            }
        }
        return $encodedValues;
    }

    private function encodeValue($type, $value)
    {
        switch ($type) {
            case 'string':
            case 'bytes':
                return $this->keccak256($value);
            case 'uint256':
            case 'int256':
                return str_pad(dechex($value), 64, '0', STR_PAD_LEFT);
            case 'address':
                return str_pad(substr($value, 2), 64, '0', STR_PAD_LEFT);
            default:
                throw new \InvalidArgumentException("Unsupported type: $type");
        }
    }

    private function keccak256($value)
    {
         return Keccak::hash($value, 256);
    }

    private function strToHex($string)
    {
        return bin2hex($string);
    }
}

使用示例

以下是如何使用 EIP712Signer 类生成符合 EIP-712 规范的签名的示例

public function test()
    {


        // 定义域
        $domain = [
            'name' => 'ClaimDomain',
            'version' => '1',
            'verifyingContract' => '验证的合约地址'
        ];

        // 定义数据结构类型
        $types = [
            'EIP712Domain' => [
                ['name' => 'name', 'type' => 'string'],
                ['name' => 'version', 'type' => 'string'],
                ['name' => 'verifyingContract', 'type' => 'address']
            ],
            'Withdrawal' => [
                ['name' => 'txId', 'type' => 'uint256'],
                ['name' => 'account', 'type' => 'address'],
                ['name' => 'amount', 'type' => 'uint256'],
                ['name' => 'deadline', 'type' => 'uint256']
            ]
        ];

        // 要签名的数据
        $message = [
            'txId' => '订单号',
            'account' => '账户',
            'amount' => '数量', // 1 ETH in wei
            'deadline' => '到期时间'// 1 hour from now
        ];

        // 私钥(测试用,实际使用中应妥善保管)
        $privateKey = '私钥';


        $signer = new EIP712Signer($domain, $types, 'Withdrawal');
        $signature = $signer->sign($message, $privateKey);

        dd($signature);
}

JS-EIP712

node 版本

v16.20

需要的依赖 package.json

{
  "name": "eip",
  "version": "1.0.0",
  "scripts": {
    "install": "npm install"
  },
  "dependencies": {
    "@metamask/eth-sig-util": "^6.0.0",
    "ethereumjs-util": "^7.1.5"
  }
}

eip712.js 方法

const ethSigUtils = require("@metamask/eth-sig-util");
const ethUtil = require("ethereumjs-util");
const SingData = () => {
    const [privateStr, name, verifyingContract, txId, account, amount, deadline] = process.argv.slice(2);
    const signParams = {
        types: {
            EIP712Domain: [
                { name: "name", type: "string" },
                { name: "version", type: "string" },
                { name: "verifyingContract", type: "address" },
            ],
            Withdrawal: [
                { name: "txId", type: "uint256" },
                { name: "account", type: "address" },
                { name: "amount", type: "uint256" },
                { name: "deadline", type: "uint256" },
            ],
        },
        primaryType: "Withdrawal",
        domain: {
            name,
            version: "1",
            verifyingContract,
        },
        message: {
            txId,
            account,
            amount,
            deadline,
        },
    };
    // console.log(JSON.stringify(signParams))
    const hash = ethSigUtils.TypedDataUtils.eip712Hash(
        signParams,
        ethSigUtils.SignTypedDataVersion.V4
    );
    const sign = ethUtil.ecsign(hash, ethUtil.toBuffer(privateStr));
    return {
        r: "0x" + sign.r.toString("hex"),
        s: sign.s.toString("hex"),
        v: sign.v,
    };
};
console.log(JSON.stringify(SingData()));

调用方法

node index.js 签名私钥 ClaimDomain 合约地址 订单号 钱包地址 数量 时间

node index.js 0x738eb03f59351a1... ClaimDomain 0xd365d9D46c3AE4c58890dC9247... 1001010 0xb549aDa4a19f13213402F12d718b42895999C8e0 100000 1999999999

浏览器调用personal_sign 方法

await ethereum
    .request({ method: 'personal_sign',params:['123','0x0211cFFf6e6266be70AAbF023a6E0b9284B95306'] })
  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_41997115

您的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值