DeFi革命:揭秘去中心化金融的核心技术与实操指南

ops/2024/9/22 10:59:08/

目录

DeFi(去中心化金融)综述

基本特点

第一,DeFi 是无许可的金融

第二,DeFi 是无门槛的金融

第三,DeFi 是无人驾驶的金融

典型商业模式

闪电贷

MakerDAO

面临的挑战

DeFi技术要点

椭圆曲线签名 

EIP-712: Signed Typed Data

三种签名类型

编码示例

1. 交易编码

2. 消息编码

3. 结构化数据编码

签名和验证

EIP-191: Signed Data Standard

关键概念和步骤

版本字节示例

使用示例

任务实操

作业一:结构化数据(Typed Data)签名

方式1:采用 ethers.js

方式2:采用 MetaMask

作业二:实现 ERC20 Permit

安装 OpenZeppelin

编写 ERC20 Permit 合约

编写 ERC721 Permit 合约

相关链接


DeFi(去中心化金融)综述

基本特点

第一,DeFi 是无许可的金融

第二,DeFi 是无门槛的金融

第三,DeFi 是无人驾驶的金融

典型商业模式

闪电贷

"闪电贷"(Flash Loan)是一种去中心化金融(DeFi)技术,允许用户在无需提供任何抵押的情况下,短时间内借用大量资金。这种贷款通常在同一个区块链交易中完成借贷和还款,因此被称为"闪电贷"。它们主要在以太坊等区块链平台上使用,并且具有高度自动化和无需信任的特点。使用闪电贷时,用户必须确保在交易结束时能够偿还贷款本金加上利息,否则交易将失败,所有操作将回滚,就像从未发生过一样。这种贷款方式可以用于套利、杠杆交易等多种金融操作。

闪电贷通过单交易执行、原子性、智能合约保障和回滚机制确保资金安全。虽然有一定风险,但这些机制共同作用,极大地降低了资金被滥用或借款失败的可能性。用户在使用闪电贷时,选择经过审计的合约和谨慎规划交易步骤也是保障资金安全的重要因素。

MakerDAO

MakerDAO 是 DeFi 生态系统中的核心项目,的主要目标是创建并维护一个去中心化的稳定币,称为 Dai,其价值与1美元挂钩。为了实现这一目标,MakerDAO 允许用户通过抵押数字资产(如以太币)来生成 Dai。用户将他们的资产作为抵押品存入智能合约中,然后根据抵押品的价值获得一定比例的 Dai。如果抵押品的价值下降到某个阈值以下,用户需要增加抵押品或偿还部分 Dai 以避免被清算。这个过程确保了 Dai 的稳定性,使其能够维持与美元的挂钩。

面临的挑战

用户使用 DeFi 产品前,必须选择正确的主网和账户,还要在账户里预留足够的 Gas 费,理解这种操作模式需要用户具备一定的知识储备;因为操作不慎,转账导致资金丢失的事例时有发生,因为私钥泄露而资产被盗的事件也常常出现。

DeFi技术要点

椭圆曲线签名 

私匙反推公钥

私匙反推公钥,公钥无法反推私钥

EIP-712: Signed Typed Data

EIP-712 是一个标准化的以太坊提案,旨在定义一种安全、用户友好且可互操作的数据签名方法。这个标准的核心在于结构化数据的签名,它使得签名的数据更具可读性,并能防止重放攻击等潜在的安全问题。

三种签名类型

  1. 交易编码(encode(transaction: T))

    • 使用 RLP 编码(Recursive Length Prefix)对交易进行编码。
    • 这种编码方法常用于对交易进行签名。
  2. 消息编码(encode(message: B^n))

    • 这种签名方式用于签名普通消息。
    • 在哈希计算之前,消息会被前置字符串 "\x19Ethereum Signed Message:\n" 进行处理。
    • 这个前置字符串是为了确保签名的消息与普通交易数据区分开来,防止混淆。
  3. 结构化数据编码(encode(domainSeparator: B^32, message: S))

    • 用于签名结构化数据(如智能合约数据)。
    • 先编码域分隔符(domainSeparator),然后编码具体消息(message)。
    • 前缀为 "\x19\x01",表示这是结构化数据的签名。
    • 这种方式确保数据的完整性和安全性。

编码示例

1. 交易编码

使用 RLP 编码交易数据:

const encodedTransaction = RLP_encode(transaction);
2. 消息编码

对消息进行编码,并添加前缀:

const message = "Hello, Ethereum!";
const encodedMessage = `\x19Ethereum Signed Message:\n${message.length}${message}`;
3. 结构化数据编码

结构化数据编码需要两个部分:域分隔符和消息。以下是一个结构化数据编码的示例:

const domain = {name: "MyDApp",version: "1",chainId: 1,verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
};const types = {EIP712Domain: [{ name: "name", type: "string" },{ name: "version", type: "string" },{ name: "chainId", type: "uint256" },{ name: "verifyingContract", type: "address" }],Message: [{ name: "content", type: "string" },{ name: "author", type: "address" }]
};const message = {content: "Hello, EIP-712!",author: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
};// 计算域分隔符哈希
const domainSeparator = ethers.utils._TypedDataEncoder.hashDomain(domain);
// 计算消息哈希
const messageHash = ethers.utils._TypedDataEncoder.hashStruct("Message", types.Message, message);// 最终编码
const encodedData = `\x19\x01${domainSeparator}${messageHash}`;

签名和验证

使用 EIP-712 标准进行签名和验证:

// 使用 ethers.js 进行签名
async function signTypedData() {const [signer] = await ethers.getSigners();const signature = await signer._signTypedData(domain, types, message);console.log("Signature:", signature);
}// 验证签名
async function verifySignature(signature) {const signerAddress = ethers.utils.verifyTypedData(domain, types, message, signature);console.log("Signer Address:", signerAddress);
}signTypedData();

EIP-191: Signed Data Standard

EIP-191 是一个关于签名数据的标准,用于确保签名的数据在区块链上是安全和有效的。它的目的是通过标准化签名数据的格式,防止签名数据在被哈希和验证时发生混淆或被误解。

关键概念和步骤

  1. 签名数据格式: EIP-191 定义了一个数据格式,该格式包含以下部分:

    • 0x19:一个字节的前缀,确保签名的数据不符合 RLP 编码(Recursive Length Prefix)标准,以防止误用。
    • 版本字节:表示数据的版本,以区分不同的签名格式。
    • 版本特定数据:根据版本字节的不同,可能包含额外的结构化数据。
    • 要签名的数据:实际要进行签名的数据。
  2. 前缀的选择

    • 0x19 被选择为前缀,因为在 personal_sign 方法中进行哈希之前,会预置以下字符串:
      "\x19Ethereum Signed Message:\n" + len(message)

      这确保了数据在哈希时具有唯一性和不可篡改性。

  3. 版本字节: 版本字节用于区分不同类型的数据签名:

    • 0x00:数据具有指定的验证者。
    • 0x01:结构化数据(对应 EIP-712 标准)。
    • 0x45:个人签名消息(与 personal_sign 方法兼容)。

版本字节示例

Version ByteEIPDescription
0x00191Data with intended validator
0x01712Structured data
0x45191personal_sign messages

使用示例

为了签名一个消息,假设我们使用 0x01 版本字节(结构化数据):

  1. 准备数据

    const versionByte = '0x01';
    const dataToSign = "My data to sign";
    
  2. 构建签名消息: 将数据按照 EIP-191 的格式进行构建:

    const message = `\x19Ethereum Signed Message:\n${dataToSign.length}${dataToSign}`;
    

  3. 签名消息: 使用以太坊私钥进行签名(假设使用 ethers.js 库):

    const { ethers } = require('ethers');
    const wallet = new ethers.Wallet(privateKey);
    const signature = await wallet.signMessage(message);
    

任务实操

作业一:结构化数据(Typed Data)签名

方式1:采用 ethers.js
  1. 安装 ethers.js

    npm install ethers
  2. 编写签名代码

    const { ethers } = require("ethers");// 定义域
    const domain = {name: "MyDApp",version: "1",chainId: 1,verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
    };// 定义数据结构
    const types = {EIP712Domain: [{ name: "name", type: "string" },{ name: "version", type: "string" },{ name: "chainId", type: "uint256" },{ name: "verifyingContract", type: "address" }],Message: [{ name: "content", type: "string" },{ name: "author", type: "address" }]
    };const message = {content: "Hello, EIP-712!",author: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
    };async function signTypedData() {const privateKey = "YOUR_PRIVATE_KEY";const wallet = new ethers.Wallet(privateKey);const signature = await wallet._signTypedData(domain, types, message);console.log("Signature:", signature);
    }signTypedData();
    

  3. 智能合约验证

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";contract VerifySignature {using ECDSA for bytes32;struct Message {string content;address author;}bytes32 private constant MESSAGE_TYPEHASH = keccak256("Message(string content,address author)");bytes32 private constant DOMAIN_SEPARATOR = keccak256(abi.encode(keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),keccak256(bytes("MyDApp")),keccak256(bytes("1")),1,0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC));function hashMessage(Message memory message) public pure returns (bytes32) {return keccak256(abi.encode(MESSAGE_TYPEHASH,keccak256(bytes(message.content)),message.author));}function verify(Message memory message, bytes memory signature) public view returns (address) {bytes32 digest = keccak256(abi.encodePacked("\x19\x01",DOMAIN_SEPARATOR,hashMessage(message)));return digest.recover(signature);}
    }
    

方式2:采用 MetaMask
  1. 安装 MetaMask

    • 在浏览器中安装 MetaMask 插件,并创建账户。
  2. 编写前端代码

    <!DOCTYPE html>
    <html>
    <head><title>MetaMask Typed Data Signing</title><script src="https://cdn.jsdelivr.net/npm/ethers/dist/ethers.min.js"></script>
    </head>
    <body><button id="sign">Sign Typed Data</button><script>const domain = {name: "MyDApp",version: "1",chainId: 1,verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"};const types = {EIP712Domain: [{ name: "name", type: "string" },{ name: "version", type: "string" },{ name: "chainId", type: "uint256" },{ name: "verifyingContract", type: "address" }],Message: [{ name: "content", type: "string" },{ name: "author", type: "address" }]};const message = {content: "Hello, EIP-712!",author: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"};document.getElementById('sign').onclick = async function() {if (typeof window.ethereum !== 'undefined') {await ethereum.request({ method: 'eth_requestAccounts' });const provider = new ethers.providers.Web3Provider(window.ethereum);const signer = provider.getSigner();const signature = await signer._signTypedData(domain, types, message);console.log("Signature:", signature);} else {console.log("MetaMask is not installed");}};</script>
    </body>
    </html>
    

作业二:实现 ERC20 Permit

安装 OpenZeppelin
npm install @openzeppelin/contracts
编写 ERC20 Permit 合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";contract MyTokenWithPermit is ERC20Permit {constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {_mint(msg.sender, 1000000 * 10 ** decimals());}
}
编写 ERC721 Permit 合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";contract MyTokenWithPermit is ERC20Permit {constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {_mint(msg.sender, 1000000 * 10 ** decimals());}
}

相关链接

  • Ethers.js SignTypedData
  • MetaMask SignTypedData V4

http://www.ppmy.cn/ops/86649.html

相关文章

vue Ref 和 Reactive 原理解析

文章目录 RefReactive Ref ref 的语义是指向一个值的引用&#xff0c;主要用于处理基本数据类型和单一值对象&#xff0c;即对值的引用进行包装和管理&#xff0c;而不是对对象的操作进行拦截&#xff0c;对于基础类型通过 getter 和 setter 实现拦截使用 Proxy 拦截对象的所有…

电脑屏幕录制软件,分享4款(2024最新)

在今天&#xff0c;我们的电脑屏幕成为了一个多彩多姿的窗口。通过它我们可以浏览网页、观看视频、处理文档、进行游戏……有时&#xff0c;我们想要记录下这些精彩瞬间&#xff0c;与朋友分享&#xff0c;或者作为教程留存&#xff0c;这时&#xff0c;电脑屏幕录制就显得尤为…

燃气安全无小事,一双专业劳保鞋让你步步安心!

燃气作为我们日常生活中不可或缺的能源之一&#xff0c;为我们的生活提供了极大便利&#xff0c;其安全性往往被忽视在忙碌的日常生活背后。然而&#xff0c;燃气事故一旦发生&#xff0c;后果往往不堪设想&#xff0c;轻则财产损失&#xff0c;重则危及生命。因此&#xff0c;…

Java—常见4种线程池详解

一、概述 java中线程池是一种基于池化技术的多线程资源管理方式&#xff0c;用于减少线程创建和销毁的开销&#xff0c;提高系统性能。java提供了几种常见的的线程池实现&#xff0c;主要通过Executors工厂类创建。 二、常见线程池 1、SingleThreadExecutor SingleThreadEx…

原生JavaScript系列面试题

大家好&#xff0c;我是有用就点赞&#xff0c;有用就扩散。 1.__ proto __ 和prototype之间有什么关系&#xff1f; 在JavaScript中万物皆对象&#xff0c;所有对象都有__ proto __ 属性&#xff0c;函数这个特殊对象除了具有__ proto __ 属性&#xff0c;还有特有的原型属性…

【React】useState:状态更新规则详解

文章目录 一、基本用法二、直接修改状态 vs 使用 setState 更新状态三、对象状态的更新四、深层次对象的更新五、函数式更新六、优化性能的建议 在 React 中&#xff0c;useState 是一个非常重要的 Hook&#xff0c;用于在函数组件中添加状态管理功能。正确理解和使用 useState…

用Python打造精彩动画与视频.2.1 Python基础语法概述

2.1 Python基础语法概述 Python作为一门功能强大且易于学习的编程语言&#xff0c;其基础语法简单直观&#xff0c;非常适合初学者入门。这一节将带你了解Python的基本语法规则&#xff0c;为后续制作动画和视频打下坚实的基础。 1. 变量与数据类型 Python的变量不需要提前声…

shopee虾皮 java后端 一面面经 整体感觉不难

面试总结&#xff1a;总体不难&#xff0c;算法题脑抽了只过了一半&#xff0c;面试官点出了问题说时间到了&#xff0c;反问一点点&#xff0c;感觉五五开&#xff0c;许愿一个二面 1.Java中的锁机制&#xff0c;什么是可重入锁 Java中的机制主要包括 synchronized关键字 Loc…