以支付为例,做一个区块链的托管,实现一个h5/Java版本的交易demo
我的博客园地址 https://www.cnblogs.com/huangyingsheng/p/19685835
web2时代,需要办理bank card💳储蓄货币,在各平台或线下扫码支付,完成购物。
在web3时代,付款不再通过bank card💳结算,而是通过去中心化的钱包。
为此,我们需要准备web3交易需要的一些条件。
1.智能合约
web3的支付不再通过调用微信/支付宝/银行等接口来完成对用户的付款,而是通过 智能合约+用户钱包 的形式完成扣款,智能合约的功能很多,可以自动托管,代币,换币,DAO投票等等,此次,我们使用智能合约,完成一次自动托管的实现,所以我们需要一个智能合约,智能合约是通过solidity语言编写的,部署在链上,部署成功后会有一个合约地址,通过这个合约地址,我们就可以像一家一人公司一样到处收款了,收到的款项都会在合约中暂存,用户同意后,就会入账到钱包里,成为可以使用的余额了。
2.钱包
web3的时代,钱包是一个人的身份ID,也是交易的工具,以Mxxxxxx钱包为例。
开始
1.准备钱包,以 MetaMask 为例,下载的谷歌浏览器插件
选择人们网络,找到Custom,选择Sepolia,Sepolia是免费测试研发使用的代币
初次使用,SepoliaETH代币是0,可以免费领取
搜索找到 Sepolia PoW Faucet,免费领取代币,像图中一样。
准备好钱包后,我们就可以开始准备智能合约了,我们使用智能合约写一个简单的托管程序,功能是用户的资金付款后,进入我们的合约,用户确认收货后,合约自动将资金转入我们的钱包里。
下方是准备好的一段Solidity智能合约的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| // SPDX-License-Identifier: GPL-3.0 //限制使用的solidity版本 pragma solidity >=0.8.2 <0.9.0;
/** * @title Escrow * @dev Store & retrieve value in a variable * @custom:dev-run-script ./scripts/deploy_with_ethers.ts */ contract Escrow {
//收款方(一人公司,散户,中大型公司)的钱包地址 address public seller;
//发布该项目的时候,需要指明收款方钱包地址,完成注入 constructor(address _seller) { seller = _seller; }
// 买家付款 "external payable 会唤起钱包进行交易,此时钱存在于合约中,也就是该智能合约" function deposit() external payable { //付款金额大于0才能通过校验 require(msg.value > 0, "Must send ETH"); }
// 查看合约余额 function getBalance() external view returns (uint256) { return address(this).balance; }
// 买家确认收货 function confirmReceived() external { // 获取余额 uint256 balance = address(this).balance; // 校验余额 require(balance > 0, "No ETH to release"); // seller 就是发布该项目的时候指定的收款人钱包地址,此时调用call方法,将合约中的余额全部提取到钱包中 (bool success, ) = seller.call{value: balance}(""); require(success, "Transfer failed"); } }
|
打开 remix.ethereum.org ,如下图所示,将上方的智能合约代码按照图中的步骤创建文件,粘贴代码,编译上传到链上节点。
获得链上的合约地址和ABI之后,我们可以做一个H5版本的支付,收货功能,体验一下链上交易。
下方准备一段H5代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <!doctype html> <html> <head> <meta charset="utf-8" /> <title>DApp Test</title> <script src="https://cdn.jsdelivr.net/npm/ethers@6.6.2/dist/ethers.umd.min.js"></script> </head>
<body> <button id="connect">连接钱包</button> <button id="pay">支付</button> <button id="confirmBtn">确认收货</button> <h5 id="show_log">...</h5>
<script> let provider; let signer; let contract;
const contractAddress = "0xx...c6"; const abi = [];
async function init() { provider = new ethers.BrowserProvider(window.ethereum);
await provider.send("eth_requestAccounts", []);
signer = await provider.getSigner();
contract = new ethers.Contract(contractAddress, abi, signer);
window.contract = contract;
document.getElementById("show_log").innerText = "钱包初始化成功,地址: " + (await signer.getAddress());
console.log("合约初始化完成"); }
async function pay() { const tx = await contract.deposit({ value: ethers.parseEther("0.01"), });
console.log("tx:", tx); document.getElementById("show_log").innerText = "支付中... tx: " + tx.hash;
const receipt = await tx.wait();
console.log("receipt:", receipt); }
async function confirm() { const tx = await window.contract.confirmReceived(); console.log("msg:", "确认收货中..."); await tx.wait(); console.log("msg:", "收货确认完成 tx: " + tx.hash); }
document.getElementById("connect").onclick = init; document.getElementById("pay").onclick = pay; document.getElementById("confirmBtn").onclick = confirm; </script> </body> </html>
|
在浏览器中打开上方的H5页面,主义H5代码中要替换之前保存的合约地址和ABI信息!!!
可以通过创建新的账户,领取免费的测试Sepolia代币,往直前的账户里支付,确认收货,发现金额正常的增加,交易成功。
按钮下方的tx后方的hash可以作为查询支付状态的key,在java后端中后台查询链上交易情况,但是要先做一些准备。
首先,需要注册一个key用来连接链上RPC,infura注册就可以免费用一个key,注册后跟着页面提示操作后,copy对应的key 
在钱包中,我们可以找到Sepolia测试的RPC地址 
<!-- Web3j 核心依赖 -->
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>4.11.0</version>
</dependency>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public static void main(String[] args) {
String nodeUrl = "https://sepolia.infura.io/v3/替换为你的key"; Web3j web3 = Web3j.build(new HttpService(nodeUrl));
String txHash = "替换为你的hash";
try { EthGetTransactionReceipt receipt = web3.ethGetTransactionReceipt(txHash).send();
if (receipt.getTransactionReceipt().isPresent()) { TransactionReceipt transactionReceipt = receipt.getTransactionReceipt().get();
if (transactionReceipt.getStatus().equals("0x1")) { System.out.println("Transaction successful!"); } else { System.out.println("Transaction failed!"); } } else { System.out.println("Transaction not mined yet."); } } catch (Exception e) { e.printStackTrace(); }
}
|
之前的转账是以H5页面唤起钱包支付的方式实现,但是Java也可以实现直接通过私钥和钱包地址完成转账。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public static void main(String[] args) throws Exception { Web3j web3j = Web3j.build( new HttpService("https://sepolia.infura.io/v3/替换为 your key") );
Credentials credentialsA = Credentials.create( "上图中复制到的钱包私钥" );
String addressB = "我的第二个钱包地址(领免费代币的那种钱包地址,非钱包私钥)";
BigInteger balanceA = web3j.ethGetBalance(credentialsA.getAddress(), DefaultBlockParameterName.LATEST) .send().getBalance(); System.out.println("钱包 A 余额: " + Convert.fromWei(balanceA.toString(), Convert.Unit.ETHER) + " ETH");
System.out.println("开始向 B 转账 0.01 ETH..."); TransactionReceipt receipt = Transfer.sendFunds( web3j, credentialsA, addressB, BigDecimal.valueOf(0.1), Convert.Unit.ETHER ).send();
System.out.println("交易完成,交易哈希: " + receipt.getTransactionHash()); }
|
之前我们调用智能合约是通过H5的形式,唤起钱包插件完成支付,智能合约的调用,Java也可以操作。
首先需要本地安装web3
brew install web3j
如果上方因为网络等问题,可以采用离线安装
1 2 3 4 5 6 7 8 9 10 11 12 13
| https://github.com/LFDT-web3j/web3j-cli/releases/tag/v1.8.0 例: 下载 web3j-shadow-1.8.0.tar
tar -xvf web3j-shadow-1.8.0.tar cd web3j-shadow-1.8.0
nano ~/.zshrc.pre-oh-my-zsh export PATH=$PATH:pwd/web3j-shadow-1.8.0/bin source ~/.zshrc.pre-oh-my-zsh
web3j --version
|
安装好web3j后,我们需要将之前智能合约的ABI信息在项目中新建文件保存。
通过下方web3j的命令对我们的ABI文件生成一个Counter.java的类文件,通过该文件就可以访问智能合约中的方法了
1 2 3 4 5
| web3j generate solidity \ -a Counter.abi \ -o src/main/java \ -p contracts
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public static void main(String[] args) throws Exception { Web3j web3j = Web3j.build( new HttpService("https://sepolia.infura.io/v3/your key") ); Credentials credentialsA = Credentials.create( "your 钱包私钥" ); String contractAddress = "0x4Cab79Bf05CC942F6c50aF031C9b8cf758bC7A7E"; Counter counter = Counter.load( contractAddress, web3j, credentialsA, new org.web3j.tx.gas.DefaultGasProvider() ); System.out.println("写入 Counter 合约值 100..."); BigInteger balance = counter.getBalance().send(); System.out.println("余额:" + balance); System.out.println("操作完成!"); }
|
总结:智能合约是链上服务,每次读操作免费,但是写操作都需要手续费,可以做托管、代币,换币,DAO投票,但是不适合高并发,所以业务逻辑还是会以私人服务器或者公司服务器的形式运行,只有关键的操作走链上,比如扣款,换币种等关键操作在链上完成。