Yakın zamanda benzersiz bir ERC-20 jetonunu temel alan alternatif bir para birimi sistemi olan Karma Money hakkında bir makale yazdım. Karma Money'i, kullanıcıların işlem ücretlerini de karma ile ödeyebilecekleri kapalı bir sistem olarak hayal ettim. Kendi blok zincirimizi oluşturmak bunu gerçeğe dönüştürmenin bir yolu olabilir, ancak bu zorlu bir iştir. Blockchain'in güvenliğini ve güvenilirliğini sağlamak için gerekli altyapıyı ve yeterince geniş bir topluluğu kurmamız gerekiyor. Mevcut bir blockchain'i kullanmak çok daha kolay olurdu. Ethereum ile tam uyumlu ve işlem ücretleri oldukça düşük olan Gnosis veya Polygon gibi zincirler bulunmaktadır. Bu zincirlerdeki bir ERC20 işleminin ücreti genellikle 1 sentten azdır. Sorun şu ki, bu ücretin zincirin kendi kripto para birimiyle ödenmesi gerekiyor ve bu da kullanıcılar için zincirin kullanımını zorlaştırabiliyor. Neyse ki bir köprü çözümü var: EIP-712 metal işlemleri.
Metatransaction durumunda kullanıcı, işlemi açıklayan bir yapı oluşturur ve ardından bunu kendi özel anahtarıyla dijital olarak imzalar. Birisine çek yazmaya benzer. Dijital olarak imzalanmış işlem daha sonra bir aktarma düğümüne gönderilir ve bu düğüm de onu akıllı bir sözleşmeye gönderir. Sözleşme imzayı doğrular ve geçerli ise işlemi gerçekleştirir. Aktarma düğümü sözleşmenin yürütülmesi için ödeme yapar.
Örneğin bir karma işleminde kullanıcı, işlemin tutarını (örneğin 10 karma dolar), tutarı göndermek istediği Ethereum adresini ve teklif etmek istediği işlem ücretini (karma dolar cinsinden) sağlar. işlem için. Bu yapı dijital olarak imzalanır ve bir aktarma düğümüne gönderilir. Düğüm, işlem ücretini kabul edilebilir bulması halinde dijital imzalı yapıyı, imzayı doğrulayan ve işlemi yürüten karma sözleşmeye gönderir. İşlem ücreti, aktarma düğümü tarafından blok zincirinin yerel para biriminde ödendiğinden, kullanıcıya, kendi blok zincirine ihtiyaç duymadan işlem için karma dolarla ödeme yapıyormuş gibi görünür.
Teoriden sonra pratiğe bakalım.
EIP-712 standardı, yapılandırılmış veri paketlerinin standartlaştırılmış bir şekilde nasıl imzalanacağını tanımlar. MetaMask bu yapılandırılmış verileri kullanıcı için okunabilir bir formatta görüntüler. MetaMask'ta gösterildiği gibi ( bu URL'de test edilebilir ) EIP-712 uyumlu bir yapı şu şekilde görünür:
Yukarıdaki işlem aşağıdaki basit kod kullanılarak oluşturuldu:
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_definition , meta verileri içeren genel bir yapının açıklamasıdır. Name alanı yapının adıdır, version alanı yapının tanım versiyonudur ve ChainId ve validingContract alanları mesajın hangi sözleşmeye yönelik olduğunu belirler. Yürütülen sözleşme, imzalanan işlemin yalnızca hedef sözleşmede yürütüldüğünden emin olmak için bu meta verileri doğrular.
karma_request_domain, EIP712Domain yapısı tarafından tanımlanan meta verilerin belirli değerini içerir.
İmza için MetaMask'a gönderdiğimiz asıl yapı transfer_request değişkeninde yer almaktadır. Tür bloğu tür tanımlarını içerir. Burada ilk öğe, meta verileri açıklayan zorunlu EIP712Domain tanımıdır. Bunu, bu durumda TransferRequest olan gerçek yapı tanımı takip eder. Kullanıcıya MetaMask'ta görünecek yapı budur. Etki alanı bloğu, meta verinin belirli değerini içerirken mesaj, kullanıcıyla imzalamak istediğimiz belirli yapıyı içerir.
Karma para söz konusu olduğunda, bir meta işlemin nasıl bir araya getirilip akıllı sözleşmeye gönderildiğine dair bir örnek şöyle görünür:
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))
Types değişkeni işlemin yapısını tanımlar. "Kimden" gönderenin adresi, "kime" ise alıcının adresidir. Tutar, aktarılacak token miktarını temsil eder. Ücret, işlemimizi gerçekleştirmemiz ve zincirin yerel para birimindeki maliyeti karşılamamız karşılığında aktarma düğümüne sunduğumuz tokenların "miktarıdır". "Bir kez", işlemin benzersizliğini sağlamak için bir sayaç görevi görür. Bu alan olmadan bir işlem birden çok kez yürütülebilir. Ancak nonce sayesinde imzalanan bir işlem yalnızca bir kez gerçekleştirilebilir.
Ethers.js tarafından sağlanansignTypedData işlevi, EIP-712 yapılarının imzalanmasını kolaylaştırır. Daha önce sunulan kodla aynı şeyi yapar ancak daha basit bir kullanımla.
MetaTransfer , meta işlemi gerçekleştirmek için karma sözleşmesinin yöntemidir. Nasıl çalıştığını görelim:
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; }
İmzayı doğrulamak için öncelikle yapının karmasını oluşturmalıyız. Bunu yapmanın tam adımları, örnek bir akıllı sözleşme ve örnek bir javascript kodu içeren EIP-712 standardında ayrıntılı olarak açıklanmaktadır.
Özetle işin özü, abi.encode kullanarak TYPEHASH'i (yapı açıklamasının hash'i) yapının alanlarıyla birleştirmemizdir. Daha sonra bir keccak256 hash üretir. Karma, Karma sözleşmesindeki EIP712 OpenZeppelin sözleşmesinden devralınan _hashTypedDataV4 yöntemine aktarılır. Bu işlev, yapımıza meta veriler ekler ve son karma değeri oluşturarak yapı doğrulamasını çok basit ve şeffaf hale getirir. En dıştaki işlev, imzalayanın adresini karma ve imzadan kurtarmaya çalışan ECDSA.tryRecover'dır. Eğer “from” parametresinin adresi eşleşiyorsa imza geçerlidir. Kodun sonunda asıl işlem gerçekleştirilir ve işlemi gerçekleştiren röle düğümü ücreti alır.
EIP-712, yapıların imzalanması için genel bir standarttır ve bu, onu meta işlemlerin uygulanmasına yönelik birçok kullanımdan yalnızca biri haline getirir. İmza sadece akıllı sözleşmelerle doğrulanamadığı için blockchain dışı uygulamalarda da oldukça faydalı olabiliyor. Örneğin, kullanıcının kendisini özel anahtarıyla tanımladığı sunucu tarafı kimlik doğrulaması için kullanılabilir. Böyle bir sistem, tipik olarak kripto para birimleriyle ilişkilendirilen yüksek düzeyde güvenlik sağlayabilir ve bir web uygulamasının yalnızca bir donanım anahtarıyla kullanılmasına olanak tanır. Ayrıca bireysel API çağrıları da MetaMask yardımıyla imzalanabiliyor.
Umarım EIP-712 standardına ilişkin bu kısa genel bakış birçokları için ilham verici olmuştur ve bunu hem blockchain tabanlı hem de blockchain dışı projelerde kullanabileceksiniz.
Her kod GitHub karma para deposunda mevcuttur.