opennft-client 需要谷歌浏览器或 Chromium 内核
Tip:用户地址下需要有百度开放网络余额才能使用转移资产,查询余额等功能。建议在百度开放网络充值0.1元。充值链接:
https://xuper.baidu.com/n/console#/finance/wallet/recharge
使用帮助二维码
插件已放置根目录下()
国内加速下载 https://gitee.com/shengjian-tech/opennft-client/raw/master/MakerONE.zip
1,浏览器选择管理扩展程序
2,首先打开开发者模式,然后解压下载的压缩包并选择加载,此时您可以看到浏览器已经安装好该插件了
3,您可以选择插件常驻
下载私钥到本地之后,打开浏览器插件进入登录页,选择本地私钥,输入安全码登录:
登录之后跳转到首页,显示目前处于百度开放网络,用户百度开放网络地址,余额。以及功能等。
转换开放网络地址为 EVM 地址的工具类 addressUtils.js
import base58 from 'bs58' import { sha256 } from 'js-sha256' export function XchainAddrToEvm(addr) { var result = '' try { // 判断是否是合约账号;判断合约账户地址仅支持 XC11111111111@xuper, xuper后缀,go-sdk有相同问题。 if (determineContractAccount(addr)) { result = contractAccountToEVMAddress(addr) } else if (determineContractName(addr)) { result = contractNameToEVMAddress(addr) } else { result = xchainAKToEVMAddress(addr) } return result } catch (err) { console.log(err) } } // 判断合约账户地址仅支持 XC11111111111@xuper, xuper后缀,go-sdk有相同问题。 function determineContractAccount(xchainAddr) { if (isAccount(xchainAddr) != 1) { return false } return xchainAddr.indexOf('@xuper') != -1 } const accountPrefix = 'XC' function isAccount(name) { if (name == '') { return -1 } if (name.indexOf(accountPrefix) != 0) { return 0 } var prefix = name.split('@')[0] prefix = prefix.substr(accountPrefix.length) if (!validRawAccount(prefix)) { return 0 } return 1 } const accountSize = 16 function validRawAccount(accountName) { if (accountName == '') { return false } if (accountName.length != accountSize) { return false } for (var i = 0; i < accountSize; i++) { if (accountName[i] >= '0' && accountName[i] <= '9') { continue } else { return false } } return true } const contractAccountPrefixs = '1112' const Word160Length = 20 function contractAccountToEVMAddress(contractAccount) { var contractAccountValid = contractAccount.slice(2, 18) var str = contractAccountPrefixs.concat(contractAccountValid) if (str.length != Word160Length) { throw new Error('slice passed as address shou have 20 byte length') } return Buffer.from(str).toString('hex').toUpperCase() } const contractNameMaxSize = 16 const contractNameMinSize = 4 const contractNameRegex = /^[a-zA-Z_]{1}[0-9a-zA-Z_.]+[0-9a-zA-Z_]$/ function determineContractName(xchainAddr) { var contractSize = xchainAddr.length if ( contractSize > contractNameMaxSize || contractSize < contractNameMinSize ) { return false } if (!contractNameRegex.test(xchainAddr)) { return false } return true } const evmAddressFiller = '-' const contractNamePrefixs = '1111' function contractNameToEVMAddress(contractName) { var contractNameLength = contractName.length var prefixStr = '' for (var i = 0; i < Word160Length - contractNameLength - 4; i++) { prefixStr += evmAddressFiller } contractName = prefixStr + contractName contractName = contractNamePrefixs + contractName if (contractName.length != Word160Length) { throw new Error('slice passed as address shou have 20 byte length') } return Buffer.from(contractName).toString('hex').toUpperCase() } function xchainAKToEVMAddress(xchainAddr) { var rawAddr = base58.decode(xchainAddr) if (rawAddr.length < 21) { throw new Error('bad address') } rawAddr = rawAddr.slice(1, 21) return Buffer.from(rawAddr, '').toString('hex').toUpperCase() } export function EvmToXchainAddr(addr) { // return addr, addrType, nil var result = '' try { var bs = Buffer.from(addr, 'hex').toString('ascii') if (bs.length != Word160Length) { throw new Error('slice passed as address shou have 20 byte length') } var evmAddrStrWithPrefix = bs // 合约账号 if (evmAddrStrWithPrefix.slice(0, 4) == contractAccountPrefixs) { result = evmAddressToContractAccount(bs) } else if (evmAddrStrWithPrefix.slice(0, 4) == contractNamePrefixs) { result = evmAddressToContractName(bs) } else { var buffer = Buffer.from(addr, 'hex') result = evmAddressToXchain(buffer) } return result } catch (err) { console.log(err) } } function evmAddressToContractAccount(addr) { return accountPrefix + addr.slice(4) + '@xuper' } function evmAddressToContractName(addr) { var index = addr.lastIndexOf(evmAddressFiller) return addr.slice(index + 1) } function evmAddressToXchain(addr) { var addTyepe = [] var addrArray = new Uint8Array(addr) addTyepe.push(1) for (var i = 0; i < addrArray.length; i++) { addTyepe.push(addrArray[i]) } var checkCode = DoubleSha256(addTyepe) var simpleCheckCode = checkCode.slice(0, 4) for (var i = 0; i < simpleCheckCode.length; i++) { addTyepe.push(simpleCheckCode[i]) } return base58.encode(addTyepe) } // DoubleSha256 执行2次SHA256,这是为了防止SHA256算法被攻破。 function DoubleSha256(data) { return UsingSha256(UsingSha256(data)) } // UsingSha256 get the hash result of data using SHA256 function UsingSha256(data) { return sha256.array(data) }
// 需要引入 XuperSDK 和上面的地址转换工具类 import XuperSDK, { Endorsement } from '@xuperchain/xuper-sdk' import { XchainAddrToEvm } from './addressUtils' // TODO 需要通过用户选择的私钥路径,读取出来私钥的内容 const private = '' // 安全码 const password = '' // 登录后 生成 账户对象 const acc = xsdk.import(password, private)
默认首页用户处于开放网络。 开放网络为下拉框,默认一个位开放网络。用户可以添加网络。添加网络可以弹框,需要参数:网络名(用户自己输入就行),节点 IP(例如:39.156.69.83:37100),链名:(例如 xuperchain), Vue 里面开放网络的 node 需要设置为https://xuper.baidu.com/nodeapi
// 默认就有的开放网络,默认开放网络的链名 const node = '39.156.69.83:37100' // vue里面设置为 https://xuper.baidu.com/nodeapi const chain = 'xuper' // 连接开放网络时需要加载背书服务, const params = { server: '39.156.69.83:37100', // ip, port // vue里面设置为 https://xuper.baidu.com/nodeapi fee: '400', // fee endorseServiceCheckAddr: 'jknGxa6eyum1JrATWvSJKW3thJ9GKHA9n', // sign address endorseServiceFeeAddr: 'aB2hpHnTBDxko3UoP2BpBZRujwhdcAFoT', // fee address } // 默认的开放网络 SDK client, 后续查询余额,调用合约使用。 const xsdk = new XuperSDK({ node, chain, plugins: [ Endorsement({ transfer: params, makeTransaction: params, }), ], }) // 用户新增网络可以生成新的sdk client并记录,切换网络即切换SDK,新增网络暂时不考虑背书插件,仅传入IP和链名即可 const xsdk = new XuperSDK({ node, chain, })
显示用户 Address
// 用户登录时生成的acc, acc.address即为用的address地址 const addr = acc.address
显示用户余额
// 查询登录用户余额 // 用户超级链账户余额 address 直接传入上面 acc.address即可。 const balance = async (adress) => { try { const result = await xsdk.getBalance(adress) debug(result.bcs[0].balance) } catch (err) { throw err } }
默认提供三个 Action 功能,转移 NFT。查询 NFT 数量,查询交易。并提供新增 Action 操作
点击功能,跳转到功能页。最上面为下拉框,用户可以选择功能,默认为上述三个,并且有一个新增操作。
默认三个功能 转移,查询,查询交易
// 默认合约名 const contractName = 'opennft' // 默认方法名 const methodName = 'safeTransferFrom' // 默认参数 // nft 转移发起者,默认取值 acc.address,acc.address需要转换,下述为转换好的。 // acc.address 格式为 ULuqhymLPGidfihUb683i2TH4qtaqZ2Dz 需要用工具类转换为evm的地址2BEF68690AE24553824BA37C003C2B9067665F81 const from = XchainAddrToEvm(acc.address) // 下述三个参数,需要用户在页面输入,提供三个输入框即可。 // nft 转移接受者 前端输入 例如 cRsoDDnDX1NjhzJtNKLS3GHudBsgyRouQ 也需要转换 const to = XchainAddrToEvm('cRsoDDnDX1NjhzJtNKLS3GHudBsgyRouQ') // 转移的 nft token id const nftTokenID = '6' // 转移数量 const amount = '1' // 转移NFT 转移操作前先查询一下账户余额。是0的话让他去充值。建议最少充值一元。 // 咱们默认提供的NFT 转移,只需要转移着的地址,tokenid,数量。给用户返回交易ID const TransferNFTEvm = async (toAddr, TokenID, Amount) => { try { const contractName = contractName const methodName = methodName const demo = await xsdk.invokeSolidityContarct( contractName, methodName, 'evm', { from: from, to: to, id: nftTokenID, amount: amount, data: '', }, '0', acc ) // 352cd3f829dded7ad1da7ab3a0c3a8776cd3ec545c617ad499abb2d29459c6ee // 交易ID 返回给用户 debug(xsdk.transactionIdToHex(demo.transaction.txid)) const result = await xsdk.postTransaction(demo.transaction, acc) // TODO 调用转移操作成功后 // TODO 调用坤那边的服务端接口解析交易 只传输txid,调用接口即可,后续不管。最后给用户返回上面的交易ID就行。 debug(result) // err 是空 证明转移成功,不是 就是执行失败。 } catch (err) { console.log(err) } }
查询自己的 NFT 余额
// 查询 NFT 余额 前端用户选择查询资产余额,需要输入NFT token ID 就行,然后返回余额数量就OK。 const queryNFTBalance = async (tokenID) => { try { const contractName = 'opennft' const methodName = 'balanceOf' const args = { account: XchainAddrToEvm(acc.address), id: tokenID, } const demo = await xsdk.invokeSolidityContarct( contractName, methodName, 'evm', args, '0', acc ) // 判断 demo.preExecutionTransaction.response.responses的长度是否大于0, 大于0 取demo.preExecutionTransaction.response.responses[length - 1] const len = demo.preExecutionTransaction.response.responses.length if (len > 0) { const str = demo.preExecutionTransaction.response.responses[len - 1].body const result = Buffer.from(str, 'base64').toString('ascii') // [{\"0\":\"10\"}] result 即为 [{\"0\":\"10\"}] 10即为想要的结果,即对应nft 的余额 debug(result) } } catch (err) { console.log(err) } }
查询交易信息
// 查询交易 前端用户选择查询交易,让用户输入交易ID,即可,返回交易信息。目前交易信息很少 // 查询交易 const GetTxDetail = async (txID) => { try { const demo = await xsdk.queryTransaction( Buffer.from(txID, 'hex').toString('base64') ) if (demo.tx == undefined) { // 证明此交易链上没有 直接报错 throw new Error('this tx undefined') } // 交易ID var txID = Buffer.from(demo.tx.txid, 'base64').toString('hex') var txReqJson = JSON.parse( Buffer.from( demo.tx.contract_requests[1].args.input, 'base64' ).toString() ) var from = '' var to = '' var tokenID = '' var amount = '' if (demo.tx.contract_requests[1].method_name == 'safeTransferFrom') { from = EvmToXchainAddr(txReqJson.from) to = EvmToXchainAddr(txReqJson.to) tokenID = txReqJson.id amount = txReqJson.amount } else { from = demo.tx.initiator tokenID = txReqJson._id amount = txReqJson._initialSupply } // 根据 tokenID 查询token id的图片路径 供浏览器跳转 const contractName = 'opennft' const methodName = 'getTokenBytes' const args = { _id: tokenID, } const res = await xsdk.invokeSolidityContarct( contractName, methodName, 'evm', args, '0', acc ) const len = res.preExecutionTransaction.response.responses.length if (len > 0) { var result = res.preExecutionTransaction.response.responses[len - 1].body var response = JSON.parse(Buffer.from(result, 'base64').toString()) var base64Addr = response[0]._response var data = Buffer.from(base64Addr, 'base64').toString() var dataJson = JSON.parse(data) } var timestamp = parseInt(demo.tx.timestamp / 1000) // 用户查看交易详情,前端显示下述txDetail信息。 var txDetail = { txID: txID, from: from, to: to, id: tokenID, amount: amount, timestamp: timestamp, } var nftDetail = { link: dataJson.link, name: dataJson.name, hash: dataJson.hash, } // 前端展示数据 console.log(txDetail) console.log(nftDetail) return txDetail, nftDetail } catch (err) { console.log(err) } }
用户新增 action 操作。不用再首页展示,只在第三张图的下拉框显示即可。用户新增功能需要传入参数较多(TODO)
// 下述,所有参数可设置默认值。用户新增功能时,用户可以设置是否用户输入。,前端不显示,不设置即前端需要输入。 // 需要传入参数 const contractName = '合约名' const method = '方法名' // 调用方法的参数。(前端可选多个) const from =''; const to = ''; ...
切换账号功能用户点击头像,可以新增账户,即新增一个 acc 对象,切换账户,即切换 acc 对象。新增账户也需要指定私钥,安全码(非必须)。(TODO)
// TODO 需要通过用户选择的私钥路径,读取出来私钥的内容 const private = '' // 安全码 const password = '' // 登录后 生成 账户对象 const acc = xsdk.import(password, private)
ClickHouse提供了原生的命令行客户端clickhouse-client来操作ClickHouse。 clickhouse-client可以在交互和非交互(批处理)模式下使用。 交互模式下,一次登录,可以执行多次查询。一般用不到,要查询数据直接用客户端软件就行了,如DBeaver。 非交互模式一般用于使用脚本执行查询语句。 使用方式 clickhouse-client [参数] 配置参数
1.安装oidc-client npm install --save vuex npm install oidc-client 2.新建oidc-client.js import Oidc from "oidc-client"; export const mgr = new Oidc.UserManager({ authority: "authority",//认证服务器 client_id: "
vue项目使用socket.io-client客户端 安装socket.io-client模块 npm install socket.io-client --save 新建一个js文件,配置socket: import Vue from ‘vue’; import io from ‘socket.io-client’; //引入socket.io-client export default new
安装 ssh2-sftp-client (npm) npm install ssh2-sftp-client API rmdir(romotePath, true) 递归删除服务器文件夹 uploadDir(localPath, romotePath) 递归上传文件夹 put(localPath, romotePath) get(romotePath, localPath) demo const
一、常用命令及例子 fabric-ca-client主要子命令 fabric-ca-client用来管理身份(包括属性管理)和证书(包括续订和回收)。主要子命令如下: affiliation:管理分支机构 certificate:管理证书 enroll:认证一个账号 gencrl:撤销证书(生成一个CRL:Certificate Revocation Lists,证书撤销表) gencsr:创建证
1.ovsdb-client简介 Ovsdb-client是通过ovsdb管理协议,来与ovsdb数据库服务器进行通信。通信的方法根据协议RFC 7047规定的有以下各种命令,见(ovsdb-client使用)。 本文主要讲一下ovsdb-client的基本命令及用法。 2.ovsdb-client使用 1.ovsdb-client list_dbs 该命令是连接到服务器,检索已知数据库的列表,每
1. clickhouse-client 底层是基于tcp协议 1.1 交互式协议 [root@clickhouse1 ~]# [root@clickhouse1 ~]# clickhouse-client -h clickhouse1 --port 9000 -u default --password default123 -m -n ClickHouse client version 21.6
本文仅针对使用过程中出现的诸多错误提示符,由于网上关于gdc-client的教程非常多,不做搬运。 1.Win 环境下的gdc-client下载与使用: 问题:ERROR: Unable to save state: [WinError 17] 系统无法将文件移到不同的磁盘驱动器。 解决方案:win环境下目前测试仅能在系统盘,即C盘运行下载,无法手动更改路径至其他,否则会报上述错误。无奈将将对应的
问题内容: 我正在编写一个webapp,其中包含适用于Firefox和chrome的浏览器插件组件。我当前的测试系统使用通过Selenium IDE创建的一系列Selenium测试。 是否可以为Firefox和chrome(也可能是其他浏览器)安装,激活和删除selenium浏览器插件? 我认为最大的担忧是安装/启用浏览器插件需要重新启动浏览器,我不确定是否可以通过selenium关闭。 通过访问
我正在使用selenium webDrive来测试我们的网站。我们还有一个浏览器插件/扩展,我们想以同样的方式进行测试。据我所知,没有任何方法可以使用web驱动程序测试插件的安装。是否有任何方法可以使用web驱动程序进行测试?如果没有,我如何自动测试插件的安装?
有没有办法调试JSX文件? 我看不见那张照片。当我查看safari/chrome中的resources选项卡时,会看到jsx文件。我们能用调试器吗?
浏览器工作原理。 目录 排版引擎 渲染 JavaScript 引擎 并发模型 内存管理 执行环境 网络通信 缓存 Cookie 跨源资源共享 安全 同源策略 Web APIs 事件 存储 定时器 Fetch 文档操作 数据通信 图形处理 音视频处理
设计开发的页面与网站需要在浏览器上预览与调试。推荐使用 Chrome 浏览器。 Chrome Safari Firefox Edge(IE)
可浏览互联网Web网页的应用程序。 浏览器的LiveArea™ 浏览Web网页 开启更多窗口 使用书签及历史记录 设定浏览器 上传
浏览工具 JS 浏览工具: URI.js platform.js history.js html2canvas 参考工具(查看浏览器是否支持某特性) caniusee.com HTML5 Please HTML5 test Browserscope webbrowsercompatibility.com iwanttouse.com/ Platform status Browser support
由于JavaScript的出现就是为了能在浏览器中运行,所以,浏览器自然是JavaScript开发者必须要关注的。 目前主流的浏览器分这么几种: IE 6~11:国内用得最多的IE浏览器,历来对W3C标准支持差。从IE10开始支持ES6标准; Chrome:Google出品的基于Webkit内核浏览器,内置了非常强悍的JavaScript引擎——V8。由于Chrome一经安装就时刻保持自升级,所以