# Gnosis safe多签交易脚本

By [franx.eth](https://paragraph.com/@franx-2) · 2022-05-09

---

Gnosis safe多签交易可以到官网去在线构造，但有时候不想在官网进行操作，或者为了规避官网的故障，可以自己在本地构造交易脚本，直接去多签合约交互，以达到多签钱包交易的目的。

首先要明确的是多签钱包实际是一个智能合约，里面存储了owner地址，多签达标门槛数等信息，当要触发某个交易时，比较简单的实现方式可以是每个owner都发出一笔交易，直到达到达标数量后，在最后一笔交易时去触发实际的多签钱包交易，但这种方式会多消耗owner钱包的gas费，而且也不那么优雅。Gnosis safe用了EIP712的方式，owner先签名交易，当达到达标门槛后，再把签名拼接在一起，执行一次交易在合约里验证签名去触发实际交易。

    const domain = {
        chainId: process.env.CHAIN_ID,//链ID
        verifyingContract: process.env.VERIFY_CONTRACT//多签合约地址
    };
    const types = {    
        SafeTx: [
            { type: "address", name: "to" },
            { type: "uint256", name: "value" },
            { type: "bytes", name: "data" },
            { type: "uint8", name: "operation" },
            { type: "uint256", name: "safeTxGas" },
            { type: "uint256", name: "baseGas" },
            { type: "uint256", name: "gasPrice" },
            { type: "address", name: "gasToken" },
            { type: "address", name: "refundReceiver" },
            { type: "uint256", name: "nonce" },
        ]
    }
    let nonce=await provider.call({
        to: process.env.VERIFY_CONTRACT,
        data: ethers.utils.keccak256(ethers.utils.toUtf8Bytes("nonce()")).slice(0,10)
        });//获取多签钱包的nonce值，防止重放攻击
    
    //The data to sign
    const value = {
        to:"",//实际交易的to地址
        value:,//实际交易的value值
        data:"",//实际交易的data
        operation:0,
        safeTxGas:0,
        baseGas:0,
        gasPrice:0,
        gasToken:"0x0000000000000000000000000000000000000000",
        refundReceiver:"0x0000000000000000000000000000000000000000",
        nonce:ethers.BigNumber.from(nonce).toNumber()
    };
    //假设owner的钱包是w1,w2,w3，以下对交易进行签名
    let signature1 = await w1._signTypedData(domain, types, value);
    let signature2 = await w2._signTypedData(domain, types, value);
    let signature3 = await w3._signTypedData(domain, types, value);
    const sig=signature1+signature2.slice(2)+signature3.slice(2);//要按钱包地址从小到大的顺序拼接地址生成的签名串，这里假设w1<w2<w3
    const data=ethers.utils.defaultAbiCoder.encode([ "address", "uint256","bytes","uint8", "uint256", "uint256", "uint256" ,"address","address","bytes"], 
        [ value.to,value.value,value.data,value.operation,value.safeTxGas,value.baseGas,value.gasPrice,value.gasToken,value.refundReceiver,sig]);//构造execTransaction参数串
    let tran={
        to:process.env.VERIFY_CONTRACT,       
        data:"0x6a761202"+data.slice(2)   
    };
    //用任意钱包地址对tran发出交易即可

---

*Originally published on [franx.eth](https://paragraph.com/@franx-2/gnosis-safe-2)*
