我注意到address提供了两个成员函数,它们执行以太网到指定地址的传输。 send失败后返回false,而transfer抛出异常。 在Solidity文档中,他们提供了一个使用两者调用Simple Open Auction的示例。

我什么时候应该使用send,什么时候应该使用transfer?

原文地址

2 人回答 0

2 个回答
投票数
最旧发布
最近发布

回答发布于 2018-09-11 21:26:59

sendtransfer都被认为是一种安全的资金转移方式,因为他们拥有2300的gas stipend。

如果你对添加transfer的原因感到好奇,可以查看有关该功能的原始讨论

||
||

回答发布于 2018-09-11 21:26:58

address.transfer()

  • 失败了抛出异常
  • 转发2,300 gas stipend(不可调),安全防止重入
  • 应该在大多数情况下使用,因为这是发送以太(ether)的最安全的方法

address.send()

  • 失败了返回false
  • 转发2,300 gas stipend(不可调),安全防止重入
  • 当你想要处理合约中的失败时使用,应该在极少数情况下使用

address.call.value().gas()()

  • 失败了返回false
  • 转发所有可用的gas(可调),不能安全防止重入
  • 当你需要控制在发送以太(ether)时转发多少gas或调用另一个合约的功能时使用

详细版本如下:

使用someAddress.send()someAddress.transfer()someAddress.call.value()()之间的相对权衡:

  • someAddress.send()someAddress.transfer()被认为是安全的,可以防止重入。虽然这些方法仍然会触发代码执行,但是被调用的合约只能获得2,300 gas stipend,这个目前只够用于记录事件。
  • x.transfer(y)等价于require(x.send(y)),如果发送失败,它将自动恢复。
  • someAddress.call.value(y)()将发送提供的以太(ether)和触发代码执行器。执行的代码被给予所有可用于执行的gas,使得这种类型的价值转移不安全,不能防止重入。

使用send()transfer()将防止重入,但这样做的代价是与任何后备功能需要超过2,300 gas的合约不兼容。我们也可以使用someAddress.call.value(ethAmount).gas(gasAmount)()来转发自定义量的gas。

试图平衡这种情况的一种模式是实现push和pull机制,对push组件使用send()transfer(),对pull组件使用call.value()()

值得指出的是,为了价值转移而专门使用的send()transfer()本身并不能使合约安全地防止重入,而只能使这些特定的价值转移对重入是安全的。

更多细节 https://consensys.github.io/smart-contract-best-practices/recommendations/#be-aware-of-the-tradeoffs-between-send-transfer-and-callvalue

添加transfer()的原因:https://github.com/ethereum/solidity/issues/610


call()也可用于发出低级CALL操作码以对另一个合约进行消息调用:

if (!contractAddress.call(bytes4(keccak256("someFunc(bool, uint256)")), true, 3)) {
    revert;
}

转发的valuegas可以定制:

contractAddress.call.gas(5000)
    .value(1000)(bytes4(keccak256("someFunc(bool, uint256)")), true, 3);

这等价于在合约中使用函数调用:

SomeContract(contractAddress).someFunc.gas(5000)
    .value(1000)(true, 3);

注意call()中输入数据的正确填充https://github.com/ethereum/solidity/issues/2884


transfer()send()call()函数由Solidity编译器转换为CALL操作码。

正如以太坊(Ethereum)wiki中的Subtleties页面所解释的那样:

CALL有多部分gas消耗:

  • 基础700
  • 如果值非零,则额外增加9000
  • 如果目标帐户尚不存在,则额外增加25000(注意:零余额和不存在是不一样的!)

非零值CALL操作的子消息(不是交易产生的顶级消息!)在呼叫账户提供的gas之上获得额外的2300gas; 这笔stipend可以看做是从非零值调用的9000强制性额外费用中支付。这可确保调用接收者始终拥有足够的gas来记录其收到的资金。

Solidity文档中解释了发送和接收以太(ether)

||
||

回答发布于 2018-09-11 21:26:58

address.transfer()

  • 失败了抛出异常
  • 转发2,300 gas stipend(不可调),安全防止重入
  • 应该在大多数情况下使用,因为这是发送以太(ether)的最安全的方法

address.send()

  • 失败了返回false
  • 转发2,300 gas stipend(不可调),安全防止重入
  • 当你想要处理合约中的失败时使用,应该在极少数情况下使用

address.call.value().gas()()

  • 失败了返回false
  • 转发所有可用的gas(可调),不能安全防止重入
  • 当你需要控制在发送以太(ether)时转发多少gas或调用另一个合约的功能时使用

详细版本如下:

使用someAddress.send()someAddress.transfer()someAddress.call.value()()之间的相对权衡:

  • someAddress.send()someAddress.transfer()被认为是安全的,可以防止重入。虽然这些方法仍然会触发代码执行,但是被调用的合约只能获得2,300 gas stipend,这个目前只够用于记录事件。
  • x.transfer(y)等价于require(x.send(y)),如果发送失败,它将自动恢复。
  • someAddress.call.value(y)()将发送提供的以太(ether)和触发代码执行器。执行的代码被给予所有可用于执行的gas,使得这种类型的价值转移不安全,不能防止重入。

使用send()transfer()将防止重入,但这样做的代价是与任何后备功能需要超过2,300 gas的合约不兼容。我们也可以使用someAddress.call.value(ethAmount).gas(gasAmount)()来转发自定义量的gas。

试图平衡这种情况的一种模式是实现push和pull机制,对push组件使用send()transfer(),对pull组件使用call.value()()

值得指出的是,为了价值转移而专门使用的send()transfer()本身并不能使合约安全地防止重入,而只能使这些特定的价值转移对重入是安全的。

更多细节 https://consensys.github.io/smart-contract-best-practices/recommendations/#be-aware-of-the-tradeoffs-between-send-transfer-and-callvalue

添加transfer()的原因:https://github.com/ethereum/solidity/issues/610


call()也可用于发出低级CALL操作码以对另一个合约进行消息调用:

if (!contractAddress.call(bytes4(keccak256("someFunc(bool, uint256)")), true, 3)) {
    revert;
}

转发的valuegas可以定制:

contractAddress.call.gas(5000)
    .value(1000)(bytes4(keccak256("someFunc(bool, uint256)")), true, 3);

这等价于在合约中使用函数调用:

SomeContract(contractAddress).someFunc.gas(5000)
    .value(1000)(true, 3);

注意call()中输入数据的正确填充https://github.com/ethereum/solidity/issues/2884


transfer()send()call()函数由Solidity编译器转换为CALL操作码。

正如以太坊(Ethereum)wiki中的Subtleties页面所解释的那样:

CALL有多部分gas消耗:

  • 基础700
  • 如果值非零,则额外增加9000
  • 如果目标帐户尚不存在,则额外增加25000(注意:零余额和不存在是不一样的!)

非零值CALL操作的子消息(不是交易产生的顶级消息!)在呼叫账户提供的gas之上获得额外的2300gas; 这笔stipend可以看做是从非零值调用的9000强制性额外费用中支付。这可确保调用接收者始终拥有足够的gas来记录其收到的资金。

Solidity文档中解释了发送和接收以太(ether)

||
||

回答发布于 2018-09-11 21:26:59

sendtransfer都被认为是一种安全的资金转移方式,因为他们拥有2300的gas stipend。

如果你对添加transfer的原因感到好奇,可以查看有关该功能的原始讨论

||
||

回答发布于 2018-09-11 21:26:59

sendtransfer都被认为是一种安全的资金转移方式,因为他们拥有2300的gas stipend。

如果你对添加transfer的原因感到好奇,可以查看有关该功能的原始讨论

||
||

回答发布于 2018-09-11 21:26:58

address.transfer()

  • 失败了抛出异常
  • 转发2,300 gas stipend(不可调),安全防止重入
  • 应该在大多数情况下使用,因为这是发送以太(ether)的最安全的方法

address.send()

  • 失败了返回false
  • 转发2,300 gas stipend(不可调),安全防止重入
  • 当你想要处理合约中的失败时使用,应该在极少数情况下使用

address.call.value().gas()()

  • 失败了返回false
  • 转发所有可用的gas(可调),不能安全防止重入
  • 当你需要控制在发送以太(ether)时转发多少gas或调用另一个合约的功能时使用

详细版本如下:

使用someAddress.send()someAddress.transfer()someAddress.call.value()()之间的相对权衡:

  • someAddress.send()someAddress.transfer()被认为是安全的,可以防止重入。虽然这些方法仍然会触发代码执行,但是被调用的合约只能获得2,300 gas stipend,这个目前只够用于记录事件。
  • x.transfer(y)等价于require(x.send(y)),如果发送失败,它将自动恢复。
  • someAddress.call.value(y)()将发送提供的以太(ether)和触发代码执行器。执行的代码被给予所有可用于执行的gas,使得这种类型的价值转移不安全,不能防止重入。

使用send()transfer()将防止重入,但这样做的代价是与任何后备功能需要超过2,300 gas的合约不兼容。我们也可以使用someAddress.call.value(ethAmount).gas(gasAmount)()来转发自定义量的gas。

试图平衡这种情况的一种模式是实现push和pull机制,对push组件使用send()transfer(),对pull组件使用call.value()()

值得指出的是,为了价值转移而专门使用的send()transfer()本身并不能使合约安全地防止重入,而只能使这些特定的价值转移对重入是安全的。

更多细节 https://consensys.github.io/smart-contract-best-practices/recommendations/#be-aware-of-the-tradeoffs-between-send-transfer-and-callvalue

添加transfer()的原因:https://github.com/ethereum/solidity/issues/610


call()也可用于发出低级CALL操作码以对另一个合约进行消息调用:

if (!contractAddress.call(bytes4(keccak256("someFunc(bool, uint256)")), true, 3)) {
    revert;
}

转发的valuegas可以定制:

contractAddress.call.gas(5000)
    .value(1000)(bytes4(keccak256("someFunc(bool, uint256)")), true, 3);

这等价于在合约中使用函数调用:

SomeContract(contractAddress).someFunc.gas(5000)
    .value(1000)(true, 3);

注意call()中输入数据的正确填充https://github.com/ethereum/solidity/issues/2884


transfer()send()call()函数由Solidity编译器转换为CALL操作码。

正如以太坊(Ethereum)wiki中的Subtleties页面所解释的那样:

CALL有多部分gas消耗:

  • 基础700
  • 如果值非零,则额外增加9000
  • 如果目标帐户尚不存在,则额外增加25000(注意:零余额和不存在是不一样的!)

非零值CALL操作的子消息(不是交易产生的顶级消息!)在呼叫账户提供的gas之上获得额外的2300gas; 这笔stipend可以看做是从非零值调用的9000强制性额外费用中支付。这可确保调用接收者始终拥有足够的gas来记录其收到的资金。

Solidity文档中解释了发送和接收以太(ether)

||
||