Gần đây tôi đã viết một bài về Karma Money , một hệ thống tiền tệ thay thế dựa trên mã thông báo ERC-20 duy nhất. Tôi đã hình dung Karma Money như một hệ thống khép kín, nơi người dùng cũng có thể trả phí giao dịch bằng nghiệp chướng. Xây dựng chuỗi khối của riêng chúng tôi sẽ là một cách để biến điều này thành hiện thực, nhưng đó là một nhiệm vụ đầy thách thức. Để đảm bảo tính bảo mật và độ tin cậy của blockchain, chúng tôi cần thiết lập cơ sở hạ tầng cần thiết và một cộng đồng đủ lớn. Sẽ đơn giản hơn nhiều nếu sử dụng một blockchain hiện có. Có những chuỗi như hoặc , hoàn toàn tương thích với Ethereum và có phí giao dịch rất thấp. Phí cho giao dịch ERC20 trên các chuỗi này thường dưới 1 xu. Vấn đề là khoản phí này phải được thanh toán bằng tiền điện tử của chính chuỗi, điều này có thể gây phức tạp cho việc sử dụng chuỗi của người dùng. May mắn thay, có một giải pháp bắc cầu, đó là giao dịch kim loại .
Ví dụ: trong một giao dịch karma, người dùng cung cấp số tiền của giao dịch (ví dụ: 10 đô la karma), địa chỉ Ethereum nơi họ muốn gửi số tiền đó và phí giao dịch (bằng đô la karma) mà họ sẵn sàng cung cấp cho giao dịch. Cấu trúc này được ký điện tử và gửi đến nút chuyển tiếp. Nếu nút thấy phí giao dịch có thể chấp nhận được thì nó sẽ gửi cấu trúc được ký điện tử cho hợp đồng nghiệp vụ để xác minh chữ ký và thực hiện giao dịch. Vì phí giao dịch được thanh toán bằng tiền tệ gốc của blockchain bằng nút chuyển tiếp, nên người dùng có vẻ như đang thanh toán bằng đô la nghiệp chướng cho giao dịch mà không cần blockchain của riêng họ.
async function main() { if (!window.ethereum || !window.ethereum.isMetaMask) { console.log("Please install MetaMask") return } const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); const chainId = await window.ethereum.request({ method: 'eth_chainId' }); const eip712domain_type_definition = { "EIP712Domain": [ { "name": "name", "type": "string" }, { "name": "version", "type": "string" }, { "name": "chainId", "type": "uint256" }, { "name": "verifyingContract", "type": "address" } ] } const karma_request_domain = { "name": "Karma Request", "version": "1", "chainId": chainId, "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" } document.getElementById('transfer_request')?.addEventListener("click", async function () { const transfer_request = { "types": { ...eip712domain_type_definition, "TransferRequest": [ { "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" } ] }, "primaryType": "TransferRequest", "domain": karma_request_domain, "message": { "to": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "amount": 1234 } } let signature = await window.ethereum.request({ "method": "eth_signTypedData_v4", "params": [ accounts[0], transfer_request ] }) alert("Signature: " + signature) }) } main()
Eip712domain_type_def định là mô tả về cấu trúc chung, chứa siêu dữ liệu. Trường tên là tên của cấu trúc, trường phiên bản là phiên bản định nghĩa của cấu trúc và các trường chainId và verifyContract xác định thông báo dành cho hợp đồng nào. Hợp đồng thực thi xác minh siêu dữ liệu này để đảm bảo rằng giao dịch đã ký chỉ được thực hiện trên hợp đồng đích.
karma_request_domain chứa giá trị cụ thể của siêu dữ liệu được xác định bởi cấu trúc EIP712Domain.
Cấu trúc thực tế mà chúng tôi gửi tới MetaMask để lấy chữ ký được chứa trong biến transfer_request . Khối loại chứa các định nghĩa loại. Ở đây, phần tử đầu tiên là định nghĩa EIP712Domain bắt buộc, mô tả siêu dữ liệu. Tiếp theo là định nghĩa cấu trúc thực tế, trong trường hợp này là TransferRequest. Đây là cấu trúc sẽ xuất hiện trong MetaMask cho người dùng. Khối miền chứa giá trị cụ thể của siêu dữ liệu, trong khi thông báo chứa cấu trúc cụ thể mà chúng tôi muốn ký với người dùng.
const types = { "TransferRequest": [ { "name": "from", "type": "address" }, { "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" }, { "name": "fee", "type": "uint256" }, { "name": "nonce", "type": "uint256" } ] } let nonce = await contract.connect(MINER).getNonce(ALICE.address) const message = { "from": ALICE.address, "to": JOHN.address, "amount": 10, "fee": 1, "nonce": nonce } const signature = await ALICE.signTypedData(karma_request_domain, types, message) await contract.connect(MINER).metaTransfer(ALICE.address, JOHN.address, 10, 1, nonce, signature) assert.equal(await contract.balanceOf(ALICE.address), ethers.toBigInt(11))
Biến loại xác định cấu trúc của giao dịch. “Từ” là địa chỉ của người gửi, trong khi “đến” là địa chỉ của người nhận. Số lượng đại diện cho số lượng token được chuyển. Phí là “số lượng” token mà chúng tôi cung cấp cho nút chuyển tiếp để đổi lấy việc thực hiện giao dịch của chúng tôi và trang trải chi phí bằng loại tiền tệ gốc của chuỗi. “Nonce” đóng vai trò như một bộ đếm để đảm bảo tính duy nhất của giao dịch. Nếu không có trường này, một giao dịch có thể được thực hiện nhiều lần. Tuy nhiên, do nonce nên một giao dịch đã ký chỉ có thể được thực hiện một lần.
Hàm signTypedData do cung cấp giúp bạn dễ dàng ký các cấu trúc EIP-712. Nó thực hiện tương tự như mã được trình bày trước đó nhưng với cách sử dụng đơn giản hơn.
MetaTransfer là phương thức của hợp đồng nghiệp lực để thực hiện giao dịch meta. Hãy xem nó hoạt động như thế nào:
function metaTransfer( address from, address to, uint256 amount, uint256 fee, uint256 nonce, bytes calldata signature ) public virtual returns (bool) { uint256 currentNonce = _useNonce(from, nonce); (address recoveredAddress, ECDSA.RecoverError err) = ECDSA.tryRecover( _hashTypedDataV4( keccak256( abi.encode( TRANSFER_REQUEST_TYPEHASH, from, to, amount, fee, currentNonce ) ) ), signature ); require( err == ECDSA.RecoverError.NoError && recoveredAddress == from, "Signature error" ); _transfer(recoveredAddress, to, amount); _transfer(recoveredAddress, msg.sender, fee); return true; }
EIP-712 là một tiêu chuẩn chung cho các cấu trúc ký tên, khiến nó chỉ là một trong nhiều ứng dụng để thực hiện các giao dịch meta. Vì chữ ký có thể được xác thực không chỉ bằng hợp đồng thông minh, nó còn có thể rất hữu ích trong các ứng dụng không phải blockchain. Ví dụ: nó có thể được sử dụng để xác thực phía máy chủ, trong đó người dùng tự nhận dạng mình bằng khóa riêng của họ. Một hệ thống như vậy có thể cung cấp mức độ bảo mật cao thường được liên kết với tiền điện tử, cho phép khả năng sử dụng ứng dụng web chỉ bằng khóa phần cứng. Ngoài ra, các lệnh gọi API riêng lẻ cũng có thể được ký với sự trợ giúp của MetaMask.