Gnosis Safe 多重签名,multisend

这篇具有很好参考价值的文章主要介绍了Gnosis Safe 多重签名,multisend。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

EIP712

EIP712是一种签名标准,主要是针对明文。EIP712详细解释
multisend,区块链,区块链
multisend,区块链,区块链

  1. EIP712签名的结构由三部分组成,分别是domainData,types,sign_message。
    签名的原理就是拿用户的私钥,对一串32个字节的哈希值进行ECDSA算法计算,得出来一个65个字节的值,由r,s,v组成。
    智能合约验签的原理就是拿到这个签名进行算法解析,接出来先是公钥,然后推出用户地址。这就是验签。
//基础准备工作,ethers.js或者web3.js都可以,这里拿ethers.js举例,直接前端引入,可以存储到本地 加载速度更快更安全,如果用外链cdn加速有时候可能会加载失败。
<script src="https://cdn.ethers.io/lib/ethers-5.6.9.umd.min.js"
    			type="application/javascript"></script>
    			
 const provider = new ethers.providers.Web3Provider(window.ethereum)//只要浏览器下载了小狐狸插件,window全局对象中都会有ethereum这个对象,如果是初学者,稍微百度一下window全局对象
 const accounts = await provider.send("eth_requestAccounts", [])//如果没有链接钱包会自动弹出来小狐狸连接框,如果链接钱包了就没什么显示

 abiCoder = ethers.utils.defaultAbiCoder//ethers.js自带的编码器,就是用来编码的,编码就是把要交易的信息打包成一个类似string的东西,在合约中为bytes,0x十六进制开头

 var signer_712 = provider.getSigner()//这个signer_712变量其实是拿到了当前链接小狐狸的账号,只包含一个用户地址,不包含多个。
 
const domainData = {
                    chainId: 4,//这个是int类型的,指定链id,很重要,比如ETH主网就必须是1否则弹不出来小狐狸,是对应上的。
                    verifyingContract: "0x7Fbf984cd60d763Eb94C4C466013f1Ee90dd78Cf",//这个是Safe保险箱合约地址
                    };

const types = {     SafeTx:  //这里要和合约匹配上,gnosis safe其中rinkeby一个合约地址为0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552,可以去观察SafeTx这里的细节
                                                             [{ name: 'to',type: 'address'},
                                                              { 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' }]}//这个只是定义所有消息数据的类型。
var sign_message = {
                        to:     sign_to_address,//类似0x40A2aCCbd92BCA938b02010E17A5b8929b49130D
                        value:  0,
                        data:   total_multi_data,//类似0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000013200d1d8a3af43788876089b57a214544f9fff9ed08500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000f94e4af78b3f222fa0f8be0f47b6ec6960866e0b0000000000000000000000000000000000000000000000004279842b41585000
                        operation:   1,//或者0,  0表示call,1表示delegatecall,这个需要一定基础的消化的
                        safeTxGas:  0,
                        baseGas:     0,
                        gasPrice:    0,
                        gasToken:    '0x0000000000000000000000000000000000000000',
                        refundReceiver:   "0x0000000000000000000000000000000000000000",
                        nonce:      sign_nonce ,//类似17,18 在gnosis 保险箱合约中 表示交易提交的次数
                    };
var signature = await signer_712._signTypedData(domainData, types, sign_message)//这里的signature就是此用户签名信息。用户签名之后,需要把这个签名信息向后端发起接口请求,让后端中心化服务器存储的。

ABI

  1. ABI是前端与区块链智能合约链接的接口,特别重要。 ABI介绍及解释
    ABI全名:Application Binary Interface,应用二进制接口文件。智能合约的接口描述,描述了字段名称、字段类型、方法名称、参数名称、参数类型、方法返回值类型等。
    当合约被编译后,对应的abi文件也就确定了。
    在前端代码中,大部分用abi无非就两种,一个是前端构造合约实例中,参数带abi文件,这个abi是已经发布的合约中自带的。一种是abi,encode的这种工具。例如,
var contracts_gnosis = {
	gnosis_factory:{
		address:'0xa6b71e26c5e0845f74c812102ca7114b6a896ab2',
		abi:[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract GnosisSafeProxy","name":"proxy","type":"address"},{"indexed":false,"internalType":"address","name":"singleton","type":"address"}],"name":"ProxyCreation","type":"event"},{"inputs":[{"internalType":"address","name":"_singleton","type":"address"},{"internalType":"bytes","name":"initializer","type":"bytes"},{"internalType":"uint256","name":"saltNonce","type":"uint256"}],"name":"calculateCreateProxyWithNonceAddress","outputs":[{"internalType":"contract GnosisSafeProxy","name":"proxy","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"singleton","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"createProxy","outputs":[{"internalType":"contract GnosisSafeProxy","name":"proxy","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_singleton","type":"address"},{"internalType":"bytes","name":"initializer","type":"bytes"},{"internalType":"uint256","name":"saltNonce","type":"uint256"},{"internalType":"contract IProxyCreationCallback","name":"callback","type":"address"}],"name":"createProxyWithCallback","outputs":[{"internalType":"contract GnosisSafeProxy","name":"proxy","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_singleton","type":"address"},{"internalType":"bytes","name":"initializer","type":"bytes"},{"internalType":"uint256","name":"saltNonce","type":"uint256"}],"name":"createProxyWithNonce","outputs":[{"internalType":"contract GnosisSafeProxy","name":"proxy","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proxyCreationCode","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"proxyRuntimeCode","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"}]
        }
}
var contract_gnosis_factory = new ethers.Contract(contracts_gnosis.gnosis_factory.address,contracts_gnosis.gnosis_factory.abi,signer_create);
//contracts_gnosis.gnosis_factory.abi就包含了很多个函数,是列表里面带json格式,大约长这样,
//[{"inputs":[{"internalType":"address","name":"_singleton","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"stateMutability":"payable","type":"fallback"}]
//他其实也可以自由定义,可以增加,也可以删减,就是一个接口。
await contract_gnosis_factory.createProxyWithNonce(gnosis_safe_address,gnosisSafeData_final,salt); 
}

创建保险箱,Safe合约

  1. 这是Gnosis safe最开始的功能,创建保险箱合约。用户点击创建按钮,提交DAO组织用户们的地址和阈值,就可以创建保险箱合约了。保险箱合约的用处就是主要是管理共同资产,然后代替DAO成员意向执行共同决策的。



async function CreateSafe(){  //创建保险箱合约
    var ethereum = window.ethereum;//只要浏览器下载了小狐狸插件,window全局对象中都会有ethereum这个对象。
    var provider = new ethers.providers.Web3Provider(window.ethereum)//把ethers的provider设置成小狐狸钱包的环境。

    await provider.send("eth_requestAccounts", []); //如果没有链接钱包会自动弹出来链接框,如果链接钱包了则什么事也不干。

    var signer_create =  provider.getSigner()//这个signer_create变量其实是拿到了当前链接小狐狸的账号,只包含一个用户地址,不包含多个。

    abiCoder = ethers.utils.defaultAbiCoder//ethers.js自带的编码器,就是用来编码的,编码就是把要交易的信息打包成一个类似string的东西,在合约中为bytes,0x十六进制开头


    var contract_gnosis_factory = new ethers.Contract(contracts_gnosis.gnosis_factory.address,contracts_gnosis.gnosis_factory.abi,signer_create);//这里的功能是链接合约,生成合约对象了,最后的signer_create参数是当前部署者地址的对象,也就是说返回的contract_gonsis_factory这个对象已经具备了很强大的和合约交互的功能了。


    var safeAccounts = [
             '0x4d2E1A38d07Eadf5C62CfDaF93547DAe09F1EF83',
             '0x592916d0D7fcaec0A0A0504134364721Aafd5e87',
             '0xF94e4af78b3f222fa0F8Be0F47b6Ec6960866E0b',
         ]//这里我测试是写死的,其实这个不应该是写死的,是最开始的DAO创始人去设置的。在前端中到时候可以写一个列表,前端接受用户输入的地址,直接保存就可以,等创建保险箱成功后,再去把信息反馈给后端。

    var numConfirmations = 1;这个是阈值,就是DAO中有几个人同意就可以执行交易,如果这里是1,那么团队中只要有一个人签名就可以执行了。

    var ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';这个是0地址,要满足gnosis合约规定方式

    var EMPTY_DATA = '0x';//这个是0字节,要满足gnosis合约规定方式

    var fallbackHandler_address = '0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4';//gnosis好像意思是这个是fallback处理的地址,我感觉没什么用,在合约交互的时候没走过这个合约。
                                                                               
    var gnosisSafeData = abiCoder.encode(['address[]','uint256','address','bytes','address','address','uint256','address'],
                        [safeAccounts,
                        numConfirmations,
     					ZERO_ADDRESS,
     					EMPTY_DATA,
     					fallbackHandler_address,
     					ZERO_ADDRESS,
     					0,
     					ZERO_ADDRESS]);//这个就是编码(其实编码就是压缩,没有很多其他的操作),把这些东西压缩编码一下,
    var gnosisSafeData_final = '0xb63e800d'+gnosisSafeData.substring(2);//0xb63e800d=web3.eth.abi.encodeFunctionSignature('myMethod(uint256,string)'),这个就是在合约字节码中找函数的对应的位置
    var gnosis_safe_address = "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552";//这个是rinkeby测试网指定的一个主合约地址,写死。
    var salt = 1657266633780;//这个其实是时间戳,是要变化的,也是传入调用合约函数的最后一个参数,具体值是当前的时间,date.now()

    await contract_gnosis_factory.createProxyWithNonce(gnosis_safe_address,gnosisSafeData_final,salt);//这个是ethers调用合约的标准方法,createProxyWithNonce这个是合约中定义的函数名,其实他这里是从ABI上找的,记得刚开始contract_gnosis_factory的变量中带了abi的,如果那个abi没有这个方法,小狐狸钱包都起不来。
}

用户自定义构造函数与转移资产

1>用户自定义构造函数
这里的构造函数更应该说是指定某些函数功能交互,就是任意合约的任意函数(可执行),哪怕不是转账任意的都行。
Safe保险箱内的用户可以构造任意的链上交易,意思就是比如这条链上随便有个NFT合约,有个投票合约,有个质押合约,我让这个Safe保险箱执行指定合约一个可执行函数都是可以的。


var contracts_gnosis_safe = {
	gnosis:{
		address:'0x7Fbf984cd60d763Eb94C4C466013f1Ee90dd78Cf',
		abi:[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"AddedOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"approvedHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"ApproveHash","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"handler","type":"address"}],"name":"ChangedFallbackHandler","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"guard","type":"address"}],"name":"ChangedGuard","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"ChangedThreshold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"DisabledModule","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"EnabledModule","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"payment","type":"uint256"}],"name":"ExecutionFailure","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"}],"name":"ExecutionFromModuleFailure","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"}],"name":"ExecutionFromModuleSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"payment","type":"uint256"}],"name":"ExecutionSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"RemovedOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"initiator","type":"address"},{"indexed":false,"internalType":"address[]","name":"owners","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"},{"indexed":false,"internalType":"address","name":"initializer","type":"address"},{"indexed":false,"internalType":"address","name":"fallbackHandler","type":"address"}],"name":"SafeSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"msgHash","type":"bytes32"}],"name":"SignMsg","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"_threshold","type":"uint256"}],"name":"addOwnerWithThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hashToApprove","type":"bytes32"}],"name":"approveHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"approvedHashes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_threshold","type":"uint256"}],"name":"changeThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataHash","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"internalType":"uint256","name":"requiredSignatures","type":"uint256"}],"name":"checkNSignatures","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataHash","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"checkSignatures","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"prevModule","type":"address"},{"internalType":"address","name":"module","type":"address"}],"name":"disableModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"enableModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Enum.Operation","name":"operation","type":"uint8"},{"internalType":"uint256","name":"safeTxGas","type":"uint256"},{"internalType":"uint256","name":"baseGas","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"address","name":"gasToken","type":"address"},{"internalType":"address","name":"refundReceiver","type":"address"},{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"encodeTransactionData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Enum.Operation","name":"operation","type":"uint8"},{"internalType":"uint256","name":"safeTxGas","type":"uint256"},{"internalType":"uint256","name":"baseGas","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"address","name":"gasToken","type":"address"},{"internalType":"address payable","name":"refundReceiver","type":"address"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"execTransaction","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Enum.Operation","name":"operation","type":"uint8"}],"name":"execTransactionFromModule","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Enum.Operation","name":"operation","type":"uint8"}],"name":"execTransactionFromModuleReturnData","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"start","type":"address"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"getModulesPaginated","outputs":[{"internalType":"address[]","name":"array","type":"address[]"},{"internalType":"address","name":"next","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOwners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"getStorageAt","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Enum.Operation","name":"operation","type":"uint8"},{"internalType":"uint256","name":"safeTxGas","type":"uint256"},{"internalType":"uint256","name":"baseGas","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"address","name":"gasToken","type":"address"},{"internalType":"address","name":"refundReceiver","type":"address"},{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"getTransactionHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"isModuleEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"prevOwner","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"_threshold","type":"uint256"}],"name":"removeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"enum Enum.Operation","name":"operation","type":"uint8"}],"name":"requiredTxGas","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"handler","type":"address"}],"name":"setFallbackHandler","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guard","type":"address"}],"name":"setGuard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_owners","type":"address[]"},{"internalType":"uint256","name":"_threshold","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"address","name":"fallbackHandler","type":"address"},{"internalType":"address","name":"paymentToken","type":"address"},{"internalType":"uint256","name":"payment","type":"uint256"},{"internalType":"address payable","name":"paymentReceiver","type":"address"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"signedMessages","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"targetContract","type":"address"},{"internalType":"bytes","name":"calldataPayload","type":"bytes"}],"name":"simulateAndRevert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"prevOwner","type":"address"},{"internalType":"address","name":"oldOwner","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"swapOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
        }
keccak_function_value = getFunctionSeletor(abi_single)//这个是函数选择器,生成是8位16进制。类似0x1a19f89
document.getElementById("keccak_function").innerHTML = keccak_function_value;

var encode_para_data =  abiCoder.encode(matchObj_copy,matchObj_paras);//这个是对交易数据进行encode编码
//举个例子,abiCoder.encode(['address', 'address', 'uint256'],
					//[['0x4d2E1A38d07Eadf5C62CfDaF93547DAe09F1EF83', '0x592916d0D7fcaec0A0A0504134364721Aafd5e87', 6000]])
//encode_para_data = 0x0000000000000000000000004d2e1a38d07eadf5c62cfdaf93547dae09f1ef83000000000000000000000000592916d0d7fcaec0a0a0504134364721aafd5e870000000000000000000000000000000000000000000000000000000000001770
//encode都是32个字节0x20, 64位一个slot(插槽)进行编码,encodePacked并不按照32个字节(64位16进制)进行编码。
//这里是不带函数选择器的,类似0x1a19f89

var single_final =  keccak_function_value + encode_para_data.substring(2);//把完整的交易打包,完整的交易由函数选择器+参数组成,keccak_function_value这个是函数选择器,类似长这个样子0x1a19f89,mencode_para_data.substring(2)这个是压缩后的参数。
//keccak_function_value类似为0x1a19f894
//single_final的值类似为0x1a19f8940000000000000000000000004d2e1a38d07eadf5c62cfdaf93547dae09f1ef83000000000000000000000000592916d0d7fcaec0a0a0504134364721aafd5e870000000000000000000000000000000000000000000000000000000000001770
//0x1a19f894 														//4个字节,8位也就是长度为8的16进制
//0000000000000000000000004d2e1a38d07eadf5c62cfdaf93547dae09f1ef83  //一个solt,32个字节,64位16进制,合约函数中表示from
//000000000000000000000000592916d0d7fcaec0a0a0504134364721aafd5e87  //一个solt,32个字节,64位16进制,合约函数中表示to
//0000000000000000000000000000000000000000000000000000000000001770  //这个是value,					  1*16**3+7*16**2+7*16=4096+1792+112=6000
//这就是一笔交易的函数信息了

single_data = single_final//如果对一笔交易来讲,这个single_final就是最后要签名的数据中的data了。(这个single_data只是你要签名信息sign_message中的一部分,是sign_message的第三个参数)


var dom_address = document.getElementById('Contract_address').value;//这里sign_to_address 是指你要执行的这个函数是哪个合约里的,这里要填写合约地址。


sign_to_address = dom_address;



sign_message = {
                  to:     sign_to_address,
                  value:  0,
                  data:   single_data,
                  operation:   0,
                  safeTxGas:  0,
                  baseGas:     0,
                  gasPrice:    0,
                  gasToken:    '0x0000000000000000000000000000000000000000',
                  refundReceiver:   "0x0000000000000000000000000000000000000000",
                  nonce:      sign_nonce ,
              };//这是最后要签名的信息sign_message



  var contracts_safe_execTransaction = new ethers.Contract(contracts_gnosis_safe.gnosis.address,contracts_gnosis_safe.gnosis.abi,signer_ExecTransaction)
              var to = sign_to_address;
              var valueInWei = 0;
              var data = sign_single_data;
              var operation = 0;
              var safeTxGas = 0;
              var baseGas = 0;
              var gasPrice = 0;
              var gasToken = "0x0000000000000000000000000000000000000000";
              var refundReceiver = "0x0000000000000000000000000000000000000000";
              var sigs = sig_s;//这些变量就是拿到最后要执行合约交易的参数,contracts_safe_execTransaction这个是合约对象,已经包含了用户要执行合约的地址,其余的变量都是要传入的参数。


            try{

            await contracts_safe_execTransaction.execTransaction(to,valueInWei,data,operation,safeTxGas,baseGas,gasPrice,gasToken,refundReceiver,sigs);//这块就是要唤醒小狐狸执行交易合约的代码

               alert('success')
               nonce ++;
            }
            catch{
                alert('执行交易失败')
            }//其实真正这个合约中只有一个fallback函数,rinkeby上地址为0x7Fbf984cd60d763Eb94C4C466013f1Ee90dd78Cf,这就是我自己的保险箱Safe合约,没有这个execTransaction函数,在创建保险箱的时候走的是0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2这个合约,但是Safe合约里fallback函数里面用了内联汇编就可以让他指向内部的singleton合约地址,地址为0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552,让这个singleton合约去验证多重签名和执行其他合约的函数,是这个意思。但是如果在构造合约实例的时候,配置的abi中没有这个execTransaction函数是会报错的。

2>转移资产

可以理解成,转移资产和用户自己构造交易在合约层面是一模一样的,都是一样的步骤。区别就是一个是用户自定义指定合约与函数,需要他自己填写函数的参数。转移资产功能只需要用户填写to地址(给谁转账),以及对应的资产数量。转移资产这种方式其实是前端提前写好的。转移资产基本都是走transfer函数,从Safe合约中转给to地址,from是Safe合约。用户只需要添加to地址与数量,转移资产功能可以转ETH主币也可以转ERC20,721,1155。
如果是转ETH,同理,用户需要添加to地址以及对应的数量,前端去动态encode生成data(接受ETH value数量)。

用户批量构造交易,整合到一笔交易,multisend

*1>每一次用户自定义构造交易
2>encodePacked和encode操作是类似的,只不过encodePacked在encode基础上针对参数类型进行了缩放。比如encodePacked(uint8),传入参数是1,那么得出的结果就是0x01,如果是encodePacked(uint16),传入参数也是1的话,得出结果是0x0001,但是encode的话,全部都是64位16进制,得出的结果是0x0000000000000000000000000000000000000000000000000000000000000001.
3>总体的流程是,用户自定义构造多笔交易,这一步只需要encode,再进行encodePacked成完整的一笔交易,然后把这个完整的每笔encodePacked之后交易一个个拼接,组成最后的多笔交易的final_multi_data。(比如用户要构造三笔交易,他先构造了一笔,前端做的事就是先encode这个data,然后把data和其他的参数一起进行encodePacked,得到对应的信息,然后构造第二笔也是一样的操作,接着把上笔完整交易encodePacked后返回的东西和这笔完整交易encodePacked后返回的东西进行直接拼接。
最后对总的信息再进行encode成bytes类型。

var uint8_operation = document.getElementById('operation').value//这个是用户在执行交易对这个合约的操作,是0或者1,暂时不用管,都写0,0在gnosis合约中表示call,1在gnosis合约定义位delegatecall
var address_to = document.getElementById('to').value//这个合约函数要执行的时候指定的合约地址,比如想法是在A合约里面有个f1函数,执行A合约里面的f1函数,我不能用f1函数去指定B合约。类似0xd1d8a3af43788876089b57a214544f9fff9ed085
var uint256_value = document.getElementById('value').value * 10 ** 18//这个是转移eth的数量,这里10 **18 是精度,就是如果页面中直接给了一个值5000,如果不带这个10**18,结果会是0.000000000000005个ETH。
var uint_data_length = ((single_data.length)-2)/2//这里是算单次交易转换成data的字节长度。2位16进制=1个字节,-2是把0x去掉,/2是拿到字节长度,而不是位长度。
var single_data = keccak_function_value + encode_para_data.substring(2)
//这single_data是单笔交易,不包含operation,函数所在的合约地址,转账ETH金额
//其中keccak_function_value是函数选择器,类似0x1a19f894
//encode_para_data 类似0x0000000000000000000000004d2e1a38d07eadf5c62cfdaf93547dae09f1ef83000000000000000000000000592916d0d7fcaec0a0a0504134364721aafd5e870000000000000000000000000000000000000000000000000000000000001770,substring(2)是把前两位0x去掉

var single_total_OAV = ethers.utils.solidityPack(["uint8", "address", "uint256","uint256", "bytes"], //ethers.utils.solidityPack就是encodePacked功能
    [uint8_operation,
    address_to,
    uint256_value,
    uint_data_length,
    single_data])
    console.log('single_total_OAV',single_total_OAV)
    multi_data_final = multi_data_final + single_total_OAV.substring(2)//multi_data_final 最开始是"0x"
    //multi_data_final 才是最后想要拿到的一次性执行多笔交易的完整的data,但是还需要最后再encode成bytes类型一下。
    //single_total_OAV类似长这样:0x00d1d8a3af43788876089b57a214544f9fff9ed085000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000641a19f8940000000000000000000000004d2e1a38d07eadf5c62cfdaf93547dae09f1ef83000000000000000000000000592916d0d7fcaec0a0a0504134364721aafd5e870000000000000000000000000000000000000000000000000000000000001770
    //把single_total_OAV的值分离开来就是
    //00    															 //uint8 encodePacked之后的值为0,0表示call,1表示delegatecall 。 值为uint8_operation
    //d1d8a3af43788876089b57a214544f9fff9ed085           				 //这个表示用户自定义想执行的哪个合约地址 ,值为address_to
    //0000000000000000000000000000000000000000000000000000000000000000   //32字节,表示转多少wei的ETH
    //0000000000000000000000000000000000000000000000000000000000000064   //32字节, 64表示 16*6+4=100,也就是交易数据带函数选择器一共是100个字节,比如交易数据就是
    //1a19f894   														 //4个字节,函数选择器
	//0000000000000000000000004d2e1a38d07eadf5c62cfdaf93547dae09f1ef83   //32个字节,这个函数中表示from地址
	//000000000000000000000000592916d0d7fcaec0a0a0504134364721aafd5e87   //32个字节,这个函数中表示to地址
	//0000000000000000000000000000000000000000000000000000000000001770   //32个字节,这个函数中表示转账的数量
	// 从1a19f894一直到最后的1770就是完整的一个交易数据,4+32*3=100 个字节, 16*6+4=100个字节。


var final_encode_multi_data = abiCoder.encode(['bytes'],
                                              [multi_data_final]);
//把multi_data_final encode成bytes类型的                
total_multi_data = '0x8d80ff0a' + final_encode_multi_data.substring(2)//这个total_multi_data 是最后multisend EIP712签名的那个data,这个才是最终的,其他的所有encode编码都是为这个data服务。指向的是固定的一个合约,帮你执行这些交易,函数也是固定的所以这里直接固定0x8d80ffoa就好,0x8d80ffoa的来源就是keccak256( function multiSend(bytes memory transactions)),然后去bytes4,取前4个字节,前8位16进制,因为哈希过后是固定长度的32字节。



var contracts_safe_execTransaction = new ethers.Contract(contracts_gnosis_safe.gnosis.address,contracts_gnosis_safe.gnosis.abi,signer_ExecTransaction)
var to = sign_to_address;  //如果这步是执行multisend的话,只能是前端自己去配置,比如rinkeby上的合约地址就是0x40A2aCCbd92BCA938b02010E17A5b8929b49130D,不同的网络要配置不同的合约,合约里面必须用内联汇编把交易分离,有时间我会讲一下内联汇编是如何通过evm code把data分离,我会着重讲内存布局
var valueInWei = 0;
var data = total_multi_data;   //这里要传入合约参数第三个参数就是data,值为total_multi_data
var operation = 1;
var safeTxGas = 0;
var baseGas = 0;
var gasPrice = 0;
var gasToken = "0x0000000000000000000000000000000000000000";
var refundReceiver = "0x0000000000000000000000000000000000000000";
var sigs = sig_s;//这里假设用户都签完名了

//这个就是执行最后交易唤醒小狐狸的函数方法,最关键的就是data,然后传入对应解析data的sign_to_address这个合约地址,操作operation为1,和用户验签后返回的signature,65字节*DAO成员通过人数阈值的一串bytes,变成sigs,把sigs带入调用合约中的最后一个参数,就可以执行多个合约多个函数的交易了。

await contracts_safe_execTransaction.execTransaction(to,valueInWei,data,operation,safeTxGas,baseGas,gasPrice,gasToken,refundReceiver,sigs);

自己做了一个测试网站,方便理解abi,encode编码

我自己有一个网站,主要是通过ABI产生data值的,网站为:http://114.116.97.234/Gnosis/eip712_gnosis_ethers文章来源地址https://www.toymoban.com/news/detail-802390.html

到了这里,关于Gnosis Safe 多重签名,multisend的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 区块链之数字签名

    区块链中采用对原本信息的哈希值进行加密的方式来进行签名 数字签名:签名和验证 一个消息M、其哈希摘要D 签名者 通过自身的 私钥Kp 生成对应的签名 S=F(D,Kp) 验证者 通过 公钥K 解密 S 得到 消息M的摘要D=F(S,K) 双方通信的时候!!! 公钥加密,私钥解密 私钥签名

    2024年02月13日
    浏览(28)
  • 高级的E2EE——交叉签名(区块链密码签名)(第一篇-SAS)

      如果你使用了上篇文章技术,在客户端中成功实现了端到端加密。那么恭喜你!这是真正值得骄傲的一步,也是值得庆祝的一步!但是,您可能已经注意到,有些事情仍处于粗糙边缘:您必须通过比较公钥来手动验证所有其他设备,解密密钥不会在您的设备之间共享等。如

    2024年01月19日
    浏览(34)
  • CA与区块链之数字签名详解

    CA与区块链验证本质上都是数字签名,首先,我们看一下什么是数字签名! 数字签名是 公钥密码学 中的一种技术,用于 验证信息的完整性和发送者的身份 。简而言之,数字签名是一种 确认信息来源和信息完整性 的手段。它通常与区块链、数字证书、加密邮件等技术结合使

    2024年02月04日
    浏览(43)
  • 区块链:对称加密、非对称加密和数字签名

    本篇博客仅从区块链的角度介绍加密算法及数字签名,重在使用,至于加密算法的内部原理这里不会详细介绍。 1 对称加密 1.1 定义   对称加密,指的是信息发送者和接收者通过使用的相同的密钥来完成数据的加密和解密。常用的对称加密算法有:AES、DES、3DES等。 1.2 AES算

    2024年02月10日
    浏览(27)
  • 【区块链】usdt充值 离线签名 离线生成地址

    前两天老同事找到我诉苦:川哥,前段时间产品不知道抽什么风想搞个USDT充值,说什么要与时俱进,与国际接轨。。。。。我tm都研究了两周了都没搞清楚这玩意到底是干嘛的,网上代码不是不全就是缺jar包的,现在搞得我都想rm -rf /*了。。 好家伙,兄弟别冲动啊。。我心想

    2024年02月02日
    浏览(32)
  • 一文读懂区块链隐私技术系列之环签名

    目录 环签名介绍 环签名原理 生成签名 验证签名

    2024年02月01日
    浏览(41)
  • 区块链中使用的加密算法和数字签名算法

    区块链中使用了多种加密算法和数字签名算法来确保数据的安全性、隐私性和可信性。以下是一些常见的加密算法和数字签名算法,它们在区块链技术中的应用: 哈希算法(Hash Functions) : 常见的哈希算法包括 SHA-256(Secure Hash Algorithm 256位)和 SHA-3(Keccak)等。 哈希算法用

    2024年02月04日
    浏览(37)
  • 区块链的两个核心概念之一签名, 另一个是共识.

    Alice的公私钥, 签名和验证签名仅仅确定了Alice对数字资产A所有权的宣言. 之后, Bob也可以用自己的私钥对资产A进行签名宣誓所有权。区块链中叫双花,即重复宣称所有权, 也称重复花费交易。这时候需要共识算法(集体成员pow或委员会代表pos监督数据的变化,达成一致意见即

    2024年02月01日
    浏览(27)
  • Blind Signature盲签名与fabric区块链结合的应用

    盲签名的概念 首先由 David Chaum 于1982年提出,盲签名实现了签名者对发送者的消息进行签名,却不能知道签名者消息的具体内容。 相当于将文件放入信封,签名者在信封上对文件进行签名,而不知道具体的文件内容。 盲签名的实现方式 盲签名的实现方式有很多,比如基于

    2024年02月03日
    浏览(28)
  • 区块链数字签名、验签,以及椭圆曲线算法JS库—elliptic的使用

    目录 一、简介 二、椭圆曲线密码elliptic 1、安装elliptic和js-sha3 2、Keccak256 3、签名过程

    2024年02月02日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包