-
Notifications
You must be signed in to change notification settings - Fork 72
Expand file tree
/
Copy pathhelper.ts
More file actions
131 lines (113 loc) · 3.64 KB
/
Copy pathhelper.ts
File metadata and controls
131 lines (113 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
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
import {
defaultEmptyWitnessArgs,
updateWitnessArgs,
isScriptValueEquals,
getSporeConfig,
} from "@spore-sdk/core";
import {
hd,
helpers,
RPC,
Address,
Hash,
Script,
HexString,
commons,
} from "@ckb-lumos/lumos";
export interface Wallet {
lock: Script;
address: Address;
signMessage(message: HexString): Hash;
signTransaction(
txSkeleton: helpers.TransactionSkeletonType
): helpers.TransactionSkeletonType;
signAndSendTransaction(
txSkeleton: helpers.TransactionSkeletonType
): Promise<Hash>;
}
/**
* Create a CKB Default Lock (Secp256k1Blake160 Sign-all) Wallet by a private-key and a SporeConfig,
* providing lock/address, and functions to sign message/transaction and send the transaction on-chain.
*/
export function createDefaultLockWallet(privateKey: HexString): Wallet {
const config = getSporeConfig();
// Generate a lock script from the private key
const defaultLock = config.lumos.SCRIPTS.SECP256K1_BLAKE160!;
const lock: Script = {
codeHash: defaultLock.CODE_HASH,
hashType: defaultLock.HASH_TYPE,
args: hd.key.privateKeyToBlake160(privateKey),
};
// Generate address from the lock script
const address = helpers.encodeToAddress(lock, {
config: config.lumos,
});
// Sign for a message
function signMessage(message: HexString): Hash {
return hd.key.signRecoverable(message, privateKey);
}
// Sign prepared signing entries,
// and then fill signatures into Transaction.witnesses
function signTransaction(
txSkeleton: helpers.TransactionSkeletonType
): helpers.TransactionSkeletonType {
const signingEntries = txSkeleton.get("signingEntries");
const signatures = new Map<HexString, Hash>();
const inputs = txSkeleton.get("inputs");
let witnesses = txSkeleton.get("witnesses");
for (let i = 0; i < signingEntries.size; i++) {
const entry = signingEntries.get(i)!;
if (entry.type === "witness_args_lock") {
// Skip if the input's lock does not match to the wallet's lock
const input = inputs.get(entry.index);
if (!input || !isScriptValueEquals(input.cellOutput.lock, lock)) {
continue;
}
// Sign message
if (!signatures.has(entry.message)) {
const sig = signMessage(entry.message);
signatures.set(entry.message, sig);
}
// Update signature to Transaction.witnesses
const signature = signatures.get(entry.message)!;
const witness = witnesses.get(entry.index, defaultEmptyWitnessArgs);
witnesses = witnesses.set(
entry.index,
updateWitnessArgs(witness, "lock", signature)
);
}
}
return txSkeleton.set("witnesses", witnesses);
}
// Sign the transaction and send it via RPC
async function signAndSendTransaction(
txSkeleton: helpers.TransactionSkeletonType
): Promise<Hash> {
// 1. Sign transaction
txSkeleton = commons.common.prepareSigningEntries(txSkeleton, {
config: config.lumos,
});
txSkeleton = signTransaction(txSkeleton);
// 2. Convert TransactionSkeleton to Transaction
const tx = helpers.createTransactionFromSkeleton(txSkeleton);
console.log("tx", tx);
// 3. Send transaction
const rpc = new RPC(config.ckbNodeUrl);
return await rpc.sendTransaction(tx, "passthrough");
}
return {
lock,
address,
signMessage,
signTransaction,
signAndSendTransaction,
};
}
export function hexStringToUint8Array(hexString: string): Uint8Array {
const len = hexString.length;
const buffer = new Uint8Array(len / 2);
for (let i = 0; i < len; i += 2) {
buffer[i / 2] = parseInt(hexString.substr(i, 2), 16);
}
return buffer;
}