@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/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": "Mon, 29 Jan 2024 16:09:49 GMT",
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 Mon, 29 Jan 2024 16:09:49 GMT and should not be manually modified.
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
- Mon, 29 Jan 2024 16:09:49 GMT
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
- export declare class BSNeo3<BSCustomName extends string = string> implements BlockchainService, BSClaimable, BSWithNameService, BSCalculableFee, BSWithNft, BSWithExplorerService {
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 neon_invoker_1 = require("@cityofzion/neon-invoker");
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 neon_invoker_1.NeonInvoker.init({
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 { networkFee, systemFee } = yield invoker.calculateFee({
119
+ const { total } = yield invoker.calculateFee({
110
120
  invocations,
111
121
  signers: [],
112
122
  });
113
- return networkFee.add(systemFee).toDecimal(this.feeToken.decimals);
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 neon_invoker_1.NeonInvoker.init({
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 = neon_parser_1.NeonParser;
145
- const invoker = yield neon_invoker_1.NeonInvoker.init({ rpcAddress: this.network.url });
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: neo3_parser_1.ABI_TYPES.HASH160.name,
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;
@@ -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 neon_invoker_1 = require("@cityofzion/neon-invoker");
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 neon_invoker_1.NeonInvoker.init({
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.9.2",
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.3.0",
11
- "@cityofzion/neon-core": "5.3.0",
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
- "@cityofzion/blockchain-service": "0.9.0",
20
- "@cityofzion/bs-asteroid-sdk": "0.7.3"
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-invoker'
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 BlockchainService, BSClaimable, BSWithNameService, BSCalculableFee, BSWithNft, BSWithExplorerService {
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 { networkFee, systemFee } = await invoker.calculateFee({
166
+ const { total } = await invoker.calculateFee({
146
167
  invocations,
147
168
  signers: [],
148
169
  })
149
170
 
150
- return networkFee.add(systemFee).toDecimal(this.feeToken.decimals)
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: ABI_TYPES.HASH160.name,
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?: string
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-invoker'
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 as string)
108
+ const symbol = u.base642utf8(response.stack[1].value)
107
109
  const token = {
108
110
  name: contractState.manifest.name,
109
111
  symbol,