Hướng dẫn cách tự tạo coin riêng của bạn trên Ethereum - DashVN

Latest

Tự do, công nghệ & đầu tư

Wednesday, August 16, 2017

Hướng dẫn cách tự tạo coin riêng của bạn trên Ethereum

Tạo một coin với blockchain tuy đã khá dễ dàng nhờ việc sửa mã nguồn của những coin có sẵn, nhưng sau đó bạn cần nhiều điều như cộng đồng người đào coin, cộng đồng người sử dụng và quảng bá cho nó... nói chung mất khá nhiều công sức vào cả những công việc liên quan đến kỹ thuật và truyền thông. Nhưng một cách thức còn dễ dàng hơn đó là tạo coin trên nền tảng của Ethereum. Hướng dẫn sau đây sẽ giúp bạn tự tạo ra coin riêng của mình trên blockchain của Ethereum. Với nền tảng này tuy bị hạn chế nhưng lại giúp bạn không phải lo lắng về kỹ thuật cũng như không cần bận tâm đến việc đào coin. Bạn có thể phát hành coin và làm ICO luôn.

Đồng coin

Chúng ta sẽ tạo ra một token. Token trong hệ sinh thái của Ethereum có thể là bất cứ dạng gì có thể giao dịch được như: coin, điểm thưởng, chứng chỉ vàng, chứng chỉ nợ, các đồ trong game,... Như vậy tất cả các token đều có những tính năng cơ bản theo một cách chuẩn như nhau. Điều đó cũng có nghĩa là các token của bạn sẽ lập tức tương thích với ví của Ethereum cũng như các phần mềm ví sử dụng cùng chuẩn này.

Mã nguồn

Bạn chỉ cần copy và dán mã nguồn sau đây và dùng luôn

pragma solidity ^0.4.8; contract tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData); } contract MyToken { /* Public variables of the token */ string public standard = 'Token 0.1'; string public name; string public symbol; uint8 public decimals; uint256 public totalSupply; /* This creates an array with all balances */ mapping (address => uint256) public balanceOf; mapping (address => mapping (address => uint256)) public allowance; /* This generates a public event on the blockchain that will notify clients */ event Transfer(address indexed from, address indexed to, uint256 value); /* This notifies clients about the amount burnt */ event Burn(address indexed from, uint256 value); /* Initializes contract with initial supply tokens to the creator of the contract */ function MyToken( uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol ) { balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens totalSupply = initialSupply; // Update total supply name = tokenName; // Set the name for display purposes symbol = tokenSymbol; // Set the symbol for display purposes decimals = decimalUnits; // Amount of decimals for display purposes } /* Send coins */ function transfer(address _to, uint256 _value) { if (_to == 0x0) throw; // Prevent transfer to 0x0 address. Use burn() instead if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows balanceOf[msg.sender] -= _value; // Subtract from the sender balanceOf[_to] += _value; // Add the same to the recipient Transfer(msg.sender, _to, _value); // Notify anyone listening that this transfer took place } /* Allow another contract to spend some tokens in your behalf */ function approve(address _spender, uint256 _value) returns (bool success) { allowance[msg.sender][_spender] = _value; return true; } /* Approve and then communicate the approved contract in a single tx */ function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { tokenRecipient spender = tokenRecipient(_spender); if (approve(_spender, _value)) { spender.receiveApproval(msg.sender, _value, this, _extraData); return true; } } /* A contract attempts to get the coins */ function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { if (_to == 0x0) throw; // Prevent transfer to 0x0 address. Use burn() instead if (balanceOf[_from] < _value) throw; // Check if the sender has enough if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows if (_value > allowance[_from][msg.sender]) throw; // Check allowance balanceOf[_from] -= _value; // Subtract from the sender balanceOf[_to] += _value; // Add the same to the recipient allowance[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } function burn(uint256 _value) returns (bool success) { if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough balanceOf[msg.sender] -= _value; // Subtract from the sender totalSupply -= _value; // Updates totalSupply Burn(msg.sender, _value); return true; } function burnFrom(address _from, uint256 _value) returns (bool success) { if (balanceOf[_from] < _value) throw; // Check if the sender has enough if (_value > allowance[_from][msg.sender]) throw; // Check allowance balanceOf[_from] -= _value; // Subtract from the sender totalSupply -= _value; // Updates totalSupply Burn(_from, _value); return true; } }

Token cơ bản nhất

Hợp đồng token là rất phức tạp. Nhưng về cơ bản thì một token sẽ có như sau:

contract MyToken { /* This creates an array with all balances */ mapping (address => uint256) public balanceOf; /* Initializes contract with initial supply tokens to the creator of the contract */ function MyToken( uint256 initialSupply ) { balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens } /* Send coins */ function transfer(address _to, uint256 _value) { if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows balanceOf[msg.sender] -= _value; // Subtract from the sender balanceOf[_to] += _value; // Add the same to the recipient } }

Hiểu mã nguồn thế nào
Nào chúng ta hãy bắt đầu với những gì cơ bản. Hãy mở phần mềm ví, vào trang Contracts và sau đó Deploy New Contract. Trên phần Solidity Contract Source Code, hãy gõ đoạn mã sau:

contract MyToken { /* This creates an array with all balances */ mapping (address => uint256) public balanceOf; }

Một mapping (ánh xạ) có nghĩa là một mảng liên kết, nơi bạn liên kết các địa chỉ với số dư của nó. Còn địa chỉ Ethereum được định dạng ở hệ số 16, trong đó số dư là các số nguyên từ 0 đến 115 quattuorvigintillion. Nếu bạn không biết một quattuorvigintillion có giá trị bao nhiêu, thì nó nhiều vigintillions nhiều hơn bất cứ điều gì bạn đang có kế hoạch sử dụng token của bạn. Từ khoá public có nghĩa là biến có thể truy cập bởi bất cứ ai trên blockchain, và có nghĩa là số dư thì đều ở dạng công khai (nó cần như vậy để các phần mềm có thể hiển thị được chúng)
Nếu bạn muốn xuất bản hợp đồng luôn, nó cũng sẽ hoạt động nhưng không hữu ích lắm: nó sẽ là một hợp đồng mà có thể truy vấn số dư coin của bất kỳ địa chỉ nào, nhưng nếu bạn chưa từng tạo một coin nào thì tất cả các địa chỉ đó đều có số dư bằng 0. Vì vậy chúng ta sẽ tạo vài coin ngay lúc đầu. Hãy thêm đoạn mã sau đây trước dấu ngoặc cuối cùng, ngay sau dòng mapping...

function MyToken() { balanceOf[msg.sender] = 21000000; }

Chú ý rằng hàm MyToken có cùng tên với hợp đồng MyToken. Điều này là rất quan trọng và nếu bạn đổi tên cái này bạn cũng phải đổi tên cái kia: đây là điều đặc biệt, hàm khởi động chỉ chạy một lần khi hợp đồng lần đầu tiên được tải vào mạng lưới. Hàm này sẽ thiết lập số dư của msg.sender, đây là người triển khai hợp đồng với số dư là 21 triệu.

Lựa chọn 21 triệu khá là ngẫu nhiên, và bạn có thể chọn bất cứ thứ gì bạn muốn, nhưng có một cách khác tốt hơn đó là một tham số cho hàm, nó giống thế này:

function MyToken(uint256 initialSupply) { balanceOf[msg.sender] = initialSupply; }

Hãy nhìn cột bên phải bên cạnh hợp đồng bạn sẽ nhìn thấy một hộp thả xuống, trên đó có ghi pic a contract. Hãy chọn hợp đồng "MyToken" và bạn sẽ nhìn thấy nó hiển thị trong phần gọi là Contructor parameters. Đó là những tham số có thể thay đổi được trong token của bạn, và bạn có thể tái sử dụng đoạn mã đó và chỉ thay đổi các biến trong tương lai.
Bây giờ bạn có một hợp đồng chức năng tạo được ra số dư của các token nhưng vì không có chức năng nào để di chuyển nó, nên tất cả đều nằm trên cùng một tài khoản. Và bây giờ chúng ta sẽ thực hiện chức năng đó. Hãy viết đoạn mã sau ngay trước dấu ngoặc cuối cùng.

/* Send coins */ function transfer(address _to, uint256 _value) { /* Add and subtract new balances */ balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; }

Đây là một chức năng rất đơn giản: nó có tham số là một người nhân và một giá trị, và khi có ai gọi hàm này nó sẽ trừ giá trị _value từ số dư của họ và thêm nó vào số dự của địa chỉ _to. Bây giờ có một vấn đề hiển nhiên là: điều gì sẽ xảy ra nếu một ai đó muốn gửi nhiều hơn những gì họ có? Và chúng ta không muốn phải xử lý vấn đề nợ trong hợp đồng đặc biệt này, do đó chúng ta chỉ việc kiểm tra nhanh và nếu người gửi không có đủ tiền thì hợp đồng sẽ đơn giản là dừng lại. Nó cũng kiểm tra tránh việc tràn số, để tránh trường hợp một số quá lớn và nó trở lại thành 0 lần nữa.

Để ngăn chặn việc thực hiện hợp đồng giữa lúc thực hiện, bạn có thể thoát hoặc tạo ra ngoại lệ (throw). Cách trước sẽ tốn ít gas hơn nhưng nó có thể làm nhức đầu hơn vì bất kỳ thay đổi nào bạn đã làm với hợp đồng cho đến thời điểm này sẽ được giữ lại. Cách còn lại thì khác, "throw" sẽ huỷ việc thực hiện toàn bộ của hợp đồng và đảo ngược những thay đổi mà giao dịch có thể đã được thực hiện và người gửi sẽ mất tất cả số Ether mà anh ta gửi để có gas. Tuy nhiên, kể từ khi Ví có thể phát hiện thấy một hợp đồng sẽ throw, nó luôn hiển thị một cảnh báo, do đó ngăn ngừa bất kỳ Ether được chi tiêu.

function transfer(address _to, uint256 _value) { /* Check if sender has balance and for overflows */ if (balanceOf[msg.sender] < _value || balanceOf[_to] + _value < balanceOf[_to]) throw; /* Add and subtract new balances */ balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; }

Bây giờ tất cả những gì còn thiếu là có một số thông tin cơ bản về hợp đồng. Trong tương lai gần, điều này có thể được xử lý bằng token registry, nhưng bây giờ chúng ta sẽ thêm chúng trực tiếp vào hợp đồng:

string public name; string public symbol; uint8 public decimals;

Và bây giờ chúng ta cập nhật hàm contructor để cho phép tất cả các biến đó được thiết lập ngay ở đầu:

/* Initializes contract with initial supply tokens to the creator of the contract */ function MyToken(uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol) { balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens name = tokenName; // Set the name for display purposes symbol = tokenSymbol; // Set the symbol for display purposes decimals = decimalUnits; // Amount of decimals for display purposes }

Cuối cùng, bây giờ chúng ta cần cái gì đó gọi là Sự kiện. Đây là những hàm trống đặc biệt nó cho phép bạn gọi để giúp các phần mềm như ví Ethereum theo dõi những hành vi xuất hiện trong hợp đồng. Sự kiện cần bắt đầu bằng một chữ cái. Hãy thêm dòng này ở phần đầu của hợp đồng để khai báo sự kiện:

event Transfer(address indexed from, address indexed to, uint256 value);

Và sau đó bạn chỉ cần thêm hai dòng bên trong hàm "Transfer"

/* Notify anyone listening that this transfer took place */ Transfer(msg.sender, _to, _value);

Bây giờ token đã sẵn sàng.

Triển khai như thế nào

Nếu bạn chưa mở phần mềm ví Ethereum, hãy mở Wallet Ethereum, vào trang Contracts và sau đó bấm vào "Deploy New Contract".

Bây giờ lấy phần mã nguồn từ phía trên và dán nó vào phần "Solidity Source Field". Nếu trình biên dịch mà không có lỗi nào, bạn sẽ thấy một ô thả xuống "pick a contract" ở bên phải. Hãy bấm vào đó và chọn hợp đồng "MyToken". Ở cột bên phải bạn sẽ thấy tất cả các tham số bạn cần để tuỳ chỉnh token của bạn. Bạn có thể điều chỉnh nó nếu muốn, nhưng do mục đích của bài hướng dẫn này chúng tôi khuyên bạn chọn những thông số như sau: 10,000 cho số lượng coin (supply), tên (name) thì bạn chọn gì cũng được, "%" cho ký hiệu  (symbol) và 2 chữ số sau phần thập phân (decimals).  App của bạn có dạng như sau:
Cuộn đến cuối trang và bạn sẽ thấy ước tính chi phí tính toán của hợp đồng đó và bạn có thể chọn một khoản phí cho bao nhiêu ête mà bạn sẵn sàng trả cho nó. Bất kỳ Ether vượt quá mà bạn không chi tiêu sẽ được trả lại cho bạn nên bạn có thể để ở chế độ thiết lập mặc định nếu muốn. Hãy bấm "deploy", gõ tên tài khoản và mật khẩu và đợi vài giây để giao dịch của bạn được chọn.

Bạn sẽ được chuyển hướng đến trang đầu tiên, nơi bạn có thể thấy giao dịch của bạn đang chờ xác nhận. Nhấp vào tài khoản có tên "Etherbase" (tài khoản chính của bạn) và sau không quá một phút, bạn sẽ thấy trên tài khoản của bạn hiển thị rằng bạn có 100% cổ phiếu bạn vừa tạo. Để gửi một số cho một vài người: chọn "Send", sau đó chọn loại tiền tệ bạn muốn gửi (Ether hoặc token vừa tạo), dán địa chỉ của người bạn vào trường "To" và nhấn "Send".
Nếu bạn gửi nó cho bạn bè, họ sẽ không nhìn thấy gì trong ví của họ. Điều này là do ví chỉ theo dõi các token mà nó biết và bạn phải thêm các token này theo cách thủ công. Bây giờ đi đến tab "Contracts" và bạn sẽ thấy một liên kết cho hợp đồng mới tạo của bạn. Nhấp vào nó để đi đến trang của nó. Vì đây là một trang hợp đồng rất đơn giản, không có nhiều việc phải làm ở đây, chỉ cần nhấp vào "copy address" và dán địa chỉ hợp đồng vào một trình soạn thảo văn bản, bạn sẽ cần nó ngay sau đây.

Để thêm một token để theo dõi, hãy đi đến trang Contracts và sau đó nhấp vào "Watch Token". Một cửa sổ popup sẽ xuất hiện và bạn chỉ cần dán địa chỉ hợp đồng. Tên token, ký hiệu và số thập phân sẽ được tự động điền nhưng nếu nó không tự điền thì bạn có thể tự điền bất cứ thứ gì bạn muốn (nó sẽ chỉ có tác dụng hiển thị trên ví của bạn). Khi bạn thực hiện việc này, bạn thấy được hiển thị số dư mà bạn có của token đó và bạn sẽ có thể gửi nó cho bất kỳ ai khác.

Và bây giờ thì bạn đã có token riêng của mình rồi. Bản thân các Token có thể đã hữu ích như là giá trị trao đổi của cộng đồng địa phương, cách theo dõi thời gian làm việc hoặc tích điểm cho khách hàng trung thành. Nhưng liệu chúng ta có thể tạo ra một loại tiền mà có một giá trị nội tại bằng cách làm cho nó trở nên hữu ích?

Cải tiến token của bạn

Bạn có thể triển khai toàn bộ token của mình mà không cần chạm vào một dòng mã, nhưng sự kỳ diệu thực sự xảy ra khi bạn bắt đầu tùy chỉnh nó. Các phần sau đây sẽ là các đề xuất về các chức năng mà bạn có thể thêm vào token của mình để làm cho nó phù hợp với nhu cầu của bạn hơn.


Quản trị trung tâm


Tất cả các DApp đều được mặc định là phi tập trung, nhưng điều đó không có nghĩa là chúng không thể có một số loại quản lý trung tâm, nếu bạn muốn. Có thể bạn muốn có khả năng tạo thêm coin, có thể bạn muốn cấm một số người sử dụng tiền của bạn. Bạn có thể thêm bất kỳ những tính năng nào vào, và tất cả những người giữ token sẽ luôn biết chính xác các quy đinh của trò chơi trước khi họ quyết định sở hữu chúng.


Để điều đó xảy ra, bạn cần một bộ điều khiển trung tâm tiền tệ. Đây có thể là một tài khoản đơn giản, nhưng cũng có thể là hợp đồng và do đó quyết định tạo thêm nhiều token phụ thuộc vào hợp đồng: nếu đó là một tổ chức dân chủ thì có thể có chức năng bỏ phiếu, hoặc nó có thể nó chỉ là một cách để hạn chế Sức mạnh của người sở hữu token.

Để làm điều đó chúng ta sẽ học về một thuộc tính rất hữu ích của hợp đồng: đó là tính thừa kế (inheritance). Thừa kế cho phép một hợp đồng thu nhận các thuộc tính của một hợp đồng cha, mà không cần phải định nghĩa lại tất cả chúng. Điều này làm cho mã nguồn sáng sủa hơn và dễ tái sử dụng hơn. Hãy thêm đoạn mã này vào dòng đầu tiên của mã nguồn của bạn, trước dòng MyToken {.

contract owned { address public owner; function owned() { owner = msg.sender; } modifier onlyOwner { if (msg.sender != owner) throw; _; } function transferOwnership(address newOwner) onlyOwner { owner = newOwner; } }

Điều này tạo ra một hợp đồng rất cơ bản mà không làm bất cứ điều gì ngoại trừ việc định nghĩa một số chức năng chung chung về một hợp đồng mà nó có thể được "sở hữu". Bây giờ bước tiếp theo là chỉ cần thêm đoạn mã is owned vào hợp đồng của bạn:

contract MyToken is owned { /* the rest of the contract as usual */

Điều này có nghĩa là tất cả các hàm bên trong MyToken bây giờ có thể truy cập vào biến owner và modifier onlyOwner. Hợp đồng cũng có một hàm để chuyển quyền sở hữu. Vì nó có thể khá thú vị để thiết lập chủ sở hữu của hợp đồng lúc khởi động, bạn cũng có thể thêm vào hàm constructor:

function MyToken( uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol, address centralMinter ) { if(centralMinter != 0 ) owner = centralMinter; 


Trung tâm tạo coin

Giả sử bạn muốn số lượng coin lưu hành được thay đổi. Đây là trường hợp mà token của bạn đại diện cho các tài sản không trên blockchain (như chứng chỉ vàng hay tiền của chính phủ phát hành) và bạn muốn tài sản ảo phản ánh tài sản thật. Đây cũng là trường hợp mà người sở hữu tiền kỳ vọng kiểm soát được giá của token, và muốn phát hành hoặc xoá bỏ lượng token khỏi lưu thông.

Đầu tiên chúng ta cần thêm một biến để lưu tổng số coin (totalSupply) và gán nó trong hàm constructor.

contract MyToken { uint256 public totalSupply; function MyToken(...) { totalSupply = initialSupply; ... } ... }

Bây giờ thêm một chức năng mới mà nó cho phép người chủ tạo thêm token mới:

function mintToken(address target, uint256 mintedAmount) onlyOwner { balanceOf[target] += mintedAmount; totalSupply += mintedAmount; Transfer(0, owner, mintedAmount); Transfer(owner, target, mintedAmount); }


Chú ý modifier onlyOwner ở phần cuối của tên hàm. Nó có nghĩa là hàm này sẽ được viết lại trong quá trình biên dịch để kế thừa mã từ chính modifier onlyOwner mà chúng ta đã định nghĩa trước đây. Đoạn mã của hàm này sẽ được chèn vào nơi có một gạch dưới trên hàm modifier, điều đó có nghĩa là hàm đặc biệt này chỉ có thể được gọi bởi tài khoản được thiết lập là chủ sở hữu. Chỉ việc thêm nó vào hợp đồng với modifier owner và bạn có thể tạo thêm nhiều coin nữa.

Đóng băng tài sản

Tùy thuộc vào trường hợp sử dụng của bạn, bạn có thể cần phải có một số rào cản về quy định đối với những người có thể và không thể sử dụng các token của bạn. Để điều đó xảy ra, bạn có thể thêm một tham số cho phép chủ sở hữu hợp đồng đóng băng hoặc gỡ băng tài sản.

Thêm biến này và chức năng này bất cứ nơi nào bên trong hợp đồng. Bạn có thể đặt chúng ở bất cứ đâu nhưng cách thực hành tốt, chúng tôi khuyên bạn nên đặt các ánh xạ (mappings) với nhau và sự kiện (events) với nhau.

mapping (address => bool) public frozenAccount; event FrozenFunds(address target, bool frozen); function freezeAccount(address target, bool freeze) onlyOwner { frozenAccount[target] = freeze; FrozenFunds(target, freeze); }

Với đoạn mã này, tất cả các tài khoản theo mặc định là không bị đóng băng nhưng chủ sở hữu có thể đặt bất kỳ tài khoản nào vào trạng thái đóng băng bằng cách gọi freezeAccount. Thật không may đóng băng không có hiệu quả thiết thực, bởi vì chúng ta đã không thêm bất cứ điều gì vào chức năng chuyển nhượng. Chúng ta đang thay đổi điều đó ngay bây giờ:

function transfer(address _to, uint256 _value) { if (frozenAccount[msg.sender]) throw;

Bây giờ bất kỳ tài khoản nào bị đóng băng vẫn sẽ còn nguyên số dư, nhưng sẽ không thể chuyển chúng. Tất cả các tài khoản đều ngầm định là không bị đóng băng cho đến khi bạn đóng băng chúng, nhưng bạn có thể dễ dàng khôi phục lại hành vi đó vào danh sách whitelist nơi bạn cần chấp thuận bằng tay cho mọi tài khoản. Chỉ cần đổi tên frozenAccount thành approvedAccount và thay đổi dòng cuối cùng thành:

if (!approvedAccount[msg.sender]) throw;

Tự động mua và bán

Cho đến nay, bạn đã dựa vào tiện ích và sự tin cậy để đánh giá trị token của bạn. Nhưng nếu bạn muốn bạn có thể làm cho giá trị của token dựa trên giá trị của Ether (hoặc các token) bằng cách tạo ra một quỹ tự động bán và mua chúng theo giá thị trường.

Trước tiên, hãy đặt giá mua và bán:

uint256 public sellPrice; uint256 public buyPrice; function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner { sellPrice = newSellPrice; buyPrice = newBuyPrice; }

Điều này có thể chấp nhận được với mức giá không thay đổi thường xuyên, vì mọi thay đổi giá mới đều yêu cầu bạn phải thực hiện giao dịch và chi tiêu một chút Ether. Nếu bạn muốn có giá thả nổi liên tục, chúng tôi khuyên bạn nên nghiên cứu standard data feeds.

Bước tiếp theo là thực hiện các hàm mua và bán:

function buy() payable returns (uint amount){ amount = msg.value / buyPrice; // calculates the amount if (balanceOf[this] < amount) throw; // checks if it has enough to sell balanceOf[msg.sender] += amount; // adds the amount to buyer's balance balanceOf[this] -= amount; // subtracts amount from seller's balance Transfer(this, msg.sender, amount); // execute an event reflecting the change return amount; // ends function and returns } function sell(uint amount) returns (uint revenue){ if (balanceOf[msg.sender] < amount ) throw; // checks if the sender has enough to sell balanceOf[this] += amount; // adds the amount to owner's balance balanceOf[msg.sender] -= amount; // subtracts the amount from seller's balance revenue = amount * sellPrice; if (!msg.sender.send(revenue)) { // sends ether to the seller: it's important throw; // to do this last to prevent recursion attacks } else { Transfer(msg.sender, this, amount); // executes an event reflecting on the change return revenue; // ends function and returns } }

Lưu ý rằng điều này sẽ không tạo ra các token mới nhưng thay đổi số dư mà hợp đồng sở hữu. Hợp đồng có thể giữ cả token của nó và Ether, và chủ sở hữu hợp đồng, trong khi có thể thiết lập giá hoặc trong một số trường hợp tạo ra các token mới (nếu có), nhưng nó không thể chạm vào token hoặc Ether của ngân hàng. Cách duy nhất hợp đồng này có thể chuyển tiền là bằng cách bán và mua chúng.

Chú ý: Việc mua và bán thì "giá" không được đặt bằng Ether, nhưng bằng wei là loại đơn vị tối thiểu của hệ thống (tương tự như cent với Euro và đô la, hoặc Satoshi với Bitcoin). Một ether bằng 1000000000000000000 wei. Vì vậy, khi đặt giá cho token của bạn bằng Ether, hãy thêm 18 số không ở cuối.

Khi tạo hợp đồng, hãy gửi đủ số Ether để có thể mua lại tất cả các token trên thị trường nếu không hợp đồng của bạn sẽ bị phá sản và người dùng của bạn sẽ không thể bán được token của họ.

Các ví dụ trước đây, tất nhiên, mô tả một hợp đồng với một người mua và người bán trung tâm, một hợp đồng thú vị hơn sẽ cho phép một thị trường nơi bất cứ ai có thể đặt giá khác nhau, hoặc có thể nó sẽ tải giá trực tiếp từ một nguồn bên ngoài.

Tự động làm đầy

Mỗi khi bạn giao dịch trên Ethereum bạn cần phải trả phí cho miner của block mà nó tính toán kết quả của hợp đồng thông minh của bạn. Điều này có thể thay đổi trong tương lai, còn hiện tại phí chỉ có thể thanh toán bằng Ether và do đó tất cả người dùng token của bạn cũng cần có Ether. Tokens trong các tài khoản có số dư nhỏ hơn phí sẽ bị mắc kẹt cho đến khi chủ sở hữu có thể trả phí. Nhưng trong một số trường hợp, bạn có thể không muốn người dùng suy nghĩ về Ethereum, blockchain hoặc làm thế nào để có được Ether, vì vậy có một cách tiếp cận có thể sẽ làm cho đồng coin của bạn tự động nạp lại số dư của người dùng ngay khi phát hiện số dư đang ở mức thấp nguy hiểm.

uint minBalanceForAccounts; function setMinBalance(uint minimumBalanceInFinney) onlyOwner { minBalanceForAccounts = minimumBalanceInFinney * 1 finney; }

Sau đó, thêm đoạn mã này vào hàm transfer để người gửi được hoàn trả:

/* Send coins */ function transfer(address _to, uint256 _value) { ... if(msg.sender.balance<minBalanceForAccounts) sell((minBalanceForAccounts-msg.sender.balance)/sellPrice); }

Hoặc bạn cũng có thể thay đổi nó để người nhận trả phí thay cho người gửi.

/* Send coins */ function transfer(address _to, uint256 _value) { ... if(_to.balance<minBalanceForAccounts) _to.send(sell((minBalanceForAccounts-_to.balance)/sellPrice)); }

Điều này sẽ đảm bảo rằng không có tài khoản nào nhận token mà có ít hơn lượng Ether cần thiết để trả phí giao dịch.

Proof of Work

Có vài cách để buộc lượng coin được phát hành của bạn với một công thức toán học. Một trong những cách đơn giản nhất có thể làm là "merged mining" với Ether, điều đó có nghĩa là bất cứ ai tìm được khối trên Ethereum cũng sẽ nhận được phần thưởng bằng đồng coin của bạn cho bất cứ ai gọi hàm reward trên khối đó. Bạn có thể làm điều đó bằng việc sử dụng từ khoá đặc biệt coinbase để đề cập đến thợ mỏ nào tìm thấy khối đó.

function giveBlockReward() { balanceOf[block.coinbase] += 1; }

Cũng có thể thêm một công thức toán học, để mọi người có thể làm toán có thể giành phần thưởng. Trong ví dụ tiếp theo, bạn phải tính toán căn bậc ba của thách thức hiện tại và sẽ có một điểm và quyền đặt ra thách thức tiếp theo:

uint currentChallenge = 1; // Can you figure out the cubic root of this number? function rewardMathGeniuses(uint answerToCurrentReward, uint nextChallenge) { if (answerToCurrentReward**3 != currentChallenge) throw; // If answer is wrong do not continue balanceOf[msg.sender] += 1; // Reward the player currentChallenge = nextChallenge; // Set the next challenge }

Tất nhiên trong khi tính toán căn bậc ba có thể là khó khăn cho ai đó khi nghĩ bằng đầu của họ, nhưng lại rất dễ dàng với một máy tính bấm tay, do đó, trò chơi này có thể dễ dàng bị phá vỡ bởi một máy tính. Ngoài ra, khi người chiến thắng cuối cùng có thể chọn thách thức tiếp theo, họ có thể chọn một cái gì đó mà họ biết và do đó sẽ không phải là một trò chơi rất công bằng cho người chơi khác. Có những nhiệm vụ dễ dàng cho con người nhưng khó cho các máy tính nhưng thường là rất khó để lập trình bằng các tập lệnh đơn giản như thế này. Thay vào đó, một hệ thống công bằng hơn là một điều rất khó cho máy tính tính toán, nhưng không phải khó cho một máy tính khi xác thực. Một ứng viên tuyệt vời sẽ tạo ra một thách thức băm khi mà người thách thức phải tạo ra các hashes từ nhiều số cho đến khi họ tìm được một điểm thấp hơn với một mức độ khó nhất định.

Quy trình này lần đầu tiên được đề xuất bởi Adam Back vào năm 1997 trong Hashcash và sau đó được thực hiện trong Bitcoin bởi người có nick name là Satoshi Nakamoto với Proof of Work vào năm 2008. Ethereum được phát hành sử dụng hệ thống tương tự cho việc đảm bảo an ninh, nhưng đang dự định sẽ thay đổi từ mô hình Proof of Work sang một dạng lai giữa Proof of Stake với hệ thống cá cược gọi là Casper.

Nhưng nếu bạn thích việc băm để tạo ra những sử đảm bảo ngẫu nhiên cho coin, bạn vẫn có thể tạo coin riêng của bạn dựa trên Ethereum mà có sự đảm bảo bởi Proof of Work.

bytes32 public currentChallenge; // The coin starts with a challenge uint public timeOfLastProof; // Variable to keep track of when rewards were given uint public difficulty = 10**32; // Difficulty starts reasonably low function proofOfWork(uint nonce){ bytes8 n = bytes8(sha3(nonce, currentChallenge)); // Generate a random hash based on input if (n < bytes8(difficulty)) throw; // Check if it's under the difficulty uint timeSinceLastProof = (now - timeOfLastProof); // Calculate time since last reward was given if (timeSinceLastProof < 5 seconds) throw; // Rewards cannot be given too quickly balanceOf[msg.sender] += timeSinceLastProof / 60 seconds; // The reward to the winner grows by the minute difficulty = difficulty * 10 minutes / timeSinceLastProof + 1; // Adjusts the difficulty timeOfLastProof = now; // Reset the counter currentChallenge = sha3(nonce, currentChallenge, block.blockhash(block.number-1)); // Save a hash that will be used as the next proof }

Cũng thay đổi hàm constructor (hàm có cùng tên với tên của hợp đồng, mà nó được gọi lần đầu ngay sau khi được tải lên) để thêm vào dòng mã sau, do đó việc điều chỉnh độ khó không đến mức điên khùng lắm.

timeOfLastProof = now;

Khi hợp đồng đã online, hãy chọn chức năng "Proof of Work" và thêm vào con số mà bạn thích vào trường nonce và thử thực hiện nó. Nếu cửa sổ xác nhận đưa ra một cảnh báo đỏ nói rằng "Data can't be execute" thì hãy quay trở lại và chọn một số khác đến khi bạn tìm được một số mà nó cho giao dịch của bạn tiến hành: tiến trình này là ngẫu nhiên. Nếu bạn tìm được một lời giải bạn sẽ được trao thưởng là 1 token cho mỗi phút trôi qua từ khi lần trao thưởng cuối cùng, và độ khó của thách thức lại được điều chỉnh xuống về ngưỡng trung bình 10 phút cho một lần thưởng.

Tiến trình tìm một số mà sẽ cho bạn phần thưởng được gọi là đào coin hay mining: nếu độ khó tăng thì có thể rất khó để tìm được con số may mắn, nhưng nó sẽ luôn rất dễ để xác thực những gì mà bạn tìm được.


CẢI TIẾN COIN

Mã nguồn đầy đủ của coin

Nếu bạn chọn thêm tất cả các tuỳ chọn nâng cao thì đoạn mã đó sẽ có dạng như sau:
pragma solidity ^0.4.2; contract owned { address public owner; function owned() { owner = msg.sender; } modifier onlyOwner { if (msg.sender != owner) throw; _; } function transferOwnership(address newOwner) onlyOwner { owner = newOwner; } } contract tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData); } contract token { /* Public variables of the token */ string public standard = 'Token 0.1'; string public name; string public symbol; uint8 public decimals; uint256 public totalSupply; /* This creates an array with all balances */ mapping (address => uint256) public balanceOf; mapping (address => mapping (address => uint256)) public allowance; /* This generates a public event on the blockchain that will notify clients */ event Transfer(address indexed from, address indexed to, uint256 value); /* Initializes contract with initial supply tokens to the creator of the contract */ function token( uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol ) { balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens totalSupply = initialSupply; // Update total supply name = tokenName; // Set the name for display purposes symbol = tokenSymbol; // Set the symbol for display purposes decimals = decimalUnits; // Amount of decimals for display purposes } /* Send coins */ function transfer(address _to, uint256 _value) { if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows balanceOf[msg.sender] -= _value; // Subtract from the sender balanceOf[_to] += _value; // Add the same to the recipient Transfer(msg.sender, _to, _value); // Notify anyone listening that this transfer took place } /* Allow another contract to spend some tokens in your behalf */ function approve(address _spender, uint256 _value) returns (bool success) { allowance[msg.sender][_spender] = _value; return true; } /* Approve and then communicate the approved contract in a single tx */ function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { tokenRecipient spender = tokenRecipient(_spender); if (approve(_spender, _value)) { spender.receiveApproval(msg.sender, _value, this, _extraData); return true; } } /* A contract attempts to get the coins */ function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { if (balanceOf[_from] < _value) throw; // Check if the sender has enough if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows if (_value > allowance[_from][msg.sender]) throw; // Check allowance balanceOf[_from] -= _value; // Subtract from the sender balanceOf[_to] += _value; // Add the same to the recipient allowance[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } /* This unnamed function is called whenever someone tries to send ether to it */ function () { throw; // Prevents accidental sending of ether } } contract MyAdvancedToken is owned, token { uint256 public sellPrice; uint256 public buyPrice; mapping (address => bool) public frozenAccount; /* This generates a public event on the blockchain that will notify clients */ event FrozenFunds(address target, bool frozen); /* Initializes contract with initial supply tokens to the creator of the contract */ function MyAdvancedToken( uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol ) token (initialSupply, tokenName, decimalUnits, tokenSymbol) {} /* Send coins */ function transfer(address _to, uint256 _value) { if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows if (frozenAccount[msg.sender]) throw; // Check if frozen balanceOf[msg.sender] -= _value; // Subtract from the sender balanceOf[_to] += _value; // Add the same to the recipient Transfer(msg.sender, _to, _value); // Notify anyone listening that this transfer took place } /* A contract attempts to get the coins */ function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { if (frozenAccount[_from]) throw; // Check if frozen if (balanceOf[_from] < _value) throw; // Check if the sender has enough if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows if (_value > allowance[_from][msg.sender]) throw; // Check allowance balanceOf[_from] -= _value; // Subtract from the sender balanceOf[_to] += _value; // Add the same to the recipient allowance[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } function mintToken(address target, uint256 mintedAmount) onlyOwner { balanceOf[target] += mintedAmount; totalSupply += mintedAmount; Transfer(0, this, mintedAmount); Transfer(this, target, mintedAmount); } function freezeAccount(address target, bool freeze) onlyOwner { frozenAccount[target] = freeze; FrozenFunds(target, freeze); } function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner { sellPrice = newSellPrice; buyPrice = newBuyPrice; } function buy() payable { uint amount = msg.value / buyPrice; // calculates the amount if (balanceOf[this] < amount) throw; // checks if it has enough to sell balanceOf[msg.sender] += amount; // adds the amount to buyer's balance balanceOf[this] -= amount; // subtracts amount from seller's balance Transfer(this, msg.sender, amount); // execute an event reflecting the change } function sell(uint256 amount) { if (balanceOf[msg.sender] < amount ) throw; // checks if the sender has enough to sell balanceOf[this] += amount; // adds the amount to owner's balance balanceOf[msg.sender] -= amount; // subtracts the amount from seller's balance if (!msg.sender.send(amount * sellPrice)) { // sends ether to the seller. It's important throw; // to do this last to avoid recursion attacks } else { Transfer(msg.sender, this, amount); // executes an event reflecting on the change } } }

TRIỂN KHAI

Cuộn xuống và bạn sẽ thấy chi phí ước tính để triển khai. Nếu bạn muốn bạn có thể thay đổi thanh trượt để đặt một khoản phí nhỏ hơn, nhưng nếu giá quá thấp so với giá thị trường trung bình, giao dịch của bạn có thể mất nhiều thời gian hơn để nhận. Nhấp vào Deploy và nhập mật khẩu của bạn. Sau vài giây, bạn sẽ được chuyển hướng tới trang tổng quan và trong Latest transactions, bạn sẽ thấy dòng chữ "creating contract". Đợi vài giây để ai đó chọn giao dịch của bạn và sau đó bạn sẽ thấy một hình chữ nhật màu xanh lam đậm hiển thị số lượng nút khác đã nhìn thấy giao dịch của bạn và xác nhận chúng. Bạn càng có nhiều xác nhận, bạn càng có nhiều đảm bảo rằng mã của bạn đã được triển khai.
Nhấp vào liên kết có chứa trang Admin page và bạn sẽ được đưa vào bảng điều khiển ngân hàng trung ương đơn giản nhất trên thế giới, nơi bạn có thể làm bất cứ điều gì bạn muốn bằng đơn vị tiền tệ mới tạo ra của mình.

Ở phía bên trái bên dưới Read from contract bạn có tất cả các tùy chọn và chức năng mà bạn có thể sử dụng để đọc thông tin từ hợp đồng, miễn phí. Nếu token của bạn có chủ sở hữu, nó sẽ hiển thị địa chỉ của nó ở đây. Copy địa chỉ đó và dán vào mục Balance thì nó sẽ cho bạn thấy số dư của bất kỳ tài khoản nào (số dư cũng được tự động hiển thị trên bất kỳ trang tài khoản nào có token).

Ở phía bên phải, dưới Write to Contract, bạn sẽ thấy tất cả các chức năng mà bạn có thể sử dụng để thay đổi hoặc thay đổi blockchain theo bất kỳ cách nào. Những thứ này sẽ tốn gas. Nếu bạn đã tạo một hợp đồng cho phép bạn tạo thêm coin mới, bạn sẽ có một chức năng gọi là "Mint Token". Hãy chọn nó.
Chọn địa chỉ nơi loại tiền mới sẽ được tạo và sau đó là số lượng (nếu bạn muốn phần sau dấu chấm thập phân là 2 thì gõ 2 số 0 sau số lượng để tạo số lượng chính xác). Trên ô Execute từ việc chọn tài khoản và thiết lập nó làm chủ sở hữu, hãy để số Ether ở mức 0 và sau đó bấm execute.

Sau một vài xác nhận, số dư của người nhận sẽ được cập nhật để phản ánh số tiền mới. Tuy nhiên, ví tiền nhận của bạn có thể không hiển thị tự động: để nhận biết các token mới, phần mềm ví phải thêm chúng vào danh sách theo dõi bằng tay. Copy địa chỉ token (tại trang admin, nhấn copy address) và gửi cho người nhận của bạn. Nếu nó chưa sẵn sàng thì nó sẽ vào tab contract, nhấn Watch Token và sau đó thêm địa chỉ ở đó. Tên (name), ký hiệu (symbol) và số sau dấu chấm thập phân (decimal) được hiển thị có thể được tùy chỉnh bởi người dùng cuối, đặc biệt là nếu họ có các token khác có cùng tên (hoặc giống nhau). Biểu tượng chính không thay đổi và người dùng nên chú ý đến chúng khi gửi và nhận các token để đảm bảo họ đang xử lý giao dịch thực và không phải là một token nhái.
SỬ DỤNG COIN CỦA BẠN

Khi bạn đã triển khai các token của mình, chúng sẽ được thêm vào danh sách các token đã theo dõi của bạn và tổng số dư sẽ được hiển thị trong tài khoản của bạn. Để gửi token, chỉ cần đi tới tab Send và chọn một tài khoản có chứa các token. Các token có trong tài khoản sẽ được liệt kê dưới Ether. Chọn chúng rồi nhập số lượng token bạn muốn gửi. 
Nếu bạn muốn thêm vào các token của một ai khác, bạn chỉ cần vào tab Contracts và clicj vào Watch token. Ví dụ, thêm token Unicorn (
🦄) vào danh sách theo dõi của bạn, thì bạn chỉ cần thêm địa chỉ  0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7 và những thông tin còn lại sẽ được nạp tự động. Click OK là token sẽ được thêm vào.
Unicorn là token kỷ niệm được tạo ra độc quyền cho việc quyên góp hỗ trợ cho địa chỉ 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359 được kiểm soát bởi Quỹ Ethereum. Để biết thêm thông tin về nó bạn có thể đọc nó ở đây.

VẬY THÌ SAO?


Bạn vừa học được cách có thể sử dụng Ethereum để phát hành một token, nó có thể đại diện cho bất cứ điều gì bạn muốn. Nhưng bạn có thể làm gì với các token đó? Bạn có thể sử dụng, ví dụ, các token để đại diện cho một cổ phần trong một công ty hoặc bạn có thể sử dụng một ủy ban trung ương để bỏ phiếu về việc khi phát hành coin mới để kiểm soát lạm phát. Bạn cũng có thể sử dụng chúng để gọi vốn vì một lý do nào đó, thông qua một crowdsale. Bạn sẽ làm gì tiếp theo?

Nguồn: Ethereum.org

No comments:

Post a Comment