From 7f34a3784167ad036a97b2ecbe93b84498c33a17 Mon Sep 17 00:00:00 2001 From: MorganDream Date: Wed, 15 Jan 2020 15:13:59 +0800 Subject: [PATCH 1/2] Add a Neo plugin --- neo-logo.png | Bin 0 -> 15575 bytes package.json | 1 + src/index.js | 4 +- src/neo/neoEngine.js | 522 ++++++++++++++++++++++++++++++++++++++++ src/neo/neoInfo.js | 41 ++++ src/neo/neoPlugin.js | 176 ++++++++++++++ src/neo/neoSchema.js | 47 ++++ src/neo/neoTypes.js | 10 + test/engine/fixtures.js | 41 +++- test/plugin/fixtures.js | 142 ++++++++++- yarn.lock | 114 ++++++++- 11 files changed, 1084 insertions(+), 14 deletions(-) create mode 100644 neo-logo.png create mode 100644 src/neo/neoEngine.js create mode 100644 src/neo/neoInfo.js create mode 100644 src/neo/neoPlugin.js create mode 100644 src/neo/neoSchema.js create mode 100644 src/neo/neoTypes.js diff --git a/neo-logo.png b/neo-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b983662b16f2b74737d9d97cc1eb67779cc8293c GIT binary patch literal 15575 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 literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 62a838d9f..23460b977 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ }, "dependencies": { "@binance-chain/javascript-sdk": "^2.14.4", + "@cityofzion/neon-js": "^4.7.2", "@fioprotocol/fiosdk": "^1.0.2", "base-x": "^1.0.4", "biggystring": "^3.0.0", diff --git a/src/index.js b/src/index.js index d19c4c239..1e9152343 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ import { makeEthereumClassicPlugin } from './ethereum/etcInfo.js' import { makeEthereumPlugin } from './ethereum/ethInfo.js' import { makeRskPlugin } from './ethereum/rskInfo.js' import { makeFioPlugin } from './fio/fioPlugin' +import { makeNeoPlugin } from './neo/neoPlugin.js' import { makeStellarPlugin } from './stellar/stellarPlugin.js' import { makeTezosPlugin } from './tezos/tezosPlugin.js' import { makeRipplePlugin } from './xrp/xrpPlugin.js' @@ -25,7 +26,8 @@ const plugins = { stellar: makeStellarPlugin, tezos: makeTezosPlugin, rsk: makeRskPlugin, - binance: makeBinancePlugin + binance: makeBinancePlugin, + neo: makeNeoPlugin } if ( diff --git a/src/neo/neoEngine.js b/src/neo/neoEngine.js new file mode 100644 index 000000000..402a2368a --- /dev/null +++ b/src/neo/neoEngine.js @@ -0,0 +1,522 @@ +// @flow +import { tx } from '@cityofzion/neon-core' +import { api, rpc, wallet } from '@cityofzion/neon-js' +import { bns } from 'biggystring' +import { + type EdgeSpendInfo, + type EdgeTransaction, + InsufficientFundsError +} from 'edge-core-js/types' + +import { CurrencyEngine } from '../common/engine.js' +import { + getDenomInfo, + getOtherParams, + promiseAny, + validateObject +} from '../common/utils.js' +import { currencyInfo } from './neoInfo.js' +import { checkAddress } from './neoPlugin.js' +import { NeoTransactionOnline } from './neoSchema.js' +import { type NeoTxOtherParams } from './neoTypes.js' +const { Account } = wallet +const { RPCClient } = rpc +const ApiProvider = api.neoscan.instance + +const PRIMARY_CURRENCY = currencyInfo.currencyCode +// const ADDRESS_POLL_MILLISECONDS = 10000 +const ACCOUNT_POLL_MILLISECONDS = 15000 +const BLOCKCHAIN_POLL_MILLISECONDS = 20000 +const TRANSACTION_POLL_MILLISECONDS = 20000 +const ADDRESS_QUERY_LOOKBACK_BLOCKS = 1 + +type NeoFunction = + | 'neo_getBalance' + | 'neo_getTx' + | 'neo_getBlockCount' + | 'neo_broadcastTx' + | 'neo_getTxHeight' + | 'neo_getTxOut' + | 'neo_getBlock' + +export class NeoEngine extends CurrencyEngine { + async multicastServers(func: NeoFunction, ...params: any): Promise { + this.log(`start to query ${func} on Neo Blockchain`) + const out = { result: '', server: 'no server' } + switch (func) { + case 'neo_getBalance': { + const address = params[0] + if (!checkAddress(address)) { + throw new Error(`${address} is not a neo address`) + } + const promises = [] + const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes + for (const node of rpcNodes) { + const client = new RPCClient(node) + promises.push(client.getAccountState(address)) + } + const response = await promiseAny(promises) + if (response) { + return response + } else { + throw new Error('NEO get balance with error') + } + } + case 'neo_broadcastTx': { + const transaction = params[0] + if (typeof transaction !== 'string' && !transaction.serialize) { + throw new Error(`${transaction} is not a transaction.`) + } + const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes + const promises = [] + for (const node of rpcNodes) { + const client = new RPCClient(node) + promises.push(client.sendRawTransaction(transaction)) + } + const response = (await promiseAny(promises)).json() + if (response && response.result) { + return response.result + } else { + throw new Error('NEO send fail with error: ' + response.error.message) + } + } + case 'neo_getTx': { + const txId = params[0] + const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes + const promises = [] + for (const node of rpcNodes) { + const client = new RPCClient(node) + promises.push(client.getRawTransaction(txId, 1)) + } + const response = await promiseAny(promises) + if (response && response.result) { + return response.result + } else { + throw new Error( + 'NEO get TX fail with error: ' + response.error.message + ) + } + } + case 'neo_getBlockCount': { + const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes + const promises = [] + for (const node of rpcNodes) { + const client = new RPCClient(node) + promises.push(client.getBlockCount()) + } + const blockHeight = await promiseAny(promises) + if (blockHeight) { + return blockHeight + } else { + throw new Error('NEO get block count fail with error') + } + } + case 'neo_getTxHeight': { + const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes + const promises = [] + for (const node of rpcNodes) { + promises.push( + rpc.queryRPC(node, { + method: 'gettransactionheight', + params + }) + ) + } + const response = (await promiseAny(promises)).json() + if (response && response.result) { + return response.result + } else { + throw new Error( + 'NEO get transaction height fail with error: ' + + response.error.message + ) + } + } + case 'neo_getTxOut': { + const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes + const promises = [] + for (const node of rpcNodes) { + const client = new RPCClient(node) + promises.push(client.getTxOut(...params)) + } + const response = (await promiseAny(promises)).json() + if (response && response.result) { + return response.result + } else { + throw new Error( + 'NEO get tx outputs with error: ' + response.error.message + ) + } + } + case 'neo_getBlock': { + const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes + const promises = [] + for (const node of rpcNodes) { + const client = new RPCClient(node) + promises.push(client.getBlock(...params)) + } + const response = (await promiseAny(promises)).json() + if (response && response.result) { + return response.result + } else { + throw new Error( + 'NEO get tx outputs with error: ' + response.error.message + ) + } + } + default: { + } + } + this.log(`NEO multicastServers ${func} ${out.server} won`) + + return out.result + } + + async checkBlockchainInnerLoop() { + try { + const blockHeight = await this.multicastServers('neo_getBlockCount') + this.log(`Got block height ${blockHeight}`) + if (this.walletLocalData.blockHeight !== blockHeight) { + this.checkDroppedTransactionsThrottled() + this.walletLocalData.blockHeight = blockHeight + this.walletLocalDataDirty = true + this.currencyEngineCallbacks.onBlockHeightChanged( + this.walletLocalData.blockHeight + ) + } + } catch (err) { + this.log('Error fetching height: ' + err) + } + } + + updateBalance(tk: string, balance: string) { + if (typeof this.walletLocalData.totalBalances[tk] === 'undefined') { + this.walletLocalData.totalBalances[tk] = '0' + } + if (!bns.eq(balance, this.walletLocalData.totalBalances[tk])) { + this.walletLocalData.totalBalances[tk] = balance + this.log(tk + ': token Address balance: ' + balance) + this.currencyEngineCallbacks.onBalanceChanged(tk, balance) + } + this.tokenCheckBalanceStatus[tk] = 1 + this.updateOnAddressesChecked() + } + + async checkAccountInnerLoop() { + const address = wallet.getAddressFromScriptHash( + wallet.getScriptHashFromPublicKey(this.walletLocalData.publicKey) + ) + + try { + const balances = (await this.multicastServers('neo_getBalance', address)) + .balances + if (!balances || balances.length === 0) { + this.updateBalance('NEO', '0') + } + for (const tk of this.walletLocalData.enabledTokens) { + for (const balance of balances) { + if (balance.asset === this.currencyInfo.defaultSettings.assets[tk]) { + const denom = getDenomInfo(this.currencyInfo, tk) + if (!denom) { + this.log(`Received unsupported currencyCode: ${tk}`) + break + } + const nativeAmount = bns.mul(balance.value, denom.multiplier) + this.updateBalance(tk, nativeAmount) + } + } + } + } catch (e) { + this.updateBalance('NEO', '0') + this.log(`Error checking NEO address balance`) + } + } + + async processTransaction(transaction: Object, currencyCode: string) { + const valid = validateObject(transaction, NeoTransactionOnline) + if (valid) { + const { vin, vout, txid, net_fee: netFee, blocktime } = transaction + const neoInputs = [] + for (let i = 0; i < vin.length; i++) { + const { txid: prevHash, vout: prevIndex } = vin[i] + const from = await this.multicastServers( + 'neo_getTxOut', + prevHash, + prevIndex + ) + if (from.asset === currencyInfo.defaultSettings.assets.NEO) { + neoInputs.push(from) + } + } + + const neoOutputs = vout.filter( + output => + output.asset === currencyInfo.defaultSettings.assets.NEO && + neoInputs.every(input => input.address !== output.address) + ) + + const nativeAmount = neoOutputs.reduce((sum, cur) => sum + cur.value, 0) + + const from = neoInputs[0].address + const to = [neoOutputs[0].address] + + const otherParams = { + from, + to, + networkFee: transaction.fees, + isNative: true + } + + const blockHeight = await this.multicastServers('neo_getTxHeight', txid) + + const edgeTransaction: EdgeTransaction = { + txid, + date: blocktime, + currencyCode, + blockHeight, + nativeAmount, + networkFee: netFee, + parentNetworkFee: '0', + ourReceiveAddresses: to, + signedTx: '', + otherParams + } + + this.addTransaction(currencyCode, edgeTransaction) + } + } + + async checkTransactionsFetch( + startBlockHeight: number, + currencyCode: string, + blockHeight: number + ): Promise { + let checkAddressSuccess: boolean = true + try { + for ( + let curHeight = startBlockHeight; + curHeight++; + curHeight <= blockHeight + ) { + const { tx: transactions } = await this.multicastServers( + 'neo_getBlock', + curHeight, + 1 + ) + for (let i = 0; i < transactions.length; i++) { + await this.processTransaction(transactions[i], currencyCode) + } + } + checkAddressSuccess = true + } catch (e) { + this.log(`Error checkTransactionsFetch ${currencyCode}`, String(e)) + checkAddressSuccess = false + } + + if (checkAddressSuccess) { + this.tokenCheckTransactionsStatus[currencyCode] = 1 + this.updateOnAddressesChecked() + return true + } else { + return false + } + } + + async checkTransactionsInnerLoop() { + const blockHeight = Number(await this.multicastServers('neo_getBlockCount')) + let startBlockHeight = 0 + const promiseArray = [] + + if ( + this.walletLocalData.lastAddressQueryHeight > + ADDRESS_QUERY_LOOKBACK_BLOCKS + ) { + startBlockHeight = + this.walletLocalData.lastAddressQueryHeight - + ADDRESS_QUERY_LOOKBACK_BLOCKS + } + + for (const currencyCode of this.walletLocalData.enabledTokens) { + promiseArray.push( + this.checkTransactionsFetch( + startBlockHeight, + currencyCode, + blockHeight - 1 + ) + ) + } + + let resultArray = [] + try { + resultArray = await Promise.all(promiseArray) + } catch (e) { + this.log('Failed to query transactions') + this.log(e.name) + this.log(e.message) + } + let successCount = 0 + for (const r of resultArray) { + if (r) successCount++ + } + if (successCount === promiseArray.length) { + this.walletLocalData.lastAddressQueryHeight = blockHeight + } + if (this.transactionsChangedArray.length > 0) { + this.currencyEngineCallbacks.onTransactionsChanged( + this.transactionsChangedArray + ) + this.transactionsChangedArray = [] + } + } + + // // **************************************************************************** + // // Public methods + // // **************************************************************************** + + async startEngine() { + this.engineOn = true + this.addToLoop('checkBlockchainInnerLoop', BLOCKCHAIN_POLL_MILLISECONDS) + this.addToLoop('checkAccountInnerLoop', ACCOUNT_POLL_MILLISECONDS) + // this.addToLoop('checkUpdateNetworkFees', NETWORKFEES_POLL_MILLISECONDS) + this.addToLoop('checkTransactionsInnerLoop', TRANSACTION_POLL_MILLISECONDS) + super.startEngine() + } + + async resyncBlockchain(): Promise { + await this.killEngine() + await this.clearBlockchainCache() + await this.startEngine() + } + + async makeSpend(edgeSpendInfoIn: EdgeSpendInfo) { + const { edgeSpendInfo, currencyCode, nativeBalance } = super.makeSpend( + edgeSpendInfoIn + ) + + /* Just consider only one target */ + if (edgeSpendInfo.spendTargets.length !== 1) { + throw new Error('Error: only one output supported now for neo') + } + + const networkFee = 0 // neo has 10 gas free. + const spendTarget = edgeSpendInfo.spendTargets[0] + const publicAddress = spendTarget.publicAddress + const nativeAmount = edgeSpendInfo.spendTargets[0].nativeAmount + const data = + spendTarget.otherParams != null ? spendTarget.otherParams.data : undefined + + if (bns.gt(nativeAmount, nativeBalance)) { + throw new InsufficientFundsError() + } + + let otherParams: Object = {} + + if (currencyCode === PRIMARY_CURRENCY) { + const neoParams: NeoTxOtherParams = { + from: this.walletLocalData.publicKey, + to: [publicAddress], + networkFee, + isNative: true, + data + } + otherParams = neoParams + } else { + const tokenInfo = this.getTokenInfo(currencyCode) + if (!tokenInfo || typeof tokenInfo.contractAddress !== 'string') { + throw new Error( + 'Error: Token not supported or invalid contract address' + ) + } + const contractAddress = tokenInfo.contractAddress + + const neoParams: NeoTxOtherParams = { + from: this.walletLocalData.publicKey, + to: [publicAddress], + networkFee, + isNative: false, + data, + asset: contractAddress + } + otherParams = neoParams + } + + // ********************************** + // Create the unsigned EdgeTransaction + + const edgeTransaction: EdgeTransaction = { + txid: '', // txid + date: 0, // date + currencyCode, // currencyCode + blockHeight: 0, // blockHeight + nativeAmount, // nativeAmount + networkFee: '' + networkFee, // networkFee, supposedly fixed + ourReceiveAddresses: [], // ourReceiveAddresses + signedTx: '', // signedTx + otherParams // otherParams + } + + return edgeTransaction + } + + async signTx(edgeTransaction: EdgeTransaction): Promise { + const otherParams = getOtherParams(edgeTransaction) + const neoApiProvider = new ApiProvider( + this.currencyInfo.defaultSettings.neoScanUrl.MainNet + ) + const privateKey = this.walletInfo.keys.neoKey + const currencyCode = edgeTransaction.currencyCode + const amount = edgeTransaction.nativeAmount + const account = new Account(privateKey) + + const denom = getDenomInfo(this.currencyInfo, currencyCode) + if (!denom) { + this.log(`Received unsupported currencyCode: ${currencyCode}`) + throw new Error(`Received unsupported currencyCode: ${currencyCode}`) + } + + const nativeAmount = parseInt(amount) / parseInt(denom.multiplier) + + const balance = await neoApiProvider.getBalance(account.address) + const signedTx = new tx.ContractTransaction() + signedTx + .addIntent('NEO', nativeAmount, otherParams.to[0]) + .calculate(balance) + .sign(privateKey) + + this.log(`SUCCESS NEO broadcastTx\n${JSON.stringify(signedTx)}`) + otherParams.serializedTx = signedTx.serialize(true) + edgeTransaction.txid = signedTx.hash + edgeTransaction.otherParams = otherParams + return edgeTransaction + } + + async broadcastTx( + edgeTransaction: EdgeTransaction + ): Promise { + const otherParams = getOtherParams(edgeTransaction) + const neoSignedTransaction = otherParams.serializedTx + const response = await this.multicastServers( + 'neo_broadcastTx', + neoSignedTransaction + ) + if (response.result) { + this.log(`SUCCESS broadcastTx\n${JSON.stringify(edgeTransaction.txid)}`) + } + this.log('edgeTransaction = ', edgeTransaction) + return edgeTransaction + } + + getDisplayPrivateSeed() { + if (this.walletInfo.keys && this.walletInfo.keys.neoKey) { + return this.walletInfo.keys.neoKey + } + return '' + } + + getDisplayPublicSeed() { + if (this.walletInfo.keys && this.walletInfo.keys.publicKey) { + return this.walletInfo.keys.publicKey + } + return '' + } +} diff --git a/src/neo/neoInfo.js b/src/neo/neoInfo.js new file mode 100644 index 000000000..0bde8ef10 --- /dev/null +++ b/src/neo/neoInfo.js @@ -0,0 +1,41 @@ +// @flow +import { type EdgeCurrencyInfo } from 'edge-core-js/types' + +import { imageServerUrl } from '../common/utils' + +const defaultSettings = { + neoRpcNodes: ['https://seed11.ngd.network:10331'], + assets: { + NEO: '0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b', + GAS: '0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7' + }, + neoScanUrl: { + MainNet: 'https://api.neoscan.io/api/main_net', + TestNet: 'https://neoscan-testnet.io/api/test_net' + } +} + +export const currencyInfo: EdgeCurrencyInfo = { + // Basic currency information: + currencyCode: 'NEO', + displayName: 'NEO', + pluginName: 'neo', + walletType: 'wallet:neo', + + defaultSettings, + + addressExplorer: 'https://neoscan.io/addresses/%s', + transactionExplorer: 'https://neoscan.io/transactions/%s', + + denominations: [ + // An array of Objects of the possible denominations for this currency + { + name: 'NEO', + multiplier: '1', + symbol: 'N' + } + ], + symbolImage: `${imageServerUrl}/neo-logo.png`, // TODO: upload currency logo + symbolImageDarkMono: `${imageServerUrl}/neo-logo.png`, // TODO: upload currency logo + metaTokens: [] +} diff --git a/src/neo/neoPlugin.js b/src/neo/neoPlugin.js new file mode 100644 index 000000000..64f199d8d --- /dev/null +++ b/src/neo/neoPlugin.js @@ -0,0 +1,176 @@ +// @flow +import { rpc, u, wallet } from '@cityofzion/neon-js' +import { bns } from 'biggystring' +import { + type EdgeCorePluginOptions, + type EdgeCurrencyEngine, + type EdgeCurrencyEngineOptions, + type EdgeCurrencyPlugin, + type EdgeEncodeUri, + type EdgeIo, + type EdgeMetaToken, + type EdgeParsedUri, + type EdgeWalletInfo +} from 'edge-core-js/types' + +import { CurrencyPlugin } from '../common/plugin' +import { getDenomInfo } from '../common/utils.js' +import { NeoEngine } from './neoEngine.js' +import { currencyInfo } from './neoInfo' + +const { RPCClient } = rpc +const { Account } = wallet + +export function checkAddress(address: string): boolean { + return address.length === 34 +} + +export class NeoPlugin extends CurrencyPlugin { + rpcClient: RPCClient + constructor(io: EdgeIo) { + super(io, 'neo', currencyInfo) + this.rpcClient = new RPCClient( + this.currencyInfo.defaultSettings.neoRpcNodes[0] + ) + } + + validPrivateKey(privateKey: string) { + return privateKey.length === 64 + } + + async importPrivateKey(privateKey: string): Promise<{ neoKey: string }> { + if (this.validPrivateKey(privateKey)) + throw new Error('Neo Private key wrong length') + return { neoKey: privateKey } + } + + async createPrivateKey(walletType: string): Promise { + const type = walletType.replace('wallet:', '') + if (type === 'neo') { + const privateKey = u.ab2hexstring(this.io.random(32)) + return { neoKey: privateKey } + } else { + throw new Error('InvalidWalletType') + } + } + + /** + * This function actually returns neo address as publickey, to keep to edge style. + * @param {EdgeWalletInfo} walletInfo + */ + async derivePublicKey(walletInfo: EdgeWalletInfo): Promise { + const type = walletInfo.type.replace('wallet:', '') + if (type === 'neo') { + const publicKey = new Account(walletInfo.keys.neoKey).address + return { publicKey } + } else { + throw new Error('InvalidWalletType') + } + } + + async parseUri( + uri: string, + currencyCode?: string, + customTokens?: Array + ): Promise { + const { edgeParsedUri, parsedUri } = this.parseUriCommon( + currencyInfo, + uri, + { + neo: true + }, + currencyCode || 'NEO', + customTokens + ) + + const valid = checkAddress(edgeParsedUri.publicAddress || '') + if (!valid) { + throw new Error('InvalidPublicAddressError') + } + + if (parsedUri.query.msg) { + edgeParsedUri.metadata = { + notes: parsedUri.query.msg + } + } + if (parsedUri.query.asset_code) { + if (parsedUri.query.asset_code.toUpperCase() !== 'XLM') { + throw new Error('ErrorInvalidCurrencyCode') + } + } + if (parsedUri.query.memo_type) { + if (parsedUri.query.memo_type !== 'MEMO_ID') { + throw new Error('ErrorInvalidMemoType') + } + } + if (parsedUri.query.memo) { + const m = bns.add(parsedUri.query.memo, '0') + // Check if the memo is an integer + if (m !== parsedUri.query.memo) { + throw new Error('ErrorInvalidMemoId') + } + edgeParsedUri.uniqueIdentifier = parsedUri.query.memo + } + return edgeParsedUri + } + + async encodeUri( + obj: EdgeEncodeUri, + customTokens?: Array + ): Promise { + const { publicAddress, nativeAmount, currencyCode } = obj + const valid = checkAddress(publicAddress) + if (!valid) { + throw new Error('InvalidPublicAddressError') + } + let amount + if (typeof nativeAmount === 'string') { + const denom = getDenomInfo( + currencyInfo, + currencyCode || 'NEO', + customTokens + ) + if (!denom) { + throw new Error('InternalErrorInvalidCurrencyCode') + } + amount = bns.div(nativeAmount, denom.multiplier, 1) + } + const encodedUri = this.encodeUriCommon(obj, 'neo', amount) + return encodedUri + } +} + +export function makeNeoPlugin(opts: EdgeCorePluginOptions): EdgeCurrencyPlugin { + const { io } = opts + + let toolsPromise: Promise + function makeCurrencyTools(): Promise { + if (toolsPromise != null) return toolsPromise + toolsPromise = Promise.resolve(new NeoPlugin(io)) + return toolsPromise + } + + async function makeCurrencyEngine( + walletInfo: EdgeWalletInfo, + opts: EdgeCurrencyEngineOptions + ): Promise { + const tools = await makeCurrencyTools() + const currencyEngine = new NeoEngine(tools, walletInfo, opts) + + // Do any async initialization necessary for the engine + await currencyEngine.loadEngine(tools, walletInfo, opts) + + // This is just to make sure otherData is Flow type checked + currencyEngine.otherData = currencyEngine.walletLocalData.otherData + + const out: EdgeCurrencyEngine = currencyEngine + + return out + } + + return { + currencyInfo, + makeCurrencyEngine, + makeCurrencyTools + } +} diff --git a/src/neo/neoSchema.js b/src/neo/neoSchema.js new file mode 100644 index 000000000..7193b493c --- /dev/null +++ b/src/neo/neoSchema.js @@ -0,0 +1,47 @@ +export const NeoTransactonOnline = { + type: 'object', + properties: { + txid: { type: 'string' }, + size: { type: 'number' }, + type: { type: 'string' }, + version: { type: 'number' }, + attributes: { type: 'array' }, + vin: { + type: 'array', + items: { + type: 'object', + properties: { + txid: { type: 'string' }, + vout: { type: 'number' } + } + } + }, + vout: { + type: 'array', + items: { + type: 'object', + properties: { + n: { type: 'number' }, + asset: { type: 'string' }, + value: { type: 'number' }, + address: { type: 'string' } + } + } + }, + sys_fee: { type: 'number' }, + net_fee: { type: 'number' }, + scripts: { + type: 'array', + items: { + type: 'object', + properties: { + invocation: { type: 'string' }, + verification: { type: 'string' } + } + } + }, + blockhash: { type: 'string' }, + confirmations: { type: 'number' }, + blockTime: { type: 'number' } + } +} diff --git a/src/neo/neoTypes.js b/src/neo/neoTypes.js new file mode 100644 index 000000000..d6099cfdb --- /dev/null +++ b/src/neo/neoTypes.js @@ -0,0 +1,10 @@ +// @flow + +export type NeoTxOtherParams = { + from: string, + to: Array, + networkFee: number, + isNative: boolean, + data?: string | null, + asset?: string // default as neo +} diff --git a/test/engine/fixtures.js b/test/engine/fixtures.js index 7c5293b36..c91833aa7 100644 --- a/test/engine/fixtures.js +++ b/test/engine/fixtures.js @@ -1,6 +1,45 @@ export default [ { - pluginId: 'ripple', + pluginName: 'neo', + WALLET_TYPE: 'wallet:neo', + 'Test Currency code': 'NEO', + key: [ + 39, + 190, + 34, + 129, + 208, + 32, + 145, + 88, + 191, + 217, + 226, + 98, + 183, + 16, + 52, + 150, + 52, + 53, + 31, + 137, + 164, + 40, + 236, + 146, + 128, + 107, + 129, + 59, + 192, + 240, + 40, + 238 + ] + }, + { + pluginName: 'ripple', WALLET_TYPE: 'wallet:ripple', 'Test Currency code': 'XRP', key: [ diff --git a/test/plugin/fixtures.js b/test/plugin/fixtures.js index 5e7199bc7..48345dbac 100644 --- a/test/plugin/fixtures.js +++ b/test/plugin/fixtures.js @@ -1,6 +1,146 @@ export default [ { - pluginId: 'binance', + pluginName: 'neo', + WALLET_TYPE: 'wallet:neo', + 'Test Currency code': 'NEO', + key: [ + 39, + 190, + 34, + 129, + 208, + 32, + 145, + 88, + 191, + 217, + 226, + 98, + 183, + 16, + 52, + 150, + 52, + 53, + 31, + 137, + 164, + 40, + 236, + 146, + 128, + 107, + 129, + 59, + 192, + 240, + 40, + 238 + ], + xpub: 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + key_length: 64, + 'invalid key name': { + type: 'wallet:neo', + keys: { neoKeyz: '12345678abcd' } + }, + 'invalid wallet type': { + type: 'wallet:neo', + keys: { neoKeyz: '12345678abcd' } + }, + parseUri: { + 'address only': [ + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu' + ], + 'invalid address': [ + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu23', + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Quf' + ], + 'uri address': [ + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu' + ], + 'uri address with amount': [ + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu?amount=12', + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + '12', + 'NEO' + ], + 'uri address with unique identifier': [ + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu?memo=123456700', + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + 'NEO', + '123456700' + ], + 'uri address with amount & label': [ + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu?amount=12&label=Johnny%20Binance', + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + '12', + 'NEO', + 'Johnny Binance' + ], + 'uri address with amount, label & message': [ + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu?amount=12&label=Johnny%20Binance&message=Hellow%20Binance%20World', + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + '12', + 'NEO', + 'Johnny Binance', + 'Hello Binance World' + ], + 'uri address with unsupported param': [ + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu?amount=12&unsupported=I%20am%20unsupported', + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + '12', + 'NEO' + ] + }, + encodeUri: { + 'address only': [ + { + publicAddress: 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu' + }, + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu' + ], + 'weird address': [ + { + publicAddress: 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu' + }, + 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu' + ], + 'invalid address': [ + { publicAddress: 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu23' }, + { publicAddress: 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Quf' } + ], + 'address & amount': [ + { + publicAddress: 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + nativeAmount: '12345678' + }, + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu?amount=12345678' + ], + 'address, amount, and label': [ + { + publicAddress: 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + nativeAmount: '12300', + currencyCode: 'NEO', + label: 'Johnny Ripple' + }, + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu?amount=12300&label=Johnny%20Ripple' + ], + 'address, amount, label, & message': [ + { + publicAddress: 'AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu', + nativeAmount: '12300', + currencyCode: 'NEO', + label: 'Johnny Ripple', + message: 'Hello World, I miss you !' + }, + 'neo:AM7cfQdn4gKb2sL1DujuLDa8GpYnTFj5Qu?amount=12300&label=Johnny%20Ripple&message=Hello%20World,%20I%20miss%20you%20!' + ] + } + }, + { + pluginName: 'binance', WALLET_TYPE: 'wallet:binance', 'Test Currency code': 'BNB', key: [ diff --git a/yarn.lock b/yarn.lock index 9044c25aa..93176c5e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -811,6 +811,52 @@ optionalDependencies: "@ledgerhq/hw-transport-node-hid" "^4.63.2" +"@cityofzion/neon-api@^4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@cityofzion/neon-api/-/neon-api-4.7.2.tgz#8d1c971ecd179fcf0816bdd5967bd5d61cc35d8d" + integrity sha512-Vvef7e6HBAWYkh7xKLr8bvxYVgV+B9RvL7DeSPVxnXzW2vkGhqPl20z38WipMkWfv5WiBYHWBaQdSAFKRaCiCA== + dependencies: + "@cityofzion/neon-core" "^4.7.2" + axios "0.19.2" + +"@cityofzion/neon-core@^4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@cityofzion/neon-core/-/neon-core-4.7.2.tgz#b28dbc6ae9d8c8a414a05f0a0e841b2c58f635de" + integrity sha512-kPWkOIkAX8xAA6/Slep/txsAdbO0pGLTGOt61+I8sqIHB04sRRo1UzjkvZON+q2OTRIKSrZDNwniivzM4X8vjQ== + dependencies: + "@types/bn.js" "4.11.6" + "@types/bs58" "4.0.1" + "@types/crypto-js" "3.1.43" + "@types/elliptic" "6.4.12" + axios "0.19.2" + bignumber.js "7.2.1" + bn.js "5.1.1" + bs58 "4.0.1" + bs58check "2.1.2" + crypto-js "3.2.0" + elliptic "6.5.2" + loglevel "1.6.7" + loglevel-plugin-prefix "0.8.4" + scrypt-js "3.0.0" + secure-random "1.1.2" + wif "2.0.6" + +"@cityofzion/neon-js@^4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@cityofzion/neon-js/-/neon-js-4.7.2.tgz#3aa7b7620e38d38b4713a6115711d87bd31fe788" + integrity sha512-+3CP3amdBZBz25+KSqIFwNSO/w4ROH5VK+LF2Ywyz3NrDLmTcMxRx7P6iawMrPHvrJBIc9GfKOqqzCYtye5Dtw== + dependencies: + "@cityofzion/neon-api" "^4.7.2" + "@cityofzion/neon-core" "^4.7.2" + "@cityofzion/neon-nep5" "^4.7.2" + +"@cityofzion/neon-nep5@^4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@cityofzion/neon-nep5/-/neon-nep5-4.7.2.tgz#5668086db7c6c9d89e80933dbd419f20ae0bbe4d" + integrity sha512-WXBXB+BMmsS1JEH78z32RHefbe2syYQId5F284a7Ck9r9DhGwyP8HCvxBycd/+vZjuvAvpPjIaib7xyV3DPc1w== + dependencies: + "@cityofzion/neon-core" "^4.7.2" + "@fioprotocol/fiojs@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@fioprotocol/fiojs/-/fiojs-1.0.1.tgz#81779437603741bc4ca1c76d119b64c4157a3874" @@ -969,11 +1015,37 @@ dependencies: any-observable "^0.3.0" +"@types/bn.js@*", "@types/bn.js@4.11.6": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bs58@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/bs58/-/bs58-4.0.1.tgz#3d51222aab067786d3bc3740a84a7f5a0effaa37" + integrity sha512-yfAgiWgVLjFCmRv8zAcOIHywYATEwiTVccTLnRp6UxTNavT55M9d/uhK3T03St/+8/z/wW+CRjGKUNmEqoHHCA== + dependencies: + base-x "^3.0.6" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/crypto-js@3.1.43": + version "3.1.43" + resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-3.1.43.tgz#b859347d6289ba13e347c335a4c9efa63337a748" + integrity sha512-EHe/YKctU3IYNBsDmSOPX/7jLHPRlx8WaiDKSY9JCTnJ8XJeM4c0ZJvx+9Gxmr2s2ihI92R+3U/gNL1sq5oRuQ== + +"@types/elliptic@6.4.12": + version "6.4.12" + resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.12.tgz#e8add831f9cc9a88d9d84b3733ff669b68eaa124" + integrity sha512-gP1KsqoouLJGH6IJa28x7PXb3cRqh83X8HCLezd2dF+XcAIMKYv53KV+9Zn6QA561E120uOqZBQ+Jy/cl+fviw== + dependencies: + "@types/bn.js" "*" + "@types/lodash@^4.14.136": version "4.14.138" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.138.tgz#34f52640d7358230308344e579c15b378d91989e" @@ -1425,7 +1497,7 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -axios@0.19.0, axios@^0.18.0, axios@^0.19.0: +axios@0.19.0, axios@0.19.2, axios@^0.18.0, axios@^0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== @@ -1476,7 +1548,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base-x@3.0.4, base-x@^1.0.1, base-x@^1.0.4, base-x@^3.0.2: +base-x@3.0.4, base-x@^1.0.1, base-x@^1.0.4, base-x@^3.0.2, base-x@^3.0.6: version "3.0.4" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.4.tgz#94c1788736da065edb1d68808869e357c977fa77" integrity sha512-UYOadoSIkEI/VrRGSG6qp93rp2WdokiAiNYDfGW5qURAY8GiAQkvMbwNNSDYiVJopqv4gCna7xqf4rrNGp+5AA== @@ -1535,16 +1607,16 @@ bigi@1.4.2, bigi@^1.1.0, bigi@^1.4.2: resolved "https://registry.yarnpkg.com/bigi/-/bigi-1.4.2.tgz#9c665a95f88b8b08fc05cfd731f561859d725825" integrity sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU= +bignumber.js@7.2.1, bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + bignumber.js@^4.0.0, bignumber.js@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-4.1.0.tgz#db6f14067c140bd46624815a7916c92d9b6c24b1" integrity sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA== -bignumber.js@^7.2.1: - version "7.2.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" - integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -1611,7 +1683,7 @@ bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@4.11.8, bn.js@^3.1.1, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.11.7, bn.js@^4.11.8, bn.js@^4.4.0: +bn.js@4.11.8, bn.js@5.1.1, bn.js@^3.1.1, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.11.7, bn.js@^4.11.8, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== @@ -2293,6 +2365,11 @@ crypto-browserify@^3.11.0, crypto-browserify@^3.12.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto-js@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.2.0.tgz#abe124867e49d2695e20fea091728276a9f761c0" + integrity sha512-J6LOZ2Iha4akgCwqTrZsCLdA5y/kJ1MxRoGzs/xtIfFSaLSs1gENHznVjYEzWZfunzt9yiViXq8bO1RyT1euhg== + crypto-js@^3.1.9-1: version "3.1.9-1" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.9-1.tgz#fda19e761fc077e01ffbfdc6e9fdfc59e8806cd8" @@ -2581,7 +2658,7 @@ elegant-spinner@^1.0.1: resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= -elliptic@6.4.1, elliptic@^5.1.0, elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: +elliptic@6.4.1, elliptic@6.5.2, elliptic@^5.1.0, elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: version "6.4.1" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ== @@ -4530,6 +4607,16 @@ log-update@^2.3.0: cli-cursor "^2.0.0" wrap-ansi "^3.0.1" +loglevel-plugin-prefix@0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz#2fe0e05f1a820317d98d8c123e634c1bd84ff644" + integrity sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g== + +loglevel@1.6.7: + version "1.6.7" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.7.tgz#b3e034233188c68b889f5b862415306f565e2c56" + integrity sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A== + long@4.0.0, long@^2.2.3, long@^4.0.0, long@~3: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -6110,6 +6197,11 @@ schema-utils@^2.6.5: ajv "^6.12.0" ajv-keywords "^3.4.1" +scrypt-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.0.tgz#52361c1f272eeaab09ec1f806ea82078bca58b15" + integrity sha512-7CC7aufwukEvqdmllR0ny0QaSg0+S22xKXrXz3ZahaV6J+fgD2YAtrjtImuoDWog17/Ty9Q4HBmnXEXJ3JkfQA== + scrypt-js@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" @@ -6152,7 +6244,7 @@ secp256k1@^3.0.1: nan "^2.2.1" safe-buffer "^5.1.0" -secure-random@^1.1.2: +secure-random@1.1.2, secure-random@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/secure-random/-/secure-random-1.1.2.tgz#ed103b460a851632d420d46448b2a900a41e7f7c" integrity sha512-H2bdSKERKdBV1SwoqYm6C0y+9EA94v6SUBOWO8kDndc4NoUih7Dv6Tsgma7zO1lv27wIvjlD0ZpMQk7um5dheQ== @@ -7351,7 +7443,7 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2" -wif@^2.0.6: +wif@2.0.6, wif@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704" integrity sha1-CNP1IFbGZnkplyb63g1DKudLRwQ= From 562094bdd09a0289694ec64eb41cf23440f7f45a Mon Sep 17 00:00:00 2001 From: Shuai Date: Fri, 14 Aug 2020 10:38:30 +0800 Subject: [PATCH 2/2] fix --- src/neo/neoEngine.js | 88 +++++++++++++++++++++-------------------- src/neo/neoInfo.js | 2 +- src/neo/neoSchema.js | 8 ++-- test/engine/fixtures.js | 4 +- test/plugin/fixtures.js | 4 +- 5 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/neo/neoEngine.js b/src/neo/neoEngine.js index 402a2368a..0ea7ada5e 100644 --- a/src/neo/neoEngine.js +++ b/src/neo/neoEngine.js @@ -36,12 +36,12 @@ type NeoFunction = | 'neo_getBlockCount' | 'neo_broadcastTx' | 'neo_getTxHeight' - | 'neo_getTxOut' + // | 'neo_getTxOut' | 'neo_getBlock' export class NeoEngine extends CurrencyEngine { async multicastServers(func: NeoFunction, ...params: any): Promise { - this.log(`start to query ${func} on Neo Blockchain`) + // this.log(`start to query ${func} on Neo Blockchain`) const out = { result: '', server: 'no server' } switch (func) { case 'neo_getBalance': { @@ -73,9 +73,9 @@ export class NeoEngine extends CurrencyEngine { const client = new RPCClient(node) promises.push(client.sendRawTransaction(transaction)) } - const response = (await promiseAny(promises)).json() - if (response && response.result) { - return response.result + const response = await promiseAny(promises) + if (response) { + return response } else { throw new Error('NEO send fail with error: ' + response.error.message) } @@ -89,8 +89,8 @@ export class NeoEngine extends CurrencyEngine { promises.push(client.getRawTransaction(txId, 1)) } const response = await promiseAny(promises) - if (response && response.result) { - return response.result + if (response) { + return response } else { throw new Error( 'NEO get TX fail with error: ' + response.error.message @@ -122,32 +122,31 @@ export class NeoEngine extends CurrencyEngine { }) ) } - const response = (await promiseAny(promises)).json() - if (response && response.result) { - return response.result - } else { - throw new Error( - 'NEO get transaction height fail with error: ' + - response.error.message - ) - } - } - case 'neo_getTxOut': { - const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes - const promises = [] - for (const node of rpcNodes) { - const client = new RPCClient(node) - promises.push(client.getTxOut(...params)) - } - const response = (await promiseAny(promises)).json() + const response = await promiseAny(promises) if (response && response.result) { return response.result } else { throw new Error( - 'NEO get tx outputs with error: ' + response.error.message + 'NEO get transaction height fail with error: ' + response.error.message ) } } + // case 'neo_getTxOut': { + // const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes + // const promises = [] + // for (const node of rpcNodes) { + // const client = new RPCClient(node) + // promises.push(client.getTxOut(...params)) + // } + // const response = await promiseAny(promises) + // if (response) { + // return response + // } else { + // throw new Error( + // 'NEO get tx outputs with error: ' + response + // ) + // } + // } case 'neo_getBlock': { const rpcNodes = this.currencyInfo.defaultSettings.neoRpcNodes const promises = [] @@ -155,9 +154,9 @@ export class NeoEngine extends CurrencyEngine { const client = new RPCClient(node) promises.push(client.getBlock(...params)) } - const response = (await promiseAny(promises)).json() - if (response && response.result) { - return response.result + const response = await promiseAny(promises) + if (response) { + return response } else { throw new Error( 'NEO get tx outputs with error: ' + response.error.message @@ -203,9 +202,7 @@ export class NeoEngine extends CurrencyEngine { } async checkAccountInnerLoop() { - const address = wallet.getAddressFromScriptHash( - wallet.getScriptHashFromPublicKey(this.walletLocalData.publicKey) - ) + const address = this.walletLocalData.publicKey try { const balances = (await this.multicastServers('neo_getBalance', address)) @@ -239,13 +236,12 @@ export class NeoEngine extends CurrencyEngine { const neoInputs = [] for (let i = 0; i < vin.length; i++) { const { txid: prevHash, vout: prevIndex } = vin[i] - const from = await this.multicastServers( - 'neo_getTxOut', - prevHash, - prevIndex - ) - if (from.asset === currencyInfo.defaultSettings.assets.NEO) { - neoInputs.push(from) + const prevTx = await this.multicastServers('neo_getTx', prevHash) + if (prevTx && prevTx.vout) { + const from = prevTx.vout[prevIndex] + if (from.asset === currencyInfo.defaultSettings.assets.NEO) { + neoInputs.push(from) + } } } @@ -257,8 +253,14 @@ export class NeoEngine extends CurrencyEngine { const nativeAmount = neoOutputs.reduce((sum, cur) => sum + cur.value, 0) - const from = neoInputs[0].address - const to = [neoOutputs[0].address] + const from = [] + if (neoInputs.length > 0) { + from.push(neoInputs[0].address) + } + const to = [] + if (neoOutputs.length > 0) { + to.push(neoOutputs[0].address) + } const otherParams = { from, @@ -413,7 +415,7 @@ export class NeoEngine extends CurrencyEngine { if (currencyCode === PRIMARY_CURRENCY) { const neoParams: NeoTxOtherParams = { - from: this.walletLocalData.publicKey, + from: [this.walletLocalData.publicKey], to: [publicAddress], networkFee, isNative: true, @@ -430,7 +432,7 @@ export class NeoEngine extends CurrencyEngine { const contractAddress = tokenInfo.contractAddress const neoParams: NeoTxOtherParams = { - from: this.walletLocalData.publicKey, + from: [this.walletLocalData.publicKey], to: [publicAddress], networkFee, isNative: false, diff --git a/src/neo/neoInfo.js b/src/neo/neoInfo.js index 0bde8ef10..1bcefbc7f 100644 --- a/src/neo/neoInfo.js +++ b/src/neo/neoInfo.js @@ -19,7 +19,7 @@ export const currencyInfo: EdgeCurrencyInfo = { // Basic currency information: currencyCode: 'NEO', displayName: 'NEO', - pluginName: 'neo', + pluginId: 'neo', walletType: 'wallet:neo', defaultSettings, diff --git a/src/neo/neoSchema.js b/src/neo/neoSchema.js index 7193b493c..31eabbde1 100644 --- a/src/neo/neoSchema.js +++ b/src/neo/neoSchema.js @@ -1,4 +1,4 @@ -export const NeoTransactonOnline = { +export const NeoTransactionOnline = { type: 'object', properties: { txid: { type: 'string' }, @@ -23,13 +23,13 @@ export const NeoTransactonOnline = { properties: { n: { type: 'number' }, asset: { type: 'string' }, - value: { type: 'number' }, + value: { type: 'string' }, address: { type: 'string' } } } }, - sys_fee: { type: 'number' }, - net_fee: { type: 'number' }, + sys_fee: { type: 'string' }, + net_fee: { type: 'string' }, scripts: { type: 'array', items: { diff --git a/test/engine/fixtures.js b/test/engine/fixtures.js index c91833aa7..075972381 100644 --- a/test/engine/fixtures.js +++ b/test/engine/fixtures.js @@ -1,6 +1,6 @@ export default [ { - pluginName: 'neo', + pluginId: 'neo', WALLET_TYPE: 'wallet:neo', 'Test Currency code': 'NEO', key: [ @@ -39,7 +39,7 @@ export default [ ] }, { - pluginName: 'ripple', + pluginId: 'ripple', WALLET_TYPE: 'wallet:ripple', 'Test Currency code': 'XRP', key: [ diff --git a/test/plugin/fixtures.js b/test/plugin/fixtures.js index 48345dbac..9e282325b 100644 --- a/test/plugin/fixtures.js +++ b/test/plugin/fixtures.js @@ -1,6 +1,6 @@ export default [ { - pluginName: 'neo', + pluginId: 'neo', WALLET_TYPE: 'wallet:neo', 'Test Currency code': 'NEO', key: [ @@ -140,7 +140,7 @@ export default [ } }, { - pluginName: 'binance', + pluginId: 'binance', WALLET_TYPE: 'wallet:binance', 'Test Currency code': 'BNB', key: [