以太坊知識教程------智慧合約的5種設計模式
阿新 • • 發佈:2021-02-06
技術標籤:以太坊Ethereum
1、自毀合約
合約自毀模式用於終止一個合約,這意味著將從區塊鏈上永久刪除這個合約。 一旦被銷燬,就不可能呼叫合約的功能,也不會在賬本中記錄交易。
eg. 貸款合約,它應當在貸款還清後自動銷燬;另一個案例是基於時間的拍賣合約,它應當在拍賣結束後 終止 —— 假設我們不需要在鏈上儲存拍賣的歷史記錄。
在處理一個被銷燬的合約時,有一些需要注意的問題:
- 合約銷燬後,傳送給該合約的交易將失敗。
- 任何傳送給被銷燬合約的資金,都將永遠丟失。
- 為避免資金損失,應當在傳送資金前確保目標合約仍然存在,移除所有對已銷燬合約的引用。
contract SelfDesctructionContract {
public address owner;
public string someValue;
modifier ownerRestricted {
require(owner == msg.sender);
_;
}
// constructor
function SelfDesctructionContract() {
owner = msg.sender;
}
// a simple setter function
function setSomeValue(string value){
someValue = value;
}
// you can call it anything you want
function destroyContract() ownerRestricted {
suicide(owner);
}
}
2、工廠合約
工廠合約用於建立和部署“子”合約。
工廠用於儲存子合約的地址,以便在必要時提取使用。
- 為什麼不把它們存在Web應用資料庫裡?
- 將地址資料存在工廠合約裡、存在區塊鏈上,更加安全不會丟失。
- 需要跟蹤所有新 建立的子合約以便同步更新資料庫。
eg. 銷售資產並跟蹤這些資產(例如,誰是資產的所有者)。 需要向負責部署資產的 函式新增payable修飾符以便銷售資產。
contract CarShop {
address[] carAssets;
function createChildContract(string brand, string model) public payable {
// insert check if the sent ether is enough to cover the car asset ...
address newCarAsset = new CarAsset(brand, model, msg.sender);
carAssets.push(newCarAsset);
}
function getDeployedChildContracts() public view returns (address[]) {
return carAssets;
}
}
contract CarAsset {
string public brand;
string public model;
address public owner;
function CarAsset(string _brand, string _model, address _owner) public {
brand = _brand;
model = _model;
owner = _owner;
}
}
3、名稱登錄檔
- 其實就是一個字串map一個struct物件。
- 問題: 假設一個合約依賴很多個合約,如果將所有這些合約的地址寫在你的應用程式碼中,如果這些合約的地址隨著時間的推移而變化,那該怎麼辦?
contract NameRegistry {
struct ContractDetails {
address owner;
address contractAddress;
uint16 version;
}
mapping(string => ContractDetails) registry;
function registerName(string name, address addr, uint16 ver) returns (bool) {
// versions should start from 1
require(ver >= 1);
ContractDetails memory info = registry[name];
require(info.owner == msg.sender);
// create info if it doesn't exist in the registry
if (info.contractAddress == address(0)) {
info = ContractDetails({
owner: msg.sender,
contractAddress: addr,
version: ver
});
} else {
info.version = ver;
info.contractAddress = addr;
}
// update record in the registry
registry[name] = info;
return true;
}
function getContractDetails(string name) constant returns(address, uint16) {
return (registry[name].contractAddress, registry[name].version);
}
}
4、對映表迭代器
由於mapping迭代效率低,所以需要一個mapping迭代模式。通過一個數組來儲存key。
實現put()函式的一個常見錯誤,是通過遍歷來檢查指定的鍵是否存在。
contract MappingIterator {
mapping(string => address) elements;
string[] keys;
function put(string key, address addr) returns (bool) {
bool exists = elements[key] == address(0)
if (!exists) {
keys.push(key);
}
elements[key] = addr;
return true;
}
function getKeyCount() constant returns (uint) {
return keys.length;
}
function getElementAtIndex(uint index) returns (address) {
return elements[keys[index]];
}
function getElement(string name) returns (address) {
return elements[name];
}
}
5. 提款模式
簡單來說,就是不用transfer,而是用send,因為一旦callback錯誤,transfer會導致異常,而send不會。
contract WithdrawalContract {
mapping(address => uint) buyers;
function buy() payable {
require(msg.value > 0);
buyers[msg.sender] = msg.value;
}
function withdraw() {
uint amount = buyers[msg.sender];
require(amount > 0);
buyers[msg.sender] = 0;
require(msg.sender.send(amount));
}
}