Skip to content

Commit

Permalink
feat: enable es module
Browse files Browse the repository at this point in the history
  • Loading branch information
Keith-CY committed Jul 16, 2024
1 parent ee11a2d commit be3e757
Show file tree
Hide file tree
Showing 44 changed files with 314 additions and 278 deletions.
1 change: 1 addition & 0 deletions packages/ckb-sdk-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"license": "MIT",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"type": "module",
"directories": {
"lib": "lib",
"lib-esm": "lib-esm",
Expand Down
4 changes: 1 addition & 3 deletions packages/ckb-sdk-core/src/generateRawTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { scriptToHash, JSBI } from '@nervosnetwork/ckb-sdk-utils'
import { EMPTY_WITNESS_ARGS } from '@nervosnetwork/ckb-sdk-utils/lib/const'
import { assertToBeHexStringOrBigint } from '@nervosnetwork/ckb-sdk-utils/lib/validators'
import { scriptToHash, JSBI, EMPTY_WITNESS_ARGS, assertToBeHexStringOrBigint } from '@nervosnetwork/ckb-sdk-utils'

const EMPTY_DATA = '0x'
const MIN_CELL_CAPACITY = `0x${(61_00_000_000).toString(16)}`
Expand Down
95 changes: 46 additions & 49 deletions packages/ckb-sdk-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/// <reference types="../types/global" />

import RPC from '@nervosnetwork/ckb-sdk-rpc'
import { ParameterRequiredException } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions'
import { ParameterRequiredException } from '@nervosnetwork/ckb-sdk-utils'
import * as utils from '@nervosnetwork/ckb-sdk-utils'

import generateRawTransaction from './generateRawTransaction'
import generateRawTransaction from './generateRawTransaction.js'

import loadCellsFromIndexer from './loadCellsFromIndexer'
import signWitnesses, { isMap } from './signWitnesses'
import { filterCellsByInputs } from './utils'
import loadCellsFromIndexer from './loadCellsFromIndexer.js'
import signWitnesses, { isMap } from './signWitnesses.js'
import { filterCellsByInputs } from './utils.js'

type Key = string
type Address = string
Expand Down Expand Up @@ -107,28 +107,30 @@ class CKB {

public signWitnesses = signWitnesses

public signTransaction = (key: Key | Map<LockHash, Key>) => (
transaction: CKBComponents.RawTransactionToSign,
cells: Array<{ outPoint: CKBComponents.OutPoint; lock: CKBComponents.Script }> = [],
) => {
if (!key) throw new ParameterRequiredException('Private key or address object')
this.#validateTransactionToSign(transaction)

const transactionHash = this.utils.rawTransactionToHash(transaction)
const inputCells = isMap(key) ? filterCellsByInputs(cells, transaction.inputs) : undefined

const signedWitnesses = this.signWitnesses(key)({
transactionHash,
witnesses: transaction.witnesses,
inputCells,
})
return {
...transaction,
witnesses: signedWitnesses.map(witness =>
typeof witness === 'string' ? witness : this.utils.serializeWitnessArgs(witness),
),
public signTransaction =
(key: Key | Map<LockHash, Key>) =>
(
transaction: CKBComponents.RawTransactionToSign,
cells: Array<{ outPoint: CKBComponents.OutPoint; lock: CKBComponents.Script }> = [],
) => {
if (!key) throw new ParameterRequiredException('Private key or address object')
this.#validateTransactionToSign(transaction)

const transactionHash = this.utils.rawTransactionToHash(transaction)
const inputCells = isMap(key) ? filterCellsByInputs(cells, transaction.inputs) : undefined

const signedWitnesses = this.signWitnesses(key)({
transactionHash,
witnesses: transaction.witnesses,
inputCells,
})
return {
...transaction,
witnesses: signedWitnesses.map(witness =>
typeof witness === 'string' ? witness : this.utils.serializeWitnessArgs(witness),
),
}
}
}

/**
* @description Generate a raw transaction object to sign
Expand All @@ -140,38 +142,38 @@ class CKB {
* fromAddress: Address, specify the address of inputs
* toAddress: Address, specify the address included in outputs
* capacity: Capacity, specify the value to transfer in this tx
*
*
* cells?: Array<RawTransactionParams.Cell>, provide
* live cells to generate input cells in this tx
*
*
* fee?: Fee, specify the fee or fee reconciler
* along with this tx, fee reconciler allows
* fee calculation on the fly
*
*
* safeMode: boolean, specify whether to skip cell
* containing data or type script or not,
* default to be true
*
*
* deps: DepCellInfo | Array<DepCellInfo>
* specify deps included in this tx, filling
* in the `cellDeps` field of a raw tx
*
*
* capacityThreshold?: Capacity, specify the minimal capacity of
* each outputs, default to be 6_100_000_000
* shannon(61 CKB) for a bare cell
*
*
* changeThreshold?: Capacity, specify the minimal capacity of
* the change cell, default to be 6_100_000_000
* shannon(61 CKB) for a bare cell, useful on
* sending a tx without change by setting it 0
*
*
* changeLockScript?: CKBComponents.Script, specify the change
* receiver of this tx, default to be the owner
* of the first input
*
*
* witnesses?: Array<CKBComponents.WitnessArgs | CKBComponents.Witness>
* specify the witness list of this tx
*
*
* outputsData?: Array<string>, specify the output data list
* of this tx
* }
Expand All @@ -180,19 +182,19 @@ class CKB {
* ```
* {
* fromAddresses: Address[], specify the address of inputs
*
* receivePairs: Array<{
*
* receivePairs: Array<{
* address: Address;
* capacity: Capacity;
* type?: CKBComponents.Script | null
* }>
* specify address, capacity and type lock
* specify address, capacity and type lock
* of outputs
*
*
* cells: Map<LockHash, RawTransactionParams.Cell[]>
* provide live cells to generate input cells
* in this tx
*
*
* fee?: same as that in 1-1 tx
* safeMode: same as that in 1-1 tx
* deps: same as that in 1-1 tx
Expand Down Expand Up @@ -421,8 +423,8 @@ class CKB {
}

public calculateDaoMaximumWithdraw = async (
depositOutPoint: CKBComponents.OutPoint,
withdraw: CKBComponents.Hash | CKBComponents.OutPoint
depositOutPoint: CKBComponents.OutPoint,
withdraw: CKBComponents.Hash | CKBComponents.OutPoint,
): Promise<string> => {
let tx = await this.rpc.getTransaction(depositOutPoint.txHash)
if (tx.txStatus.status !== 'committed') throw new Error('Transaction is not committed yet')
Expand All @@ -441,14 +443,9 @@ class CKB {
}
const [depositHeader, withDrawHeader] = await Promise.all([
this.rpc.getHeader(depositBlockHash),
this.rpc.getHeader(withdrawBlockHash)
this.rpc.getHeader(withdrawBlockHash),
])
return utils.calculateMaximumWithdraw(
celloutput,
celloutputData,
depositHeader.dao,
withDrawHeader.dao
)
return utils.calculateMaximumWithdraw(celloutput, celloutputData, depositHeader.dao, withDrawHeader.dao)
}

#secp256k1DepsShouldBeReady = () => {
Expand Down
11 changes: 5 additions & 6 deletions packages/ckb-sdk-core/src/signWitnessGroup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { blake2b, hexToBytes, PERSONAL, toUint64Le, serializeWitnessArgs } from '@nervosnetwork/ckb-sdk-utils'
import ECPair from '@nervosnetwork/ckb-sdk-utils/lib/ecpair'
import { serializeMultisigConfig, MultisigConfig } from './multisig'
import { blake2b, hexToBytes, PERSONAL, toUint64Le, serializeWitnessArgs, ECPair } from '@nervosnetwork/ckb-sdk-utils'
import { serializeMultisigConfig, MultisigConfig } from './multisig.js'

export type SignatureProvider = string | ((message: string | Uint8Array) => string)
type TransactionHash = string
Expand All @@ -9,20 +8,20 @@ function signWitnessGroup(
sk: SignatureProvider,
transactionHash: TransactionHash,
witnessGroup: StructuredWitness[],
multisigConfig?: MultisigConfig
multisigConfig?: MultisigConfig,
): StructuredWitness[]
function signWitnessGroup(
sk: (message: string | Uint8Array, witness: StructuredWitness[]) => Promise<string>,
transactionHash: TransactionHash,
witnessGroup: StructuredWitness[],
multisigConfig?: MultisigConfig
multisigConfig?: MultisigConfig,
): Promise<StructuredWitness[]>

function signWitnessGroup(
sk: SignatureProvider | ((message: string | Uint8Array, witness: StructuredWitness[]) => Promise<string>),
transactionHash: TransactionHash,
witnessGroup: StructuredWitness[],
multisigConfig?: MultisigConfig
multisigConfig?: MultisigConfig,
) {
if (!witnessGroup.length) {
throw new Error('WitnessGroup cannot be empty')
Expand Down
140 changes: 71 additions & 69 deletions packages/ckb-sdk-core/src/signWitnesses.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { serializeWitnessArgs } from '@nervosnetwork/ckb-sdk-utils'
import { ParameterRequiredException } from '@nervosnetwork/ckb-sdk-utils/lib/exceptions'
import signWitnessGroup, { SignatureProvider } from './signWitnessGroup'
import groupScripts from './groupScripts'
import { getMultisigStatus, isMultisigConfig, MultisigConfig, serializeMultisigConfig, SignStatus } from './multisig'
import { serializeWitnessArgs, ParameterRequiredException } from '@nervosnetwork/ckb-sdk-utils'
import signWitnessGroup, { SignatureProvider } from './signWitnessGroup.js'
import groupScripts from './groupScripts.js'
import { getMultisigStatus, isMultisigConfig, MultisigConfig, serializeMultisigConfig, SignStatus } from './multisig.js'

type LockHash = string
type TransactionHash = string
Expand Down Expand Up @@ -39,82 +38,85 @@ export const isMap = <K = any, V = any>(val: any): val is Map<K, V> => {

function isMultisigOption(params: any): params is MultisigOption {
if (params.sk && params.blake160 && params.config && params.signatures) {
if ((typeof params.sk === 'string' || typeof params.sk === 'function')
&& typeof params.blake160 === 'string'
&& Array.isArray(params.signatures)
&& isMultisigConfig(params.config)
if (
(typeof params.sk === 'string' || typeof params.sk === 'function') &&
typeof params.blake160 === 'string' &&
Array.isArray(params.signatures) &&
isMultisigConfig(params.config)
) {
return true
return true
}
throw new Error('Multisig options is incorrect')
}
throw new Error('Multisig options miss some property')
}

const signWitnesses: SignWitnesses = (key: SignatureProvider | Map<LockHash, SignatureProvider | MultisigOption>) => ({
transactionHash,
witnesses = [],
inputCells = [],
skipMissingKeys = false,
}: {
transactionHash: string
witnesses: StructuredWitness[]
inputCells: CachedLock[]
skipMissingKeys: boolean
}) => {
if (!key) throw new ParameterRequiredException('Signature provider')
if (!transactionHash) throw new ParameterRequiredException('Transaction hash')
if (!witnesses.length) throw new Error('Witnesses is empty')
const signWitnesses: SignWitnesses =
(key: SignatureProvider | Map<LockHash, SignatureProvider | MultisigOption>) =>
({
transactionHash,
witnesses = [],
inputCells = [],
skipMissingKeys = false,
}: {
transactionHash: string
witnesses: StructuredWitness[]
inputCells: CachedLock[]
skipMissingKeys: boolean
}) => {
if (!key) throw new ParameterRequiredException('Signature provider')
if (!transactionHash) throw new ParameterRequiredException('Transaction hash')
if (!witnesses.length) throw new Error('Witnesses is empty')

if (isMap(key)) {
if (!inputCells.length) {
throw new Error(`Cell shouldn't be empty when key is Map`)
}
const rawWitnesses = witnesses
const restWitnesses = witnesses.slice(inputCells.length)
const groupedScripts = groupScripts(inputCells)
groupedScripts.forEach((indices, lockhash) => {
const sk = key.get(lockhash)
if (!sk) {
if (!skipMissingKeys) {
throw new Error(`The signature provider to sign lockhash ${lockhash} is not found`)
} else {
return
}
if (isMap(key)) {
if (!inputCells.length) {
throw new Error(`Cell shouldn't be empty when key is Map`)
}

const ws = [...indices.map(idx => witnesses[idx]), ...restWitnesses]
if (typeof sk === 'object' && isMultisigOption(sk)) {
const witnessIncludeSignature = signWitnessGroup(sk.sk, transactionHash, ws, sk.config)[0]
// is multisig sign
const firstWitness = rawWitnesses[indices[0]]
if (typeof firstWitness !== 'object') {
throw new Error('The first witness in the group should be type of WitnessArgs')
const rawWitnesses = witnesses
const restWitnesses = witnesses.slice(inputCells.length)
const groupedScripts = groupScripts(inputCells)
groupedScripts.forEach((indices, lockhash) => {
const sk = key.get(lockhash)
if (!sk) {
if (!skipMissingKeys) {
throw new Error(`The signature provider to sign lockhash ${lockhash} is not found`)
} else {
return
}
}
let lockAfterSign = (witnessIncludeSignature as CKBComponents.WitnessArgs).lock
if (firstWitness.lock) {
lockAfterSign = firstWitness.lock + lockAfterSign?.slice(2)

const ws = [...indices.map(idx => witnesses[idx]), ...restWitnesses]
if (typeof sk === 'object' && isMultisigOption(sk)) {
const witnessIncludeSignature = signWitnessGroup(sk.sk, transactionHash, ws, sk.config)[0]
// is multisig sign
const firstWitness = rawWitnesses[indices[0]]
if (typeof firstWitness !== 'object') {
throw new Error('The first witness in the group should be type of WitnessArgs')
}
let lockAfterSign = (witnessIncludeSignature as CKBComponents.WitnessArgs).lock
if (firstWitness.lock) {
lockAfterSign = firstWitness.lock + lockAfterSign?.slice(2)
} else {
lockAfterSign = serializeMultisigConfig(sk.config) + lockAfterSign?.slice(2)
}
const firstWitSigned = { ...firstWitness, lock: lockAfterSign }
rawWitnesses[indices[0]] = firstWitSigned
if (getMultisigStatus(sk.config, [...sk.signatures, sk.blake160]) === SignStatus.Signed) {
indices.forEach(idx => {
const wit = rawWitnesses[idx]
rawWitnesses[idx] = typeof wit === 'string' ? wit : serializeWitnessArgs(wit)
})
}
} else {
lockAfterSign = serializeMultisigConfig(sk.config) + lockAfterSign?.slice(2)
}
const firstWitSigned = { ...firstWitness, lock: lockAfterSign }
rawWitnesses[indices[0]] = firstWitSigned
if(getMultisigStatus(sk.config, [...sk.signatures, sk.blake160]) === SignStatus.Signed) {
indices.forEach(idx => {
const wit = rawWitnesses[idx]
rawWitnesses[idx] = typeof wit === 'string' ? wit : serializeWitnessArgs(wit)
})
const witnessIncludeSignature = signWitnessGroup(sk, transactionHash, ws)[0]
rawWitnesses[indices[0]] = witnessIncludeSignature
}
} else {
const witnessIncludeSignature = signWitnessGroup(sk, transactionHash, ws)[0]
rawWitnesses[indices[0]] = witnessIncludeSignature
}
})
return rawWitnesses
}
})
return rawWitnesses
}

const signedWitnesses = signWitnessGroup(key, transactionHash, witnesses)
return signedWitnesses
}
const signedWitnesses = signWitnessGroup(key, transactionHash, witnesses)
return signedWitnesses
}

export default signWitnesses
1 change: 1 addition & 0 deletions packages/ckb-sdk-rpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"license": "MIT",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"type": "module",
"directories": {
"lib": "lib",
"lib-esm": "lib-esm",
Expand Down
Loading

0 comments on commit be3e757

Please sign in to comment.