增加native 的rpc调用

This commit is contained in:
cebgcontract 2022-10-28 17:50:14 +08:00
parent 1ce1628f0b
commit c32e60076a
12 changed files with 352 additions and 121 deletions

27
src/api/WalletApi.ts Normal file
View File

@ -0,0 +1,27 @@
import { WALLET_API_HOST } from "../config/constants";
import { GET_JSON, POST_JSON } from "../lib/Http";
export function googleAuth(idToken: string) {
const url = `${WALLET_API_HOST}/wallet/login/google`;
return POST_JSON(url, { token: idToken });
}
export function getWalletInfo() {
const url = `${WALLET_API_HOST}/wallet/info`;
return GET_JSON(url);
}
export function uploadWalletInfo(data) {
const url = `${WALLET_API_HOST}/wallet/info`;
return POST_JSON(url, data);
}
export function fetchUserCollection() {
const url = `${WALLET_API_HOST}/wallet/collection`;
return GET_JSON(url);
}
export function uploadUserCollection(data) {
const url = `${WALLET_API_HOST}/wallet/collection`;
return POST_JSON(url, data);
}

View File

@ -1,24 +1,36 @@
let createWalletEvents = () => ({ var createWalletEvents = () => ({
events: {}, events: {},
emit (event, ...args) { emit(event, ...args) {
for (let i of this.events[event] || []) { for (let i of this.events[event] || []) {
i(...args) i(...args);
} }
}, },
on (event, cb) { on(event, cb) {
;(this.events[event] = this.events[event] || []).push(cb) (this.events[event] = this.events[event] || []).push(cb);
return () => (this.events[event] = this.events[event].filter(i => i !== cb)) return () =>
(this.events[event] = this.events[event].filter((i) => i !== cb));
},
once(event, cb) {
var callback = (...args) => {
this.events[event] = this.events[event].filter((i) => i !== callback);
cb(...args);
};
(this.events[event] = this.events[event] || []).push(callback);
}, },
listen(event, cb) { listen(event, cb) {
;(this.events[event] = this.events[event] || []).push(cb) (this.events[event] = this.events[event] || []).push(cb);
return () => (this.events[event] = this.events[event].filter(i => i !== cb)) return () =>
} (this.events[event] = this.events[event].filter((i) => i !== cb));
}) },
remove(event, cb) {
this.events[event] = this.events[event].filter((i) => i !== cb);
},
});
export { createWalletEvents } export { createWalletEvents };
export const WALLET_CHAIN_CHANGE = 'wallet_chain_change' export const WALLET_CHAIN_CHANGE = "wallet_chain_change";
export const WALLET_ACCOUNT_CHANGE = 'wallet_account_change' export const WALLET_ACCOUNT_CHANGE = "wallet_account_change";
export const WALLET_TOKEN_TYPE_CHANGE = 'wallet_token_type_change' export const WALLET_TOKEN_TYPE_CHANGE = "wallet_token_type_change";

9
src/config/WalletEnv.ts Normal file
View File

@ -0,0 +1,9 @@
import { createWalletEvents } from "../common/WalletEvent";
import { singleton } from "../decorator/singleton.decorator";
@singleton
export class WalletEnv {
public idToken: string;
public token: string;
public handler = createWalletEvents();
}

View File

@ -114,53 +114,61 @@ export const DEFAULT_NFT_TYPES = {
}, },
137: { 137: {
hero: { hero: {
address: "0x0EB362BD40F2288fF25A6Ee1b487cB0cb4638e0D", address: "0xaE08adb5278B107D2501e7c61907e41FEf3887D7",
type: "erc721", type: "erc721",
}, },
weapon: { weapon: {
address: "0x29F67A372AC1c6AcF478A564992D421FE20F2cc8", address: "0xee0044BF2ACEf7C3D7f6781d8f5DC4d2Dd1CE64c",
type: "erc721", type: "erc721",
}, },
chip: { chip: {
address: "0x54B6ED7EDe9355b471985439421Aa1DC7Da6Dc20", address: "0xc058411B15E544291765F15B13c88582b7bceaD0",
type: "erc1155", type: "erc1155",
}, },
shard: { shard: {
address: "0x54B6ED7EDe9355b471985439421Aa1DC7Da6Dc20", address: "0x1d4c7908E6a6795aE4335D0F072B0A129AAFFdc1",
type: "erc1155", type: "erc1155",
}, },
}, },
80001: { 80001: {
hero: { hero: {
address: "0x0EB362BD40F2288fF25A6Ee1b487cB0cb4638e0D", address: "0xaE08adb5278B107D2501e7c61907e41FEf3887D7",
type: "erc721", type: "erc721",
}, },
weapon: { weapon: {
address: "0x29F67A372AC1c6AcF478A564992D421FE20F2cc8", address: "0xee0044BF2ACEf7C3D7f6781d8f5DC4d2Dd1CE64c",
type: "erc721", type: "erc721",
}, },
chip: { chip: {
address: "0x54B6ED7EDe9355b471985439421Aa1DC7Da6Dc20", address: "0xc058411B15E544291765F15B13c88582b7bceaD0",
type: "erc1155", type: "erc1155",
}, },
shard: { shard: {
address: "0x54B6ED7EDe9355b471985439421Aa1DC7Da6Dc20", address: "0x1d4c7908E6a6795aE4335D0F072B0A129AAFFdc1",
type: "erc1155", type: "erc1155",
}, },
}, },
}; };
export const JC_CONTRACTS = { export const JC_CONTRACTS = {
321: {}, 321: {
322: {}, nftMall: "0xa44927698D0aC8EF29e91508839cd6e10f773EE0",
evolveFactory: "0x07Bad070e403a4Bad2Eec3BA3894c4524d3d2674",
minterFactory: "0x5ecEFA2707e3f09B9A169ae696B36Df8dB7410ED",
},
322: {
nftMall: "0xa44927698D0aC8EF29e91508839cd6e10f773EE0",
evolveFactory: "0x07Bad070e403a4Bad2Eec3BA3894c4524d3d2674",
minterFactory: "0x5ecEFA2707e3f09B9A169ae696B36Df8dB7410ED",
},
137: { 137: {
nftMall: "", nftMall: "0xa44927698D0aC8EF29e91508839cd6e10f773EE0",
evolveFactory: "", evolveFactory: "0x07Bad070e403a4Bad2Eec3BA3894c4524d3d2674",
minterFactory: "", minterFactory: "0x5ecEFA2707e3f09B9A169ae696B36Df8dB7410ED",
}, },
80001: { 80001: {
nftMall: "", nftMall: "0xa44927698D0aC8EF29e91508839cd6e10f773EE0",
evolveFactory: "", evolveFactory: "0x07Bad070e403a4Bad2Eec3BA3894c4524d3d2674",
minterFactory: "", minterFactory: "0x5ecEFA2707e3f09B9A169ae696B36Df8dB7410ED",
}, },
}; };

View File

@ -1 +1,3 @@
export const WALLET_STORAGE_KEY_NAME = 'jc_wallet_data' export const WALLET_STORAGE_KEY_NAME = "jc_wallet_data";
export const WALLET_API_HOST = "http://10.0.1.3:3007";

View File

@ -19,12 +19,14 @@ import { IAccount, INFT, initAccount, initNFT } from "./data/DataModel";
import { import {
checkPassword, checkPassword,
loadData, loadData,
loadToken,
saveData, saveData,
savePassword, savePassword,
} from "./manage/DataManage"; } from "./manage/DataManage";
import { WALLET_STORAGE_KEY_NAME } from "./config/constants"; import { WALLET_STORAGE_KEY_NAME } from "./config/constants";
import { DEFALUT_TOKENS, JC_CONTRACTS } from "./config/chain_config"; import { DEFALUT_TOKENS, JC_CONTRACTS } from "./config/chain_config";
import { import {
loadInternalWallet,
newAccount, newAccount,
newMnemonic, newMnemonic,
restoreWalletByMnemonic, restoreWalletByMnemonic,
@ -35,6 +37,7 @@ import { ERC1155Standard } from "./standards/ERC1155Standard";
import { ZWalletConnect } from "./comp/ZWalletConnect"; import { ZWalletConnect } from "./comp/ZWalletConnect";
import { getJCErc721Info, getTypeByAddress, UNKNOW } from "./util/chain.util"; import { getJCErc721Info, getTypeByAddress, UNKNOW } from "./util/chain.util";
import { JCStandard } from "./standards/JCStandard"; import { JCStandard } from "./standards/JCStandard";
import { NativeSvr } from "./services/NativeSvr";
var global = var global =
(typeof globalThis !== "undefined" && globalThis) || (typeof globalThis !== "undefined" && globalThis) ||
@ -80,6 +83,7 @@ export default class JCWallet {
public erc721Standard: ERC721Standard; public erc721Standard: ERC721Standard;
public erc1155Standard: ERC1155Standard; public erc1155Standard: ERC1155Standard;
public jcStandard: JCStandard; public jcStandard: JCStandard;
public nativeSvr: NativeSvr;
public wConnect: ZWalletConnect; public wConnect: ZWalletConnect;
public mainHandlers = createWalletEvents(); public mainHandlers = createWalletEvents();
public data: IAccount[] = []; public data: IAccount[] = [];
@ -89,15 +93,8 @@ export default class JCWallet {
private rpcUrl: string = ""; private rpcUrl: string = "";
public rpc: any = {}; public rpc: any = {};
constructor({ constructor({ type, chain }: { type: number; chain: number }) {
type, this.nativeSvr = new NativeSvr();
chain,
password,
}: {
type: number;
chain: number;
password: string;
}) {
this.walletType = type; this.walletType = type;
chain = chain || 80001; chain = chain || 80001;
let data = AllChains.find((o) => o.id === chain); let data = AllChains.find((o) => o.id === chain);
@ -108,38 +105,40 @@ export default class JCWallet {
this.rpcUrl = data.rpc; this.rpcUrl = data.rpc;
console.log(`rpc url: ${this.rpcUrl}`); console.log(`rpc url: ${this.rpcUrl}`);
if (this.walletType === WalletType.INTERNAL) { if (this.walletType === WalletType.INTERNAL) {
if (!password) { this.prepareInternalWallet();
throw new Error("need password");
}
if (!checkPassword(password)) {
throw new Error("password error");
}
this.password = password;
savePassword(password);
this.initInternalWallet();
} }
this.init({ chains: [80001], password: this.password }); this.init({ chains: [80001], password: this.password });
window.jc = { wallet: this }; window.jc = { wallet: this };
} }
private initInternalWallet() { private prepareInternalWallet() {
var start = Date.now(); let token = loadToken();
this.web3 = new Web3(this.rpcUrl); if (!token) {
console.log(`init web3 cost: ${(Date.now() - start) / 1000}`); // check if token expired
this.erc20Standard = new ERC20Standard(this.web3); } else {
this.erc721Standard = new ERC721Standard(this.web3); // to goole login
this.erc1155Standard = new ERC1155Standard(this.web3); }
this.jcStandard = new JCStandard(this.web3); }
start = Date.now(); public async initInternalWallet() {
this.wallet = this.web3.eth.accounts.wallet.load( await loadInternalWallet();
this.password, // var start = Date.now();
WALLET_STORAGE_KEY_NAME // this.web3 = new Web3(this.rpcUrl);
); // console.log(`init web3 cost: ${(Date.now() - start) / 1000}`);
console.log(`load wallet cost: ${(Date.now() - start) / 1000}`); // this.erc20Standard = new ERC20Standard(this.web3);
start = Date.now(); // this.erc721Standard = new ERC721Standard(this.web3);
this.data = loadData(); // this.erc1155Standard = new ERC1155Standard(this.web3);
console.log(`init wallet ext data cost: ${(Date.now() - start) / 1000}`); // this.jcStandard = new JCStandard(this.web3);
// start = Date.now();
// this.wallet = this.web3.eth.accounts.wallet.load(
// this.password,
// WALLET_STORAGE_KEY_NAME
// );
// console.log(`load wallet cost: ${(Date.now() - start) / 1000}`);
// start = Date.now();
// this.data = loadData();
// console.log(`init wallet ext data cost: ${(Date.now() - start) / 1000}`);
} }
/** /**
* init wallet connect * init wallet connect
@ -189,13 +188,14 @@ export default class JCWallet {
} }
} }
} }
if (this.walletType !== WalletType.INTERNAL) { return;
return; // if (this.walletType !== WalletType.INTERNAL) {
} // return;
if (!this.wallet || this.wallet.length === 0) { // }
// this.createAccount(); // if (!this.wallet || this.wallet.length === 0) {
this.newWallet(password); // // this.createAccount();
} // this.newWallet(password);
// }
} }
public newWallet(password: string) { public newWallet(password: string) {

View File

@ -1,9 +1,71 @@
import 'whatwg-fetch' import "whatwg-fetch";
import { WalletEnv } from "../config/WalletEnv";
export async function request(url, option) {
let headers = new Headers();
headers.append("Content-Type", "application/json");
let walletEnv = new WalletEnv();
if (walletEnv.token) {
headers.append("Authorization", `Bearer ${walletEnv.token}`);
}
let optionInt: any = {
method: "GET",
mode: "cors",
headers,
cache: "no-cache",
};
Object.assign(optionInt, option);
// console.log("request option", JSON.stringify(optionInt));
return fetch(url, optionInt);
}
export async function GET(url: string) { export async function GET(url: string) {
return fetch(url) return request(url, {});
} }
export async function GET_JSON(url: string) { export async function GET_JSON(url: string) {
return fetch(url).then(res => {return res.json()}) return GET(url).then((res) => {
} return res.json();
});
}
export async function POST(url, data) {
let option = {
method: "POST",
body: JSON.stringify(data),
};
return request(url, option);
}
export async function POST_JSON(url, data) {
return POST(url, data).then((res) => {
return res.json();
});
}
/**
* var headers = new Headers();
headers.append("Content-Type", "application/json");
if (window.chain.logined) {
formData.token = window.chain.token;
}
try {
let data = await fetch(
FORM_URL,
{
method: 'POST',
headers,
mode: 'cors',
cache: 'no-cache',
body: JSON.stringify(formData)
},
)
.then(res => {
return res.json()
})
if (!data.errcode) {
showSuccess();
}
} catch (err) {
console.log('error post data', err);
}
*/

View File

@ -1,37 +1,45 @@
import { IAccount } from "../data/DataModel"; import { IAccount } from "../data/DataModel";
import { aesDecrypt, aesEncrypt } from "../util/crypto.util"; import { aesDecrypt, aesEncrypt } from "../util/crypto.util";
import sha256 from 'crypto-js/sha256' import sha256 from "crypto-js/sha256";
const LOCAL_ACCOUNT_DATAS = 'local_account_datas' const LOCAL_ACCOUNT_DATAS = "local_account_datas";
const LOCAL_WALLET_MNEMONIC = 'local_wallet_mnemonic' const LOCAL_WALLET_TOKEN = "local_wallet_token";
const LOCAL_WALLET_PASSWORD = 'local_wallet_password'; const LOCAL_WALLET_MNEMONIC = "local_wallet_mnemonic";
const LOCAL_PASSWORD_SALT = '_jcwallet'; const LOCAL_WALLET_PASSWORD = "local_wallet_password";
const LOCAL_PASSWORD_SALT = "_jcwallet";
export function loadToken() {
return localStorage.getItem(LOCAL_WALLET_TOKEN);
}
export function loadData(){ export function saveToken(token: string) {
const dataStr = localStorage.getItem(LOCAL_ACCOUNT_DATAS) localStorage.setItem(LOCAL_WALLET_TOKEN, token);
let result: IAccount[] = [] }
export function loadData() {
const dataStr = localStorage.getItem(LOCAL_ACCOUNT_DATAS);
let result: IAccount[] = [];
if (dataStr) { if (dataStr) {
try { try {
result = JSON.parse(dataStr) result = JSON.parse(dataStr);
} catch (err) { } catch (err) {
console.log('load local data error') console.log("load local data error");
} }
} }
return result return result;
} }
export function saveData(datas: IAccount[]) { export function saveData(datas: IAccount[]) {
const dataStr = JSON.stringify(datas) const dataStr = JSON.stringify(datas);
localStorage.setItem(LOCAL_ACCOUNT_DATAS, dataStr) localStorage.setItem(LOCAL_ACCOUNT_DATAS, dataStr);
} }
export function loadMnemonic(password: string) { export function loadMnemonic(password: string) {
let dataStr: string = localStorage.getItem(LOCAL_WALLET_MNEMONIC) let dataStr: string = localStorage.getItem(LOCAL_WALLET_MNEMONIC);
if (dataStr) { if (dataStr) {
dataStr = aesDecrypt(dataStr, password) dataStr = aesDecrypt(dataStr, password);
} }
return dataStr return dataStr;
} }
export function saveMnemonic(mnemonic: string, password: string) { export function saveMnemonic(mnemonic: string, password: string) {
@ -48,4 +56,4 @@ export function checkPassword(password: string) {
export function savePassword(password: string) { export function savePassword(password: string) {
const dataStr = sha256(password + LOCAL_PASSWORD_SALT).toString(); const dataStr = sha256(password + LOCAL_PASSWORD_SALT).toString();
localStorage.setItem(LOCAL_WALLET_PASSWORD, dataStr); localStorage.setItem(LOCAL_WALLET_PASSWORD, dataStr);
} }

View File

@ -1,23 +1,71 @@
import { hdkey } from 'ethereumjs-wallet' import { hdkey } from "ethereumjs-wallet";
import { generateMnemonic, mnemonicToSeedSync } from "bip39"; import { generateMnemonic, mnemonicToSeedSync } from "bip39";
import { loadMnemonic, saveMnemonic } from './DataManage'; import { loadMnemonic, saveMnemonic } from "./DataManage";
import { NativeSvr } from "../services/NativeSvr";
import { getWalletInfo, googleAuth, uploadWalletInfo } from "../api/WalletApi";
import { WalletEnv } from "../config/WalletEnv";
import { md5Hash, sha1Hash } from "../util/crypto.util";
export function newAccount(password: string, index: number) { export function newAccount(password: string, index: number) {
const mnemonic = loadMnemonic(password) const mnemonic = loadMnemonic(password);
const seed = mnemonicToSeedSync(mnemonic) const seed = mnemonicToSeedSync(mnemonic);
const hdWallet = hdkey.fromMasterSeed(seed) const hdWallet = hdkey.fromMasterSeed(seed);
const keyPair1 = hdWallet.derivePath(`m/44'/60'/0'/0/${index}`) const keyPair1 = hdWallet.derivePath(`m/44'/60'/0'/0/${index}`);
const w1 = keyPair1.getWallet() const w1 = keyPair1.getWallet();
return {address: w1.getAddressString(), privateKey: w1.getPrivateKeyString()} return {
address: w1.getAddressString(),
privateKey: w1.getPrivateKeyString(),
};
} }
export function newMnemonic(password: string) { export function newMnemonic(password: string) {
let mnemonic = generateMnemonic() let mnemonic = generateMnemonic();
saveMnemonic(mnemonic, password) saveMnemonic(mnemonic, password);
return mnemonic return mnemonic;
} }
export function restoreWalletByMnemonic(mnemonic: string, password: string) { export function restoreWalletByMnemonic(mnemonic: string, password: string) {
saveMnemonic(mnemonic, password) saveMnemonic(mnemonic, password);
} }
export async function loadInternalWallet() {
let res: any = await new NativeSvr().signWithGoogle();
console.log("native res: " + res);
let tokenRes = await googleAuth(res);
console.log("wallet token: " + tokenRes.data?.token);
if (tokenRes.errcode || !tokenRes.data?.token) {
return;
}
new WalletEnv().token = tokenRes.data.token;
let infoRes = await getWalletInfo();
if (infoRes.errcode) {
return;
}
console.log("wallet info: " + JSON.stringify(infoRes.data));
let seed = infoRes.data.oid + infoRes.data.is + infoRes.data.salt;
let seedHash = md5Hash(seed);
let idHash = md5Hash(infoRes.data.oid);
if (!infoRes.data.key) {
let time = Date.now();
//@ts-ignore
let strWallwt = jsb.generateWallet(idHash, seedHash);
console.log("generate wallet cost: " + (Date.now() - time) / 1000);
let walletInfo = JSON.parse(strWallwt);
console.log(strWallwt);
setImmediate(function () {
uploadWalletInfo({ key: walletInfo.master });
});
} else {
//@ts-ignore
jsb.prepareWallet(idHash, seedHash, infoRes.data.key);
}
let signStr = walletSign("111");
console.log("sign str: " + signStr);
}
export function walletSign(str: string) {
//@ts-ignore
let result = jsb.walletSign(str);
return result;
}

44
src/services/NativeSvr.ts Normal file
View File

@ -0,0 +1,44 @@
import { payloadId } from "@walletconnect/utils";
import { createWalletEvents } from "../common/WalletEvent";
import { singleton } from "../decorator/singleton.decorator";
@singleton
export class NativeSvr {
_event = createWalletEvents();
_subscribeToResponse(id: string, callback) {
this._event.on(`response:${id}`, callback);
}
_subscribeToCallResponse(id) {
return new Promise((resolve, reject) => {
this._subscribeToResponse(id, (result) => {
if (result.errcode) {
reject(result.errcode);
return;
}
resolve(result.data);
});
});
}
public handleNativeCallback(...args) {
let id = args[0];
let result = JSON.parse(args[1]);
this._event.emit(`response:${id}`, result);
}
public signWithGoogle() {
let id = payloadId();
//@ts-ignore
jsb.signWithGoogle(id);
return this._subscribeToCallResponse(id);
}
public signOutGoogle() {
let id = payloadId();
//@ts-ignore
jsb.signOutGoogle(id);
return this._subscribeToCallResponse(id);
}
}

View File

@ -22,6 +22,7 @@ export class JCStandard {
}) { }) {
let address = JC_CONTRACTS[window.jc.wallet.currentChain.id].nftMall; let address = JC_CONTRACTS[window.jc.wallet.currentChain.id].nftMall;
const contract = new this.web3.eth.Contract(abiNftMall, address); const contract = new this.web3.eth.Contract(abiNftMall, address);
//TODO:: increaseAllowance before call
let gas = await contract.methods let gas = await contract.methods
.buy721NFT(addresses, values, signature) .buy721NFT(addresses, values, signature)
.estimateGas({ gas: 1000000 }); .estimateGas({ gas: 1000000 });

View File

@ -1,5 +1,15 @@
import { AES, enc, mode, pad } from 'crypto-js'; import { AES, enc, mode, pad } from "crypto-js";
import sha256 from 'crypto-js/sha256' import sha256 from "crypto-js/sha256";
import sha1 from "crypto-js/sha1";
import md5 from "crypto-js/md5";
export function sha1Hash(str: string) {
return sha1(str).toString();
}
export function md5Hash(str: string) {
return md5(str).toString();
}
function generateIV(password: string) { function generateIV(password: string) {
const key = sha256(password).toString(); const key = sha256(password).toString();
@ -7,26 +17,26 @@ function generateIV(password: string) {
const ivHex = keyHex.clone(); const ivHex = keyHex.clone();
ivHex.sigBytes = 16; ivHex.sigBytes = 16;
ivHex.words.splice(4); ivHex.words.splice(4);
return {keyHex,ivHex} return { keyHex, ivHex };
} }
export function aesEncrypt(text: string, password: string) { export function aesEncrypt(text: string, password: string) {
const {keyHex, ivHex} = generateIV(password); const { keyHex, ivHex } = generateIV(password);
const messageHex = enc.Utf8.parse(text); const messageHex = enc.Utf8.parse(text);
const encrypted = AES.encrypt(messageHex, keyHex, { const encrypted = AES.encrypt(messageHex, keyHex, {
"iv": ivHex, iv: ivHex,
"mode": mode.CBC, mode: mode.CBC,
"padding": pad.Pkcs7 padding: pad.Pkcs7,
}); });
return encrypted.toString(); return encrypted.toString();
} }
export function aesDecrypt(encryptedText: string, password: string) { export function aesDecrypt(encryptedText: string, password: string) {
const {keyHex, ivHex} = generateIV(password); const { keyHex, ivHex } = generateIV(password);
const decrypt = AES.decrypt(encryptedText, keyHex, { const decrypt = AES.decrypt(encryptedText, keyHex, {
"iv": ivHex, iv: ivHex,
"mode": mode.CBC, mode: mode.CBC,
"padding": pad.Pkcs7 padding: pad.Pkcs7,
}); });
return enc.Utf8.stringify(decrypt); return enc.Utf8.stringify(decrypt);
} }