跳至主要内容

從 Solidity 與其他合約互動

smart contracts
solidity
remix
deploying
composability
進階
jdourlens
2020年4月5日
5 分鐘閱讀

在先前的教學中,我們學習了許多知識,像是如何部署您的第一個智能合約,以及為它新增一些功能,例如使用修飾詞控制存取 (opens in a new tab)在 Solidity 中處理錯誤 (opens in a new tab)。 在本教學中,我們將學習如何從現有合約部署智能合約並與其互動。

我們將建立一個名為 CounterFactory 的合約工廠,讓任何人都能夠透過它來建立自己的 Counter 智能合約。 首先,這是我們初始 Counter 智能合約的程式碼:

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "您不是此合約的擁有者");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "您需要使用工廠");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
顯示全部

請注意,我們稍微修改了合約程式碼,以便追蹤工廠的地址和合約擁有者的地址。 當您從另一個合約呼叫某合約的程式碼時,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}
5
6function 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),以防止這種可能的情況發生。

要部署我們的合約,您將需要提供 CounterFactoryCounter 兩者的程式碼。 例如在 Remix 中部署時,您需要選擇 CounterFactory。

這是完整的程式碼:

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "您不是此合約的擁有者");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "您需要使用工廠");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
34
35contract CounterFactory {
36
37 mapping(address => Counter) _counters;
38
39 function createCounter() public {
40 require (_counters[msg.sender] == Counter(0));
41 _counters[msg.sender] = new Counter(msg.sender);
42 }
43
44 function increment() public {
45 require (_counters[msg.sender] != Counter(0));
46 Counter(_counters[msg.sender]).increment(msg.sender);
47 }
48
49 function getCount(address account) public view returns (uint256) {
50 require (_counters[account] != Counter(0));
51 return (_counters[account].getCount());
52 }
53
54 function getMyCount() public view returns (uint256) {
55 return (getCount(msg.sender));
56 }
57
58}
顯示全部

編譯後,在 Remix 的部署區塊中,您將會選擇要部署的工廠:

在 Remix 中選擇要部署的工廠

然後您可以操作您的合約工廠,並檢查值的變化。 如果您想從不同的地址呼叫智能合約,您需要在 Remix 的帳戶 (Account) 選項中變更地址。

頁面最後更新時間: 2023年8月15日

這個使用教學對你有幫助嗎?