/**
* Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*/
import React from 'react';
import {
getAddressFromPrivateKey,
getPubKeyFromPrivateKey,
fromBech32Address,
} from '@zilliqa-js/crypto';
import { Long, units, BN, validation } from '@zilliqa-js/util';
import { Transaction } from '@zilliqa-js/account';
import { bytes, Zilliqa } from '@zilliqa-js/zilliqa';
import { HTTPProvider, RPCMethod } from '@zilliqa-js/core';
export enum NETWORK {
IsolatedServer = 'isolated_server',
TestNet = 'testnet',
}
const initialState = {
config: undefined as any,
curNetwork: undefined as any,
zilliqa: undefined as Zilliqa | undefined,
version: undefined as number | undefined,
provider: undefined as HTTPProvider | undefined,
isAuth: undefined as boolean | undefined,
address: undefined as string | undefined,
publicKey: undefined as string | undefined,
privateKey: undefined as string | undefined,
};
export const ZilContext = React.createContext(initialState);
export class ZilProvider extends React.Component {
public readonly state = initialState;
async componentDidMount() {
const res = await fetch('config.json');
const config = await res.json();
console.log('config', config);
this.setState({ config }, () => this.initState());
}
initState = (networkKey?: string) => {
let curNetworkKey = networkKey || NETWORK.TestNet;
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
const networkParam = params['network'];
const isValidNetwork = [NETWORK.IsolatedServer, NETWORK.TestNet].includes(
networkParam as NETWORK
);
if (isValidNetwork) {
curNetworkKey = networkParam;
}
if (networkParam === undefined || !isValidNetwork) {
const urlSearchParams = new URLSearchParams(window.location.search);
urlSearchParams.set('network', curNetworkKey);
window.history.replaceState(
null,
'',
`${window.location.pathname}?${urlSearchParams.toString()}`
);
}
const { config } = this.state;
const curNetwork =
curNetworkKey === NETWORK.TestNet ? config[NETWORK.TestNet] : config[NETWORK.IsolatedServer];
const provider = new HTTPProvider(curNetwork.nodeUrl);
const zilliqa = new Zilliqa(curNetwork.nodeUrl, provider);
const version = bytes.pack(curNetwork.chainId, curNetwork.msgVersion);
this.setState({
curNetwork: curNetwork,
zilliqa: zilliqa,
version: version,
provider: provider,
isAuth: undefined as boolean | undefined,
address: undefined as string | undefined,
publicKey: undefined as string | undefined,
privateKey: undefined as string | undefined,
});
};
public accessWallet = (privateKey: string) => {
try {
const address = getAddressFromPrivateKey(privateKey);
const publicKey = getPubKeyFromPrivateKey(privateKey);
const zilliqa = this.state.zilliqa as Zilliqa;
zilliqa.wallet.addByPrivateKey(privateKey);
this.setState({
isAuth: true,
privateKey,
publicKey,
address,
zilliqa,
});
} catch (error) {
this.setState({ isAuth: false });
}
};
private getParams = async (toAddr, amountInZil) => {
const zilliqa = this.state.zilliqa as Zilliqa;
const version = this.state.version as number;
const response = await zilliqa.blockchain.getMinimumGasPrice();
const gasPrice: string = response.result || '';
const amountInQa = units.toQa(amountInZil, units.Units.Zil);
return {
toAddr,
version: version,
amount: amountInQa,
gasPrice: new BN(gasPrice.toString()),
gasLimit: Long.fromNumber(50),
};
};
public send = async ({ args }): Promise => {
const { amount, toAddress } = args;
const zilliqa = this.state.zilliqa as Zilliqa;
const provider = this.state.provider as HTTPProvider;
const tx = new Transaction(await this.getParams(toAddress, amount), provider);
const signedTx = await zilliqa.wallet.sign(tx);
const { txParams } = signedTx;
// Send a transaction to the network
const res = await provider.send(RPCMethod.CreateTransaction, txParams);
if (res.error !== undefined) throw new Error(res.error.message);
return res.result ? res.result.TranID : undefined;
};
public getBalance = async (): Promise => {
const zilliqa = this.state.zilliqa as Zilliqa;
const address = this.state.address as string;
if (typeof address !== 'string') {
return '0';
}
const res = await zilliqa.blockchain.getBalance(address);
if (res.error !== undefined) return '0';
return res.result ? res.result.balance : '0';
};
public getMinGasPrice = async (): Promise => {
const zilliqa = this.state.zilliqa as Zilliqa;
const res = await zilliqa.blockchain.getMinimumGasPrice();
if (res.error !== undefined) throw new Error(res.error.message);
return res.result ? res.result : '0';
};
public faucet = async ({ args, signal }): Promise => {
const { token, toAddress } = args;
let address = toAddress;
// address is either ByStr20 or bech32
//
// ByStr20: 20 byte hexadecimal string
// e.g. 0x573EC96638C8bB1c386394602E1460634F02aDdA
//
// bech32: A bech32 with a human-readable prefix of zil
// e.g. zil12ulvje3ceza3cwrrj3szu9rqvd8s9tw69c978p
if (validation.isBech32(toAddress)) {
address = fromBech32Address(toAddress);
}
const body = JSON.stringify({
address,
token,
});
const { curNetwork } = this.state;
const res = await fetch(curNetwork.faucetUrl, {
signal,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body,
});
if (!res.ok) {
throw new Error('Failed to run faucet, you may have reached maximum request limit.');
}
const data = await res.json();
return data ? data.txId : undefined;
};
public clearAuth = () => {
const { curNetwork } = this.state;
this.initState(curNetwork.name);
};
public switchNetwork = (key) => {
const urlSearchParams = new URLSearchParams(window.location.search);
urlSearchParams.set('network', key);
window.history.replaceState(
null,
'',
`${window.location.pathname}?${urlSearchParams.toString()}`
);
this.initState(key);
};
public render() {
return (
{this.props.children}
);
}
}