当前位置:   article > 正文

以太坊签名,验证签名, EIP712domain Permit授权并转账_以太坊验签permit方法失败

以太坊验签permit方法失败


需求

  • dapp 签名/验签登录 主要针对中心化接口鉴权;小狐狸签名时最好能让用户看到签名内容
  • 学习EIP712Domain

一、Dapp 验签登录

参考链接
第二十九课 如何实现MetaMask签名授权后DAPP一键登录功能?
以太坊签名数据以及验证

两种签名
1、直接对内容签名(小狐狸可以看到hello)

web3.personal.sign(web3.fromUtf8("hello"));
  • 1

2、对内容sha3后签名(小狐狸看到的是一串hash,没法看到hello)

web3.personal.sign(web3.utils.sha3("hello"));
  • 1

可以通过ecRecover 验证签名

web3.eth.personal.ecRecover(signtxt,sig)
  • 1

上面两种签名,

  • 第一种的签名结果,在合约中ecRecover会验证失败,
  • 第二种可以,但是小狐狸签名时看不到内容

如果外部验签(比如后台),使用 personal.ecRecover 需要节点支持,开放了personal, infura就没开放,所以无法使用

后面同事发现了方法,可以用infura节点验签

web3.eth.accounts.recover(signtxt,sig)
  • 1

web3.eth.accounts.recover(signtxt,sig) 具体实现过程

源码 https://github.com/ChainSafe/web3.js/tree/1.x/packages

web3-eth-accounts

//上面调用时两个参数,所以preFixed是false
//再直接最后的 Account.recover(message, signature);
Accounts.prototype.recover = function recover(message, signature, preFixed) {
    var args = [].slice.apply(arguments);


    if (_.isObject(message)) {
        return this.recover(message.messageHash, Account.encodeSignature([message.v, message.r, message.s]), true);
    }

    if (!preFixed) {
        message = this.hashMessage(message);
    }

    if (args.length >= 4) {
        preFixed = args.slice(-1)[0];
        preFixed = _.isBoolean(preFixed) ? !!preFixed : false;

        return this.recover(message, Account.encodeSignature(args.slice(1, 4)), preFixed); // v, r, s
    }
    return Account.recover(message, signature);
};

//内部有加前缀!
Accounts.prototype.hashMessage = function hashMessage(data) {
    var messageHex = utils.isHexStrict(data) ? data : utils.utf8ToHex(data);
    var messageBytes = utils.hexToBytes(messageHex);
    var messageBuffer = Buffer.from(messageBytes);
    var preamble = '\x19Ethereum Signed Message:\n' + messageBytes.length;
    var preambleBuffer = Buffer.from(preamble);
    var ethMessage = Buffer.concat([preambleBuffer, messageBuffer]);
    return Hash.keccak256s(ethMessage);
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

Account.recover(message, signature);
源码 https://github.com/maiavictor/eth-lib

const recover = (hash, signature) => {
  const vals = decodeSignature(signature);
  const vrs = { v: Bytes.toNumber(vals[0]), r: vals[1].slice(2), s: vals[2].slice(2) };
  const ecPublicKey = secp256k1.recoverPubKey(new Buffer(hash.slice(2), "hex"), vrs, vrs.v < 2 ? vrs.v : 1 - vrs.v % 2); // because odd vals mean v=0... sadly that means v=0 means v=1... I hate that
  const publicKey = "0x" + ecPublicKey.encode("hex", false).slice(2);
  const publicHash = keccak256(publicKey);
  const address = toChecksum("0x" + publicHash.slice(-40));
  return address;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

如有需要node中测试,可以将上面代码(hashMessage/recover)直接扣出来用即可 下面是相关导包
需要两个库

  • npm i web3-utils
  • npm i eth-lib
const utils = require('web3-utils');
const Hash = require('eth-lib/lib/hash');
const Bytes = require("eth-lib/lib/bytes");
const decodeSignature = hex => [Bytes.slice(64, Bytes.length(hex), hex), Bytes.slice(0, 32, hex), Bytes.slice(32, 64, hex)];
const elliptic = require("elliptic");
const secp256k1 = new elliptic.ec("secp256k1");
const { keccak256, keccak256s } = require("eth-lib/lib/hash");
const toChecksum = address => {
    const addressHash = keccak256s(address.slice(2));
    let checksumAddress = "0x";
    for (let i = 0; i < 40; i++) checksumAddress += parseInt(addressHash[i + 2], 16) > 7 ? address[i + 2].toUpperCase() : address[i + 2];
    return checksumAddress;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

golang的实现

参考 以太坊go-ethereum签名部分源码解析 https://blog.csdn.net/weixin_30407613/article/details/99244163

func verifySig(from, sigHex string, msg []byte) bool {
	fromAddr := common.HexToAddress(from)

	sig := hexutil.MustDecode(sigHex)
	if sig[64] != 27 && sig[64] != 28 {
		return false
	}
	sig[64] -= 27

	pubKey, err := crypto.SigToPub(signHash(msg), sig)
	if err != nil {
		return false
	}

	recoveredAddr := crypto.PubkeyToAddress(*pubKey)
	fmt.Println("addr: ", recoveredAddr)
	return fromAddr == recoveredAddr
}

func signHash(data []byte) []byte {
	msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
	return crypto.Keccak256([]byte(msg))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

使用


verifySig("0x0000000000000000000000000000000000000000",encodedTxStr,[]byte("hello"))
  • 1
  • 2

也可以使用EIP712,小狐狸签名时用户也可以看到实际内容

二、token EIP712Domain

参考链接
ethereum/EIPs
metamask-sign-typed-data-v4 该链接查看页面底部的Example/JavaScript

eip712的概念查看文档…
这里主要说明怎么用,根据自己需求扩展

Domain 格式

这个格式能不能改,没测

    constructor(uint256 chainId_) public {
        DOMAIN_SEPARATOR = keccak256(abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256(bytes(name)),
            keccak256(bytes(version)),
            chainId_,
            address(this)
        ));
    }
    
    //合约中验签
    //下面格式"\x19\x01",DOMAIN_SEPARATOR, 
    //就像eth_sign 中的 hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message})
    function shaInfo(address holder,address spender,uint256 nonce,uint256 expiry,uint256 value)public view returns(bytes32){
         bytes32  digest =
            keccak256(abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH,
                                     holder,
                                     spender,
                                     nonce,
                                     expiry,
                                     value))
        ));
        return digest;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

Permit 格式

该格式实际可以随意修改,Permit和Domain实际就是对应的结构体,结构体名称就是type
参考eip-712例子 Example.sol

  • typeHash就对结构体和所有属性类型进行keccak256
  • DOMAIN_SEPARATOR 是对有值的结构体进行 keccak256,作用是xxxx (避免滥用, 712里面也加了chainId和nonce)
    struct Person {
        string name;
        address wallet;
    }
    bytes32 constant PERSON_TYPEHASH = keccak256("Person(string name,address wallet)");
  • 1
  • 2
  • 3
  • 4
  • 5

所以,如果想仿着改,还是很容易的…

套格式

可参考 example.js

  • domain/message 是两个结构体的实际内容
  • types 是两个结构体的结构
  • primaryType: ‘Permit’, 签名的message的type
const msgParams = JSON.stringify({
    domain: {
        name: 'TDai Stablecoin',
        version: '1',
        chainId: 4,
        verifyingContract: '0xddaAd340b0f1Ef65169Ae5E41A8b10776a75482d',
    },

    // Defining the message signing data content.
    message: {
        holder: '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4',
        spender: '0x0fC5025C764cE34df352757e82f7B5c4Df39A836',
        nonce: 1,
        expiry: 1640966400,
        value: 10000,
    },
    // Refers to the keys of the *types* object below.
    primaryType: 'Permit',
    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'},
        ],
        Permit: [
            {name: 'holder', type: 'address'},
            {name: 'spender', type: 'address'},
            {name: 'nonce', type: 'uint256'},
            {name: 'expiry', type: 'uint256'},
            {name: 'value', type: 'uint256'}
        ],
    },
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
如何签名
node签名

参考eip-712例子 Example.js

//在example.js中加这些,就可以直接node example.js 看结果了
console.log("获取私钥")
const privateKey =ethUtil.toBuffer("0x503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb");
const address = ethUtil.privateToAddress(privateKey);
console.log(ethUtil.bufferToHex(address));
const sig = ethUtil.ecsign(signHash(), privateKey);
console.log("签名后的的信息");
console.log(sig);
console.log("---  "+ethUtil.bufferToHex(sig.r)+" ; "+ethUtil.bufferToHex(sig.s)+" ; "+sig.v);
let mailHash = encodeData(typedData.primaryType,typedData.message);
console.log(ethUtil.bufferToHex(ethUtil.keccak256(mailHash)));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
ethers 签名
//ethers
const ethers = require('ethers');
const config = require('../config/constants');
const { TypedDataEncoder } = require('ethers');
let customHttpProvider = new ethers.JsonRpcProvider(config.rpc);
let wallet = new ethers.Wallet(config.filler_prikey, customHttpProvider);
async function ethersSign() {
    console.log('ethers--------')
    let test = {
        "domain": {
            "name": "TDai Stablecoin",
            "version": "1",
            "chainId": 4,
            "verifyingContract": "0xddaAd340b0f1Ef65169Ae5E41A8b10776a75482d",
        },

        // Defining the message signing data content.
        "data": {
            "holder": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
            "spender": "0x0fC5025C764cE34df352757e82f7B5c4Df39A836",
            "nonce": 1,
            "expiry": 1640966400,
            "value": 10000,
        },
        // Refers to the keys of the *types* object below.
        "primaryType": "Permit",
        "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" },
            // ],
            "Permit": [
                { "name": "holder", "type": "address" },
                { "name": "spender", "type": "address" },
                { "name": "nonce", "type": "uint256" },
                { "name": "expiry", "type": "uint256" },
                { "name": "value", "type": "uint256" }
            ],
        },
    }
    const encoder = TypedDataEncoder.from(test.types);
    console.log(encoder.primaryType);
    console.log(encoder.encode(test.data));

    console.log(TypedDataEncoder.getPrimaryType(test.types));
    console.log(TypedDataEncoder.hash(test.domain, test.types, test.data));

    const signature = wallet.signingKey.sign(TypedDataEncoder.hash(test.domain, test.types, test.data))
    console.log(signature.toJSON())
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
网页小狐狸签名

参考 metamask-sign-typed-data-v4
rpc

  • signTypedData_v1
  • signTypedData_v3
  • signTypedData_v4

这三种都可以, 具体下面有demo

        var params = [from, msgParams]
        var method = 'eth_signTypedData_v4'
        web3.currentProvider.sendAsync({
            method,
            params,
            from,
        }, function (err, result) {
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

根据Dai的代码修改的demo

包括合约和前端代码
DAI.sol
有部分改动,

  • 精度改成2
  • Permit内容有修改,改成传入多少value,就授权多少value,而不是-1
  • 注意chainId要填对应的,否则小狐狸不给签… 如rinkeby是4
/**
 *Submitted for verification at Etherscan.io on 2019-11-14
*/

// hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
pragma solidity =0.5.12;

contract LibNote {
    event LogNote(
        bytes4   indexed  sig,
        address  indexed  usr,
        bytes32  indexed  arg1,
        bytes32  indexed  arg2,
        bytes             data
    ) anonymous;

    modifier note {
        _;
        assembly {
            // log an 'anonymous' event with a constant 6 words of calldata
            // and four indexed topics: selector, caller, arg1 and arg2
            let mark := msize                         // end of memory ensures zero
            mstore(0x40, add(mark, 288))              // update free memory pointer
            mstore(mark, 0x20)                        // bytes type data offset
            mstore(add(mark, 0x20), 224)              // bytes size (padded)
            calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
            log4(mark, 288,                           // calldata
                 shl(224, shr(224, calldataload(0))), // msg.sig
                 caller,                              // msg.sender
                 calldataload(4),                     // arg1
                 calldataload(36)                     // arg2
                )
        }
    }
}

contract Dai is LibNote {
    // --- Auth ---
    mapping (address => uint) public wards;
    function rely(address guy) external note auth { wards[guy] = 1; }
    function deny(address guy) external note auth { wards[guy] = 0; }
    modifier auth {
        require(wards[msg.sender] == 1, "Dai/not-authorized");
        _;
    }

    // --- ERC20 Data ---
    string  public constant name     = "TDai Stablecoin";
    string  public constant symbol   = "TDAI";
    string  public constant version  = "1";
    uint8   public constant decimals = 2;
    uint256 public totalSupply;

    mapping (address => uint)                      public balanceOf;
    mapping (address => mapping (address => uint)) public allowance;
    mapping (address => uint)                      public nonces;

    event Approval(address indexed src, address indexed guy, uint wad);
    event Transfer(address indexed src, address indexed dst, uint wad);

    // --- Math ---
    function add(uint x, uint y) internal pure returns (uint z) {
        require((z = x + y) >= x);
    }
    function sub(uint x, uint y) internal pure returns (uint z) {
        require((z = x - y) <= x);
    }

    // --- EIP712 niceties ---
    bytes32 public DOMAIN_SEPARATOR;
    // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,uint256 value)");
    bytes32 public constant PERMIT_TYPEHASH = 0x63f12011971eae53910a7ea124c7d16788b74790706dc6d7358718ff7ce8dd13;

    constructor(uint256 chainId_) public {
        wards[msg.sender] = 1;
        DOMAIN_SEPARATOR = keccak256(abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256(bytes(name)),
            keccak256(bytes(version)),
            chainId_,
            address(this)
        ));
        mint(msg.sender,1000000);
    }

    // --- Token ---
    function transfer(address dst, uint wad) external returns (bool) {
        return transferFrom(msg.sender, dst, wad);
    }
    function transferFrom(address src, address dst, uint wad)
        public returns (bool)
    {
        require(balanceOf[src] >= wad, "Dai/insufficient-balance");
        if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
            require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");
            allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
        }
        balanceOf[src] = sub(balanceOf[src], wad);
        balanceOf[dst] = add(balanceOf[dst], wad);
        emit Transfer(src, dst, wad);
        return true;
    }
    function mint(address usr, uint wad) public  {
        balanceOf[usr] = add(balanceOf[usr], wad);
        totalSupply    = add(totalSupply, wad);
        emit Transfer(address(0), usr, wad);
    }
    function burn(address usr, uint wad) external {
        require(balanceOf[usr] >= wad, "Dai/insufficient-balance");
        if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
            require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance");
            allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
        }
        balanceOf[usr] = sub(balanceOf[usr], wad);
        totalSupply    = sub(totalSupply, wad);
        emit Transfer(usr, address(0), wad);
    }
    function approve(address usr, uint wad) external returns (bool) {
        allowance[msg.sender][usr] = wad;
        emit Approval(msg.sender, usr, wad);
        return true;
    }

    // --- Alias ---
    function push(address usr, uint wad) external {
        transferFrom(msg.sender, usr, wad);
    }
    function pull(address usr, uint wad) external {
        transferFrom(usr, msg.sender, wad);
    }
    function move(address src, address dst, uint wad) external {
        transferFrom(src, dst, wad);
    }

    // --- Approve by signature ---
    function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                    uint256 value, uint8 v, bytes32 r, bytes32 s) external
    {
        bytes32 digest =
            keccak256(abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH,
                                     holder,
                                     spender,
                                     nonce,
                                     expiry,
                                     value))
        ));

        require(holder != address(0), "Dai/invalid-address-0");
        require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
        require(expiry == 0 || now <= expiry, "Dai/permit-expired");
        require(nonce == nonces[holder]++, "Dai/invalid-nonce");
        allowance[holder][spender] = value;
        emit Approval(holder, spender, value);
    }
    
    function shaInfo(address holder,address spender,uint256 nonce,uint256 expiry,uint256 value)public view returns(bytes32){
         bytes32  digest =
            keccak256(abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH,
                                     holder,
                                     spender,
                                     nonce,
                                     expiry,
                                     value))
        ));
        return digest;
    }
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174

transferFromDai.sol

pragma solidity =0.5.12;
interface IToken {

    function balanceOf(address _owner) external view returns (uint256 balance);

    function transfer(address _to, uint256 _value) external returns (bool success);

    function transferFrom(address _from, address _to, uint256 _value) external returns
    (bool success);

    function approve(address _spender, uint256 _value) external returns (bool success);

    function allowance(address _owner, address _spender) external view returns
    (uint256 remaining);
    function nonces(address)external view returns(uint256 n);
    function permit(address holder, address spender, uint256 nonce, uint256 expiry, uint256 allowed, uint8 v, bytes32 r, bytes32 s) external;
}
contract transferFromDai{
    
    event Zero(address addr,uint256 zero);
    event Nnn(address addr,uint256);
    //这个代币是上面发布的dai,
    address tokenAddr = 0xddaAd340b0f1Ef65169Ae5E41A8b10776a75482d;
    //这个时间写死2022,方便测试不用修改..
    uint256 time2022 = 1640966400;
    // function permit(address holder, address spender, uint256 nonce, uint256 expiry,
    //                 bool allowed, uint8 v, bytes32 r, bytes32 s) external
    function deposit(uint256 nonce, uint256 value, uint8 v, bytes32 r, bytes32 s)public{
        IToken token = IToken(tokenAddr);
        if(token.allowance(msg.sender,address(this)) ==0){
            emit Zero(address(6),6);
            token.permit(msg.sender,address(this),nonce,time2022,value,v,r,s);
        }else{
            emit Nnn(address(2),2);
            
        }
        
        token.transferFrom(msg.sender,address(this),value);
    }
    
    function Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)public{}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

前端代码
domainParams.js

//如自己部署,注意修改 domain内的chainId,contract
//修改message中的实际签名信息  holder/spender
//修改abi和地址
const msgParams = JSON.stringify({
    domain: {
        name: 'TDai Stablecoin',
        version: '1',
        chainId: 4,
        verifyingContract: '0xddaAd340b0f1Ef65169Ae5E41A8b10776a75482d',
    },

    // Defining the message signing data content.
    message: {
        holder: '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4',
        spender: '0x0fC5025C764cE34df352757e82f7B5c4Df39A836',
        nonce: 1,
        expiry: 1640966400,
        value: 10000,
    },
    // Refers to the keys of the *types* object below.
    primaryType: 'Permit',
    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'},
        ],
        Permit: [
            {name: 'holder', type: 'address'},
            {name: 'spender', type: 'address'},
            {name: 'nonce', type: 'uint256'},
            {name: 'expiry', type: 'uint256'},
            {name: 'value', type: 'uint256'}
        ],
    },
});
const TEST_ADDR = '0x0fC5025C764cE34df352757e82f7B5c4Df39A836';
const TEST_ABI = [];
const DAI_ADDR = '0xddaAd340b0f1Ef65169Ae5E41A8b10776a75482d';
const DAI_ABI = [];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
<!--<input type="text" placeholder="nonce" id="edit_nonce">-->
<input type="text" placeholder="数量" id="edit_num">
<button onclick="sign()">签名并充值代币</button>
<br />
<br />
<input type="text" placeholder="增发数量" id="edit_mint">
<button onclick="mint()">增发dai</button>
</body>

</html>
<script src="https://cdn.bootcdn.net/ajax/libs/web3/1.3.0/web3.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="./js/meta/domainParams.js"></script>
<script>
    window.onload = function () {
        wallet()
    }
    let account0 = '';
    function wallet() {
        console.log(window.ethereum)
        if (window.ethereum) {
            Web3 = new Web3(ethereum);
            try {
                ethereum.enable();
            } catch (error) {
            }
        } else if (typeof Web3 !== 'undefined') {
            Web3 = new Web3(Web3.currentProvider);
        } else {
            Web3 = new Web3(new Web3.providers.HttpProvider('https://rinkeby.infura.io/v3/-'));
        }
        Web3.eth.getAccounts().then(function (res) {
            account0 = res[0];
            console.log("账号: " + account0);
        });
    }

    async function sign() {
        let from = account0;
        console.log("签名并发送交易");
        let tokenContract = new  Web3.eth.Contract(DAI_ABI,DAI_ADDR);
        let nonce = await tokenContract.methods.nonces(from).call();
        console.log(nonce);

        let num = $('#edit_num').val();

        num = Number.parseInt(num);

        let tempObj = JSON.parse(msgParams);
        tempObj.message.nonce = nonce;
        tempObj.message.value = num;
        tempObj.message.holder = from;

        // var params = [from, msgParams]
        var params = [from, JSON.stringify(tempObj)]
        var method = 'eth_signTypedData_v4'
        Web3.currentProvider.sendAsync({
            method,
            params,
            from,
        }, function (err, result) {
            console.log("签名结果");
            console.log(err);
            console.log(result);

            let signResult = result.result;

            let r = signResult.slice(0, 66)
            let s = '0x' + signResult.slice(66, 130)
            let v = '0x' + signResult.slice(130, 132)
            let contract = new Web3.eth.Contract(TEST_ABI,TEST_ADDR);
            contract.methods.deposit(nonce,num,v,r,s).send({from:from},function (err,r) {
                console.log("发送结果: ")
                console.log(err);
                console.log(r);
            });
        })
    }
    function mint() {
        let num = $('#edit_mint').val();
        num = Number.parseInt(num,16).toString(16);
        console.log(num)
        let tokenContract = new  Web3.eth.Contract(DAI_ABI,DAI_ADDR);
        tokenContract.methods.mint(account0,num).send({from:account0})
    }

</script>



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/906888
推荐阅读
相关标签
  

闽ICP备14008679号