Solidity

基於地址類型轉換問題的合約多態性

  • January 31, 2021

我是剛接觸solidity 的新手,很難理解多態性和契約的類型轉換。

我將child_1契約的地址傳遞給getChild2Value(address adr),後者將地址類型轉換為child_2契約。它是否應該在我發送child_1地址並將其類型轉換為child_2時不引發錯誤或異常,而是從 child_1 函式獲得返回。

謝謝。

編譯指示 ^0.8.0;

contract parent{   
 
   function getValue( 
   ) public view virtual returns(uint) {   
       return 10;   
   }   
}   
 
contract child is parent{   
     
   function getValue( 
   ) public view override returns(uint) {   
       return 15;   
   }   
}   
   
contract child_2 is parent{   
     
   function getValue( 
   ) public view override returns(uint) {   
       return 20;   
   }   
}   
 
contract ContractPolymorphism {   
   function getChildValue(address adr) public view returns(uint){
       return child(adr).getValue();
   }
   function getChild2Value(address adr) public view returns(uint){
       return child_2(adr).getValue();
   }
}

在此處輸入圖像描述

我將 child_1 契約的地址傳遞給 getChild2Value(address adr)。它是否應該在我發送 child_1 地址並將其類型轉換為 child_2 時引發錯誤或異常

我認為您期待類型安全的響應,但部署的契約實例不支持該響應。換句話說,部署的合約不會被轉換為不同的類型。它們只是帶有字節碼的地址。換句話說,類型安全是一種編譯時特性,不會擴展到已部署的合約。

這就是說,從地址“adr”的“子”合約返回函式“getValue”的響應:

return child(adr).getValue();

它可以child(adr)毫無怨言地通過,因為它將接受任何地址並為其分配契約中child定義的類型。您對此類型的內部描述不必完整,也不一定準確。當您嘗試與合約互動時,不准確的地方就會開始出現。

它可以毫無問題地通過函式呼叫。

child(adr).getValue()

ContractPolymorphism關於這個外部合約的唯一相關資訊的角度來看,函式簽名和合約地址。它需要知道的一切都在編譯器製定的這個介面描述中擷取:

function getValue() external view returns(uint);

兩個版本中都存在相同的功能,child因此按預期發送消息並接收響應。

為了說明繼承和組合是如何工作的,請考慮以下一組契約:

interface IERC20 { ... // minimal
contract ERC20 is IERC20 { ... // everything important

contract TokenA is ERC20 { ... // instances
contract TokenB is ERC20 { ...
contract TokenC is ERC20 { ...

contract WorkWithAnyToken {

 // when a contract is a function param, externally it is an address
 // and internally, it is cast as a contract.

 function withFromAny(IERC20 token, uint amount) public ... 
   token.transfer(msg.sender, amount);
 }
}

分解它:

  1. 該介面具有未定義的功能。它列出了外部介面。
  2. 履行承諾。通過繼承介面,我們知道除非定義了介面的所有功能,否則它不會部署。
  3. 製作代幣很容易。它們都可以具有獨特的屬性,但必須實現相同的基本功能,並且我們知道基本介面沒有偏離。
  4. 我們可以製作一個與介面抽像一起工作的函式(它不需要實際令牌的所有程式碼 - 只需要介面)。我們需要一個地址來實例化令牌。我們可以function foo(address bar) ... { IERC20 token = IERC20(bar);在函式參數中將其轉換為 IERC20。從外部看,沒有區別。它始終是一個地址,因為 ABI 不支持像合約這樣的複雜類型。它是內部類型安全的 - 在編譯時檢查。

重要的是,沒有檢查傳入的地址是三個定義的代幣之一,甚至是其他人的 ERC20 合約。是的,您可以使用 IERC20 來實例化 ERC20。您只是在說“這是在地址上與合約對話的方式”。因此,如果您想將輸入的地址限制為“受信任的”合約,那麼您的合約需要一個白名單,並且您的函式需要在互動之前進行檢查。

如果沒有這樣的功能,在互動階段就會出現問題。如果您盲目地信任並且結果證明它不是預期類型的實例,則會導致執行時錯誤。

對於咯咯笑,考慮一下這種匯總這些觀點的安排。部署父母和家庭。然後敲擊父級以部署子級。查看事件日誌中的子地址。與家人一起檢查:

// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.7.4;

interface IChild {
  function birthday() external view returns(uint);  
}

contract Child is IChild {
   
   uint public override birthday;
   
   constructor() {
       birthday = block.timestamp;
   }
}


contract Parent {
   
   event NewChild(Child child);
   
   function newChild() public returns(Child child) {
       child = new Child();
       emit NewChild(child);
   }
}

contract Family {
   
   // The interface is more compact than the implementation which makes this contract smaller. 
   // We're only concerned with the message format so the contracts can talk. 
   
   function getBirthday(IChild child) public view returns(uint birthday) {
       return child.birthday();
   }
}

希望能幫助到你。

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