import { utils } from "@ckb-lumos/base"; import { BIish } from "@ckb-lumos/bi"; import { sudt } from "@ckb-lumos/common-scripts"; import { parseAddress } from "@ckb-lumos/helpers"; import { Address, commons, helpers, Script } from "@ckb-lumos/lumos"; import { readBigUInt128LE } from "@lay2/pw-core"; import { CkbClient } from "./ckb_client"; import { CkbAccount } from "./ckb_account"; import { ScriptType } from "./types"; import { calcFromInfos } from "./utils"; export class CoinClient { ckbClient: CkbClient; constructor(ckbClient: CkbClient) { this.ckbClient = ckbClient; } /** * @param from * @param to * @param fee * @returns transaction hash */ public async transferCkb( from: CkbAccount, to: Map, fee?: BIish ): Promise { let txSkeleton = helpers.TransactionSkeleton({ cellProvider: this.ckbClient.indexer, }); const config = this.ckbClient.config; for (var [toAddress, amount] of to) { txSkeleton = await commons.common.transfer( txSkeleton, calcFromInfos(from), toAddress, amount, undefined, undefined, { config } ); } return await this.ckbClient.submitTransaction(txSkeleton, from, fee); } /** * @param from * @param to * @param sudtToken * @param fee * @returns transaction hash */ public async transferSudt( from: CkbAccount, to: Map, sudtToken: string, fee?: BIish ): Promise { let txSkeleton = helpers.TransactionSkeleton({ cellProvider: this.ckbClient.indexer, }); const config = this.ckbClient.config; for (var [toAddress, amount] of to) { txSkeleton = await sudt.transfer( txSkeleton, calcFromInfos(from), sudtToken, toAddress, amount, undefined, undefined, undefined, { config } ); } return await this.ckbClient.submitTransaction(txSkeleton, from, fee); } public async getCkbBalance( address: string, lockOnly: boolean = true ): Promise { const lock = parseAddress(address, { config: this.ckbClient.config }); const searchKey = { script: lock, script_type: ScriptType.lock, }; let cells = (await this.ckbClient.indexer.getCells(searchKey)).objects; if (lockOnly) { cells = cells.filter((cell) => !cell.cell_output.type); } const balance = cells .map((cell) => BigInt(cell.cell_output.capacity)) .reduce((p, c) => p + c, 0n); return balance; } public async getSudtBalance(address: string, udt: Script): Promise { const lock = parseAddress(address, { config: this.ckbClient.config }); const searchKey = { script: lock, script_type: ScriptType.lock, filter: { script: udt, }, }; const cells = (await this.ckbClient.indexer.getCells(searchKey)).objects; const balance = cells .map((cell) => this.getSUDTAmount(cell.data)) .reduce((p, c) => p + c, 0n); return balance; } getSUDTAmount(cellData: string): bigint { const sudtAmountData = cellData.slice(0, 34); return BigInt(readBigUInt128LE(sudtAmountData).toString()); } public async issueToken( from: CkbAccount, amount: BIish, fee?: BIish ): Promise { let txSkeleton = helpers.TransactionSkeleton({ cellProvider: this.ckbClient.indexer, }); txSkeleton = await sudt.issueToken( txSkeleton, calcFromInfos(from)[0], amount, undefined, undefined, { config: this.ckbClient.config } ); return await this.ckbClient.submitTransaction(txSkeleton, from, fee); } public calcToken(from: CkbAccount): string { return this.calcSudtScript(from).args; } public calcSudtScript(from: CkbAccount): Script { const template = this.ckbClient.config.SCRIPTS.SUDT; if (!template) { throw new Error("Provided config does not have SUDT script setup!"); } return { code_hash: template.CODE_HASH, hash_type: template.HASH_TYPE, args: utils.computeScriptHash(from.lockScript), }; } } export default { CoinClient, };