@offckb/cli 0.1.0-rc5 → 0.1.0-rc6

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/README.md CHANGED
@@ -23,6 +23,9 @@ Start building on Nervos blockchain, right now, right away!
23
23
  - [Install](#install)
24
24
  - [Usage](#usage)
25
25
  - [Get started](#get-started)
26
+ - [Step 1: Create A Project](#step-1-create-a-project)
27
+ - [Step 2: Start the Devnet](#step-2-start-the-devnet)
28
+ - [Step 3: Access Pre-funded Accounts](#step-3-access-pre-funded-accounts)
26
29
  - [Built-in scripts](#built-in-scripts)
27
30
  - [Accounts](#accounts)
28
31
  - [About Lumos](#about-lumos)
@@ -48,6 +51,8 @@ offckb list-hashes # list built-in scripts hashes, equals `ckb list-hashes`
48
51
 
49
52
  ## Get started
50
53
 
54
+ ### Step 1: Create A Project
55
+
51
56
  ```sh
52
57
  offckb init my-awesome-ckb-dapp
53
58
 
@@ -70,7 +75,9 @@ Server running at http://localhost:1234
70
75
  ✨ Built in 10ms
71
76
  ```
72
77
 
73
- open another terminal and start the devnet:
78
+ ### Step 2: Start the Devnet
79
+
80
+ Open another terminal and run:
74
81
 
75
82
  ```sh
76
83
  offckb node
@@ -83,7 +90,11 @@ CKB-Miner: 2024-03-04 14:35:17.567 +00:00 client INFO ckb_miner::miner Found! #
83
90
  #...
84
91
  ```
85
92
 
86
- open another terminal and check the accounts to use:
93
+ You can leave this terminal open to keep the devnet running, feel free to `ctrl+c` to exit the terminal and stop the local blockchain.
94
+
95
+ ### Step 3: Access Pre-funded Accounts
96
+
97
+ Open another terminal and check the accounts to use:
87
98
 
88
99
  ```sh
89
100
  offckb accounts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@offckb/cli",
3
- "version": "0.1.0-rc5",
3
+ "version": "0.1.0-rc6",
4
4
  "description": "ckb development network for your first try",
5
5
  "author": "Retric Su <retric@cryptape.com>",
6
6
  "license": "MIT",
@@ -1,33 +1,25 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import ReactDOM from 'react-dom';
3
- import { Cell, Script } from '@ckb-lumos/lumos';
4
- import {
5
- capacityOf,
6
- computeLockScriptHashFromPrivateKey,
7
- generateAccountFromPrivateKey,
8
- issueToken,
9
- queryIssuedTokenCells,
10
- readTokenAmount,
11
- } from './lib';
3
+ import { Cell, Script, Transaction, utils } from '@ckb-lumos/lumos';
4
+ import { issueToken, queryIssuedTokenCells, transferTokenToAddress } from './lib';
5
+ import { capacityOf, generateAccountFromPrivateKey, readTokenAmount } from './util';
12
6
 
13
7
  const app = document.getElementById('root');
14
8
  ReactDOM.render(<App />, app);
15
9
 
16
- export function App() {
10
+ function IssuedToken() {
17
11
  const [privKey, setPrivKey] = useState('');
18
- const [fromAddr, setFromAddr] = useState('');
19
- const [fromLock, setFromLock] = useState<Script>();
12
+ const [lockScript, setLockScript] = useState<Script>();
20
13
  const [balance, setBalance] = useState('0');
21
-
22
14
  const [amount, setAmount] = useState('');
23
- const [cells, setCells] = useState<Cell[]>([]);
15
+ const [issuedTokenCell, setIssuedTokenCell] = useState<Cell>();
16
+ const [txHash, setTxHash] = useState<string>();
24
17
 
25
18
  useEffect(() => {
26
19
  const updateFromInfo = async () => {
27
20
  const { lockScript, address } = generateAccountFromPrivateKey(privKey);
28
21
  const capacity = await capacityOf(address);
29
- setFromAddr(address);
30
- setFromLock(lockScript);
22
+ setLockScript(lockScript);
31
23
  setBalance(capacity.toString());
32
24
  };
33
25
 
@@ -37,7 +29,6 @@ export function App() {
37
29
  }, [privKey]);
38
30
 
39
31
  const onInputPrivKey = (e: React.ChangeEvent<HTMLInputElement>) => {
40
- // Regular expression to match a valid private key with "0x" prefix
41
32
  const priv = e.target.value;
42
33
  const privateKeyRegex = /^0x[0-9a-fA-F]{64}$/;
43
34
 
@@ -50,57 +41,172 @@ export function App() {
50
41
  );
51
42
  }
52
43
  };
53
-
54
44
  const enabledIssue = +amount > 0 && +balance > 6100000000;
55
- const enabledCheck = privKey.length > 0;
45
+
56
46
  return (
57
- <div>
58
- <h1>
59
- Issue Custom Token{' '}
60
- <small>
61
- <a href="https://github.com/XuJiandong/rfcs/blob/xudt/rfcs/0052-extensible-udt/0052-extensible-udt.md#xudt-witness">
62
- {'(xUDT specs)'}
63
- </a>
64
- </small>
65
- </h1>
47
+ <>
48
+ <h2>Issue Custom Token</h2>
66
49
  <p></p>
67
50
  <label htmlFor="private-key">Private Key: </label>&nbsp;
68
51
  <input id="private-key" type="text" onChange={onInputPrivKey} />
69
52
  <ul>
70
- <li>CKB Address: {fromAddr}</li>
53
+ <li>Balance(Total Capacity): {(+balance).toLocaleString()}</li>
71
54
  <li>
72
- Current lock script:
73
- <pre>{JSON.stringify(fromLock, null, 2)}</pre>
55
+ Lock Script:
56
+ <pre>{JSON.stringify(lockScript, null, 2)}</pre>
74
57
  </li>
75
-
76
- <li>Total capacity: {(+balance).toLocaleString()}</li>
58
+ <li>Lock Script Hash: {lockScript && utils.computeScriptHash(lockScript)}</li>
77
59
  </ul>
78
60
  <br />
79
- <label htmlFor="amount">Token Amount</label>
61
+ <label htmlFor="amount">Token Amount: </label>
80
62
  &nbsp;
81
- <input id="amount" type="text" onChange={(e) => setAmount(e.target.value)} />
63
+ <input id="amount" type="number" onChange={(e) => setAmount(e.target.value)} />
82
64
  <br />
83
- <button disabled={!enabledIssue} onClick={() => issueToken(privKey, amount).catch(alert)}>
84
- Issue token
65
+ <button
66
+ disabled={!enabledIssue}
67
+ onClick={() =>
68
+ issueToken(privKey, amount)
69
+ .then((result) => {
70
+ setIssuedTokenCell(result.targetOutput);
71
+ setTxHash(result.hash);
72
+ })
73
+ .catch(alert)
74
+ }
75
+ >
76
+ Issue Token
85
77
  </button>
86
- <br />
87
- <br />
88
- <hr />
89
- <p>after issued token, click the below button to check it</p>
90
- <button disabled={!enabledCheck} onClick={() => queryIssuedTokenCells(privKey).then(setCells).catch(alert)}>
91
- Check issued token
78
+ <div>
79
+ {txHash && issuedTokenCell && (
80
+ <>
81
+ <h4>Result</h4>
82
+ <li>Transaction hash: {txHash}</li>
83
+ <li>
84
+ Token xUDT args: {issuedTokenCell.cellOutput.type.args}{' '}
85
+ <strong>
86
+ {
87
+ '(Noticed that the xUDT args works like the unique id for your issued token, Think of it like an ERC20 contract address)'
88
+ }
89
+ </strong>
90
+ </li>
91
+ <li>
92
+ Token cell: <pre>{JSON.stringify(issuedTokenCell, null, 2)}</pre>
93
+ </li>
94
+ </>
95
+ )}
96
+ </div>
97
+ </>
98
+ );
99
+ }
100
+
101
+ function ViewIssuedToken() {
102
+ const [xudtArgs, setXudtArgs] = useState<string>();
103
+ const [cells, setCells] = useState<Cell[]>([]);
104
+ return (
105
+ <>
106
+ <h2>View Custom Token</h2>
107
+ <div>
108
+ <label htmlFor="xudt-args">xDUT args: </label>&nbsp;
109
+ <input id="xudt-args" type="text" onChange={(e) => setXudtArgs(e.target.value)} />
110
+ </div>
111
+
112
+ <button disabled={!xudtArgs} onClick={() => queryIssuedTokenCells(xudtArgs).then(setCells).catch(alert)}>
113
+ Query Issued Token
92
114
  </button>
93
115
  {cells.length > 0 && <h3>Result: all the cells which hosted this issued token</h3>}
94
116
  {cells.map((cell, index) => (
95
117
  <div key={index}>
96
118
  <p>Cell #{index}</p>
97
- <p>token amount: {readTokenAmount(cell.data).toNumber()}</p>
98
- <p>issuer lockScript Hash: {computeLockScriptHashFromPrivateKey(privKey)}</p>
99
- <p>token xudt args: {cell.cellOutput.type.args}</p>
100
- <p>token holder's lockScript args: {cell.cellOutput.lock.args}</p>
119
+ <li>Token amount: {readTokenAmount(cell.data).toNumber()}</li>
120
+ <li>Token xUDT args: {cell.cellOutput.type.args}</li>
121
+ <li>Token holder's lock script args: {cell.cellOutput.lock.args}</li>
101
122
  <hr />
102
123
  </div>
103
124
  ))}
125
+ </>
126
+ );
127
+ }
128
+
129
+ function TransferIssuedToken() {
130
+ const [udtArgs, setUdtArgs] = useState<string>('');
131
+ const [senderPrivkey, setSenderPrivkey] = useState<string>('');
132
+ const [transferAmount, setTransferAmount] = useState('');
133
+ const [receiverAddress, setReceiverAddress] = useState('');
134
+ const [tx, setTx] = useState<Transaction>();
135
+
136
+ const onInputSenderPrivKey = (e: React.ChangeEvent<HTMLInputElement>) => {
137
+ const priv = e.target.value;
138
+ const privateKeyRegex = /^0x[0-9a-fA-F]{64}$/;
139
+ const isValid = privateKeyRegex.test(priv);
140
+ if (isValid) {
141
+ setSenderPrivkey(priv);
142
+ } else {
143
+ alert(
144
+ `Invalid private key: must start with 0x and 32 bytes length. Ensure you're using a valid private key from the offckb accounts list.`,
145
+ );
146
+ }
147
+ };
148
+
149
+ const enabledCheck =
150
+ senderPrivkey.length > 0 && udtArgs.length > 0 && transferAmount.length > 0 && receiverAddress.length > 0;
151
+
152
+ return (
153
+ <>
154
+ <h2>Transfer Custom Token</h2>
155
+ <label htmlFor="sender-private-key">Private Key: </label>&nbsp;
156
+ <input id="sender-private-key" type="text" onChange={onInputSenderPrivKey} />
157
+ <br />
158
+ <label htmlFor="udt">xUDT args: </label>
159
+ &nbsp;
160
+ <input id="udt" type="text" onChange={(e) => setUdtArgs(e.target.value)} />
161
+ <br />
162
+ <label htmlFor="transferAmount">Transfer Token Amount: </label>
163
+ &nbsp;
164
+ <input id="transferAmount" type="number" onChange={(e) => setTransferAmount(e.target.value)} />
165
+ <br />
166
+ <label htmlFor="receiverAddress">Receiver Address: </label>
167
+ &nbsp;
168
+ <input id="receiverAddress" type="text" onChange={(e) => setReceiverAddress(e.target.value)} />
169
+ <br />
170
+ <button
171
+ disabled={!enabledCheck}
172
+ onClick={() =>
173
+ transferTokenToAddress(udtArgs, senderPrivkey, transferAmount, receiverAddress)
174
+ .then((res) => setTx(res.tx))
175
+ .catch(alert)
176
+ }
177
+ >
178
+ Transfer Custom Token
179
+ </button>
180
+ <div>
181
+ {tx && (
182
+ <>
183
+ <h4>Result</h4>
184
+ <li>
185
+ Transaction: <pre>{JSON.stringify(tx, null, 2)}</pre>
186
+ </li>
187
+ </>
188
+ )}
189
+ </div>
190
+ </>
191
+ );
192
+ }
193
+
194
+ export function App() {
195
+ return (
196
+ <div>
197
+ <h1>
198
+ xUDT Scripts Dapp Example &nbsp;
199
+ <small>
200
+ <a href="https://github.com/XuJiandong/rfcs/blob/xudt/rfcs/0052-extensible-udt/0052-extensible-udt.md#xudt-witness">
201
+ {'(see xUDT specs)'}
202
+ </a>
203
+ </small>
204
+ </h1>
205
+ <IssuedToken />
206
+ <hr />
207
+ <ViewIssuedToken />
208
+ <hr />
209
+ <TransferIssuedToken />
104
210
  </div>
105
211
  );
106
212
  }
@@ -1,74 +1,12 @@
1
- import { helpers, Address, Script, hd, config, Cell, commons, BI, utils, CellDep } from '@ckb-lumos/lumos';
2
- import { values, blockchain } from '@ckb-lumos/base';
1
+ import { helpers, hd, config, Cell, commons, BI, utils } from '@ckb-lumos/lumos';
2
+ import { blockchain, HexString } from '@ckb-lumos/base';
3
3
  import { indexer, lumosConfig, rpc } from './ckb';
4
- import { TransactionSkeletonType } from '@ckb-lumos/helpers';
5
4
  import { bytes, number } from '@ckb-lumos/codec';
6
5
  import { xudtWitnessType } from './scheme';
6
+ import { addCellDep, generateAccountFromPrivateKey } from './util';
7
7
 
8
8
  config.initializeConfig(lumosConfig);
9
9
 
10
- type Account = {
11
- lockScript: Script;
12
- address: Address;
13
- pubKey: string;
14
- };
15
- export const generateAccountFromPrivateKey = (privKey: string): Account => {
16
- const pubKey = hd.key.privateToPublic(privKey);
17
- const args = hd.key.publicKeyToBlake160(pubKey);
18
- const template = lumosConfig.SCRIPTS['SECP256K1_BLAKE160']!;
19
- const lockScript = {
20
- codeHash: template.CODE_HASH,
21
- hashType: template.HASH_TYPE,
22
- args: args,
23
- };
24
- const address = helpers.encodeToAddress(lockScript, { config: lumosConfig });
25
- return {
26
- lockScript,
27
- address,
28
- pubKey,
29
- };
30
- };
31
-
32
- export const computeLockScriptHashFromPrivateKey = (privkey: string) => {
33
- const { lockScript } = generateAccountFromPrivateKey(privkey);
34
- return utils.computeScriptHash(lockScript);
35
- };
36
-
37
- export async function capacityOf(address: string): Promise<BI> {
38
- const collector = indexer.collector({
39
- lock: helpers.parseAddress(address, { config: lumosConfig }),
40
- });
41
-
42
- let balance = BI.from(0);
43
- for await (const cell of collector.collect()) {
44
- balance = balance.add(cell.cellOutput.capacity);
45
- }
46
-
47
- return balance;
48
- }
49
-
50
- export function addCellDep(txSkeleton: TransactionSkeletonType, newCellDep: CellDep): TransactionSkeletonType {
51
- const cellDep = txSkeleton.get('cellDeps').find((cellDep) => {
52
- return (
53
- cellDep.depType === newCellDep.depType &&
54
- new values.OutPointValue(cellDep.outPoint, { validate: false }).equals(
55
- new values.OutPointValue(newCellDep.outPoint, { validate: false }),
56
- )
57
- );
58
- });
59
-
60
- if (!cellDep) {
61
- txSkeleton = txSkeleton.update('cellDeps', (cellDeps) => {
62
- return cellDeps.push({
63
- outPoint: newCellDep.outPoint,
64
- depType: newCellDep.depType,
65
- });
66
- });
67
- }
68
-
69
- return txSkeleton;
70
- }
71
-
72
10
  export async function issueToken(privKey: string, amount: string) {
73
11
  const { lockScript } = generateAccountFromPrivateKey(privKey);
74
12
  const xudtDeps = lumosConfig.SCRIPTS.XUDT;
@@ -152,14 +90,11 @@ export async function issueToken(privKey: string, amount: string) {
152
90
 
153
91
  const hash = await rpc.sendTransaction(tx, 'passthrough');
154
92
  console.log('The transaction hash is', hash);
155
- alert(`The transaction hash is ${hash}`);
93
+ return { hash, targetOutput };
156
94
  }
157
95
 
158
- export async function queryIssuedTokenCells(privKey: string) {
159
- const { lockScript } = generateAccountFromPrivateKey(privKey);
96
+ export async function queryIssuedTokenCells(xudtArgs: HexString) {
160
97
  const xudtDeps = lumosConfig.SCRIPTS.XUDT;
161
-
162
- const xudtArgs = utils.computeScriptHash(lockScript) + '00000000';
163
98
  const typeScript = {
164
99
  codeHash: xudtDeps.CODE_HASH,
165
100
  hashType: xudtDeps.HASH_TYPE,
@@ -174,6 +109,144 @@ export async function queryIssuedTokenCells(privKey: string) {
174
109
  return collected;
175
110
  }
176
111
 
177
- export function readTokenAmount(amount: string) {
178
- return number.Uint128LE.unpack(amount);
112
+ export async function transferTokenToAddress(
113
+ udtIssuerArgs: string,
114
+ senderPrivKey: string,
115
+ amount: string,
116
+ receiverAddress: string,
117
+ ) {
118
+ const { lockScript: senderLockScript } = generateAccountFromPrivateKey(senderPrivKey);
119
+
120
+ const receiverLockScript = helpers.parseAddress(receiverAddress);
121
+
122
+ const xudtDeps = lumosConfig.SCRIPTS.XUDT;
123
+ const lockDeps = lumosConfig.SCRIPTS.SECP256K1_BLAKE160;
124
+
125
+ const xudtArgs = udtIssuerArgs;
126
+ const typeScript = {
127
+ codeHash: xudtDeps.CODE_HASH,
128
+ hashType: xudtDeps.HASH_TYPE,
129
+ args: xudtArgs,
130
+ };
131
+
132
+ let txSkeleton = helpers.TransactionSkeleton();
133
+ txSkeleton = addCellDep(txSkeleton, {
134
+ outPoint: {
135
+ txHash: lockDeps.TX_HASH,
136
+ index: lockDeps.INDEX,
137
+ },
138
+ depType: lockDeps.DEP_TYPE,
139
+ });
140
+ txSkeleton = addCellDep(txSkeleton, {
141
+ outPoint: {
142
+ txHash: xudtDeps.TX_HASH,
143
+ index: xudtDeps.INDEX,
144
+ },
145
+ depType: xudtDeps.DEP_TYPE,
146
+ });
147
+
148
+ const targetOutput: Cell = {
149
+ cellOutput: {
150
+ capacity: '0x0',
151
+ lock: receiverLockScript,
152
+ type: typeScript,
153
+ },
154
+ data: bytes.hexify(number.Uint128LE.pack(amount)),
155
+ };
156
+
157
+ const capacity = helpers.minimalCellCapacity(targetOutput);
158
+ targetOutput.cellOutput.capacity = '0x' + capacity.toString(16);
159
+ // additional 0.001 ckb for tx fee
160
+ // the tx fee could calculated by tx size
161
+ // this is just a simple example
162
+ const neededCapacity = BI.from(capacity.toString(10)).add(100000);
163
+ let collectedSum = BI.from(0);
164
+ let collectedAmount = BI.from(0);
165
+ const collected: Cell[] = [];
166
+ const collector = indexer.collector({ lock: senderLockScript, type: typeScript });
167
+ for await (const cell of collector.collect()) {
168
+ collectedSum = collectedSum.add(cell.cellOutput.capacity);
169
+ collectedAmount = collectedAmount.add(number.Uint128LE.unpack(cell.data));
170
+ collected.push(cell);
171
+ if (collectedAmount >= BI.from(amount)) break;
172
+ }
173
+
174
+ let changeOutputTokenAmount = BI.from(0);
175
+ if (collectedAmount.gt(BI.from(amount))) {
176
+ changeOutputTokenAmount = collectedAmount.sub(BI.from(amount));
177
+ }
178
+
179
+ const changeOutput: Cell = {
180
+ cellOutput: {
181
+ capacity: '0x0',
182
+ lock: senderLockScript,
183
+ type: typeScript,
184
+ },
185
+ data: bytes.hexify(number.Uint128LE.pack(changeOutputTokenAmount.toString(10))),
186
+ };
187
+
188
+ const changeOutputNeededCapacity = BI.from(helpers.minimalCellCapacity(changeOutput));
189
+
190
+ const extraNeededCapacity = collectedSum.lt(neededCapacity)
191
+ ? neededCapacity.sub(collectedSum).add(changeOutputNeededCapacity)
192
+ : collectedSum.sub(neededCapacity).add(changeOutputNeededCapacity);
193
+
194
+ if (extraNeededCapacity.gt(0)) {
195
+ let extraCollectedSum = BI.from(0);
196
+ const extraCollectedCells: Cell[] = [];
197
+ const collector = indexer.collector({ lock: senderLockScript, type: 'empty' });
198
+ for await (const cell of collector.collect()) {
199
+ extraCollectedSum = extraCollectedSum.add(cell.cellOutput.capacity);
200
+ extraCollectedCells.push(cell);
201
+ if (extraCollectedSum >= extraNeededCapacity) break;
202
+ }
203
+
204
+ if (extraCollectedSum.lt(extraNeededCapacity)) {
205
+ throw new Error(`Not enough CKB for change, ${extraCollectedSum} < ${extraNeededCapacity}`);
206
+ }
207
+
208
+ txSkeleton = txSkeleton.update('inputs', (inputs) => inputs.push(...extraCollectedCells));
209
+
210
+ const change2Capacity = extraCollectedSum.sub(changeOutputNeededCapacity);
211
+ if (change2Capacity.gt(61000000000)) {
212
+ changeOutput.cellOutput.capacity = changeOutputNeededCapacity.toHexString();
213
+ const changeOutput2: Cell = {
214
+ cellOutput: {
215
+ capacity: change2Capacity.toHexString(),
216
+ lock: senderLockScript,
217
+ },
218
+ data: '0x',
219
+ };
220
+ txSkeleton = txSkeleton.update('outputs', (outputs) => outputs.push(changeOutput2));
221
+ } else {
222
+ changeOutput.cellOutput.capacity = extraCollectedSum.toHexString();
223
+ }
224
+ }
225
+
226
+ txSkeleton = txSkeleton.update('inputs', (inputs) => inputs.push(...collected));
227
+ txSkeleton = txSkeleton.update('outputs', (outputs) => outputs.push(targetOutput, changeOutput));
228
+ /* 65-byte zeros in hex */
229
+ const lockWitness =
230
+ '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
231
+
232
+ const inputTypeWitness = xudtWitnessType.pack({ extension_data: [] });
233
+ const outputTypeWitness = xudtWitnessType.pack({ extension_data: [] });
234
+ const witnessArgs = blockchain.WitnessArgs.pack({
235
+ lock: lockWitness,
236
+ inputType: inputTypeWitness,
237
+ outputType: outputTypeWitness,
238
+ });
239
+ const witness = bytes.hexify(witnessArgs);
240
+ txSkeleton = txSkeleton.update('witnesses', (witnesses) => witnesses.set(0, witness));
241
+
242
+ // signing
243
+ txSkeleton = commons.common.prepareSigningEntries(txSkeleton);
244
+ const message = txSkeleton.get('signingEntries').get(0)?.message;
245
+ const Sig = hd.key.signRecoverable(message!, senderPrivKey);
246
+ const tx = helpers.sealTransaction(txSkeleton, [Sig]);
247
+ console.log('tx: ', tx);
248
+
249
+ const txHash = await rpc.sendTransaction(tx, 'passthrough');
250
+ console.log('The transaction hash is', txHash);
251
+ return { txHash, tx };
179
252
  }
@@ -0,0 +1,66 @@
1
+ import { helpers, Address, Script, hd, BI, CellDep } from '@ckb-lumos/lumos';
2
+ import { values } from '@ckb-lumos/base';
3
+ import { indexer, lumosConfig } from './ckb';
4
+ import { TransactionSkeletonType } from '@ckb-lumos/helpers';
5
+ import { number } from '@ckb-lumos/codec';
6
+
7
+ type Account = {
8
+ lockScript: Script;
9
+ address: Address;
10
+ pubKey: string;
11
+ };
12
+ export const generateAccountFromPrivateKey = (privKey: string): Account => {
13
+ const pubKey = hd.key.privateToPublic(privKey);
14
+ const args = hd.key.publicKeyToBlake160(pubKey);
15
+ const template = lumosConfig.SCRIPTS['SECP256K1_BLAKE160']!;
16
+ const lockScript = {
17
+ codeHash: template.CODE_HASH,
18
+ hashType: template.HASH_TYPE,
19
+ args: args,
20
+ };
21
+ const address = helpers.encodeToAddress(lockScript, { config: lumosConfig });
22
+ return {
23
+ lockScript,
24
+ address,
25
+ pubKey,
26
+ };
27
+ };
28
+
29
+ export async function capacityOf(address: string): Promise<BI> {
30
+ const collector = indexer.collector({
31
+ lock: helpers.parseAddress(address, { config: lumosConfig }),
32
+ });
33
+
34
+ let balance = BI.from(0);
35
+ for await (const cell of collector.collect()) {
36
+ balance = balance.add(cell.cellOutput.capacity);
37
+ }
38
+
39
+ return balance;
40
+ }
41
+
42
+ export function addCellDep(txSkeleton: TransactionSkeletonType, newCellDep: CellDep): TransactionSkeletonType {
43
+ const cellDep = txSkeleton.get('cellDeps').find((cellDep) => {
44
+ return (
45
+ cellDep.depType === newCellDep.depType &&
46
+ new values.OutPointValue(cellDep.outPoint, { validate: false }).equals(
47
+ new values.OutPointValue(newCellDep.outPoint, { validate: false }),
48
+ )
49
+ );
50
+ });
51
+
52
+ if (!cellDep) {
53
+ txSkeleton = txSkeleton.update('cellDeps', (cellDeps) => {
54
+ return cellDeps.push({
55
+ outPoint: newCellDep.outPoint,
56
+ depType: newCellDep.depType,
57
+ });
58
+ });
59
+ }
60
+
61
+ return txSkeleton;
62
+ }
63
+
64
+ export function readTokenAmount(amount: string) {
65
+ return number.Uint128LE.unpack(amount);
66
+ }