從 Solidity 與其他合約互動
在先前的教學中,我們學習了許多知識,像是如何部署您的第一個智能合約,以及為它新增一些功能,例如使用修飾詞控制存取 (opens in a new tab)或在 Solidity 中處理錯誤 (opens in a new tab)。 在本教學中,我們將學習如何從現有合約部署智能合約並與其互動。
我們將建立一個名為 CounterFactory 的合約工廠,讓任何人都能夠透過它來建立自己的 Counter 智能合約。 首先,這是我們初始 Counter 智能合約的程式碼:
1pragma solidity 0.5.17;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "您不是此合約的擁有者");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "您需要使用工廠");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}顯示全部請注意,我們稍微修改了合約程式碼,以便追蹤工廠的地址和合約擁有者的地址。 當您從另一個合約呼叫某合約的程式碼時,msg.sender 將會是我們合約工廠的地址。 這是一個非常重要的理解要點,因為使用合約與其他合約互動是常見的做法。 因此,在複雜情況下,您應該要留意誰是發送者。
為此,我們也新增了 onlyFactory 修飾詞,它能確保改變狀態的函式只能由工廠呼叫,工廠會將原始呼叫者當作參數傳遞。
在我們新的 CounterFactory(它會管理所有其他的 Counter)內部,我們會新增一個 mapping,將擁有者與其計數器合約的地址關聯起來:
1mapping(address => Counter) _counters;在以太坊中,映射 (mapping) 相當於 javascript 中的物件,可以將 A 型別的鍵對應到 B 型別的值。在此案例中,我們將擁有者的地址與其 Counter 的實例對應起來。
為某人實例化一個新的 Counter,看起來會像這樣:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }我們首先檢查該使用者是否已擁有一個計數器。 如果該使用者沒有計數器,我們會將其地址傳遞給 Counter 建構函式以實例化一個新的計數器,並將新建立的實例指派給 mapping。
要取得特定 Counter 的計數,會像這樣:
1function getCount(address account) public view returns (uint256) {2 require (_counters[account] != Counter(0));3 return (_counters[account].getCount());4}56function getMyCount() public view returns (uint256) {7 return (getCount(msg.sender));8}第一個函式會檢查指定地址是否存在 Counter 合約,然後從實例中呼叫 getCount 方法。 第二個函式 getMyCount 只是個簡潔的作法,直接將 msg.sender 傳遞給 getCount 函式。
increment 函式非常相似,但是它將原始交易的發送者傳遞給 Counter 合約:
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }請注意,如果呼叫太多次,我們的計數器可能會發生溢出。 您應該盡可能使用 SafeMath 函式庫 (opens in a new tab),以防止這種可能的情況發生。
要部署我們的合約,您將需要提供 CounterFactory 和 Counter 兩者的程式碼。 例如在 Remix 中部署時,您需要選擇 CounterFactory。
這是完整的程式碼:
1pragma solidity 0.5.17;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "您不是此合約的擁有者");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "您需要使用工廠");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}3435contract CounterFactory {3637 mapping(address => Counter) _counters;3839 function createCounter() public {40 require (_counters[msg.sender] == Counter(0));41 _counters[msg.sender] = new Counter(msg.sender);42 }4344 function increment() public {45 require (_counters[msg.sender] != Counter(0));46 Counter(_counters[msg.sender]).increment(msg.sender);47 }4849 function getCount(address account) public view returns (uint256) {50 require (_counters[account] != Counter(0));51 return (_counters[account].getCount());52 }5354 function getMyCount() public view returns (uint256) {55 return (getCount(msg.sender));56 }5758}顯示全部編譯後,在 Remix 的部署區塊中,您將會選擇要部署的工廠:
然後您可以操作您的合約工廠,並檢查值的變化。 如果您想從不同的地址呼叫智能合約,您需要在 Remix 的帳戶 (Account) 選項中變更地址。
頁面最後更新時間: 2023年8月15日
