フロントエンドチーム小林です。
HashPortでは、パレットチェーンというブロックチェーンを運営し、パレットトークン(PLT)というトークンを発行しています。
PLTは、社内外のプロジェクトで広く使われるようになってきており、私自身もPLTの決済の実装プロジェクトに関わりました。イーサリアムベースでの開発フローについては十分理解しているつもりでしたが、PLTでの実装は色々工夫が必要な点が多かったです。その気づきをまとめていきます。
ETH決済とPLT決済の違いについて
ETH決済もPLT決済も送金するという意味では似ています。
大きな違いとしては、使うウォレットが異なることと、PLTはトークンであり、スマートコントラクトベースでやりとりする必要があるという点があります。
ウォレット | 単位 | 種類 | |
---|---|---|---|
ETH決済 | Metamask など | ETH | 基軸通貨 |
PLT決済 | PLTウォレット | PLT | ERC20(トークン) |
作るもの
・ETHWallet接続ボタンを押すと、Metamaskが反応し接続できます。
・ETH送付ボタンを押すと、Metamaskが反応し0.02ETH送付できます。
・PLTWallet接続ボタンを押すと、バーコードが表示され、PLTウォレットで読み取ると接続できます。
・PLT送付ボタンを押すと、PLTWalletが反応し0.02PLT送付できます。
ETH
PLT
実装
フロントエンドの準備
フレームワークはVue3を使います。
cd ~
npm init vue@latest
選択肢はそれぞれ下記のように選択します。
✔ Project name: … <your-project-name>
plteth_frontend
✔ Add TypeScript? … No / Yes
Yes
✔ Add JSX Support? … No / Yes
No
✔ Add Vue Router for Single Page Application development? … No / Yes
No
✔ Add Pinia for state management? … No / Yes
No
✔ Add Vitest for Unit testing? … No / Yes
No
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes
No
✔ Add ESLint for code quality? … No / Yes
Yes
✔ Add Prettier for code formatting? … No / Yes
Yes
cd plteth_frontend
npm install
npm run dev
http://localhost:3000/
にアクセスして、表示されることを確認します。
サンプル
下記が、ETH決済とPLT決済の両方を試すことができるサンプルです。
後のセクションで解説します。
App.vue
<script setup lang="ts">
import Web3 from "web3/dist/web3.min.js";
import detectEthereumProvider from "@metamask/detect-provider";
import { ref } from "vue";
import PLTContractAbi from "@/abi/PLTContract.json";
import type { AbiItem } from "web3-utils";
import type { PLTContract } from "@/abi/PLTContract";
import WalletConnectProvider from "@walletconnect/web3-provider/dist/umd/index.min.js";
const web3 = ref();
const account = ref();
const provider = ref();
const PLTCONTRACT_ADDRESS = "0x****************************************";
const connectETHWallet = async () => {
provider.value = await detectEthereumProvider();
web3.value = new Web3(provider.value);
const ethereum = (window as any).ethereum;
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
account.value = accounts[0];
console.log(account.value);
};
const ethTransfer = async () => {
const value = web3.value.utils.toWei("0.002", "ether");
const receipt = await web3.value.eth.sendTransaction({
from: account.value,
to: "0x****************************************",
value: value,
});
console.log(receipt);
};
const connectPLTWallet = async () => {
const chainID = 12345678;
const paletteRPC = "https://**************";
// WalletConnectに接続
provider.value = new WalletConnectProvider({
rpc: {
chainID: paletteRPC,
},
});
await provider.value.enable();
web3.value = new Web3(provider.value);
// アカウントを取得
const accounts = await web3.value.eth.getAccounts();
account.value = accounts[0];
console.log(account.value);
};
const pltTransfer = async () => {
const PLTContract = new web3.value.eth.Contract(
PLTContractAbi as AbiItem[],
PLTCONTRACT_ADDRESS
) as PLTContract;
const value = web3.value.utils.toWei("0.002", "ether");
const data = PLTContract.methods
.transfer("0x****************************************", value)
.encodeABI();
const receipt = await web3.value.eth.sendTransaction({
from: account.value,
to: PLTCONTRACT_ADDRESS,
value,
data,
gasPrice: 0,
gas: 0,
});
console.log(receipt);
};
</script>
<template>
<h1>ETH送付</h1>
<button @click="connectETHWallet">ETHWallet接続</button>
<button @click="ethTransfer">ETH送付</button>
<br />
<h1>PLT送付</h1>
<button @click="connectPLTWallet">PLTWallet接続</button>
<button @click="pltTransfer">PLT送付</button>
</template>
ライブラリ(npm)
web3
@metamask/detect-provider
typechain
ライブラリ(グローバル)
solc
ETH決済の実装方法
ウォレットへ接続
Metamaskに接続し、得られたproviderをもとにweb3オブジェクトを生成します。
その後、アカウントの情報も取得します。Metamaskへの接続方法については、下記リンクを参照ください。
https://docs.metamask.io/guide/getting-started.html#basic-considerations
const connectETHWallet = async () => {
provider.value = await detectEthereumProvider();
web3.value = new Web3(provider.value);
const ethereum = (window as any).ethereum;
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
account.value = accounts[0];
console.log(account.value);
};
ETH送付
0.02etherを0x****************************************
というアドレスに送ります。
const ethTransfer = async () => {
const value = web3.value.utils.toWei("0.002", "ether");
const receipt = await web3.value.eth.sendTransaction({
from: account.value,
to: "0x****************************************",
value: value,
});
console.log(receipt);
};
PLT決済の実装方法
ABIの保存
PLTContractのABIを src/abi/PLTContract.json と保存します。
ABIの型生成
Typescriptを使うため、typechainを用いて、src/abi/PLTContract.jsonから型を生成します。
npm install --save-dev typechain
package.jsonのscriptsの箇所にtypechainの記述を加えて、npmから使えるようにします。
"scripts": {
"dev": "vite",
"build": "run-p type-check build-only",
"preview": "vite preview --port 4173",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"typechain": "typechain --target=web3-v1 $npm_config_filepath --outDir $npm_config_distpath"
},
typechainを用いて型生成をします。src/abi/PLTContract.d.ts のように生成されます。
npm run typechain --filepath="src/abi/PLTContract.json" --distpath="src/abi"
ウォレットへ接続
現状PLTウォレットへ接続するためには、WalletConnectを介する必要があります。WalletConnectについては下記を参照してください。
https://docs.walletconnect.com/quick-start/dapps/web3-provider
provider.enable()でWalletConnectを有効化すると、QRコードが表示されます。
それをPLTウォレットで読み取ると接続できます。
接続には、イーサリアムとは異なるchainID, RPCを用います。
今回は、テスト用のchainID, RPCを使っています。
const connectPLTWallet = async () => {
const chainID = 12345678;
const paletteRPC = "https://**************";
// WalletConnectに接続
provider.value = new WalletConnectProvider({
rpc: {
chainID: paletteRPC,
},
});
await provider.value.enable();
web3.value = new Web3(provider.value);
// アカウントを取得
const accounts = await web3.value.eth.getAccounts();
account.value = accounts[0];
console.log(account.value);
};
PLT送付
0.02PLTを0x****************************************
というアドレスに送ります。
まずは、PLTContractをABIとコントラクトアドレスをもとに生成します。
PLTContractからtransfer関数を呼ぶという処理をdataとして保存し、sendTransaction時に一緒に渡します。
const pltTransfer = async () => {
const PLTContract = new web3.value.eth.Contract(
PLTContractAbi as AbiItem[],
PLTCONTRACT_ADDRESS
) as PLTContract;
const value = web3.value.utils.toWei("0.002", "ether");
const data = PLTContract.methods
.transfer("0x****************************************", value)
.encodeABI();
const receipt = await web3.value.eth.sendTransaction({
from: account.value,
to: PLTCONTRACT_ADDRESS,
value: value,
data,
gasPrice: 0,
gas: 0,
});
console.log(receipt);
};
まとめ
今回は、ETH決済とPLT決済を実装し、比較を行いました。
PLT決済では、スマートコントラクトとやりとりする必要があるため、ABIやコントラクトアドレスが必要で、実装がワンステップ増え、工夫する必要があります。