首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何手动编码和发送事务数据?

如何手动编码和发送事务数据?
EN

Ethereum用户
提问于 2021-11-23 19:23:28
回答 2查看 8.4K关注 0票数 6

假设我希望我的合同(在装配中)更高效,并且能够自己处理一个比标准msg.data更高效的反序列化。

我愿意把时间花在客户身上,这样合同处理才能更高效--基本的问题是,我在客户端应该做些什么来调用完全定制的msg.data,包括函数选择器?

foo(bytes memory encodedParams)

有什么方法可以让我接管整个msg.data,包括函数选择器吗?我该怎么从客户那里打那个电话?

EN

回答 2

Ethereum用户

发布于 2022-02-22 09:56:46

问题是你想要怎样的习惯。一般来说,我会从合同方面开始。

使用Solidity函数选择器并不是真正的要求。这主要是一个允许在不同的库和契约中容易重用的标准。

您可以使用回退方法使自定义方法具有稳健性:

代码语言:javascript
复制
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

contract Bytecode {
    fallback(bytes calldata data) external returns (bytes memory) {
        if (bytes4(data) == 0x0000dead) {
            return "Dead";
        }
        return "Alive";
    }
}

这可以通过阵列切片扩展到使用自定义参数编码。让我们假设我们希望使用打包编码来优化calldata大小:

代码语言:javascript
复制
contract Custom {
    fallback(bytes calldata data) external returns (bytes memory) {
        if (bytes4(data) == 0x0000dead) {
            uint8 hasAddress = uint8(bytes1(data[4:5]));
            address someAddress = address(bytes20(data[5:25]));
            if (hasAddress > 0) {
                return abi.encode(someAddress);
            }
            return "Dead";
        }
        return "Alive";
    }
}

现在,这个契约可以使用可靠的打包编码来调用。

例如,如何在坚实的环境中调用它:

代码语言:javascript
复制
(bool success, bytes memory data) = custom.call(abi.encodePacked(bytes4(0x0000dead), true, this));

例如,如何通过醚提供者调用它:

代码语言:javascript
复制
const data = await provider.call({
  to: "<your_contract_address>",
  data: ethers.utils.solidityPack(["bytes4", "boolean", "address"], ["0x0000dead", true, "0xE5f2A565Ee0Aa9836B4c80a07C8b32aAd7978e22"])
})

这些调用返回原始数据,要处理响应,仍然需要对其进行解码。

例如如何在坚实的情况下解码它:

代码语言:javascript
复制
(address response) = abi.decode(data, (address));

例如,如何通过醚类AbiCoder调用它:

代码语言:javascript
复制
const [response] = ethers.utils.defaultAbiCoder.decode([ "address" ], data);

用这种方法你可以做各种各样的魔术。但这是非常低的水平,在许多情况下,只会降低您的呼叫数据气体成本。许多气体成本不是由calldata编码造成的,而是由对calldata和一般变量的可靠性强制执行的检查造成的。

其中一些支票是:

  • 动态长度类型(如arraysbytes )的边界检查
  • 屏蔽不使用完整32个字节的基本类型,如addressboolean
  • 数学运算的溢出检查。

这些检查提高了合同的安全性,并防止意外流被利用。

其中一些检查可以使用unchecked区块禁用。但是,如果您想要完全优化它,就必须使用YUL/装配

对于使用YUL进行自定义参数编码的示例,您可以查看来自安全团队的多发送合同。在这里,它还使用打包编码,您可以看到示例代码如何对多发送实用模块中的以太类数据执行编码。

票数 11
EN

Ethereum用户

发布于 2022-02-22 08:28:19

首先,需要计算ABI编码的函数调用数据字符串,如下所示:

代码语言:javascript
复制
const data = web3.eth.abi.encodeFunctionCall({
    name: 'insertYourFunctionName',
    type: 'function',
    inputs: [{
        type: 'insertSolidityTypeOfParam1',
        name: 'insertNameOfParam1'
    },{
        type: 'insertSolidityTypeOfParam2',
        name: 'insertNameOfParam2'
    },
    ...
    ]}, [insertValueOfParam1, insertValueOfParam2, ...]);

在您的具体情况下,计算的数据如下:

代码语言:javascript
复制
const data = web3.eth.abi.encodeFunctionCall({
    name: 'foo',
    type: 'function',
    inputs: [{
        type: 'bytes',
        name: 'encodedParams'
    }]}, [insertEncodedParamsString]);

虽然您不需要导入ABI文件,但是您确实需要了解要调用的函数的结构。

下一步是用计算的数据发送实际事务,如下所示:

代码语言:javascript
复制
await web3.eth.sendTransaction({
    from: yourWalletAddress,
    to: contractAddress,
    data: data
});

因此,实际上,当您使用ABI进行标准的web3函数调用时,上面的内容将与在引擎盖下发生的情况相同:

代码语言:javascript
复制
const contract = new Contract(abi, contractAddress);

await contract.methods.insertYourFunctionName(
    insertValueOfParam1,
    insertValueOfParam2,
    ...
).send({ from: yourWalletAddress });

或者在你的情况下,会是:

代码语言:javascript
复制
const contract = new Contract(abi, contractAddress);

await contract.methods.foo(
    insertEncodedParamsString
).send({ from: yourWalletAddress });
票数 2
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/114146

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档