@cityofzion/bs-neo3 0.4.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/.rush/temp/operation/build/state.json +1 -1
  2. package/.rush/temp/package-deps_build.json +20 -0
  3. package/.rush/temp/shrinkwrap-deps.json +2 -2
  4. package/bs-neo3.build.log +1 -86
  5. package/dist/BSNeo3.d.ts +22 -12
  6. package/dist/BSNeo3.js +85 -55
  7. package/dist/constants.d.ts +10 -3
  8. package/docs/assets/search.js +1 -1
  9. package/docs/classes/BDSNeo3.html +13 -13
  10. package/docs/classes/BSNeo3.html +80 -36
  11. package/docs/interfaces/DoraNeo3Abi.html +6 -6
  12. package/docs/interfaces/DoraNeo3Asset.html +9 -9
  13. package/docs/interfaces/DoraNeo3AssetState.html +6 -6
  14. package/docs/interfaces/DoraNeo3Balance.html +5 -5
  15. package/docs/interfaces/DoraNeo3ConsensusNode.html +3 -3
  16. package/docs/interfaces/DoraNeo3Contract.html +8 -8
  17. package/docs/interfaces/DoraNeo3Event.html +3 -3
  18. package/docs/interfaces/DoraNeo3Features.html +1 -1
  19. package/docs/interfaces/DoraNeo3HistoryState.html +3 -3
  20. package/docs/interfaces/DoraNeo3Invocation.html +3 -3
  21. package/docs/interfaces/DoraNeo3Item.html +11 -11
  22. package/docs/interfaces/DoraNeo3Manifest.html +9 -9
  23. package/docs/interfaces/DoraNeo3Metadata.html +12 -12
  24. package/docs/interfaces/DoraNeo3Method.html +12 -12
  25. package/docs/interfaces/DoraNeo3Nef.html +7 -7
  26. package/docs/interfaces/DoraNeo3Notification.html +4 -4
  27. package/docs/interfaces/DoraNeo3Parameter.html +3 -3
  28. package/docs/interfaces/DoraNeo3Permission.html +3 -3
  29. package/docs/interfaces/DoraNeo3Signer.html +3 -3
  30. package/docs/interfaces/DoraNeo3Token.html +6 -6
  31. package/docs/interfaces/DoraNeo3Transaction.html +16 -16
  32. package/docs/interfaces/DoraNeo3TransactionHistory.html +3 -3
  33. package/docs/interfaces/DoraNeo3Transfer.html +9 -9
  34. package/docs/interfaces/DoraNeo3Witness.html +3 -3
  35. package/docs/variables/DORA_ASSET.html +1 -1
  36. package/docs/variables/DORA_BALANCE.html +1 -1
  37. package/docs/variables/DORA_CONTRACT.html +1 -1
  38. package/docs/variables/DORA_NODES.html +1 -1
  39. package/docs/variables/DORA_TRANSACTION.html +1 -1
  40. package/docs/variables/DORA_TRANSACTIONS.html +1 -1
  41. package/docs/variables/explorerOptions.html +1 -1
  42. package/docs/variables/gasInfoNeo3.html +11 -2
  43. package/docs/variables/neoInfoNeo3.html +11 -2
  44. package/jest.config.ts +13 -0
  45. package/jest.setup.ts +1 -0
  46. package/package.json +15 -7
  47. package/src/BSNeo3.ts +221 -221
  48. package/src/DoraBDSNeo3.ts +180 -0
  49. package/src/FlamingoEDSNeo3.ts +45 -0
  50. package/src/GhostMarketNDSNeo3.ts +117 -0
  51. package/src/RpcBDSNeo3.ts +155 -0
  52. package/src/__tests__/BDSNeo3.spec.ts +126 -0
  53. package/src/__tests__/BSNeo3.spec.ts +142 -0
  54. package/src/__tests__/FlamingoEDSNeo3.spec.ts +45 -0
  55. package/src/__tests__/GhostMarketNDSNeo3.spec.ts +43 -0
  56. package/src/__tests__/utils/sleep.ts +1 -0
  57. package/src/assets/tokens/common.json +14 -0
  58. package/src/assets/tokens/mainnet.json +116 -0
  59. package/src/constants.ts +26 -10
  60. package/src/index.ts +4 -2
  61. package/tsconfig.build.json +4 -0
  62. package/tsconfig.json +3 -7
  63. package/bs-neo3.build.error.log +0 -84
  64. package/dist/excpetions.d.ts +0 -3
  65. package/dist/excpetions.js +0 -8
  66. package/src/BDSNeo3.ts +0 -168
  67. package/src/assets/blockchain_icon_neo.png +0 -0
  68. package/src/assets/blockchain_icon_neo_white.png +0 -0
  69. package/src/assets/tokens.json +0 -129
  70. package/src/exceptions.ts +0 -5
  71. package/src/explorer/dora/DoraNeo3Responses.ts +0 -207
  72. package/src/explorer/dora/DoraNeo3Routes.ts +0 -6
  73. package/src/explorer/index.ts +0 -8
@@ -0,0 +1,45 @@
1
+ import {
2
+ Currency,
3
+ ExchangeDataService,
4
+ Network,
5
+ NetworkType,
6
+ TokenPricesResponse,
7
+ } from '@cityofzion/blockchain-service'
8
+ import axios, { AxiosInstance } from 'axios'
9
+
10
+ type FlamingoTokenInfoPricesResponse = {
11
+ symbol: string
12
+ usd_price: number
13
+ }[]
14
+
15
+ export class FlamingoEDSNeo3 implements ExchangeDataService {
16
+ readonly networkType: NetworkType
17
+ private axiosInstance: AxiosInstance
18
+
19
+ constructor(networkType: NetworkType) {
20
+ this.networkType = networkType
21
+ this.axiosInstance = axios.create({ baseURL: 'https://api.flamingo.finance' })
22
+ }
23
+
24
+ async getTokenPrices(currency: Currency): Promise<TokenPricesResponse[]> {
25
+ if (this.networkType !== 'mainnet') throw new Error('Exchange is only available on mainnet')
26
+
27
+ const { data: prices } = await this.axiosInstance.get<FlamingoTokenInfoPricesResponse>('/token-info/prices')
28
+
29
+ let currencyRatio: number = 1
30
+
31
+ if (currency !== 'USD') {
32
+ currencyRatio = await this.getCurrencyRatio(currency)
33
+ }
34
+
35
+ return prices.map(price => ({
36
+ price: price.usd_price * currencyRatio,
37
+ symbol: price.symbol,
38
+ }))
39
+ }
40
+
41
+ private async getCurrencyRatio(currency: Currency): Promise<number> {
42
+ const { data } = await this.axiosInstance.get<number>(`/fiat/exchange-rate?pair=USD_${currency}`)
43
+ return data
44
+ }
45
+ }
@@ -0,0 +1,117 @@
1
+ import {
2
+ NftResponse,
3
+ NftsResponse,
4
+ NetworkType,
5
+ NftDataService,
6
+ GetNftParam,
7
+ GetNftsByAddressParams,
8
+ } from '@cityofzion/blockchain-service'
9
+ import qs from 'query-string'
10
+ import axios from 'axios'
11
+
12
+ import { GHOSTMARKET_CHAIN_BY_NETWORK_TYPE, GHOSTMARKET_URL_BY_NETWORK_TYPE } from './constants'
13
+
14
+ type GhostMarketNFT = {
15
+ tokenId: string
16
+ contract: {
17
+ chain?: string
18
+ hash: string
19
+ symbol: string
20
+ }
21
+ creator: {
22
+ address?: string
23
+ offchainName?: string
24
+ }
25
+ apiUrl?: string
26
+ ownerships: {
27
+ owner: {
28
+ address?: string
29
+ }
30
+ }[]
31
+ collection: {
32
+ name?: string
33
+ logoUrl?: string
34
+ }
35
+ metadata: {
36
+ description: string
37
+ mediaType: string
38
+ mediaUri: string
39
+ mintDate: number
40
+ mintNumber: number
41
+ name: string
42
+ }
43
+ }
44
+
45
+ type GhostMarketAssets = {
46
+ assets: GhostMarketNFT[]
47
+ next: string
48
+ }
49
+
50
+ export class GhostMarketNDSNeo3 implements NftDataService {
51
+ private networkType: NetworkType
52
+
53
+ constructor(networkType: NetworkType) {
54
+ this.networkType = networkType
55
+ }
56
+
57
+ async getNftsByAddress({ address, size = 18, cursor, page }: GetNftsByAddressParams): Promise<NftsResponse> {
58
+ const url = this.getUrlWithParams({
59
+ size,
60
+ owners: [address],
61
+ cursor: cursor,
62
+ })
63
+ const { data } = await axios.get<GhostMarketAssets>(url)
64
+ const nfts = data.assets ?? []
65
+
66
+ return { nextCursor: data.next, items: nfts.map(this.parse.bind(this)) }
67
+ }
68
+
69
+ async getNft({ contractHash, tokenId }: GetNftParam): Promise<NftResponse> {
70
+ const url = this.getUrlWithParams({
71
+ contract: contractHash,
72
+ tokenIds: [tokenId],
73
+ })
74
+ const { data } = await axios.get<GhostMarketAssets>(url)
75
+ return this.parse(data.assets[0])
76
+ }
77
+
78
+ private treatGhostMarketImage(srcImage?: string) {
79
+ if (!srcImage) {
80
+ return
81
+ }
82
+
83
+ if (srcImage.startsWith('ipfs://')) {
84
+ const [, imageId] = srcImage.split('://')
85
+
86
+ return `https://ghostmarket.mypinata.cloud/ipfs/${imageId}`
87
+ }
88
+
89
+ return srcImage
90
+ }
91
+
92
+ private getUrlWithParams(params: any) {
93
+ const parameters = qs.stringify(
94
+ {
95
+ chain: GHOSTMARKET_CHAIN_BY_NETWORK_TYPE[this.networkType],
96
+ ...params,
97
+ },
98
+ { arrayFormat: 'bracket' }
99
+ )
100
+ return `${GHOSTMARKET_URL_BY_NETWORK_TYPE[this.networkType]}/assets?${parameters}`
101
+ }
102
+
103
+ private parse(data: GhostMarketNFT) {
104
+ const nftResponse: NftResponse = {
105
+ collectionImage: this.treatGhostMarketImage(data.collection?.logoUrl),
106
+ id: data.tokenId,
107
+ contractHash: data.contract.hash,
108
+ symbol: data.contract.symbol,
109
+ collectionName: data.collection?.name,
110
+ image: this.treatGhostMarketImage(data.metadata.mediaUri),
111
+ isSVG: String(data.metadata.mediaType).includes('svg+xml'),
112
+ name: data.metadata.name,
113
+ }
114
+
115
+ return nftResponse
116
+ }
117
+ }
@@ -0,0 +1,155 @@
1
+ import {
2
+ BDSClaimable,
3
+ BalanceResponse,
4
+ BlockchainDataService,
5
+ ContractMethod,
6
+ ContractParameter,
7
+ ContractResponse,
8
+ Network,
9
+ Token,
10
+ TransactionResponse,
11
+ TransactionsByAddressParams,
12
+ TransactionsByAddressResponse,
13
+ } from '@cityofzion/blockchain-service'
14
+ import { rpc, u } from '@cityofzion/neon-core'
15
+ import { NeonInvoker } from '@cityofzion/neon-invoker'
16
+ import { TOKENS } from './constants'
17
+
18
+ export class RPCBDSNeo3 implements BlockchainDataService, BDSClaimable {
19
+ protected readonly tokenCache: Map<string, Token> = new Map()
20
+ protected readonly feeToken: Token
21
+ protected readonly claimToken: Token
22
+ readonly network: Network
23
+
24
+ constructor(network: Network, feeToken: Token, claimToken: Token) {
25
+ this.network = network
26
+ this.feeToken = feeToken
27
+ this.claimToken = claimToken
28
+ }
29
+
30
+ async getTransaction(hash: string): Promise<TransactionResponse> {
31
+ try {
32
+ const rpcClient = new rpc.RPCClient(this.network.url)
33
+ const response = await rpcClient.getRawTransaction(hash, true)
34
+
35
+ return {
36
+ hash: response.hash,
37
+ block: response.validuntilblock,
38
+ fee: u.BigInteger.fromNumber(response.netfee ?? 0)
39
+ .add(u.BigInteger.fromNumber(response.sysfee ?? 0))
40
+ .toDecimal(this.feeToken.decimals),
41
+ notifications: [],
42
+ transfers: [],
43
+ time: response.blocktime,
44
+ }
45
+ } catch {
46
+ throw new Error(`Transaction not found: ${hash}`)
47
+ }
48
+ }
49
+
50
+ async getTransactionsByAddress(_params: TransactionsByAddressParams): Promise<TransactionsByAddressResponse> {
51
+ throw new Error('Method not supported.')
52
+ }
53
+
54
+ async getContract(contractHash: string): Promise<ContractResponse> {
55
+ try {
56
+ const rpcClient = new rpc.RPCClient(this.network.url)
57
+ const contractState = await rpcClient.getContractState(contractHash)
58
+
59
+ const methods = contractState.manifest.abi.methods.map<ContractMethod>(method => ({
60
+ name: method.name,
61
+ parameters: method.parameters.map<ContractParameter>(parameter => ({
62
+ name: parameter.name,
63
+ type: parameter.type,
64
+ })),
65
+ }))
66
+
67
+ return {
68
+ hash: contractState.hash,
69
+ name: contractState.manifest.name,
70
+ methods,
71
+ }
72
+ } catch {
73
+ throw new Error(`Contract not found: ${contractHash}`)
74
+ }
75
+ }
76
+
77
+ async getTokenInfo(tokenHash: string): Promise<Token> {
78
+ const localToken = TOKENS[this.network.type].find(token => token.hash === tokenHash)
79
+ if (localToken) return localToken
80
+
81
+ if (this.tokenCache.has(tokenHash)) {
82
+ return this.tokenCache.get(tokenHash)!
83
+ }
84
+ try {
85
+ const rpcClient = new rpc.RPCClient(this.network.url)
86
+ const contractState = await rpcClient.getContractState(tokenHash)
87
+
88
+ const invoker = await NeonInvoker.init({
89
+ rpcAddress: this.network.url,
90
+ })
91
+
92
+ const response = await invoker.testInvoke({
93
+ invocations: [
94
+ {
95
+ scriptHash: tokenHash,
96
+ operation: 'decimals',
97
+ args: [],
98
+ },
99
+ { scriptHash: tokenHash, operation: 'symbol', args: [] },
100
+ ],
101
+ })
102
+
103
+ const decimals = Number(response.stack[0].value)
104
+ const symbol = u.base642utf8(response.stack[1].value as string)
105
+ const token = {
106
+ name: contractState.manifest.name,
107
+ symbol,
108
+ hash: contractState.hash,
109
+ decimals,
110
+ }
111
+
112
+ this.tokenCache.set(tokenHash, token)
113
+
114
+ return token
115
+ } catch {
116
+ throw new Error(`Token not found: ${tokenHash}`)
117
+ }
118
+ }
119
+
120
+ async getBalance(address: string): Promise<BalanceResponse[]> {
121
+ const rpcClient = new rpc.RPCClient(this.network.url)
122
+ const response = await rpcClient.getNep17Balances(address)
123
+
124
+ const promises = response.balance.map<Promise<BalanceResponse>>(async balance => {
125
+ let token: Token = {
126
+ hash: balance.assethash,
127
+ name: '-',
128
+ symbol: '-',
129
+ decimals: 8,
130
+ }
131
+ try {
132
+ token = await this.getTokenInfo(balance.assethash)
133
+ } catch {}
134
+
135
+ return {
136
+ amount: u.BigInteger.fromNumber(balance.amount).toDecimal(token?.decimals ?? 8),
137
+ token,
138
+ }
139
+ })
140
+ const balances = await Promise.all(promises)
141
+
142
+ return balances
143
+ }
144
+
145
+ async getBlockHeight(): Promise<number> {
146
+ const rpcClient = new rpc.RPCClient(this.network.url)
147
+ return await rpcClient.getBlockCount()
148
+ }
149
+
150
+ async getUnclaimed(address: string): Promise<string> {
151
+ const rpcClient = new rpc.RPCClient(this.network.url)
152
+ const response = await rpcClient.getUnclaimedGas(address)
153
+ return u.BigInteger.fromNumber(response).toDecimal(this.claimToken.decimals)
154
+ }
155
+ }
@@ -0,0 +1,126 @@
1
+ import { BDSClaimable, BlockchainDataService } from '@cityofzion/blockchain-service'
2
+ import { DoraBDSNeo3 } from '../DoraBDSNeo3'
3
+ import { RPCBDSNeo3 } from '../RpcBDSNeo3'
4
+ import { DEFAULT_URL_BY_NETWORK_TYPE, TOKENS } from '../constants'
5
+
6
+ const gasToken = TOKENS.testnet.find(t => t.symbol === 'GAS')!
7
+ let doraBDSNeo3 = new DoraBDSNeo3({ type: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet }, gasToken, gasToken)
8
+ let rpcBDSNeo3 = new RPCBDSNeo3({ type: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet }, gasToken, gasToken)
9
+
10
+ describe('BDSNeo3', () => {
11
+ it.each([doraBDSNeo3, rpcBDSNeo3])(
12
+ 'Should be able to get transaction - %s',
13
+ async (bdsNeo3: BlockchainDataService) => {
14
+ const hash = '0x70e7381c5dee6e81becd02844e4e0199f6b3df834213bc89418dc4da32cf3f21'
15
+ const transaction = await bdsNeo3.getTransaction(hash)
16
+
17
+ expect(transaction).toEqual(
18
+ expect.objectContaining({
19
+ block: expect.any(Number),
20
+ hash,
21
+ notifications: [],
22
+ transfers: [],
23
+ time: expect.any(Number),
24
+ fee: expect.any(String),
25
+ })
26
+ )
27
+ }
28
+ )
29
+
30
+ it.each([doraBDSNeo3, rpcBDSNeo3])(
31
+ 'Should be able to get transactions of address - %s',
32
+ async (bdsNeo3: BlockchainDataService) => {
33
+ const address = 'NNmTVFrSPhe7zjgN6iq9cLgXJwLZziUKV6'
34
+ try {
35
+ const response = await bdsNeo3.getTransactionsByAddress({ address, page: 1 })
36
+
37
+ response.transactions.forEach(transaction => {
38
+ expect(transaction).toEqual(
39
+ expect.objectContaining({
40
+ block: expect.any(Number),
41
+ hash: expect.any(String),
42
+ notifications: expect.arrayContaining([
43
+ expect.objectContaining({
44
+ eventName: expect.any(String),
45
+ state: expect.arrayContaining([
46
+ {
47
+ type: expect.any(String),
48
+ value: expect.any(String),
49
+ },
50
+ ]),
51
+ }),
52
+ ]),
53
+ transfers: expect.arrayContaining([
54
+ expect.objectContaining({
55
+ amount: expect.any(String),
56
+ from: expect.any(String),
57
+ to: expect.any(String),
58
+ type: 'asset',
59
+ }),
60
+ ]),
61
+ time: expect.any(Number),
62
+ fee: expect.any(String),
63
+ })
64
+ )
65
+ })
66
+ } catch {}
67
+ }
68
+ )
69
+
70
+ it.each([doraBDSNeo3, rpcBDSNeo3])('Should be able to get contract - %s', async (bdsNeo3: BlockchainDataService) => {
71
+ const hash = '0xd2a4cff31913016155e38e474a2c06d08be276cf'
72
+ const contract = await bdsNeo3.getContract(hash)
73
+ expect(contract).toEqual({
74
+ hash: hash,
75
+ name: 'GasToken',
76
+ methods: expect.arrayContaining([
77
+ expect.objectContaining({
78
+ name: expect.any(String),
79
+ parameters: expect.arrayContaining([
80
+ expect.objectContaining({ name: expect.any(String), type: expect.any(String) }),
81
+ ]),
82
+ }),
83
+ ]),
84
+ })
85
+ })
86
+
87
+ it.each([doraBDSNeo3, rpcBDSNeo3])(
88
+ 'Should be able to get token info - %s',
89
+ async (bdsNeo3: BlockchainDataService) => {
90
+ const hash = '0xd2a4cff31913016155e38e474a2c06d08be276cf'
91
+ const token = await bdsNeo3.getTokenInfo(hash)
92
+
93
+ expect(token).toEqual({
94
+ decimals: 8,
95
+ hash: hash,
96
+ name: 'GasToken',
97
+ symbol: 'GAS',
98
+ })
99
+ }
100
+ )
101
+
102
+ it.each([doraBDSNeo3, rpcBDSNeo3])('Should be able to get balance - %s', async (bdsNeo3: BlockchainDataService) => {
103
+ const address = 'NNmTVFrSPhe7zjgN6iq9cLgXJwLZziUKV6'
104
+ const balance = await bdsNeo3.getBalance(address)
105
+ balance.forEach(balance => {
106
+ expect(balance).toEqual({
107
+ amount: expect.any(String),
108
+ token: {
109
+ hash: expect.any(String),
110
+ name: expect.any(String),
111
+ symbol: expect.any(String),
112
+ decimals: expect.any(Number),
113
+ },
114
+ })
115
+ })
116
+ })
117
+
118
+ it.each([doraBDSNeo3, rpcBDSNeo3])(
119
+ 'Should be able to get unclaimed - %s',
120
+ async (bdsNeo3: BlockchainDataService & BDSClaimable) => {
121
+ const address = 'NNmTVFrSPhe7zjgN6iq9cLgXJwLZziUKV6'
122
+ const unclaimed = await bdsNeo3.getUnclaimed(address)
123
+ expect(unclaimed).toEqual(expect.any(String))
124
+ }
125
+ )
126
+ })
@@ -0,0 +1,142 @@
1
+ import { wallet } from '@cityofzion/neon-js'
2
+ import { sleep } from './utils/sleep'
3
+ import { BSNeo3 } from '../BSNeo3'
4
+ import { generateMnemonic } from '@cityofzion/bs-asteroid-sdk'
5
+
6
+ let bsNeo3: BSNeo3
7
+
8
+ describe('BSNeo3', () => {
9
+ beforeAll(() => {
10
+ bsNeo3 = new BSNeo3('neo3', { type: 'testnet', url: 'https://testnet1.neo.coz.io:443' })
11
+ })
12
+
13
+ it('Should be able to validate an address', () => {
14
+ const validAddress = 'NPRMF5bmYuW23DeDJqsDJenhXkAPSJyuYe'
15
+ const invalidAddress = 'invalid address'
16
+
17
+ expect(bsNeo3.validateAddress(validAddress)).toBeTruthy()
18
+ expect(bsNeo3.validateAddress(invalidAddress)).toBeFalsy()
19
+ })
20
+
21
+ it('Should be able to validate an encrypted key', () => {
22
+ const validEncryptedKey = '6PYVPVe1fQznphjbUxXP9KZJqPMVnVwCx5s5pr5axRJ8uHkMtZg97eT5kL'
23
+ const invalidEncryptedKey = 'invalid encrypted key'
24
+
25
+ expect(bsNeo3.validateEncrypted(validEncryptedKey)).toBeTruthy()
26
+ expect(bsNeo3.validateEncrypted(invalidEncryptedKey)).toBeFalsy()
27
+ })
28
+
29
+ it('Should be able to validate a wif', () => {
30
+ const validWif = 'L44B5gGEpqEDRS9vVPz7QT35jcBG2r3CZwSwQ4fCewXAhAhqGVpP'
31
+ const invalidWif = 'invalid wif'
32
+
33
+ expect(bsNeo3.validateKey(validWif)).toBeTruthy()
34
+ expect(bsNeo3.validateKey(invalidWif)).toBeFalsy()
35
+ })
36
+
37
+ it('Should be able to validate an domain', () => {
38
+ const validDomain = 'test.neo'
39
+ const invalidDomain = 'invalid domain'
40
+
41
+ expect(bsNeo3.validateNameServiceDomainFormat(validDomain)).toBeTruthy()
42
+ expect(bsNeo3.validateNameServiceDomainFormat(invalidDomain)).toBeFalsy()
43
+ })
44
+
45
+ it('Should be able to generate a mnemonic', () => {
46
+ expect(() => {
47
+ const mnemonic = generateMnemonic()
48
+ expect(mnemonic).toHaveLength(12)
49
+ }).not.toThrowError()
50
+ })
51
+
52
+ it('Should be able to gererate a account from mnemonic', () => {
53
+ const mnemonic = generateMnemonic()
54
+ const account = bsNeo3.generateAccountFromMnemonic(mnemonic, 0)
55
+
56
+ expect(bsNeo3.validateAddress(account.address)).toBeTruthy()
57
+ expect(bsNeo3.validateKey(account.key)).toBeTruthy()
58
+ })
59
+
60
+ it('Should be able to generate a account from wif', () => {
61
+ const mnemonic = generateMnemonic()
62
+ const account = bsNeo3.generateAccountFromMnemonic(mnemonic, 0)
63
+
64
+ const accountFromWif = bsNeo3.generateAccountFromKey(account.key)
65
+ expect(account).toEqual(expect.objectContaining(accountFromWif))
66
+ })
67
+
68
+ it('Should be able to decrypt a encrypted key', async () => {
69
+ const mnemonic = generateMnemonic()
70
+ const account = bsNeo3.generateAccountFromMnemonic(mnemonic, 0)
71
+ const password = 'TestPassword'
72
+ const encryptedKey = await wallet.encrypt(account.key, password)
73
+ const decryptedAccount = await bsNeo3.decrypt(encryptedKey, password)
74
+ expect(account).toEqual(expect.objectContaining(decryptedAccount))
75
+ }, 20000)
76
+
77
+ it.skip('Should be able to calculate transfer fee', async () => {
78
+ const account = bsNeo3.generateAccountFromKey(process.env.TESTNET_PRIVATE_KEY as string)
79
+
80
+ const fee = await bsNeo3.calculateTransferFee({
81
+ senderAccount: account,
82
+ intent: {
83
+ amount: '0.00000001',
84
+ receiverAddress: 'NPRMF5bmYuW23DeDJqsDJenhXkAPSJyuYe',
85
+ tokenHash: bsNeo3.feeToken.hash,
86
+ tokenDecimals: bsNeo3.feeToken.decimals,
87
+ },
88
+ })
89
+
90
+ expect(fee).toEqual({
91
+ total: expect.any(Number),
92
+ networkFee: expect.any(Number),
93
+ systemFee: expect.any(Number),
94
+ })
95
+ })
96
+
97
+ it.skip('Should be able to transfer', async () => {
98
+ const account = bsNeo3.generateAccountFromKey(process.env.TESTNET_PRIVATE_KEY as string)
99
+ const balance = await bsNeo3.blockchainDataService.getBalance(account.address)
100
+ const gasBalance = balance.find(b => b.token.symbol === bsNeo3.feeToken.symbol)
101
+ expect(Number(gasBalance?.amount)).toBeGreaterThan(0.00000001)
102
+
103
+ const transactionHash = await bsNeo3.transfer({
104
+ senderAccount: account,
105
+ intent: {
106
+ amount: '0.00000001',
107
+ receiverAddress: 'NPRMF5bmYuW23DeDJqsDJenhXkAPSJyuYe',
108
+ tokenHash: bsNeo3.feeToken.hash,
109
+ tokenDecimals: bsNeo3.feeToken.decimals,
110
+ },
111
+ })
112
+
113
+ expect(transactionHash).toEqual(expect.any(String))
114
+ })
115
+
116
+ it.skip('Should be able to claim', async () => {
117
+ const account = bsNeo3.generateAccountFromKey(process.env.TESTNET_PRIVATE_KEY as string)
118
+
119
+ const maxTries = 10
120
+ let tries = 0
121
+
122
+ while (tries < maxTries) {
123
+ try {
124
+ const unclaimed = await bsNeo3.blockchainDataService.getUnclaimed(account.address)
125
+ if (Number(unclaimed) <= 0) continue
126
+
127
+ const transactionHash = await bsNeo3.claim(account)
128
+ expect(transactionHash).toEqual(expect.any(String))
129
+ break
130
+ } catch (error) {
131
+ await sleep(4000)
132
+ } finally {
133
+ tries++
134
+ }
135
+ }
136
+ }, 60000)
137
+
138
+ it('Should be able to resolve a name service domain', async () => {
139
+ const owner = await bsNeo3.resolveNameServiceDomain('neo.neo')
140
+ expect(owner).toEqual('Nj39M97Rk2e23JiULBBMQmvpcnKaRHqxFf')
141
+ })
142
+ })
@@ -0,0 +1,45 @@
1
+ import { FlamingoEDSNeo3 } from '../FlamingoEDSNeo3'
2
+
3
+ let flamingoEDSNeo3: FlamingoEDSNeo3
4
+
5
+ describe('FlamingoEDSNeo3', () => {
6
+ beforeAll(() => {
7
+ flamingoEDSNeo3 = new FlamingoEDSNeo3('mainnet')
8
+ })
9
+ it('Should return a list with prices of tokens using USD', async () => {
10
+ const tokenPriceList = await flamingoEDSNeo3.getTokenPrices('USD')
11
+
12
+ tokenPriceList.forEach(tokenPrice => {
13
+ expect(tokenPrice).toEqual({
14
+ price: expect.any(Number),
15
+ symbol: expect.any(String),
16
+ })
17
+ })
18
+ })
19
+
20
+ it('Should return a list with prices of tokens using BRL', async () => {
21
+ const tokenPriceListInUSD = await flamingoEDSNeo3.getTokenPrices('USD')
22
+ const tokenPriceList = await flamingoEDSNeo3.getTokenPrices('BRL')
23
+
24
+ tokenPriceList.forEach((tokenPrice, index) => {
25
+ expect(tokenPrice.price).toBeGreaterThan(tokenPriceListInUSD[index].price)
26
+ expect(tokenPrice).toEqual({
27
+ price: expect.any(Number),
28
+ symbol: expect.any(String),
29
+ })
30
+ })
31
+ })
32
+
33
+ it('Should return a list with prices of tokens using EUR', async () => {
34
+ const tokenPriceListInUSD = await flamingoEDSNeo3.getTokenPrices('USD')
35
+ const tokenPriceList = await flamingoEDSNeo3.getTokenPrices('EUR')
36
+
37
+ tokenPriceList.forEach((tokenPrice, index) => {
38
+ expect(tokenPrice.price).toBeLessThan(tokenPriceListInUSD[index].price)
39
+ expect(tokenPrice).toEqual({
40
+ price: expect.any(Number),
41
+ symbol: expect.any(String),
42
+ })
43
+ })
44
+ })
45
+ })
@@ -0,0 +1,43 @@
1
+ import { GhostMarketNDSNeo3 } from '../GhostMarketNDSNeo3'
2
+
3
+ let ghostMarketNDSNeo3: GhostMarketNDSNeo3
4
+
5
+ describe('GhostMarketNDSNeo3', () => {
6
+ beforeAll(() => {
7
+ ghostMarketNDSNeo3 = new GhostMarketNDSNeo3('mainnet')
8
+ })
9
+
10
+ it('Get NFT', async () => {
11
+ const nft = await ghostMarketNDSNeo3.getNft({
12
+ contractHash: '0xaa4fb927b3fe004e689a278d188689c9f050a8b2',
13
+ tokenId: 'SVBLTUYxMTY1',
14
+ })
15
+ expect(nft).toEqual(
16
+ expect.objectContaining({
17
+ id: 'SVBLTUYxMTY1',
18
+ contractHash: '0xaa4fb927b3fe004e689a278d188689c9f050a8b2',
19
+ symbol: 'TTM',
20
+ collectionImage: expect.any(String),
21
+ collectionName: 'TOTHEMOON',
22
+ image: expect.any(String),
23
+ isSVG: expect.any(Boolean),
24
+ name: 'Pink Moon Fish',
25
+ })
26
+ )
27
+ })
28
+ it('Get NFTS by address', async () => {
29
+ const nfts = await ghostMarketNDSNeo3.getNftsByAddress({
30
+ address: 'NNmTVFrSPhe7zjgN6iq9cLgXJwLZziUKV6',
31
+ })
32
+ expect(nfts.items.length).toBeGreaterThan(0)
33
+ nfts.items.forEach(nft => {
34
+ expect(nft).toEqual(
35
+ expect.objectContaining({
36
+ symbol: expect.any(String),
37
+ id: expect.any(String),
38
+ contractHash: expect.any(String),
39
+ })
40
+ )
41
+ })
42
+ })
43
+ })
@@ -0,0 +1 @@
1
+ export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "symbol": "GAS",
4
+ "name": "GASToken",
5
+ "hash": "d2a4cff31913016155e38e474a2c06d08be276cf",
6
+ "decimals": 8
7
+ },
8
+ {
9
+ "symbol": "NEO",
10
+ "name": "NeoToken",
11
+ "hash": "ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
12
+ "decimals": 0
13
+ }
14
+ ]