@cityofzion/bs-neo3 0.9.2 → 0.10.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 +10 -9
- package/.rush/temp/shrinkwrap-deps.json +154 -73
- package/CHANGELOG.json +38 -1
- package/CHANGELOG.md +17 -2
- package/dist/BSNeo3.d.ts +5 -2
- package/dist/BSNeo3.js +21 -10
- package/dist/GhostMarketNDSNeo3.js +4 -0
- package/dist/LedgerServiceNeo3.d.ts +12 -0
- package/dist/LedgerServiceNeo3.js +93 -0
- package/dist/RpcBDSNeo3.js +6 -2
- package/package.json +9 -9
- package/src/BSNeo3.ts +32 -9
- package/src/GhostMarketNDSNeo3.ts +5 -1
- package/src/LedgerServiceNeo3.ts +107 -0
- package/src/RpcBDSNeo3.ts +4 -2
- package/src/__tests__/BSNeo3.spec.ts +26 -0
- package/src/__tests__/GhostMarketNDSNeo3.spec.ts +5 -0
package/CHANGELOG.json
CHANGED
|
@@ -1,14 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cityofzion/bs-neo3",
|
|
3
3
|
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.10.0",
|
|
6
|
+
"tag": "@cityofzion/bs-neo3_v0.10.0",
|
|
7
|
+
"date": "Thu, 04 Apr 2024 19:52:20 GMT",
|
|
8
|
+
"comments": {
|
|
9
|
+
"minor": [
|
|
10
|
+
{
|
|
11
|
+
"comment": "Add ledger support"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"dependency": [
|
|
15
|
+
{
|
|
16
|
+
"comment": "Updating dependency \"@cityofzion/blockchain-service\" to `0.10.0`"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"version": "0.9.3",
|
|
23
|
+
"tag": "@cityofzion/bs-neo3_v0.9.3",
|
|
24
|
+
"date": "Wed, 28 Feb 2024 17:43:01 GMT",
|
|
25
|
+
"comments": {
|
|
26
|
+
"patch": [
|
|
27
|
+
{
|
|
28
|
+
"comment": "Add creator infomations in nft methods return"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"dependency": [
|
|
32
|
+
{
|
|
33
|
+
"comment": "Updating dependency \"@cityofzion/blockchain-service\" to `0.9.1`"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
4
38
|
{
|
|
5
39
|
"version": "0.9.2",
|
|
6
40
|
"tag": "@cityofzion/bs-neo3_v0.9.2",
|
|
7
|
-
"date": "
|
|
41
|
+
"date": "Tue, 30 Jan 2024 18:26:00 GMT",
|
|
8
42
|
"comments": {
|
|
9
43
|
"patch": [
|
|
10
44
|
{
|
|
11
45
|
"comment": "Fixed bug preventing decryption in dev mode in React Native"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"comment": "Fixed lint errors that were preventing the build"
|
|
12
49
|
}
|
|
13
50
|
]
|
|
14
51
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
# Change Log - @cityofzion/bs-neo3
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Thu, 04 Apr 2024 19:52:20 GMT and should not be manually modified.
|
|
4
|
+
|
|
5
|
+
## 0.10.0
|
|
6
|
+
Thu, 04 Apr 2024 19:52:20 GMT
|
|
7
|
+
|
|
8
|
+
### Minor changes
|
|
9
|
+
|
|
10
|
+
- Add ledger support
|
|
11
|
+
|
|
12
|
+
## 0.9.3
|
|
13
|
+
Wed, 28 Feb 2024 17:43:01 GMT
|
|
14
|
+
|
|
15
|
+
### Patches
|
|
16
|
+
|
|
17
|
+
- Add creator infomations in nft methods return
|
|
4
18
|
|
|
5
19
|
## 0.9.2
|
|
6
|
-
|
|
20
|
+
Tue, 30 Jan 2024 18:26:00 GMT
|
|
7
21
|
|
|
8
22
|
### Patches
|
|
9
23
|
|
|
10
24
|
- Fixed bug preventing decryption in dev mode in React Native
|
|
25
|
+
- Fixed lint errors that were preventing the build
|
|
11
26
|
|
|
12
27
|
## 0.9.1
|
|
13
28
|
Wed, 06 Dec 2023 21:51:30 GMT
|
package/dist/BSNeo3.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { BlockchainDataService, BlockchainService, BSClaimable, Account, ExchangeDataService, BDSClaimable, Token, BSWithNameService, Network, PartialBy, TransferParam, BSCalculableFee, NftDataService, BSWithNft, AccountWithDerivationPath, BSWithExplorerService, ExplorerService } from '@cityofzion/blockchain-service';
|
|
2
|
-
|
|
1
|
+
import { BlockchainDataService, BlockchainService, BSClaimable, Account, ExchangeDataService, BDSClaimable, Token, BSWithNameService, Network, PartialBy, TransferParam, BSCalculableFee, NftDataService, BSWithNft, AccountWithDerivationPath, BSWithExplorerService, ExplorerService, BSWithLedger } from '@cityofzion/blockchain-service';
|
|
2
|
+
import { LedgerServiceNeo3 } from './LedgerServiceNeo3';
|
|
3
|
+
export declare class BSNeo3<BSCustomName extends string = string> implements BlockchainService, BSClaimable, BSWithNameService, BSCalculableFee, BSWithNft, BSWithExplorerService, BSWithLedger {
|
|
3
4
|
readonly blockchainName: BSCustomName;
|
|
4
5
|
readonly feeToken: Token;
|
|
5
6
|
readonly claimToken: Token;
|
|
@@ -7,6 +8,7 @@ export declare class BSNeo3<BSCustomName extends string = string> implements Blo
|
|
|
7
8
|
readonly derivationPath: string;
|
|
8
9
|
blockchainDataService: BlockchainDataService & BDSClaimable;
|
|
9
10
|
nftDataService: NftDataService;
|
|
11
|
+
ledgerService: LedgerServiceNeo3;
|
|
10
12
|
exchangeDataService: ExchangeDataService;
|
|
11
13
|
explorerService: ExplorerService;
|
|
12
14
|
tokens: Token[];
|
|
@@ -18,6 +20,7 @@ export declare class BSNeo3<BSCustomName extends string = string> implements Blo
|
|
|
18
20
|
validateKey(key: string): boolean;
|
|
19
21
|
validateNameServiceDomainFormat(domainName: string): boolean;
|
|
20
22
|
generateAccountFromMnemonic(mnemonic: string[] | string, index: number): AccountWithDerivationPath;
|
|
23
|
+
generateAccountFromPublicKey(publicKey: string): Account;
|
|
21
24
|
generateAccountFromKey(key: string): Account;
|
|
22
25
|
decrypt(encryptedKey: string, password: string): Promise<Account>;
|
|
23
26
|
encrypt(key: string, password: string): Promise<string>;
|
package/dist/BSNeo3.js
CHANGED
|
@@ -11,9 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.BSNeo3 = void 0;
|
|
13
13
|
const neon_js_1 = require("@cityofzion/neon-js");
|
|
14
|
-
const
|
|
15
|
-
const neon_parser_1 = require("@cityofzion/neon-parser");
|
|
16
|
-
const neo3_parser_1 = require("@cityofzion/neo3-parser");
|
|
14
|
+
const neon_dappkit_1 = require("@cityofzion/neon-dappkit");
|
|
17
15
|
const RpcBDSNeo3_1 = require("./RpcBDSNeo3");
|
|
18
16
|
const DoraBDSNeo3_1 = require("./DoraBDSNeo3");
|
|
19
17
|
const constants_1 = require("./constants");
|
|
@@ -21,8 +19,10 @@ const FlamingoEDSNeo3_1 = require("./FlamingoEDSNeo3");
|
|
|
21
19
|
const GhostMarketNDSNeo3_1 = require("./GhostMarketNDSNeo3");
|
|
22
20
|
const bs_asteroid_sdk_1 = require("@cityofzion/bs-asteroid-sdk");
|
|
23
21
|
const DoraESNeo3_1 = require("./DoraESNeo3");
|
|
22
|
+
const LedgerServiceNeo3_1 = require("./LedgerServiceNeo3");
|
|
24
23
|
class BSNeo3 {
|
|
25
24
|
constructor(blockchainName, network) {
|
|
25
|
+
this.ledgerService = new LedgerServiceNeo3_1.LedgerServiceNeo3();
|
|
26
26
|
this.blockchainName = blockchainName;
|
|
27
27
|
this.tokens = constants_1.TOKENS[network.type];
|
|
28
28
|
this.derivationPath = constants_1.DERIVATION_PATH;
|
|
@@ -70,6 +70,16 @@ class BSNeo3 {
|
|
|
70
70
|
const { address } = new neon_js_1.wallet.Account(key);
|
|
71
71
|
return { address, key, type: 'wif', derivationPath: path };
|
|
72
72
|
}
|
|
73
|
+
generateAccountFromPublicKey(publicKey) {
|
|
74
|
+
if (!neon_js_1.wallet.isPublicKey(publicKey))
|
|
75
|
+
throw new Error('Invalid public key');
|
|
76
|
+
const account = new neon_js_1.wallet.Account(publicKey);
|
|
77
|
+
return {
|
|
78
|
+
address: account.address,
|
|
79
|
+
key: account.publicKey,
|
|
80
|
+
type: 'publicKey',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
73
83
|
generateAccountFromKey(key) {
|
|
74
84
|
const type = neon_js_1.wallet.isWIF(key) ? 'wif' : neon_js_1.wallet.isPrivateKey(key) ? 'privateKey' : undefined;
|
|
75
85
|
if (!type)
|
|
@@ -101,24 +111,25 @@ class BSNeo3 {
|
|
|
101
111
|
calculateTransferFee(param) {
|
|
102
112
|
return __awaiter(this, void 0, void 0, function* () {
|
|
103
113
|
const account = new neon_js_1.wallet.Account(param.senderAccount.key);
|
|
104
|
-
const invoker = yield
|
|
114
|
+
const invoker = yield neon_dappkit_1.NeonInvoker.init({
|
|
105
115
|
rpcAddress: this.network.url,
|
|
106
116
|
account,
|
|
107
117
|
});
|
|
108
118
|
const invocations = this.buildTransferInvocation(param, account);
|
|
109
|
-
const {
|
|
119
|
+
const { total } = yield invoker.calculateFee({
|
|
110
120
|
invocations,
|
|
111
121
|
signers: [],
|
|
112
122
|
});
|
|
113
|
-
return
|
|
123
|
+
return total.toString();
|
|
114
124
|
});
|
|
115
125
|
}
|
|
116
126
|
transfer(param) {
|
|
117
127
|
return __awaiter(this, void 0, void 0, function* () {
|
|
118
128
|
const account = new neon_js_1.wallet.Account(param.senderAccount.key);
|
|
119
|
-
const invoker = yield
|
|
129
|
+
const invoker = yield neon_dappkit_1.NeonInvoker.init({
|
|
120
130
|
rpcAddress: this.network.url,
|
|
121
131
|
account,
|
|
132
|
+
signingCallback: param.isLedger ? this.ledgerService.getSigningCallback(param.ledgerTransport) : undefined,
|
|
122
133
|
});
|
|
123
134
|
const invocations = this.buildTransferInvocation(param, account);
|
|
124
135
|
const transactionHash = yield invoker.invokeFunction({
|
|
@@ -141,8 +152,8 @@ class BSNeo3 {
|
|
|
141
152
|
resolveNameServiceDomain(domainName) {
|
|
142
153
|
var _a;
|
|
143
154
|
return __awaiter(this, void 0, void 0, function* () {
|
|
144
|
-
const parser =
|
|
145
|
-
const invoker = yield
|
|
155
|
+
const parser = neon_dappkit_1.NeonParser;
|
|
156
|
+
const invoker = yield neon_dappkit_1.NeonInvoker.init({ rpcAddress: this.network.url });
|
|
146
157
|
const response = yield invoker.testInvoke({
|
|
147
158
|
invocations: [
|
|
148
159
|
{
|
|
@@ -156,7 +167,7 @@ class BSNeo3 {
|
|
|
156
167
|
throw new Error((_a = response.exception) !== null && _a !== void 0 ? _a : 'unrecognized response');
|
|
157
168
|
}
|
|
158
169
|
const parsed = parser.parseRpcResponse(response.stack[0], {
|
|
159
|
-
type:
|
|
170
|
+
type: 'Hash160',
|
|
160
171
|
});
|
|
161
172
|
const address = parser.accountInputToAddress(parsed.replace('0x', ''));
|
|
162
173
|
return address;
|
|
@@ -68,6 +68,10 @@ class GhostMarketNDSNeo3 {
|
|
|
68
68
|
image: this.treatGhostMarketImage(data.metadata.mediaUri),
|
|
69
69
|
isSVG: String(data.metadata.mediaType).includes('svg+xml'),
|
|
70
70
|
name: data.metadata.name,
|
|
71
|
+
creator: {
|
|
72
|
+
address: data.creator.address,
|
|
73
|
+
name: data.creator.offchainName,
|
|
74
|
+
},
|
|
71
75
|
};
|
|
72
76
|
return nftResponse;
|
|
73
77
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LedgerService } from '@cityofzion/blockchain-service';
|
|
2
|
+
import Transport from '@ledgerhq/hw-transport';
|
|
3
|
+
import { api } from '@cityofzion/neon-js';
|
|
4
|
+
export declare class LedgerServiceNeo3 implements LedgerService {
|
|
5
|
+
getAddress(transport: Transport): Promise<string>;
|
|
6
|
+
getSigningCallback(transport: Transport): api.SigningFunction;
|
|
7
|
+
getSignature(transport: Transport, serializedTransaction: string, networkMagic: number, addressIndex?: number): Promise<string>;
|
|
8
|
+
getPublicKey(transport: Transport, addressIndex?: number): Promise<string>;
|
|
9
|
+
private toBip44Buffer;
|
|
10
|
+
private to8BitHex;
|
|
11
|
+
private derSignatureToHex;
|
|
12
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.LedgerServiceNeo3 = void 0;
|
|
13
|
+
const neon_js_1 = require("@cityofzion/neon-js");
|
|
14
|
+
const neon_dappkit_1 = require("@cityofzion/neon-dappkit");
|
|
15
|
+
class LedgerServiceNeo3 {
|
|
16
|
+
getAddress(transport) {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
const publicKey = yield this.getPublicKey(transport);
|
|
19
|
+
const { address } = new neon_js_1.wallet.Account(publicKey);
|
|
20
|
+
return address;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
getSigningCallback(transport) {
|
|
24
|
+
return (transaction, { witnessIndex, network }) => __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const publicKey = yield this.getPublicKey(transport);
|
|
26
|
+
const account = new neon_js_1.wallet.Account(publicKey);
|
|
27
|
+
const witnessScriptHash = neon_js_1.wallet.getScriptHashFromVerificationScript(transaction.witnesses[witnessIndex].verificationScript.toString());
|
|
28
|
+
if (account.scriptHash !== witnessScriptHash) {
|
|
29
|
+
throw new Error('Invalid witness script hash');
|
|
30
|
+
}
|
|
31
|
+
const signature = yield this.getSignature(transport, transaction.serialize(false), network, witnessIndex);
|
|
32
|
+
return signature;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
getSignature(transport, serializedTransaction, networkMagic, addressIndex = 0) {
|
|
36
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
const bip44Buffer = this.toBip44Buffer(addressIndex);
|
|
38
|
+
yield transport.send(0x80, 0x02, 0, 0x80, bip44Buffer, [0x9000]);
|
|
39
|
+
yield transport.send(0x80, 0x02, 1, 0x80, Buffer.from(neon_dappkit_1.NeonParser.numToHex(networkMagic, 4, true), 'hex'), [0x9000]);
|
|
40
|
+
const chunks = serializedTransaction.match(/.{1,510}/g) || [];
|
|
41
|
+
for (let i = 0; i < chunks.length - 1; i++) {
|
|
42
|
+
yield transport.send(0x80, 0x02, 2 + i, 0x80, Buffer.from(chunks[i], 'hex'), [0x9000]);
|
|
43
|
+
}
|
|
44
|
+
const response = yield transport.send(0x80, 0x02, 2 + chunks.length, 0x00, Buffer.from(chunks[chunks.length - 1], 'hex'), [0x9000]);
|
|
45
|
+
if (response.length <= 2) {
|
|
46
|
+
throw new Error(`No more data but Ledger did not return signature!`);
|
|
47
|
+
}
|
|
48
|
+
return this.derSignatureToHex(response.toString('hex'));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
getPublicKey(transport, addressIndex = 0) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
const bip44Buffer = this.toBip44Buffer(addressIndex);
|
|
54
|
+
const result = yield transport.send(0x80, 0x04, 0x00, 0x00, bip44Buffer, [0x9000]);
|
|
55
|
+
const publicKey = result.toString('hex').substring(0, 130);
|
|
56
|
+
return publicKey;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
toBip44Buffer(addressIndex = 0, changeIndex = 0, accountIndex = 0) {
|
|
60
|
+
const accountHex = this.to8BitHex(accountIndex + 0x80000000);
|
|
61
|
+
const changeHex = this.to8BitHex(changeIndex);
|
|
62
|
+
const addressHex = this.to8BitHex(addressIndex);
|
|
63
|
+
return Buffer.from('8000002C' + '80000378' + accountHex + changeHex + addressHex, 'hex');
|
|
64
|
+
}
|
|
65
|
+
to8BitHex(num) {
|
|
66
|
+
const hex = num.toString(16);
|
|
67
|
+
return '0'.repeat(8 - hex.length) + hex;
|
|
68
|
+
}
|
|
69
|
+
derSignatureToHex(response) {
|
|
70
|
+
const ss = new neon_js_1.u.StringStream(response);
|
|
71
|
+
// The first byte is format. It is usually 0x30 (SEQ) or 0x31 (SET)
|
|
72
|
+
// The second byte represents the total length of the DER module.
|
|
73
|
+
ss.read(2);
|
|
74
|
+
// Now we read each field off
|
|
75
|
+
// Each field is encoded with a type byte, length byte followed by the data itself
|
|
76
|
+
ss.read(1); // Read and drop the type
|
|
77
|
+
const r = ss.readVarBytes();
|
|
78
|
+
ss.read(1);
|
|
79
|
+
const s = ss.readVarBytes();
|
|
80
|
+
// We will need to ensure both integers are 32 bytes long
|
|
81
|
+
const integers = [r, s].map(i => {
|
|
82
|
+
if (i.length < 64) {
|
|
83
|
+
i = '0'.repeat(i.length - 64) + i;
|
|
84
|
+
}
|
|
85
|
+
if (i.length > 64) {
|
|
86
|
+
i = i.substr(-64);
|
|
87
|
+
}
|
|
88
|
+
return i;
|
|
89
|
+
});
|
|
90
|
+
return integers.join('');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.LedgerServiceNeo3 = LedgerServiceNeo3;
|
package/dist/RpcBDSNeo3.js
CHANGED
|
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.RPCBDSNeo3 = void 0;
|
|
13
13
|
const neon_core_1 = require("@cityofzion/neon-core");
|
|
14
|
-
const
|
|
14
|
+
const neon_dappkit_1 = require("@cityofzion/neon-dappkit");
|
|
15
15
|
const constants_1 = require("./constants");
|
|
16
16
|
class RPCBDSNeo3 {
|
|
17
17
|
constructor(network, feeToken, claimToken) {
|
|
@@ -82,7 +82,7 @@ class RPCBDSNeo3 {
|
|
|
82
82
|
try {
|
|
83
83
|
const rpcClient = new neon_core_1.rpc.RPCClient(this.network.url);
|
|
84
84
|
const contractState = yield rpcClient.getContractState(tokenHash);
|
|
85
|
-
const invoker = yield
|
|
85
|
+
const invoker = yield neon_dappkit_1.NeonInvoker.init({
|
|
86
86
|
rpcAddress: this.network.url,
|
|
87
87
|
});
|
|
88
88
|
const response = yield invoker.testInvoke({
|
|
@@ -95,6 +95,10 @@ class RPCBDSNeo3 {
|
|
|
95
95
|
{ scriptHash: tokenHash, operation: 'symbol', args: [] },
|
|
96
96
|
],
|
|
97
97
|
});
|
|
98
|
+
if (!neon_dappkit_1.TypeChecker.isStackTypeInteger(response.stack[0]))
|
|
99
|
+
throw new Error('Invalid decimals');
|
|
100
|
+
if (!neon_dappkit_1.TypeChecker.isStackTypeByteString(response.stack[1]))
|
|
101
|
+
throw new Error('Invalid symbol');
|
|
98
102
|
const decimals = Number(response.stack[0].value);
|
|
99
103
|
const symbol = neon_core_1.u.base642utf8(response.stack[1].value);
|
|
100
104
|
const token = {
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cityofzion/bs-neo3",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"repository": "https://github.com/CityOfZion/blockchain-services",
|
|
7
7
|
"author": "Coz",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@cityofzion/neon-js": "5.
|
|
11
|
-
"@cityofzion/neon-core": "5.
|
|
12
|
-
"@cityofzion/neon-invoker": "1.6.0",
|
|
13
|
-
"@cityofzion/neo3-invoker": "1.6.0",
|
|
14
|
-
"@cityofzion/neon-parser": "1.6.2",
|
|
15
|
-
"@cityofzion/neo3-parser": "1.6.0",
|
|
10
|
+
"@cityofzion/neon-js": "5.5.1",
|
|
11
|
+
"@cityofzion/neon-core": "5.5.1",
|
|
16
12
|
"@cityofzion/dora-ts": "0.0.11",
|
|
17
13
|
"axios": "1.5.1",
|
|
18
14
|
"query-string": "7.1.3",
|
|
19
|
-
"@
|
|
20
|
-
"@cityofzion/
|
|
15
|
+
"@ledgerhq/hw-transport": "~6.30.5",
|
|
16
|
+
"@cityofzion/neon-dappkit": "0.4.1",
|
|
17
|
+
"@cityofzion/neon-dappkit-types": "~0.3.1",
|
|
18
|
+
"@ledgerhq/hw-transport-node-hid": "~6.28.5",
|
|
19
|
+
"@cityofzion/bs-asteroid-sdk": "0.7.3",
|
|
20
|
+
"@cityofzion/blockchain-service": "0.10.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/jest": "29.5.3",
|
package/src/BSNeo3.ts
CHANGED
|
@@ -16,14 +16,11 @@ import {
|
|
|
16
16
|
AccountWithDerivationPath,
|
|
17
17
|
BSWithExplorerService,
|
|
18
18
|
ExplorerService,
|
|
19
|
+
BSWithLedger,
|
|
19
20
|
} from '@cityofzion/blockchain-service'
|
|
20
21
|
import { api, u, wallet } from '@cityofzion/neon-js'
|
|
21
22
|
import Neon from '@cityofzion/neon-core'
|
|
22
|
-
import { NeonInvoker } from '@cityofzion/neon-
|
|
23
|
-
import { NeonParser } from '@cityofzion/neon-parser'
|
|
24
|
-
import { ABI_TYPES } from '@cityofzion/neo3-parser'
|
|
25
|
-
import { ContractInvocation } from '@cityofzion/neo3-invoker'
|
|
26
|
-
|
|
23
|
+
import { NeonInvoker, NeonParser } from '@cityofzion/neon-dappkit'
|
|
27
24
|
import { RPCBDSNeo3 } from './RpcBDSNeo3'
|
|
28
25
|
import { DoraBDSNeo3 } from './DoraBDSNeo3'
|
|
29
26
|
import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NEO_NS_HASH, TOKENS } from './constants'
|
|
@@ -31,9 +28,19 @@ import { FlamingoEDSNeo3 } from './FlamingoEDSNeo3'
|
|
|
31
28
|
import { GhostMarketNDSNeo3 } from './GhostMarketNDSNeo3'
|
|
32
29
|
import { keychain } from '@cityofzion/bs-asteroid-sdk'
|
|
33
30
|
import { DoraESNeo3 } from './DoraESNeo3'
|
|
31
|
+
import { ContractInvocation } from '@cityofzion/neon-dappkit-types'
|
|
32
|
+
import { LedgerServiceNeo3 } from './LedgerServiceNeo3'
|
|
34
33
|
|
|
35
34
|
export class BSNeo3<BSCustomName extends string = string>
|
|
36
|
-
implements
|
|
35
|
+
implements
|
|
36
|
+
BlockchainService,
|
|
37
|
+
BSClaimable,
|
|
38
|
+
BSWithNameService,
|
|
39
|
+
BSCalculableFee,
|
|
40
|
+
BSWithNft,
|
|
41
|
+
BSWithExplorerService,
|
|
42
|
+
BSWithLedger
|
|
43
|
+
{
|
|
37
44
|
readonly blockchainName: BSCustomName
|
|
38
45
|
readonly feeToken: Token
|
|
39
46
|
readonly claimToken: Token
|
|
@@ -42,6 +49,7 @@ export class BSNeo3<BSCustomName extends string = string>
|
|
|
42
49
|
|
|
43
50
|
blockchainDataService!: BlockchainDataService & BDSClaimable
|
|
44
51
|
nftDataService!: NftDataService
|
|
52
|
+
ledgerService: LedgerServiceNeo3 = new LedgerServiceNeo3()
|
|
45
53
|
exchangeDataService!: ExchangeDataService
|
|
46
54
|
explorerService!: ExplorerService
|
|
47
55
|
tokens: Token[]
|
|
@@ -102,6 +110,18 @@ export class BSNeo3<BSCustomName extends string = string>
|
|
|
102
110
|
return { address, key, type: 'wif', derivationPath: path }
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
generateAccountFromPublicKey(publicKey: string): Account {
|
|
114
|
+
if (!wallet.isPublicKey(publicKey)) throw new Error('Invalid public key')
|
|
115
|
+
|
|
116
|
+
const account = new wallet.Account(publicKey)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
address: account.address,
|
|
120
|
+
key: account.publicKey,
|
|
121
|
+
type: 'publicKey',
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
105
125
|
generateAccountFromKey(key: string): Account {
|
|
106
126
|
const type = wallet.isWIF(key) ? 'wif' : wallet.isPrivateKey(key) ? 'privateKey' : undefined
|
|
107
127
|
if (!type) throw new Error('Invalid key')
|
|
@@ -135,6 +155,7 @@ export class BSNeo3<BSCustomName extends string = string>
|
|
|
135
155
|
|
|
136
156
|
async calculateTransferFee(param: TransferParam): Promise<string> {
|
|
137
157
|
const account = new wallet.Account(param.senderAccount.key)
|
|
158
|
+
|
|
138
159
|
const invoker = await NeonInvoker.init({
|
|
139
160
|
rpcAddress: this.network.url,
|
|
140
161
|
account,
|
|
@@ -142,19 +163,21 @@ export class BSNeo3<BSCustomName extends string = string>
|
|
|
142
163
|
|
|
143
164
|
const invocations = this.buildTransferInvocation(param, account)
|
|
144
165
|
|
|
145
|
-
const {
|
|
166
|
+
const { total } = await invoker.calculateFee({
|
|
146
167
|
invocations,
|
|
147
168
|
signers: [],
|
|
148
169
|
})
|
|
149
170
|
|
|
150
|
-
return
|
|
171
|
+
return total.toString()
|
|
151
172
|
}
|
|
152
173
|
|
|
153
174
|
async transfer(param: TransferParam): Promise<string> {
|
|
154
175
|
const account = new wallet.Account(param.senderAccount.key)
|
|
176
|
+
|
|
155
177
|
const invoker = await NeonInvoker.init({
|
|
156
178
|
rpcAddress: this.network.url,
|
|
157
179
|
account,
|
|
180
|
+
signingCallback: param.isLedger ? this.ledgerService.getSigningCallback(param.ledgerTransport) : undefined,
|
|
158
181
|
})
|
|
159
182
|
|
|
160
183
|
const invocations = this.buildTransferInvocation(param, account)
|
|
@@ -196,7 +219,7 @@ export class BSNeo3<BSCustomName extends string = string>
|
|
|
196
219
|
}
|
|
197
220
|
|
|
198
221
|
const parsed = parser.parseRpcResponse(response.stack[0] as any, {
|
|
199
|
-
type:
|
|
222
|
+
type: 'Hash160',
|
|
200
223
|
})
|
|
201
224
|
const address = parser.accountInputToAddress(parsed.replace('0x', ''))
|
|
202
225
|
return address
|
|
@@ -19,7 +19,7 @@ type GhostMarketNFT = {
|
|
|
19
19
|
symbol: string
|
|
20
20
|
}
|
|
21
21
|
creator: {
|
|
22
|
-
address
|
|
22
|
+
address: string
|
|
23
23
|
offchainName?: string
|
|
24
24
|
}
|
|
25
25
|
apiUrl?: string
|
|
@@ -110,6 +110,10 @@ export class GhostMarketNDSNeo3 implements NftDataService {
|
|
|
110
110
|
image: this.treatGhostMarketImage(data.metadata.mediaUri),
|
|
111
111
|
isSVG: String(data.metadata.mediaType).includes('svg+xml'),
|
|
112
112
|
name: data.metadata.name,
|
|
113
|
+
creator: {
|
|
114
|
+
address: data.creator.address,
|
|
115
|
+
name: data.creator.offchainName,
|
|
116
|
+
},
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
return nftResponse
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { LedgerService } from '@cityofzion/blockchain-service'
|
|
2
|
+
import Transport from '@ledgerhq/hw-transport'
|
|
3
|
+
import { wallet, api, u } from '@cityofzion/neon-js'
|
|
4
|
+
import { NeonParser } from '@cityofzion/neon-dappkit'
|
|
5
|
+
|
|
6
|
+
export class LedgerServiceNeo3 implements LedgerService {
|
|
7
|
+
async getAddress(transport: Transport): Promise<string> {
|
|
8
|
+
const publicKey = await this.getPublicKey(transport)
|
|
9
|
+
const { address } = new wallet.Account(publicKey)
|
|
10
|
+
|
|
11
|
+
return address
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getSigningCallback(transport: Transport): api.SigningFunction {
|
|
15
|
+
return async (transaction, { witnessIndex, network }) => {
|
|
16
|
+
const publicKey = await this.getPublicKey(transport)
|
|
17
|
+
const account = new wallet.Account(publicKey)
|
|
18
|
+
|
|
19
|
+
const witnessScriptHash = wallet.getScriptHashFromVerificationScript(
|
|
20
|
+
transaction.witnesses[witnessIndex].verificationScript.toString()
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if (account.scriptHash !== witnessScriptHash) {
|
|
24
|
+
throw new Error('Invalid witness script hash')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const signature = await this.getSignature(transport, transaction.serialize(false), network, witnessIndex)
|
|
28
|
+
|
|
29
|
+
return signature
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async getSignature(transport: Transport, serializedTransaction: string, networkMagic: number, addressIndex = 0) {
|
|
34
|
+
const bip44Buffer = this.toBip44Buffer(addressIndex)
|
|
35
|
+
await transport.send(0x80, 0x02, 0, 0x80, bip44Buffer, [0x9000])
|
|
36
|
+
await transport.send(0x80, 0x02, 1, 0x80, Buffer.from(NeonParser.numToHex(networkMagic, 4, true), 'hex'), [0x9000])
|
|
37
|
+
|
|
38
|
+
const chunks = serializedTransaction.match(/.{1,510}/g) || []
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < chunks.length - 1; i++) {
|
|
41
|
+
await transport.send(0x80, 0x02, 2 + i, 0x80, Buffer.from(chunks[i], 'hex'), [0x9000])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const response = await transport.send(
|
|
45
|
+
0x80,
|
|
46
|
+
0x02,
|
|
47
|
+
2 + chunks.length,
|
|
48
|
+
0x00,
|
|
49
|
+
Buffer.from(chunks[chunks.length - 1], 'hex'),
|
|
50
|
+
[0x9000]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if (response.length <= 2) {
|
|
54
|
+
throw new Error(`No more data but Ledger did not return signature!`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this.derSignatureToHex(response.toString('hex'))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getPublicKey(transport: Transport, addressIndex = 0): Promise<string> {
|
|
61
|
+
const bip44Buffer = this.toBip44Buffer(addressIndex)
|
|
62
|
+
|
|
63
|
+
const result = await transport.send(0x80, 0x04, 0x00, 0x00, bip44Buffer, [0x9000])
|
|
64
|
+
const publicKey = result.toString('hex').substring(0, 130)
|
|
65
|
+
|
|
66
|
+
return publicKey
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private toBip44Buffer(addressIndex = 0, changeIndex = 0, accountIndex = 0) {
|
|
70
|
+
const accountHex = this.to8BitHex(accountIndex + 0x80000000)
|
|
71
|
+
const changeHex = this.to8BitHex(changeIndex)
|
|
72
|
+
const addressHex = this.to8BitHex(addressIndex)
|
|
73
|
+
|
|
74
|
+
return Buffer.from('8000002C' + '80000378' + accountHex + changeHex + addressHex, 'hex')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private to8BitHex(num: number): string {
|
|
78
|
+
const hex = num.toString(16)
|
|
79
|
+
return '0'.repeat(8 - hex.length) + hex
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private derSignatureToHex(response: string): string {
|
|
83
|
+
const ss = new u.StringStream(response)
|
|
84
|
+
// The first byte is format. It is usually 0x30 (SEQ) or 0x31 (SET)
|
|
85
|
+
// The second byte represents the total length of the DER module.
|
|
86
|
+
ss.read(2)
|
|
87
|
+
// Now we read each field off
|
|
88
|
+
// Each field is encoded with a type byte, length byte followed by the data itself
|
|
89
|
+
ss.read(1) // Read and drop the type
|
|
90
|
+
const r = ss.readVarBytes()
|
|
91
|
+
ss.read(1)
|
|
92
|
+
const s = ss.readVarBytes()
|
|
93
|
+
|
|
94
|
+
// We will need to ensure both integers are 32 bytes long
|
|
95
|
+
const integers = [r, s].map(i => {
|
|
96
|
+
if (i.length < 64) {
|
|
97
|
+
i = '0'.repeat(i.length - 64) + i
|
|
98
|
+
}
|
|
99
|
+
if (i.length > 64) {
|
|
100
|
+
i = i.substr(-64)
|
|
101
|
+
}
|
|
102
|
+
return i
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
return integers.join('')
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/RpcBDSNeo3.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
TransactionsByAddressResponse,
|
|
13
13
|
} from '@cityofzion/blockchain-service'
|
|
14
14
|
import { rpc, u } from '@cityofzion/neon-core'
|
|
15
|
-
import { NeonInvoker } from '@cityofzion/neon-
|
|
15
|
+
import { NeonInvoker, TypeChecker } from '@cityofzion/neon-dappkit'
|
|
16
16
|
import { TOKENS } from './constants'
|
|
17
17
|
|
|
18
18
|
export class RPCBDSNeo3 implements BlockchainDataService, BDSClaimable {
|
|
@@ -102,8 +102,10 @@ export class RPCBDSNeo3 implements BlockchainDataService, BDSClaimable {
|
|
|
102
102
|
],
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
+
if (!TypeChecker.isStackTypeInteger(response.stack[0])) throw new Error('Invalid decimals')
|
|
106
|
+
if (!TypeChecker.isStackTypeByteString(response.stack[1])) throw new Error('Invalid symbol')
|
|
105
107
|
const decimals = Number(response.stack[0].value)
|
|
106
|
-
const symbol = u.base642utf8(response.stack[1].value
|
|
108
|
+
const symbol = u.base642utf8(response.stack[1].value)
|
|
107
109
|
const token = {
|
|
108
110
|
name: contractState.manifest.name,
|
|
109
111
|
symbol,
|