@cityofzion/bs-neo3 0.4.2 → 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.
- package/.rush/temp/operation/build/state.json +1 -1
- package/.rush/temp/package-deps_build.json +20 -0
- package/.rush/temp/shrinkwrap-deps.json +2 -2
- package/bs-neo3.build.log +1 -86
- package/dist/BSNeo3.d.ts +22 -12
- package/dist/BSNeo3.js +85 -55
- package/dist/constants.d.ts +10 -3
- package/docs/assets/search.js +1 -1
- package/docs/classes/BDSNeo3.html +13 -13
- package/docs/classes/BSNeo3.html +80 -36
- package/docs/interfaces/DoraNeo3Abi.html +6 -6
- package/docs/interfaces/DoraNeo3Asset.html +9 -9
- package/docs/interfaces/DoraNeo3AssetState.html +6 -6
- package/docs/interfaces/DoraNeo3Balance.html +5 -5
- package/docs/interfaces/DoraNeo3ConsensusNode.html +3 -3
- package/docs/interfaces/DoraNeo3Contract.html +8 -8
- package/docs/interfaces/DoraNeo3Event.html +3 -3
- package/docs/interfaces/DoraNeo3Features.html +1 -1
- package/docs/interfaces/DoraNeo3HistoryState.html +3 -3
- package/docs/interfaces/DoraNeo3Invocation.html +3 -3
- package/docs/interfaces/DoraNeo3Item.html +11 -11
- package/docs/interfaces/DoraNeo3Manifest.html +9 -9
- package/docs/interfaces/DoraNeo3Metadata.html +12 -12
- package/docs/interfaces/DoraNeo3Method.html +12 -12
- package/docs/interfaces/DoraNeo3Nef.html +7 -7
- package/docs/interfaces/DoraNeo3Notification.html +4 -4
- package/docs/interfaces/DoraNeo3Parameter.html +3 -3
- package/docs/interfaces/DoraNeo3Permission.html +3 -3
- package/docs/interfaces/DoraNeo3Signer.html +3 -3
- package/docs/interfaces/DoraNeo3Token.html +6 -6
- package/docs/interfaces/DoraNeo3Transaction.html +16 -16
- package/docs/interfaces/DoraNeo3TransactionHistory.html +3 -3
- package/docs/interfaces/DoraNeo3Transfer.html +9 -9
- package/docs/interfaces/DoraNeo3Witness.html +3 -3
- package/docs/variables/DORA_ASSET.html +1 -1
- package/docs/variables/DORA_BALANCE.html +1 -1
- package/docs/variables/DORA_CONTRACT.html +1 -1
- package/docs/variables/DORA_NODES.html +1 -1
- package/docs/variables/DORA_TRANSACTION.html +1 -1
- package/docs/variables/DORA_TRANSACTIONS.html +1 -1
- package/docs/variables/explorerOptions.html +1 -1
- package/docs/variables/gasInfoNeo3.html +11 -2
- package/docs/variables/neoInfoNeo3.html +11 -2
- package/jest.config.ts +13 -0
- package/jest.setup.ts +1 -0
- package/package.json +14 -6
- package/src/BSNeo3.ts +221 -221
- package/src/DoraBDSNeo3.ts +180 -0
- package/src/FlamingoEDSNeo3.ts +45 -0
- package/src/GhostMarketNDSNeo3.ts +117 -0
- package/src/RpcBDSNeo3.ts +155 -0
- package/src/__tests__/BDSNeo3.spec.ts +126 -0
- package/src/__tests__/BSNeo3.spec.ts +142 -0
- package/src/__tests__/FlamingoEDSNeo3.spec.ts +45 -0
- package/src/__tests__/GhostMarketNDSNeo3.spec.ts +43 -0
- package/src/__tests__/utils/sleep.ts +1 -0
- package/src/assets/tokens/common.json +14 -0
- package/src/assets/tokens/mainnet.json +116 -0
- package/src/constants.ts +26 -10
- package/src/index.ts +4 -2
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +3 -7
- package/bs-neo3.build.error.log +0 -84
- package/dist/excpetions.d.ts +0 -3
- package/dist/excpetions.js +0 -8
- package/src/BDSNeo3.ts +0 -168
- package/src/assets/blockchain_icon_neo.png +0 -0
- package/src/assets/blockchain_icon_neo_white.png +0 -0
- package/src/assets/tokens.json +0 -129
- package/src/exceptions.ts +0 -5
- package/src/explorer/dora/DoraNeo3Responses.ts +0 -207
- package/src/explorer/dora/DoraNeo3Routes.ts +0 -6
- package/src/explorer/index.ts +0 -8
package/src/BSNeo3.ts
CHANGED
|
@@ -1,223 +1,223 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import {
|
|
2
|
+
BlockchainDataService,
|
|
3
|
+
BlockchainService,
|
|
4
|
+
BSClaimable,
|
|
5
|
+
Account,
|
|
6
|
+
ExchangeDataService,
|
|
7
|
+
BDSClaimable,
|
|
8
|
+
Token,
|
|
9
|
+
BSWithNameService,
|
|
10
|
+
Network,
|
|
11
|
+
PartialBy,
|
|
12
|
+
TransferParam,
|
|
13
|
+
BSCalculableFee,
|
|
14
|
+
NftDataService,
|
|
15
|
+
BSWithNft,
|
|
16
|
+
AccountWithDerivationPath,
|
|
17
|
+
} from '@cityofzion/blockchain-service'
|
|
18
|
+
import { api, u, wallet } from '@cityofzion/neon-js'
|
|
19
|
+
import Neon from '@cityofzion/neon-core'
|
|
7
20
|
import { NeonInvoker } from '@cityofzion/neon-invoker'
|
|
8
|
-
import {NeonParser} from
|
|
21
|
+
import { NeonParser } from '@cityofzion/neon-parser'
|
|
9
22
|
import { ABI_TYPES } from '@cityofzion/neo3-parser'
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
decimalAmt: amount,
|
|
212
|
-
})
|
|
213
|
-
}
|
|
214
|
-
return intents
|
|
215
|
-
}
|
|
216
|
-
private signTransfer(account: Account) {
|
|
217
|
-
const neoAccount = new wallet.Account(account.address)
|
|
218
|
-
const result: api.signingConfig = {
|
|
219
|
-
signingCallback: api.signWithAccount(neoAccount)
|
|
220
|
-
}
|
|
221
|
-
return result
|
|
222
|
-
}
|
|
223
|
-
}
|
|
23
|
+
import { ContractInvocation } from '@cityofzion/neo3-invoker'
|
|
24
|
+
|
|
25
|
+
import { RPCBDSNeo3 } from './RpcBDSNeo3'
|
|
26
|
+
import { DoraBDSNeo3 } from './DoraBDSNeo3'
|
|
27
|
+
import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NEO_NS_HASH, TOKENS } from './constants'
|
|
28
|
+
import { FlamingoEDSNeo3 } from './FlamingoEDSNeo3'
|
|
29
|
+
import { GhostMarketNDSNeo3 } from './GhostMarketNDSNeo3'
|
|
30
|
+
import { keychain } from '@cityofzion/bs-asteroid-sdk'
|
|
31
|
+
|
|
32
|
+
export class BSNeo3<BSCustomName extends string = string>
|
|
33
|
+
implements BlockchainService, BSClaimable, BSWithNameService, BSCalculableFee, BSWithNft
|
|
34
|
+
{
|
|
35
|
+
readonly blockchainName: BSCustomName
|
|
36
|
+
readonly feeToken: Token
|
|
37
|
+
readonly claimToken: Token
|
|
38
|
+
readonly burnToken: Token
|
|
39
|
+
readonly derivationPath: string
|
|
40
|
+
|
|
41
|
+
blockchainDataService!: BlockchainDataService & BDSClaimable
|
|
42
|
+
nftDataService!: NftDataService
|
|
43
|
+
exchangeDataService!: ExchangeDataService
|
|
44
|
+
tokens: Token[]
|
|
45
|
+
network!: Network
|
|
46
|
+
|
|
47
|
+
constructor(blockchainName: BSCustomName, network: PartialBy<Network, 'url'>) {
|
|
48
|
+
this.blockchainName = blockchainName
|
|
49
|
+
this.tokens = TOKENS[network.type]
|
|
50
|
+
|
|
51
|
+
this.derivationPath = DERIVATION_PATH
|
|
52
|
+
this.feeToken = this.tokens.find(token => token.symbol === 'GAS')!
|
|
53
|
+
this.burnToken = this.tokens.find(token => token.symbol === 'NEO')!
|
|
54
|
+
this.claimToken = this.tokens.find(token => token.symbol === 'GAS')!
|
|
55
|
+
this.setNetwork(network)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setNetwork(param: PartialBy<Network, 'url'>) {
|
|
59
|
+
const network = {
|
|
60
|
+
type: param.type,
|
|
61
|
+
url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.type],
|
|
62
|
+
}
|
|
63
|
+
this.network = network
|
|
64
|
+
|
|
65
|
+
if (network.type === 'custom') {
|
|
66
|
+
this.blockchainDataService = new RPCBDSNeo3(network, this.feeToken, this.claimToken)
|
|
67
|
+
} else {
|
|
68
|
+
this.blockchainDataService = new DoraBDSNeo3(network, this.feeToken, this.claimToken)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.exchangeDataService = new FlamingoEDSNeo3(network.type)
|
|
72
|
+
this.nftDataService = new GhostMarketNDSNeo3(network.type)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
validateAddress(address: string): boolean {
|
|
76
|
+
return wallet.isAddress(address, 53)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
validateEncrypted(encryptedKey: string): boolean {
|
|
80
|
+
return wallet.isNEP2(encryptedKey)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
validateKey(key: string): boolean {
|
|
84
|
+
return wallet.isWIF(key) || wallet.isPrivateKey(key)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
validateNameServiceDomainFormat(domainName: string): boolean {
|
|
88
|
+
if (!domainName.endsWith('.neo')) return false
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
generateAccountFromMnemonic(mnemonic: string[] | string, index: number): AccountWithDerivationPath {
|
|
93
|
+
keychain.importMnemonic(Array.isArray(mnemonic) ? mnemonic.join(' ') : mnemonic)
|
|
94
|
+
const path = this.derivationPath.replace('?', index.toString())
|
|
95
|
+
const childKey = keychain.generateChildKey('neo', path)
|
|
96
|
+
const key = childKey.getWIF()
|
|
97
|
+
const { address } = new wallet.Account(key)
|
|
98
|
+
return { address, key, type: 'wif', derivationPath: path }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
generateAccountFromKey(key: string): Account {
|
|
102
|
+
const type = wallet.isWIF(key) ? 'wif' : wallet.isPrivateKey(key) ? 'privateKey' : undefined
|
|
103
|
+
if (!type) throw new Error('Invalid key')
|
|
104
|
+
|
|
105
|
+
const { address } = new wallet.Account(key)
|
|
106
|
+
return { address, key, type }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async decrypt(encryptedKey: string, password: string): Promise<Account> {
|
|
110
|
+
let BsReactNativeDecrypt: any
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const { NativeModules } = require('react-native')
|
|
114
|
+
BsReactNativeDecrypt = NativeModules.BsReactNativeDecrypt
|
|
115
|
+
} catch {
|
|
116
|
+
const key = await wallet.decrypt(encryptedKey, password)
|
|
117
|
+
return this.generateAccountFromKey(key)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!BsReactNativeDecrypt) {
|
|
121
|
+
throw new Error('@CityOfZion/bs-react-native-decrypt is not installed')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const privateKey = await BsReactNativeDecrypt.decryptNeo3(encryptedKey, password)
|
|
125
|
+
return this.generateAccountFromKey(privateKey)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async calculateTransferFee(param: TransferParam): Promise<string> {
|
|
129
|
+
const account = new wallet.Account(param.senderAccount.key)
|
|
130
|
+
const invoker = await NeonInvoker.init({
|
|
131
|
+
rpcAddress: this.network.url,
|
|
132
|
+
account,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const invocations = this.buildTransferInvocation(param, account)
|
|
136
|
+
|
|
137
|
+
const { networkFee, systemFee } = await invoker.calculateFee({
|
|
138
|
+
invocations,
|
|
139
|
+
signers: [],
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
return networkFee.add(systemFee).toDecimal(this.feeToken.decimals)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async transfer(param: TransferParam): Promise<string> {
|
|
146
|
+
const account = new wallet.Account(param.senderAccount.key)
|
|
147
|
+
const invoker = await NeonInvoker.init({
|
|
148
|
+
rpcAddress: this.network.url,
|
|
149
|
+
account,
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const invocations = this.buildTransferInvocation(param, account)
|
|
153
|
+
|
|
154
|
+
const transactionHash = await invoker.invokeFunction({
|
|
155
|
+
invocations,
|
|
156
|
+
signers: [],
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
return transactionHash
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async claim(account: Account): Promise<string> {
|
|
163
|
+
const neoAccount = new wallet.Account(account.key)
|
|
164
|
+
const facade = await api.NetworkFacade.fromConfig({ node: this.network.url })
|
|
165
|
+
|
|
166
|
+
const transactionHash = await facade.claimGas(neoAccount, {
|
|
167
|
+
signingCallback: api.signWithAccount(neoAccount),
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
return transactionHash
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async resolveNameServiceDomain(domainName: string): Promise<any> {
|
|
174
|
+
const parser = NeonParser
|
|
175
|
+
const invoker = await NeonInvoker.init({ rpcAddress: this.network.url })
|
|
176
|
+
const response = await invoker.testInvoke({
|
|
177
|
+
invocations: [
|
|
178
|
+
{
|
|
179
|
+
scriptHash: NEO_NS_HASH,
|
|
180
|
+
operation: 'ownerOf',
|
|
181
|
+
args: [{ type: 'String', value: domainName }],
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
if (response.stack.length === 0) {
|
|
187
|
+
throw new Error(response.exception ?? 'unrecognized response')
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const parsed = parser.parseRpcResponse(response.stack[0] as any, {
|
|
191
|
+
type: ABI_TYPES.HASH160.name,
|
|
192
|
+
})
|
|
193
|
+
const address = parser.accountInputToAddress(parsed.replace('0x', ''))
|
|
194
|
+
return address
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private buildTransferInvocation(
|
|
198
|
+
{ intent, tipIntent }: TransferParam,
|
|
199
|
+
account: Neon.wallet.Account
|
|
200
|
+
): ContractInvocation[] {
|
|
201
|
+
const intents = [intent, ...(tipIntent ? [tipIntent] : [])]
|
|
202
|
+
|
|
203
|
+
const invocations: ContractInvocation[] = intents.map(intent => {
|
|
204
|
+
return {
|
|
205
|
+
operation: 'transfer',
|
|
206
|
+
scriptHash: intent.tokenHash,
|
|
207
|
+
args: [
|
|
208
|
+
{ type: 'Hash160', value: account.address },
|
|
209
|
+
{ type: 'Hash160', value: intent.receiverAddress },
|
|
210
|
+
{
|
|
211
|
+
type: 'Integer',
|
|
212
|
+
value: intent.tokenDecimals
|
|
213
|
+
? u.BigInteger.fromDecimal(intent.amount, intent.tokenDecimals).toString()
|
|
214
|
+
: intent.amount,
|
|
215
|
+
},
|
|
216
|
+
{ type: 'Any', value: '' },
|
|
217
|
+
],
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
return invocations
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BalanceResponse,
|
|
3
|
+
ContractResponse,
|
|
4
|
+
TransactionsByAddressParams,
|
|
5
|
+
TransactionsByAddressResponse,
|
|
6
|
+
TransactionResponse,
|
|
7
|
+
TransactionNotifications,
|
|
8
|
+
Network,
|
|
9
|
+
Token,
|
|
10
|
+
TransactionTransferNft,
|
|
11
|
+
TransactionTransferAsset,
|
|
12
|
+
} from '@cityofzion/blockchain-service'
|
|
13
|
+
import { wallet, u } from '@cityofzion/neon-js'
|
|
14
|
+
import { NeoRESTApi } from '@cityofzion/dora-ts/dist/api'
|
|
15
|
+
import { RPCBDSNeo3 } from './RpcBDSNeo3'
|
|
16
|
+
import { TOKENS } from './constants'
|
|
17
|
+
|
|
18
|
+
const NeoRest = new NeoRESTApi({
|
|
19
|
+
doraUrl: 'https://dora.coz.io',
|
|
20
|
+
endpoint: '/api/v2/neo3',
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export class DoraBDSNeo3 extends RPCBDSNeo3 {
|
|
24
|
+
readonly network: Network
|
|
25
|
+
|
|
26
|
+
constructor(network: Network, feeToken: Token, claimToken: Token) {
|
|
27
|
+
if (network.type === 'custom') {
|
|
28
|
+
throw new Error('DoraBDSNeo3 does not support custom networks')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
super(network, feeToken, claimToken)
|
|
32
|
+
this.network = network
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async getTransaction(hash: string): Promise<TransactionResponse> {
|
|
36
|
+
try {
|
|
37
|
+
const data = await NeoRest.transaction(hash, this.network.type)
|
|
38
|
+
return {
|
|
39
|
+
block: data.block,
|
|
40
|
+
time: Number(data.time),
|
|
41
|
+
hash: data.hash,
|
|
42
|
+
fee: u.BigInteger.fromNumber(data.netfee ?? 0)
|
|
43
|
+
.add(u.BigInteger.fromNumber(data.sysfee ?? 0))
|
|
44
|
+
.toDecimal(this.feeToken.decimals),
|
|
45
|
+
notifications: [],
|
|
46
|
+
transfers: [],
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
throw new Error(`Transaction not found: ${hash}`)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async getTransactionsByAddress({
|
|
54
|
+
address,
|
|
55
|
+
page = 1,
|
|
56
|
+
}: TransactionsByAddressParams): Promise<TransactionsByAddressResponse> {
|
|
57
|
+
const data = await NeoRest.addressTXFull(address, page, this.network.type)
|
|
58
|
+
const transactions = await Promise.all(
|
|
59
|
+
data.items.map(async (item): Promise<TransactionResponse> => {
|
|
60
|
+
const filteredTransfers = item.notifications.filter(
|
|
61
|
+
item => item.event_name === 'Transfer' && (item.state.value.length === 3 || item.state.value.length === 4)
|
|
62
|
+
)
|
|
63
|
+
const transferPromises = filteredTransfers.map<Promise<TransactionTransferAsset | TransactionTransferNft>>(
|
|
64
|
+
async ({ contract: contractHash, state: { value: properties } }) => {
|
|
65
|
+
const isAsset = properties.length === 3
|
|
66
|
+
|
|
67
|
+
const from = properties[0].value
|
|
68
|
+
const to = properties[1].value
|
|
69
|
+
const convertedFrom = from ? this.convertByteStringToAddress(from) : 'Mint'
|
|
70
|
+
const convertedTo = to ? this.convertByteStringToAddress(to) : 'Burn'
|
|
71
|
+
|
|
72
|
+
if (isAsset) {
|
|
73
|
+
const token = await this.getTokenInfo(contractHash)
|
|
74
|
+
const [, , { value: amount }] = properties
|
|
75
|
+
return {
|
|
76
|
+
amount: u.BigInteger.fromNumber(amount).toDecimal(token.decimals ?? 0),
|
|
77
|
+
from: convertedFrom,
|
|
78
|
+
to: convertedTo,
|
|
79
|
+
contractHash,
|
|
80
|
+
type: 'token',
|
|
81
|
+
token,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
from: convertedFrom,
|
|
87
|
+
to: convertedTo,
|
|
88
|
+
tokenId: properties[3].value,
|
|
89
|
+
contractHash,
|
|
90
|
+
type: 'nft',
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
const transfers = await Promise.all(transferPromises)
|
|
95
|
+
|
|
96
|
+
const notifications = item.notifications.map<TransactionNotifications>(notification => ({
|
|
97
|
+
eventName: notification.event_name,
|
|
98
|
+
state: notification.state as any,
|
|
99
|
+
}))
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
block: item.block,
|
|
103
|
+
time: Number(item.time),
|
|
104
|
+
hash: item.hash,
|
|
105
|
+
fee: u.BigInteger.fromNumber(item.netfee ?? 0)
|
|
106
|
+
.add(u.BigInteger.fromNumber(item.sysfee ?? 0))
|
|
107
|
+
.toDecimal(this.feeToken.decimals),
|
|
108
|
+
transfers,
|
|
109
|
+
notifications,
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
totalCount: data.totalCount,
|
|
116
|
+
transactions,
|
|
117
|
+
limit: 15,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async getContract(contractHash: string): Promise<ContractResponse> {
|
|
122
|
+
try {
|
|
123
|
+
const data = await NeoRest.contract(contractHash, this.network.type)
|
|
124
|
+
return {
|
|
125
|
+
hash: data.hash,
|
|
126
|
+
methods: data.manifest.abi?.methods ?? [],
|
|
127
|
+
name: data.manifest.name,
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
throw new Error(`Contract not found: ${contractHash}`)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async getTokenInfo(tokenHash: string): Promise<Token> {
|
|
135
|
+
const localToken = TOKENS[this.network.type].find(token => token.hash === tokenHash)
|
|
136
|
+
if (localToken) return localToken
|
|
137
|
+
|
|
138
|
+
if (this.tokenCache.has(tokenHash)) {
|
|
139
|
+
return this.tokenCache.get(tokenHash)!
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const { decimals, symbol, name, scripthash } = await NeoRest.asset(tokenHash, this.network.type)
|
|
144
|
+
const token = {
|
|
145
|
+
decimals: Number(decimals),
|
|
146
|
+
symbol,
|
|
147
|
+
name,
|
|
148
|
+
hash: scripthash,
|
|
149
|
+
}
|
|
150
|
+
this.tokenCache.set(tokenHash, token)
|
|
151
|
+
|
|
152
|
+
return token
|
|
153
|
+
} catch {
|
|
154
|
+
throw new Error(`Token not found: ${tokenHash}`)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async getBalance(address: string): Promise<BalanceResponse[]> {
|
|
159
|
+
const response = await NeoRest.balance(address, this.network.type)
|
|
160
|
+
|
|
161
|
+
const promises = response.map<Promise<BalanceResponse | undefined>>(async balance => {
|
|
162
|
+
try {
|
|
163
|
+
const token = await this.getTokenInfo(balance.asset)
|
|
164
|
+
return {
|
|
165
|
+
amount: u.BigInteger.fromNumber(balance.balance).toDecimal(token.decimals),
|
|
166
|
+
token,
|
|
167
|
+
}
|
|
168
|
+
} catch {}
|
|
169
|
+
})
|
|
170
|
+
const balances = await Promise.all(promises)
|
|
171
|
+
const filteredBalances = balances.filter(balance => balance !== undefined) as BalanceResponse[]
|
|
172
|
+
return filteredBalances
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private convertByteStringToAddress(byteString: string): string {
|
|
176
|
+
const account = new wallet.Account(u.reverseHex(u.HexString.fromBase64(byteString).toString()))
|
|
177
|
+
|
|
178
|
+
return account.address
|
|
179
|
+
}
|
|
180
|
+
}
|