Develop WebApps with Vue using Polkadot.js
Element
一般的,以下三个要素会出现在任何程序上的一次操作中
- 发/收信方:Account钱包地址
- 通道: WebSocket长连接
- 信息: Message可被加密
Account
依赖:
@polkadot/util-crypto
用于生成随机的密钥@polkadot/keyring
用于管理钱包账户,/testing
可以同步开发测试账户@vue-polkadot/vue-settings
同步链节点的基本信息(比如转账时要用到的货币单位)@vue-polkadot/vue-identicon
用于显示账户头像@polkadot/util
一些工具,例如数据格式化
安装keyring
// store.ts
import { cryptoWaitReady } from '@polkadot/util-crypto'
import testKeyring from '@polkadot/keyring/testing'
const myKeyring = async (store: any) => {
await cryptoWaitReady()
const keyring = testKeyring({ type: 'sr25519' })
Vue.prototype.$keyring = keyring
}
在组件中调用keyring生成钱包账户
// Account.vue
import { Vue } from 'vue-property-decorator'
export default class Account extends Vue {
public newAccount: any = {
name: '',
mnemonicSeed: '',
address: '',
password: ''
}
private getAddressFromSeed(mnemonicSeed: string): any {
return this.$keyring.createFromUri(
mnemonicSeed.trim(),
this.getMeta(),
'sr25519'
)
}
private getMeta(): any {
return {
name: this.newAccount?.name || '',
whereCreated: 'Earth',
whenCreated: Date.now()
}
}
}
creat接口用于创建账户,add接口用于在本地内存里添加账户。
const json = pair.toJson(password)
this.$keyring.addFromJson(json)
交易转账时,金额数据需要额外处理
static sendBalance(
sender: string | any,
recipient: string,
amount: number,
tokenDecimals: number
): Promise {
let value = numberToHex(amount * 1e3 * 10 ** (tokenDecimals || 12))
return exec(sender, 'balances', 'transfer', [recipient, value])
}
Link to Chain
将web客户端与链侧应用建立websocket长连接,以保持数据通信,随后将polkdot-api实例化。
执行的时机一般只需一次,发生在页面加载后。
- connect with websocket
- make the api global
Code
// 引入依赖
import { ApiPromise, WsProvider } from '@polkadot/api'
import { EventEmitter } from 'events'
// 定义接口类型
export interface ApiService {
connect(apiUrl: string, types: any): Promise
disconnect(): void
// registerCustomTypes(userTypes: string, apiUrl?: string): Promise;
}
/**
* Singleton instance for @polkadot/api.
*/
export default class Api extends EventEmitter implements ApiService {
private static _instance: Api = new Api()
private _api!: ApiPromise
private _apiUrl!: string
/**
* getInstance
* @returns Api Instance
*/
public static getInstance(): Api {
return Api._instance
}
private constructor() {
super()
}
/**
* connect
* @requires apiUrl: string
* @returns instance of polkadot-js/api instance
*/
public async connect(
apiUrl: string,
types: Record
): Promise {
if (!apiUrl) {
throw new TypeError(
`[VUE API] ERR: Unable to init api with apiUrl ${apiUrl}`
)
}
try {
const provider = new WsProvider(apiUrl)
const apiPromise = await ApiPromise.create({ provider, types })
this.setApi(apiPromise)
this._emit('connect', apiPromise)
// const isReady = apiPromise.isReady.then(api => {
// this.setApi(apiPromise)
// this._emit('connect', apiPromise)
// })
} catch (err) {
this._emit('error', err)
throw err
}
this.setUrl(apiUrl)
return this._api
}
/**
* disconnect
*/
public disconnect(): void {
if (this._api) {
// this._api.once('disconnected', () => this._emit('disconnect', this._apiUrl));
this._api.disconnect()
this.setUrl('')
}
}
private setApi(api: ApiPromise) {
this._api = api
}
private setUrl(apiUrl: string) {
this._apiUrl = apiUrl
}
get api(): ApiPromise {
return this._api
}
/**
* tryEmit
*
*/
public _emit(message = 'event', payload?: ApiPromise | Error): void {
this.emit(message, payload)
}
}
Tips:
websocket
连接失败的异常无法捕获,暂不考虑解决。event
依赖主要处理connect事件的成功/失败监听,若果在外部已有其他的异常捕获机制则可以去掉此处。@vue-polkadot/vue-api
插件和此代码功能一致,前者有些针对vue的封装。@vue-polkadot/vue-api
在实例化时没有传入type参数,如果有自定义type需要传入的要另做处理。
Query & Transactions
Query
import Connector from '@/api/util/connector'
static async getBalance(address: string): Promise {
let { api } = Connector.getInstance()
const { data: balance } = await api.query.system.account(address)
return balance.free
}
Transactions
import Connector from '@/api/util/connector'
let { api } = Connector.getInstance()
const transfer = await api.tx[section][method](...params)
/*unsub将执行持续的监听。 返回的回调函数可以在出块或确认块后停止监听。*/
const unsub = await transfer.signAndSend(
sender, // account: AddressOrPair,
{}, //options: Partial
()=>{ //statusCb
// after isFinalized
unsub()
}
)
Transactions with Extension
import Connector from '@/lib/substrate/Api'
import {
web3Accounts,
web3Enable,
web3FromAddress
} from '@polkadot/extension-dapp'
let { api } = Connector.getInstance()
const allInjected = await web3Enable('Dipole')
const allAccounts = await web3Accounts()
// finds an injector for an address
const sender = allAccounts[0].address
const injector = await web3FromAddress(sender)
api.setSigner(injector.signer)
Transactions with password
if (!sender.isLocked) {
await unSubFoo(payload)
} else {
try {
// unlock account success
sender.decodePkcs8(password)
await unSubFoo(payload)
} catch (err){
// unlock password failed
console.log(err)
}
}