@cityofzion/bs-neo3 0.7.3 → 0.9.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.
- package/.rush/temp/operation/build/all.log +1 -0
- package/.rush/temp/operation/build/state.json +1 -1
- package/.rush/temp/package-deps_build.json +9 -7
- package/.rush/temp/shrinkwrap-deps.json +354 -130
- package/CHANGELOG.json +22 -0
- package/CHANGELOG.md +11 -0
- package/bs-neo3.build.log +1 -2
- package/dist/BSNeo3.d.ts +29 -28
- package/dist/BSNeo3.js +187 -184
- package/dist/DoraBDSNeo3.d.ts +12 -12
- package/dist/DoraBDSNeo3.js +169 -169
- package/dist/DoraESNeo3.d.ts +7 -7
- package/dist/DoraESNeo3.js +19 -19
- package/dist/FlamingoEDSNeo3.d.ts +8 -8
- package/dist/FlamingoEDSNeo3.js +45 -44
- package/dist/GhostMarketNDSNeo3.d.ts +10 -10
- package/dist/GhostMarketNDSNeo3.js +75 -75
- package/dist/RpcBDSNeo3.d.ts +16 -16
- package/dist/RpcBDSNeo3.js +153 -153
- package/dist/assets/tokens/common.json +14 -14
- package/dist/assets/tokens/mainnet.json +116 -116
- package/dist/constants.d.ts +7 -7
- package/dist/constants.js +28 -28
- package/dist/index.d.ts +6 -6
- package/dist/index.js +22 -22
- package/jest.config.ts +13 -13
- package/jest.setup.ts +1 -1
- package/package.json +33 -32
- package/src/BSNeo3.ts +232 -228
- package/src/DoraBDSNeo3.ts +180 -180
- package/src/DoraESNeo3.ts +19 -19
- package/src/FlamingoEDSNeo3.ts +47 -45
- package/src/GhostMarketNDSNeo3.ts +117 -117
- package/src/RpcBDSNeo3.ts +157 -157
- package/src/__tests__/BDSNeo3.spec.ts +126 -126
- package/src/__tests__/BSNeo3.spec.ts +150 -142
- package/src/__tests__/DoraESNeo3.spec.ts +23 -23
- package/src/__tests__/FlamingoEDSNeo3.spec.ts +48 -45
- package/src/__tests__/GhostMarketNDSNeo3.spec.ts +43 -43
- package/src/__tests__/utils/sleep.ts +1 -1
- package/src/assets/tokens/common.json +13 -13
- package/src/assets/tokens/mainnet.json +115 -115
- package/src/constants.ts +29 -29
- package/src/index.ts +6 -6
- package/tsconfig.build.json +4 -4
- package/tsconfig.json +14 -14
- package/dist/BDSNeo3.d.ts +0 -15
- package/dist/BDSNeo3.js +0 -211
- package/dist/assets/tokens.json +0 -128
- package/dist/exceptions.d.ts +0 -3
- package/dist/exceptions.js +0 -8
- package/dist/explorer/dora/DoraNeo3Responses.d.ts +0 -174
- package/dist/explorer/dora/DoraNeo3Responses.js +0 -3
- package/dist/explorer/dora/DoraNeo3Routes.d.ts +0 -6
- package/dist/explorer/dora/DoraNeo3Routes.js +0 -9
- package/dist/explorer/index.d.ts +0 -6
- package/dist/explorer/index.js +0 -23
- package/docs/.nojekyll +0 -1
- package/docs/assets/highlight.css +0 -22
- package/docs/assets/main.js +0 -58
- package/docs/assets/search.js +0 -1
- package/docs/assets/style.css +0 -1280
- package/docs/classes/BDSNeo3.html +0 -253
- package/docs/classes/BSNeo3.html +0 -442
- package/docs/index.html +0 -82
- package/docs/interfaces/DoraNeo3Abi.html +0 -78
- package/docs/interfaces/DoraNeo3Asset.html +0 -117
- package/docs/interfaces/DoraNeo3AssetState.html +0 -96
- package/docs/interfaces/DoraNeo3Balance.html +0 -89
- package/docs/interfaces/DoraNeo3ConsensusNode.html +0 -75
- package/docs/interfaces/DoraNeo3Contract.html +0 -110
- package/docs/interfaces/DoraNeo3Event.html +0 -75
- package/docs/interfaces/DoraNeo3Features.html +0 -84
- package/docs/interfaces/DoraNeo3HistoryState.html +0 -75
- package/docs/interfaces/DoraNeo3Invocation.html +0 -75
- package/docs/interfaces/DoraNeo3Item.html +0 -131
- package/docs/interfaces/DoraNeo3Manifest.html +0 -117
- package/docs/interfaces/DoraNeo3Metadata.html +0 -138
- package/docs/interfaces/DoraNeo3Method.html +0 -102
- package/docs/interfaces/DoraNeo3Nef.html +0 -103
- package/docs/interfaces/DoraNeo3Notification.html +0 -82
- package/docs/interfaces/DoraNeo3Parameter.html +0 -75
- package/docs/interfaces/DoraNeo3Permission.html +0 -75
- package/docs/interfaces/DoraNeo3Signer.html +0 -75
- package/docs/interfaces/DoraNeo3Token.html +0 -96
- package/docs/interfaces/DoraNeo3Transaction.html +0 -166
- package/docs/interfaces/DoraNeo3TransactionHistory.html +0 -75
- package/docs/interfaces/DoraNeo3Transfer.html +0 -117
- package/docs/interfaces/DoraNeo3Witness.html +0 -75
- package/docs/modules.html +0 -122
- package/docs/variables/DORA_ASSET.html +0 -81
- package/docs/variables/DORA_BALANCE.html +0 -81
- package/docs/variables/DORA_CONTRACT.html +0 -81
- package/docs/variables/DORA_NODES.html +0 -81
- package/docs/variables/DORA_TRANSACTION.html +0 -81
- package/docs/variables/DORA_TRANSACTIONS.html +0 -81
- package/docs/variables/explorerOptions.html +0 -86
- package/docs/variables/gasInfoNeo3.html +0 -90
- package/docs/variables/neoInfoNeo3.html +0 -90
|
@@ -1,117 +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
|
-
}
|
|
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
|
+
}
|
package/src/RpcBDSNeo3.ts
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
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
|
-
maxTimeToConfirmTransactionInMs: number = 1000 * 60 * 2
|
|
25
|
-
|
|
26
|
-
constructor(network: Network, feeToken: Token, claimToken: Token) {
|
|
27
|
-
this.network = network
|
|
28
|
-
this.feeToken = feeToken
|
|
29
|
-
this.claimToken = claimToken
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async getTransaction(hash: string): Promise<TransactionResponse> {
|
|
33
|
-
try {
|
|
34
|
-
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
35
|
-
const response = await rpcClient.getRawTransaction(hash, true)
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
hash: response.hash,
|
|
39
|
-
block: response.validuntilblock,
|
|
40
|
-
fee: u.BigInteger.fromNumber(response.netfee ?? 0)
|
|
41
|
-
.add(u.BigInteger.fromNumber(response.sysfee ?? 0))
|
|
42
|
-
.toDecimal(this.feeToken.decimals),
|
|
43
|
-
notifications: [],
|
|
44
|
-
transfers: [],
|
|
45
|
-
time: response.blocktime,
|
|
46
|
-
}
|
|
47
|
-
} catch {
|
|
48
|
-
throw new Error(`Transaction not found: ${hash}`)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async getTransactionsByAddress(_params: TransactionsByAddressParams): Promise<TransactionsByAddressResponse> {
|
|
53
|
-
throw new Error('Method not supported.')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async getContract(contractHash: string): Promise<ContractResponse> {
|
|
57
|
-
try {
|
|
58
|
-
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
59
|
-
const contractState = await rpcClient.getContractState(contractHash)
|
|
60
|
-
|
|
61
|
-
const methods = contractState.manifest.abi.methods.map<ContractMethod>(method => ({
|
|
62
|
-
name: method.name,
|
|
63
|
-
parameters: method.parameters.map<ContractParameter>(parameter => ({
|
|
64
|
-
name: parameter.name,
|
|
65
|
-
type: parameter.type,
|
|
66
|
-
})),
|
|
67
|
-
}))
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
hash: contractState.hash,
|
|
71
|
-
name: contractState.manifest.name,
|
|
72
|
-
methods,
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
throw new Error(`Contract not found: ${contractHash}`)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async getTokenInfo(tokenHash: string): Promise<Token> {
|
|
80
|
-
const localToken = TOKENS[this.network.type].find(token => token.hash === tokenHash)
|
|
81
|
-
if (localToken) return localToken
|
|
82
|
-
|
|
83
|
-
if (this.tokenCache.has(tokenHash)) {
|
|
84
|
-
return this.tokenCache.get(tokenHash)!
|
|
85
|
-
}
|
|
86
|
-
try {
|
|
87
|
-
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
88
|
-
const contractState = await rpcClient.getContractState(tokenHash)
|
|
89
|
-
|
|
90
|
-
const invoker = await NeonInvoker.init({
|
|
91
|
-
rpcAddress: this.network.url,
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
const response = await invoker.testInvoke({
|
|
95
|
-
invocations: [
|
|
96
|
-
{
|
|
97
|
-
scriptHash: tokenHash,
|
|
98
|
-
operation: 'decimals',
|
|
99
|
-
args: [],
|
|
100
|
-
},
|
|
101
|
-
{ scriptHash: tokenHash, operation: 'symbol', args: [] },
|
|
102
|
-
],
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
const decimals = Number(response.stack[0].value)
|
|
106
|
-
const symbol = u.base642utf8(response.stack[1].value as string)
|
|
107
|
-
const token = {
|
|
108
|
-
name: contractState.manifest.name,
|
|
109
|
-
symbol,
|
|
110
|
-
hash: contractState.hash,
|
|
111
|
-
decimals,
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
this.tokenCache.set(tokenHash, token)
|
|
115
|
-
|
|
116
|
-
return token
|
|
117
|
-
} catch {
|
|
118
|
-
throw new Error(`Token not found: ${tokenHash}`)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async getBalance(address: string): Promise<BalanceResponse[]> {
|
|
123
|
-
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
124
|
-
const response = await rpcClient.getNep17Balances(address)
|
|
125
|
-
|
|
126
|
-
const promises = response.balance.map<Promise<BalanceResponse>>(async balance => {
|
|
127
|
-
let token: Token = {
|
|
128
|
-
hash: balance.assethash,
|
|
129
|
-
name: '-',
|
|
130
|
-
symbol: '-',
|
|
131
|
-
decimals: 8,
|
|
132
|
-
}
|
|
133
|
-
try {
|
|
134
|
-
token = await this.getTokenInfo(balance.assethash)
|
|
135
|
-
} catch {}
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
amount: u.BigInteger.fromNumber(balance.amount).toDecimal(token?.decimals ?? 8),
|
|
139
|
-
token,
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
const balances = await Promise.all(promises)
|
|
143
|
-
|
|
144
|
-
return balances
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async getBlockHeight(): Promise<number> {
|
|
148
|
-
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
149
|
-
return await rpcClient.getBlockCount()
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async getUnclaimed(address: string): Promise<string> {
|
|
153
|
-
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
154
|
-
const response = await rpcClient.getUnclaimedGas(address)
|
|
155
|
-
return u.BigInteger.fromNumber(response).toDecimal(this.claimToken.decimals)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
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
|
+
maxTimeToConfirmTransactionInMs: number = 1000 * 60 * 2
|
|
25
|
+
|
|
26
|
+
constructor(network: Network, feeToken: Token, claimToken: Token) {
|
|
27
|
+
this.network = network
|
|
28
|
+
this.feeToken = feeToken
|
|
29
|
+
this.claimToken = claimToken
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getTransaction(hash: string): Promise<TransactionResponse> {
|
|
33
|
+
try {
|
|
34
|
+
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
35
|
+
const response = await rpcClient.getRawTransaction(hash, true)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
hash: response.hash,
|
|
39
|
+
block: response.validuntilblock,
|
|
40
|
+
fee: u.BigInteger.fromNumber(response.netfee ?? 0)
|
|
41
|
+
.add(u.BigInteger.fromNumber(response.sysfee ?? 0))
|
|
42
|
+
.toDecimal(this.feeToken.decimals),
|
|
43
|
+
notifications: [],
|
|
44
|
+
transfers: [],
|
|
45
|
+
time: response.blocktime,
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error(`Transaction not found: ${hash}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getTransactionsByAddress(_params: TransactionsByAddressParams): Promise<TransactionsByAddressResponse> {
|
|
53
|
+
throw new Error('Method not supported.')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getContract(contractHash: string): Promise<ContractResponse> {
|
|
57
|
+
try {
|
|
58
|
+
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
59
|
+
const contractState = await rpcClient.getContractState(contractHash)
|
|
60
|
+
|
|
61
|
+
const methods = contractState.manifest.abi.methods.map<ContractMethod>(method => ({
|
|
62
|
+
name: method.name,
|
|
63
|
+
parameters: method.parameters.map<ContractParameter>(parameter => ({
|
|
64
|
+
name: parameter.name,
|
|
65
|
+
type: parameter.type,
|
|
66
|
+
})),
|
|
67
|
+
}))
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
hash: contractState.hash,
|
|
71
|
+
name: contractState.manifest.name,
|
|
72
|
+
methods,
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
throw new Error(`Contract not found: ${contractHash}`)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async getTokenInfo(tokenHash: string): Promise<Token> {
|
|
80
|
+
const localToken = TOKENS[this.network.type].find(token => token.hash === tokenHash)
|
|
81
|
+
if (localToken) return localToken
|
|
82
|
+
|
|
83
|
+
if (this.tokenCache.has(tokenHash)) {
|
|
84
|
+
return this.tokenCache.get(tokenHash)!
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
88
|
+
const contractState = await rpcClient.getContractState(tokenHash)
|
|
89
|
+
|
|
90
|
+
const invoker = await NeonInvoker.init({
|
|
91
|
+
rpcAddress: this.network.url,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const response = await invoker.testInvoke({
|
|
95
|
+
invocations: [
|
|
96
|
+
{
|
|
97
|
+
scriptHash: tokenHash,
|
|
98
|
+
operation: 'decimals',
|
|
99
|
+
args: [],
|
|
100
|
+
},
|
|
101
|
+
{ scriptHash: tokenHash, operation: 'symbol', args: [] },
|
|
102
|
+
],
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const decimals = Number(response.stack[0].value)
|
|
106
|
+
const symbol = u.base642utf8(response.stack[1].value as string)
|
|
107
|
+
const token = {
|
|
108
|
+
name: contractState.manifest.name,
|
|
109
|
+
symbol,
|
|
110
|
+
hash: contractState.hash,
|
|
111
|
+
decimals,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.tokenCache.set(tokenHash, token)
|
|
115
|
+
|
|
116
|
+
return token
|
|
117
|
+
} catch {
|
|
118
|
+
throw new Error(`Token not found: ${tokenHash}`)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getBalance(address: string): Promise<BalanceResponse[]> {
|
|
123
|
+
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
124
|
+
const response = await rpcClient.getNep17Balances(address)
|
|
125
|
+
|
|
126
|
+
const promises = response.balance.map<Promise<BalanceResponse>>(async balance => {
|
|
127
|
+
let token: Token = {
|
|
128
|
+
hash: balance.assethash,
|
|
129
|
+
name: '-',
|
|
130
|
+
symbol: '-',
|
|
131
|
+
decimals: 8,
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
token = await this.getTokenInfo(balance.assethash)
|
|
135
|
+
} catch {}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
amount: u.BigInteger.fromNumber(balance.amount).toDecimal(token?.decimals ?? 8),
|
|
139
|
+
token,
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
const balances = await Promise.all(promises)
|
|
143
|
+
|
|
144
|
+
return balances
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async getBlockHeight(): Promise<number> {
|
|
148
|
+
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
149
|
+
return await rpcClient.getBlockCount()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async getUnclaimed(address: string): Promise<string> {
|
|
153
|
+
const rpcClient = new rpc.RPCClient(this.network.url)
|
|
154
|
+
const response = await rpcClient.getUnclaimedGas(address)
|
|
155
|
+
return u.BigInteger.fromNumber(response).toDecimal(this.claimToken.decimals)
|
|
156
|
+
}
|
|
157
|
+
}
|