@cityofzion/neon-js
Version:
Neon-JS SDK for interacting with NEO blockchain
209 lines • 7.74 kB
JavaScript
import { CONST, rpc, wallet, sc, tx, u } from "@cityofzion/neon-core";
import { setBlockExpiry, addFees } from "../helpers";
export class Nep17Contract {
constructor(contractHash, config) {
this.contractHash = contractHash;
this.config = config;
this.rpcClient = new rpc.RPCClient(config.rpcAddress);
}
/**
* Get the number of tokens owned by NEO address
*/
async balanceOf(address) {
if (!wallet.isAddress(address)) {
throw new Error("Address is not a valid NEO address");
}
try {
const response = await this.rpcClient.invokeFunction(this.contractHash.toString(), "balanceOf", [sc.ContractParam.hash160(address)]);
if (response.state == "FAULT") {
throw Error;
}
const decimals = await this.decimals();
if (decimals === 0) {
return parseInt(response.stack[0].value);
}
else {
const divider = Math.pow(10, decimals);
return parseInt(response.stack[0].value) / divider;
}
}
catch (e) {
throw new Error(`Failed to get balance of address. Error: ${e}`);
}
}
/**
* Get the number of decimals the token can have
*/
async decimals() {
if (this._decimals)
return this._decimals;
try {
const response = await this.rpcClient.invokeFunction(this.contractHash.toString(), "decimals");
if (response.state === "FAULT") {
throw Error;
}
this._decimals = parseInt(response.stack[0].value);
return this._decimals;
}
catch (e) {
throw new Error(`Failed to get decimals for contract: ${this.contractHash.toString()}. Error: ${e}`);
}
}
/**
* Get the human readable name of the token
*/
async name() {
if (this._name)
return this._name;
try {
const response = await this.rpcClient.getContractState(this.contractHash.toString());
this._name = response.manifest.name;
return this._name;
}
catch (e) {
throw new Error(`Failed to get name for contract: ${this.contractHash.toString()}. Error: ${e}`);
}
}
/**
* Get the abbreviated name of the token.
* Often used to represent the token in exchanges
*/
async symbol() {
if (this._symbol)
return this._symbol;
try {
const response = await this.rpcClient.invokeFunction(this.contractHash.toString(), "symbol");
if (response.state === "FAULT") {
throw Error;
}
this._symbol = u.utf82base64(response.stack[0].value);
return this._symbol;
}
catch (e) {
throw new Error(`Failed to get symbol for contract: ${this.contractHash.toString()}. Error: ${e}`);
}
}
/**
* Get the total amount of tokens deployed to the system
*
* Note: this is not the same as the total freely available tokens for exchanging.
* A certain amount might be locked in the contract until a specific release date.
*/
async totalSupply() {
try {
const response = await this.rpcClient.invokeFunction(this.contractHash.toString(), "totalSupply");
if (response.state === "FAULT") {
throw Error;
}
return parseInt(response.stack[0].value);
}
catch (e) {
throw new Error(`Failed to get total supply for contract: ${this.contractHash.toString()}. Error: ${e}`);
}
}
/**
* Move tokens from one address to another
* @param from - source NEO address
* @param to - destination NEO address
* @param amount - quantity of tokens to send
*/
async transfer(from, to, amount) {
if (!wallet.isAddress(from)) {
throw new Error("From address is not a valid NEO address");
}
if (!wallet.isAddress(to)) {
throw new Error("To address is not a valid NEO address");
}
if (amount <= 0) {
throw new Error("Invalid amount");
}
if (this.config.account === undefined ||
this.config.account.address != from) {
throw new Error("Invalid account or account address does not match 'from' address");
}
const balance = await this.balanceOf(from);
if (balance < amount) {
throw new Error("Insufficient funds");
}
const decimals = await this.decimals();
const builder = new sc.ScriptBuilder();
const amtToTransfer = decimals == 0 ? amount : amount * Math.pow(10, decimals);
builder.emitAppCall(this.contractHash, "transfer", [
u.HexString.fromHex(wallet.getScriptHashFromAddress(from)),
u.HexString.fromHex(wallet.getScriptHashFromAddress(to)),
amtToTransfer,
sc.ContractParam.any(null),
]);
builder.emit(sc.OpCode.ASSERT);
const transaction = new tx.Transaction();
transaction.script = u.HexString.fromHex(builder.build());
await setBlockExpiry(transaction, this.config, this.config.blocksTillExpiry);
// add a sender
transaction.addSigner({
account: this.config.account.scriptHash,
scopes: "CalledByEntry",
});
await addFees(transaction, this.config);
transaction.sign(this.config.account, this.config.networkMagic);
return await this.rpcClient.sendRawTransaction(transaction);
}
}
export class NEOContract extends Nep17Contract {
/**
* Convenience class initializing a Nep17Contract to the NEO token
* exposing additional claim functions
* @param config -
*/
constructor(config) {
super(u.HexString.fromHex(CONST.NATIVE_CONTRACT_HASH.NeoToken), config);
}
/**
* Move tokens from one address to another
* @param from - source NEO address
* @param to - destination NEO address
* @param amount - quantity of tokens to send
*/
async transfer(from, to, amount) {
if (!Number.isInteger(amount)) {
throw new Error("Amount must be an integer");
}
// remainder of the input checks is done in the super class
return await super.transfer(from, to, amount);
}
/**
* Claim gas for address
* @param address - NEO address
* @returns transaction id
*/
async claimGas(address) {
if (!wallet.isAddress(address)) {
throw new Error("From address is not a valid NEO address");
}
const unclaimed = await this.rpcClient.getUnclaimedGas(address);
if (u.BigInteger.fromNumber(unclaimed).compare(50000000) < 0) {
throw new Error("Minimum claim value is 0.5");
}
const balance = await this.balanceOf(address);
return await this.transfer(address, address, balance);
}
/**
* Get the available bonus GAS for address
* @param address - NEO address
*/
async getUnclaimedGas(address) {
if (!wallet.isAddress(address)) {
throw new Error("From address is not a valid NEO address");
}
return parseFloat(await this.rpcClient.getUnclaimedGas(address));
}
}
export class GASContract extends Nep17Contract {
/**
* Convenience class initializing a Nep17Contract to GAS token
* @param config -
*/
constructor(config) {
super(u.HexString.fromHex(CONST.NATIVE_CONTRACT_HASH.GasToken), config);
}
}
//# sourceMappingURL=base.js.map