UNPKG

@cityofzion/neon-js

Version:

Neon-JS SDK for interacting with NEO blockchain

209 lines 7.74 kB
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