Contract-Development

合約之間的乙太幣交易

  • April 20, 2022

我試圖弄清楚合約之間的交易是如何運作的,以及你能做什麼和不能做什麼。我想要一份契約,當被另一份契約戳穿時,該契約對支付給誰做出一些合乎邏輯的決定。

這是我一直試圖模仿這種行為的契約:

pragma solidity ^0.4.17;

contract DecisionMaker{

   Sender sd;

   function someLogicToDecidePayment(address receiver, address sender) public {
       //Some logic then:
       sd = Sender(sender);
       sd.send_transfer(receiver, 1000);
   }

}

contract Sender {
   DecisionMaker dm;

   constructor() public payable{}

   function send(address _receiver) payable {
       _receiver.call.value(10000000).gas(20317)();
   }

   function send_transfer(address _receiver, uint _amount) payable public {
       _receiver.transfer(_amount);
   }

   function placeBuy(address receiver, address decisionMaker) public {
       dm = DecisionMaker(decisionMaker);
       dm.someLogicToDecidePayment(receiver, address(this));
   }


} 

contract Receiver {
   uint public balance = 0;

   constructor() public payable{}

   function () payable {
     balance += msg.value;
   }
}

嘗試從發件人契約執行 placeBuy 時。這將創建一個 DecisionMaker 實例,因此該函式可以執行 someLogicToDecidePayment()。按照一些邏輯來決定向誰付款,我希望合約從發送者合約呼叫 send_transfer 以向接收者合約發送一些乙太幣。但是,這失敗了,並出現以下錯誤:

VM error: revert.
revert  The transaction has been reverted to the initial state.
Note: The constructor should be payable if you send value.  Debug the transaction to get more information. 

如果我使用 Sender Contract 中的函式 send 而不是 send_transfer,則交易會通過,但 Receiver 的餘額不會增加。使用合約時,我似乎無法通過交易發送價值。誰能幫我理解為什麼我不能用這兩種方法成功地將乙太幣從一個契約轉移到另一個契約?

先感謝您。

編輯

按照 Lượng 的建議,我已向發送方和接收方添加了一個應付建構子。不幸的是,我仍然無法使用傳輸函式在合約之間發送乙太幣 - send_transfer() - 但是成功地使用了 send() 方法。我已經為任何想在未來測試它的人附上了我的測試腳本:

const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());

const compiledDecision = require('../ethereum/build/DecisionMaker.json');
const compiledReceiver = require('../ethereum/build/Receiver.json');
const compiledSender = require('../ethereum/build/Sender.json');

let accounts;
let decision;
let sender;
let receiver;
let confirmation;
let post_balance;


beforeEach(async () => {
   accounts = await web3.eth.getAccounts();

   decision = await new web3.eth.Contract(JSON.parse(compiledDecision.interface))
       .deploy({ data: compiledDecision.bytecode })
       .send({ from: accounts[0], gas: '1999999' });

   receiver = await new web3.eth.Contract(JSON.parse(compiledReceiver.interface))
       .deploy({ data: compiledReceiver.bytecode })
       .send({ from: accounts[0], gas: '1999999' });

   sender = await new web3.eth.Contract(JSON.parse(compiledSender.interface))
       .deploy({ data: compiledSender.bytecode })
       .send({ from: accounts[0], gas: '1999999' });

});

describe('Testing Contracts', () => {
   it('Attempts to make a transaction from sender to receiver through DecisionMaker', async () =>{
       let pre_balance = await receiver.methods.balance().call();

       await sender.methods.deposit().send({
           from: accounts[0],
           gas: 1000000,
           value: 1000000
       });

       let senderBalance = await web3.eth.getBalance(sender.options.address);
       console.log('senders contract balance', senderBalance);
       console.log('pre balance of receiver (variable)', pre_balance);

       try{
       let confirmation= await sender.methods.placeBuy(receiver.options.address, decision.options.address).send({
           from: accounts[0],
           gas: 1999999
       })
       post_balance = await receiver.methods.balance().call();
       }catch(err){
       console.log(err);
       }

   let balanceOfAddress = await web3.eth.getBalance(receiver.options.address);
   senderBalance = await web3.eth.getBalance(sender.options.address);
   console.log('confirmation of transaction', confirmation);
   console.log('post balance of receiver (variable)', post_balance);
   console.log('balance of receiver contract address', balanceOfAddress);
   console.log('senders balance post transaction', senderBalance);

   console.log('finished test');
   });
});

我仍然很好奇,如果有人能就為什麼 send_transfer() 函式不起作用但 send() 起作用的原因提出建議,我將不勝感激:)

The constructor should be payable if you send value.

如果您的智能合約使用應付函式,則創建應付建構子。在這種情況下,您的合約發件人尚未創建應付建構子

一些觀察。

我不認為依靠具有自定義備份功能的接收器是一種好的形式,除非您確定所有接收器都是您控制的契約。對於其他參與者來說,這不是一個現實的期望。

您遇到的另一個問題是與transferandsend方法相關的 2,300 gas 津貼。這些是通過使攻擊者挨餓來防止重入攻擊。2,300 足以接受資金並發出和事件,但僅此而已。

寫入儲存是不夠的(5,000 用於覆蓋非零,20,000 用於新插槽)。因此,balances[msg.sender] += ...導致氣體不足的情況。

我看到了兩種方法來處理這個問題,你可以決定哪種方式感覺正確。

  1. Escrow 方法:發件人將資金轉發給決策者,決策者要麼使用它們,要麼歸還它們。
  2. Approval 方法:Sender 從 Decider 獲得信號,如果獲得批准,則繼續進行支出。

無論哪種方式都可以編碼,因此它可以毫無例外地工作。

第一種方法需要一個時髦的call語法來將一些 eth 和所有的氣體轉發給 Decider。呼叫合約並從 Solidity 發送價值

決定然後去:

// decide what to do
if(isGo) {
 receiver.transfer(msg.value);
} else :
 msg.sender.transfer(msg.value);
}

另一種方式:Spender 通過以下方式尋求批准:

require(dm.approveThis(args, ...));
// you don't get here unless approveThis was true
receiver.transfer(amount);

希望能幫助到你。

引用自:https://ethereum.stackexchange.com/questions/54778