Anchor与Scatter一起用
最后更新于
最后更新于
一、登录时选择钱包
把选择的钱包写入本地存储,最好写入sessionStorage
中,这样页面关闭时就不存在了,下次页面重新打开需要重新登录。
注意:如果是APLink访问,则不需要选择Anchor
钱包,毕竟没办法扫码自己。
export const isAPLink = window.navigator.userAgent
.toLowerCase()
.includes('aplink');
如果是APlink访问,直接选择第一个钱包,即scatter,否则弹出钱包选择框,让用户自己选择钱包。
<div
className={styles.btn}
onClick={() => isAPLink ? selectWallet(walletList[0]) : setShowWalletList(true)}
>
{intl.get('connectWallet')}
</div>
如果是anchor登录,需要把link
实例存放好,因为无关页面渲染,所以在NFTone中直接存入全局变量中。
export function initLink() {
if (!window.__LINK__) {
const transport = new AnchorLinkBrowserTransport();
const link = new AnchorLink({
transport,
service: "https://fwd.aplink.app", // 'ws://192.168.80.152:7001', // 'http://fwd.aplink.app', //
chains: [
{
chainId: network.chainId,
nodeUrl: `${network.protocol}://${network.host}`,
},
],
});
window.__LINK__ = link;
}
return window.__LINK__;
}
// 登录
export async function login(dispatch) {
const link = initLink();
const identity = await link.login(scope);
// ...
}
二、具体交易的发起
写个工具函数实现不同钱包发起交易不同
import { scope } from './anchor';
import { getClient } from './client';
import { WALLET } from './const';
import storage from './storage';
export type action = {
contract: string; // 合约名称
name: string; // 合约方法名
data: any; // 合约参数
};
/**
* 调用合约
*/
export async function transact(actions: action[]) {
// 过滤出正确的action
actions = actions.filter(item => checkAction(item));
if (!Array.isArray(actions) || !actions.length) {
return;
}
const wallet = storage.get("wallet");
try {
if (wallet === WALLET.ANCHOR) {
return await anchor(actions);
} else if (wallet === WALLET.SCATTER) {
return await scatter(actions);
}
} catch (e) {
const error: any = typeof e === 'string' ? JSON.parse(e) : e;
if (error?.error) {
const { code, details = [] } = error.error;
if (code === 3050003) {
// 断言错误
const res = details[0]?.message?.match(/\$\$\$(\d+)\$\$\$/);
if (res) {
message.error(
`$$$${res[1]}$$$: ${intl.get(`contract_error_${res[1]}`)}`,
);
} else {
message.error(`${code}: ${details[0]?.message}`);
}
} else {
message.error(`${code}: ${intl.get(code)}`);
}
}
throw e;
}
}
function checkAction(action: action) {
const { contract, data, name } = action;
if (!contract || !data || !name) {
return false;
}
return true;
}
/**
* Scatter发起交易
* @param actions
* @returns
*/
async function scatter(actions: action[]) {
const client = await getClient();
if (actions.length === 1) {
const { contract, data, name } = actions[0];
const contractObj = await client.contract(contract);
return await contractObj[name](data);
} else {
return await client.transaction(
actions.map(action => action.contract),
obj => {
for (const action of actions) {
obj[action.contract.replace(".", "_")][action.name](action.data);
}
},
);
}
}
/**
* Anchor发起交易
* @param actions
* @returns
*/
async function anchor(actions: action[]) {
const walletAddress = storage.get("walletAddress");
const authority = storage.get("authority");
// @ts-ignore
const session = await window.__LINK__.restoreSession(scope);
const toAction = (action: action) => ({
account: action.contract,
name: action.name,
authorization: [
{
actor: walletAddress,
permission: authority,
},
],
data: action.data,
});
if (actions.length === 1) {
return await session.transact(
{ action: toAction(actions[0]) },
{ broadcast: true },
);
} else {
return await session.transact(
{ actions: actions.map(item => toAction(item)) },
{ broadcast: true },
);
}
}
然后我们就可以愉快的修改以前基于Scatter
写的代码了。
const params = {
from: walletAddress,
to: detail.project_contract_address,
quantity: asset.stringify({
...amaxCurrencyTokens[token.symbol],
amount: totalPrice,
}),
memo: `booth:${detail.project_id}`,
};
const client = await getClient();
const contract = await client.contract(token.contract);
const res = await contract.transfer(params);
修改为
const params = {
from: walletAddress,
to: detail.project_contract_address,
quantity: asset.stringify({
...amaxCurrencyTokens[token.symbol],
amount: totalPrice,
}),
memo: `booth:${detail.project_id}`,
};
// 把需要的参数抽出来,推送交易由transact方法来完成,transact方法实现按不同钱包推送交易。
const res = await transact([{
contract: token.contract,
name: "transfer",
data: params
}]);
是不是很简单
三、不依赖钱包的链上查询,则使用 '@amax/amaxjs
来实现
import Amax from '@amax/amaxjs';
export function getLocalClient() {
const client = Amax({
httpEndpoint: `${network.protocol}://${network.host}`,
chainId: network.chainId,
});
return client;
}
const client = await getLocalClient();
const id = Number(params.id);
try {
return await client.getTableRows({
code: contractName.pass,
scope: contractName.pass,
table: 'passes',
json: true,
limit: 1,
index_position: 1,
upper_bound: id,
lower_bound: id,
});
} catch (error) {
console.log(error);
}
// ...或者其它操作
如何与后端进行登录验证?
export async function login(dispatch) {
const link = initLink();
const identity = await link.login(scope);
const { account, proof, proofKey, proofValid } = await verifyProof(
link,
identity,
);
const walletAddress = proof.signer.actor.toString();
const authority = proof.signer.permission.toString();
const chainId = network.chainId;
storage.set('walletAddress', walletAddress);
storage.set('authority', authority);
storage.set('chainId', chainId);
// proof信息传给后端进行签名校验,成功返回token,失败则退出link登录。
await dispatch({
type: 'login/login',
params: {
signature: proof.signature.toString(),
message: '',
wallet_address: walletAddress,
authority,
scope: proof.scope.toString(),
expiration: proof.expiration.toString(),
},
callback: async ({ data, code }) => {
if (code === 200) {
updateToken(data.token);
await dispatch({
type: 'global/updateState',
state: {
isLogin: true,
walletAddress,
chainId,
},
});
setTimeout(eventBus.trigger, 400, 'getBalance');
} else {
await link.clearSessions(scope);
storage.remove('walletAddress');
storage.remove('authority');
storage.remove('chainId');
}
},
});
}