本文介绍了如何构建一个符合反洗钱(AML)和打击恐怖主义融资(CFT)规范的去中心化金融(DeFi)质押去中心化应用(dApp),使用了QuickNode的风险评估API和Chainlink Functions,将外部数据集成到智能合约中。通过此指南,开发者可以理解在去中心化应用中实施合规性检查的重要性、评估钱包风险的方式以及如何在EVM网络上部署相应的dApp。
遵守反洗钱(AML) 和 反恐融资(CFT) 法规在去中心化金融(DeFi)中已成为一种优先事项。监管检查在增加,区块链项目必须实施安全措施以防止非法金融活动。你即将构建的应用程序利用 QuickNode 的风险评估 API 来评估钱包的风险配置,并使用 Chainlink Functions 将这些链下数据引入智能合约。
本指南将引导你构建一个符合 AML 和 CFT 的质押 dApp,该 dApp 集成了 QuickNode 的风险评估 API 和 Chainlink Functions,以在前端和智能合约层面实施安全策略。无论你是一个新的 DeFi 合规性开发者还是经验丰富的区块链工程师,本指南都将帮助你理解在 dApp 中实施合规检查的原因、内容和方法。
免责声明
本指南中提供的智能合约和代码仅用于教育目的,没有经过审计。在未经彻底测试、审计和安全审查之前,不应在生产环境中使用。
区块链的匿名和无需许可的特性使其在合法和非法金融活动中都具有吸引力。全球各地的当局正在收紧监管,以打击 DeFi 中的欺诈、制裁违规和恐怖融资。
遵守 AML 和 CFT 法规可能是区块链项目的战略必要性。理由如下:
QuickNode 市场 提供两种强大的安全插件,以帮助开发者实施合规性:
该插件分析链下数据(例如,交易模式、已知关联)以为钱包地址分配风险分数(0–100)。较低的分数表示更高的风险,还有其他细节如严重性和实体类型(例如,“制裁名单”)。这是你即将构建的应用程序中使用的插件。
该插件利用 AI 驱动的风险分析高效监控钱包风险,提供风险分数、实体标签,并标记可疑活动,以确保交易的安全和合规。它使用 AI 解析交易行为并识别各方,提供准确的钱包风险分数和实体标签(例如,“已知交易所”或“嫌疑诈骗者”)。钱包风险检查器访问一个巨大的全球制裁名单数据库,包括美国 OFAC、联合国、加拿大、英国、欧盟、瑞士和澳大利亚,并从主要区块链网络和全球情报源提取数据。
风险评估 API 和钱包风险检查器都通过简单的 API 调用提供风险分数和实体标签,使它们易于集成到应用程序中进行合规检查。它们都着眼于 AML/CFT 合规,利用链下数据评估钱包风险。对于本指南,我们关注于风险评估 API,但请注意,可以通过最小的调整将钱包风险检查器替换使用。
在前端使用这些插件阻止用户交互非常简单,只需进行 API 调用并检查响应。然而,在智能合约层面实施合规性需要弥合链下数据和链上逻辑之间的差距,因为它们在一个隔离的环境中操作,无法直接访问像 API 这样的外部数据源。
为了解决这个问题,我们需要一种安全的去中心化解决方案从智能合约获取和传递链下数据。这就是 Chainlink Functions 的作用。
Chainlink Functions 是一种去中心化的预言机解决方案,旨在将智能合约与外部数据源和链下计算连接起来。
对于不熟悉 Chainlink Functions 的开发者来说,它扩展了 Chainlink 预言机网络的能力,通过在安全、去中心化的环境中执行自定义 JavaScript 代码。该代码可以与 API 交互、处理数据,并将结果返回到区块链,同时保持去中心化预言机网络的信任性和可靠性。
在 AML 和 CFT 合规 dApp 的背景下,Chainlink Functions 在实现链上合规性方面发挥着关键作用,通过连接智能合约和 QuickNode 风险评估 API 来弥合二者之间的差距。其工作原理如下:
source.js
)。在此 dApp 中,代码通过钱包地址调用风险评估 API,提取响应中的 score
字段(例如,{"score": 1, "severity": "CRITICAL_RISK"}
),并将数值风险分数返回给区块链。fulfillRequest
回调函数将其发送到智能合约。这会更新 RiskBasedStaking.sol
合约中的 riskScores
映射,启用链上合规性执行。该 dApp 的架构无缝集成了前端、智能合约和预言机组件。以下是数据流:
/api/check-risk
路由的请求。{ "score": 1, "severity": "CRITICAL_RISK" }
)。fulfillRequest
函数,更新钱包的风险分数。RiskBasedStaking.sol风险评估 APIChainlink FunctionsAPI (api/check-risk)Next.js 前端用户RiskBasedStaking.sol风险评估 APIChainlink FunctionsAPI (api/check-risk)Next.js 前端用户交互触发 api/check-risk发送请求调用风险评估 API返回分数更新风险基础质押合约执行规则
在进入部署部分之前,了解智能合约和 Chainlink Functions 集成的组件至关重要。本节提供有关智能合约和 API 如何协同工作更多的细节。
RiskBasedStaking.sol
RiskBasedStaking.sol
智能合约管理质押操作,通过 Chainlink Functions 获取的风险分数执行合规。它确保存款中的钱包仅限于具有可接受风险水平的钱包。
stakedBalances
:一个映射(address => uint256
),跟踪每个用户存入的 ETH。riskScores
:一个映射(address => uint256
),存储每个钱包的风险分数。0
的分数表示该钱包尚未被检查。riskThreshold
:一个 uint256
值(例如,30),表示质押所需的最低风险分数。较低的分数表示更高的风险。pendingRequests
:一个映射(address => bool
),防止同一用户的重复风险检查请求。s_lastRequestId
, s_lastResponse
, s_lastError
:存储最近的 Chainlink Functions 请求 ID、响应和错误的变量,用于调试。isAllowedToStake(address user)
riskScores[user]
大于 0
(即已检查)并至少等于 riskThreshold
,则返回 true
。stake()
调用以执行合规性。sendRequest(...)
目的:发起 Chainlink Functions 请求以获取风险分数。
访问:限制为合约所有者(onlyOwner
)。
参数:包括 source
(JavaScript 代码)、args
(用户地址)、subscriptionId
和 gasLimit
。
逻辑:
确保仅提供一个参数(用户地址)。
如果 pendingRequests[user]
为 true
则防止重复请求。
使用 Chainlink 的 FunctionsRequest
库初始化并发送请求。
通过映射 s_lastRequestId
跟踪请求。
事件:发出 RiskCheckRequested(user, requestId)
。
fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err)
目的:处理 Chainlink Functions 响应。
访问:内部,由 Chainlink 自动调用。
逻辑:
从 requestToUser[requestId]
获取用户地址。
清除 pendingRequests[user]
。
如果没有错误(err.length == 0
),解码 response
为 uint256
风险分数,更新 riskScores[user]
,并发出 RiskScoreUpdated(user, score)
。
存储响应/错误并发出 Response(requestId, response, err)
。
stake()
目的:允许用户在通过风险检查后质押 ETH。
逻辑:
检查 riskScores[user]
是否为 0
:
pendingRequests[user]
为 true
,则回滚并返回 PendingRiskCheck
。NoRiskScore
。如果 riskScores[user] < riskThreshold
,则回滚并返回 RiskyAddress
。
更新 stakedBalances[user]
和 totalStaked
,然后发出 Staked(user, amount)
。
withdraw(uint256 amount)
目的:允许用户提取已质押的 ETH。
逻辑:
确保 stakedBalances[user] >= amount
,否则回滚并返回 InsufficientBalance
。
更新余额并将 ETH 转移给用户。
发出 Withdrawn(user, amount)
。
/api/check-risk
/api/check-risk
路由连接前端和智能合约,使用 Chainlink Functions 工具包触发风险评估。它处理链下请求过程并用结果更新合约。
routerAddress
:Chainlink Functions 路由器地址(例如,Base 主网的 0xf9B8fc078197181C841c296C876945aaa425B278
)。donId
:去中心化预言机网络(DON) ID(例如,fun-base-mainnet-1
)。gatewayUrls
:上传密钥到 Chainlink 网关的 URL。source.js
:Chainlink Functions 执行的 JavaScript 代码,调用风险评估 API 并返回风险分数。secrets
:包含敏感数据的对象(例如,QUICKNODE_ENDPOINT
),加密并上传到 DON。Chainlink Functions Playground 是一个出色的工具,可用于测试和调试源代码。你可以用它来模拟请求并查看响应。
读取源代码: 路由从文件系统读取 source.js
,该代码定义了通过用户地址调用风险评估 API 以提取风险分数的逻辑。
设置签名者: 使用 ethers.js
从 PRIVATE_KEY
(存储在 .env
文件中)创建一个钱包,并将其连接到 QuickNode 提供者(QUICKNODE_ENDPOINT
)。
管理密钥: SecretsManager
加密 secrets
对象(例如,QUICKNODE_ENDPOINT
)并使用指定的 slotId
和到期时间将其上传到 DON。
发送请求: 使用以下参数调用智能合约的 sendRequest
:
source.js
代码。slotId
和 version
)。args
。监听响应: ResponseListener
等待 Chainlink 的响应。使用 decodeResult
将 responseBytesHexstring
解码为 uint256
风险分数。
错误处理: 处理无效地址或 API 失败等错误,返回适当的 JSON 响应(例如,{ success: false, error: "无效的钱包地址" }
)。
现在,你已经理解了 dApp 的架构和组件,让我们来看看如何在本地机器或首选网络上设置它。
虽然 Chainlink Functions 支持测试网进行实验(例如,Sepolia),但 QuickNode 的安全插件如风险评估 API 设计用于在主网上操作(你可以在它们的插件页面上确认支持的网络)。在本指南中,我们将在 Base 主网上进行部署,以确保与风险评估 API 的完全兼容。
按照以下详细步骤在本地或首选网络上设置 dApp:
git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd qn-guide-examples/sample-dapps/aml-and-cft-compliant-dapp
sc_getAddressAnalysis
和 sc_getTransactionAnalysis
方法。curl -L https://foundry.paradigm.xyz | bash
foundryup
这将安装 Foundry,一个用于以太坊智能合约开发的工具包,我们将使用它来编译和部署我们的合约。
cd foundry
forge install smartcontractkit/foundry-chainlink-toolkit --no-commit
forge install foundry-rs/forge-std --no-commit
这些库提供用于与 Chainlink Functions 交互的工具和智能合约开发的标准实用工具。
remappings.txt
文件,以便在 Foundry 中正确映射已安装的依赖项:forge remappings > remappings.txt
cast wallet import your-wallet-name --interactive
将 your-wallet-name
替换为你钱包的名称(例如,deployer
)。你将被提示输入私钥并设置加密密码。--interactive
标志确保私钥不会以任何形式保存在你的 shell 历史记录中,以提高安全性。
将 .env.sample
复制为 .env
并进行更新:
BASE_RPC_URL=<your-quicknode-endpoint>
BASESCAN_API_KEY=<your-basescan-api-key>
将 BASE_RPC_URL
替换为你的 QuickNode 端点 URL,将 BASESCAN_API_KEY
替换为你的 BaseScan API 密钥。
检查 BaseScan API 密钥文档 以获取有关如何获取你的 BaseScan API 密钥的说明。
使变量生效:
source .env
修改 script/RiskBasedStaking.s.sol
文件,将 Chainlink Functions router
地址更新为与你所选网络匹配的地址。请参考 Chainlink Functions 支持的网络 文档以获取适合你的网络的正确地址。
确保使用经过校验的以太坊地址,以避免部署错误。如果你不确定校验和验证,可以使用 地址校验和工具 来生成和验证正确的校验和格式。
script/RiskBasedStaking.s.sol
// 针对特定链的 Chainlink Functions 路由器地址
address router = 0xf9B8fc078197181C841c296C876945aaa425B278;
forge script script/RiskBasedStaking.s.sol:RiskBasedStakingScript --rpc-url $BASE_RPC_URL --account your-wallet-name --broadcast --verify -vvv
--verify
标志会在区块浏览器(例如,Basescan)上验证合约;如果你不想验证,可以将其删除。
注意输出中的已部署合约地址——你将需要它进行 Chainlink Functions 和前端设置。
部署智能合约后,你需要设置Chainlink Functions 订阅,以允许合约请求风险评估。请按照以下步骤进行:
创建 Chainlink Functions 订阅
将你的智能合约添加为消费者
检查特定网络的详细信息
获取你的订阅 ID
有关详细设置指南,请参阅 Chainlink Functions 文档。
我们使用 Next.js 和 TypeScript 进行类型安全,使用 Wagmi 和 Viem 进行钱包连接和智能合约交互,使用 Mantine UI 实现精美的 UI 设计。
cd ../next-app
该目录包含前端代码,经过 Next.js 构建,支持服务器端渲染和 API 路由。
npm install
此命令将安装 package.json
中列出的所有所需包。关键依赖项包括:
next
和 react
:用于构建 Next.js 应用程序的核心库。wagmi
和 viem
:以太坊钱包连接和智能合约交互的库,实现与区块链的无缝集成。@mantine/core
和 @mantine/hooks
:Mantine UI 组件和Hook,用于构建现代响应式界面。@chainlink/functions-toolkit
:与 Chainlink Functions 交互的工具包,使用于 /api/check-risk
路由,以触发风险评估请求。ethers
:用于以太坊实用工具的库,支持对交易的签名和与智能合约的交互。为什么需要同时使用
viem
和ethers
? 因为ethers
是 chainlink-functions-toolkit 的依赖,而viem
是 wagmi 的依赖。我们需要两者来与区块链交互并执行钱包操作。
在 next-app
目录中创建一个 .env
文件以存储环境变量:
添加以下变量:
WALLETCONNECT_PROJECT_ID=<your-walletconnect-id>
PRIVATE_KEY=<your-private-key>
QUICKNODE_ENDPOINT=<your-quicknode-endpoint>
CONTRACT_ADDRESS=<deployed-contract-address>
SUBSCRIPTION_ID=<chainlink-subscription-id>
变量的说明和获取方法:
WALLETCONNECT_PROJECT_ID
:这为你 WalletConnect 项目 ID,启用前端钱包连接所需。请在 WalletConnect Cloud 上注册以创建项目并获取你的 ID(例如,a1b2c3d4...
)。PRIVATE_KEY
:指定用于签署 Chainlink Functions 请求的钱包的私钥。这可确保只有授权请求被发出,从而防止滥用你的 Chainlink 订阅。你可以使用步骤 3 中导入的同一钱包。安全地使用 MetaMask 或钱包 CLI 导出私钥,但绝不要公开它。QUICKNODE_ENDPOINT
:你的含有风险评估 API 的 QuickNode RPC 端点 URL。你在步骤 2 中设置 QuickNode 端点时获得(例如,https://your-endpoint.quicknode.com
)。CONTRACT_ADDRESS
:已部署 RiskBasedStaking.sol
合约的地址。你在步骤 3 中运行部署脚本后记下(例如,0x1234...
)。SUBSCRIPTION_ID
:Chainlink Functions 订阅 ID,允许访问风险评估请求。你将在步骤 4 中设置 Chainlink 订阅时获得(例如,1234
)。在 src/app/api/check-risk/route.ts
文件中更新链和网络的正确路由器地址和 DON ID。
src/app/api/check-risk/route.ts
// Chainlink Functions 配置
const routerAddress = "0xf9B8fc078197181C841c296C876945aaa425B278";
const donId = "fun-base-mainnet-1";
const gatewayUrls = [
"https://01.functions-gateway.chain.link/",
"https://02.functions-gateway.chain.link/",
];
npm run dev
此命令将以开发模式启动应用程序,并启用热重载以便更轻松調试。
一旦你的 dApp 设置完成,可以通过浏览器界面访问并与之交互。
应用概述 | 带分数的应用概述 |
---|---|
![]() |
![]() |
查询智能合约中的 riskScores
映射,以确认你的风险分数已正确更新:
如果你是新的智能合约交互用户,可以按照我们的 与智能合约交互指南 了解如何使用 tools.js 或区块浏览器的界面读取和写入合约。
此验证确保风险评估 API 的风险分数在链上得到妥善反映。
要将你的 dApp 从一个可运行的原型提升到一个适合生产的应用程序,考虑以下实用改进:
审计和安全性:
风险检查的速率限制:
优化 LINK 和交易成本:
gasLimit
和最小化链上操作优化 gas 使用情况。用户体验增强:
severity
或 entityType
)提供反馈,如果风险分数未达到阈值。安全最佳实践:
PRIVATE_KEY
——切勿在客户端代码中暴露。使用专用签署钱包来进行 Chainlink 请求,并保持最小资金。riskThreshold
)添加访问控制或多重签名要求。测试和验证:
本指南为你提供了构建符合 AML 和 CFT 的 dApp 的步骤,使用 QuickNode 的风险评估 API 和 Chainlink Functions。你已经设置了质押界面,验证了 Chainlink 订阅和智能合约交互,并探讨了增强解决方案的方法。通过实施上述附加步骤,你可以创建一个不仅合规,而且高效、安全、用户友好的 dApp。
如果你有任何问题,请随时使用我们的 Discord 服务器或通过以下表单提供反馈。通过关注我们 Twitter 和我们的 Telegram公告频道,保持最新消息。
告诉我们 如果你有任何反馈或新主题请求。我们非常乐意听取你的意见。
- 原文链接: quicknode.com/guides/eth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!