Skip to content

Commit

Permalink
feat(sudt-manager): api
Browse files Browse the repository at this point in the history
  • Loading branch information
Daryl-L committed Nov 22, 2023
1 parent 0c3b3bc commit ef557cb
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 92 deletions.
28 changes: 11 additions & 17 deletions packages/samples/sudt/kuai.config.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
import { KuaiConfig } from '@ckb-js/kuai-core';

let redisOpt = undefined;
let redisOpt = undefined
if (process.env.REDIS_OPT) {
try {
redisOpt = JSON.parse(process.env.REDIS_OPT);
redisOpt = JSON.parse(process.env.REDIS_OPT)
} catch (error) {
//ignore error, if error redisOpt will be undefined
}
}

// fallback to REDISUSER due to https://github.com/ckb-js/kuai/pull/423#issuecomment-1668983983
const REDIS_USER = redisOpt?.username ?? process.env.REDISUSER;
const REDIS_PASSWORD = redisOpt?.password ?? process.env.REDISPASSWORD;
const REDIS_HOST = process.env.REDIS_HOST ?? process.env.REDISHOST;
const REDIS_PORT = process.env.REDIS_PORT ?? process.env.REDISPORT;
const REDIS_USER = redisOpt?.username ?? process.env.REDISUSER
const REDIS_PASSWORD = redisOpt?.password ?? process.env.REDISPASSWORD
const REDIS_HOST = process.env.REDIS_HOST ?? process.env.REDISHOST
const REDIS_PORT = process.env.REDIS_PORT ?? process.env.REDISPORT

const redisAuth = REDIS_USER && REDIS_PASSWORD ? { username: REDIS_USER, password: REDIS_PASSWORD } : undefined;
const redisAuth = REDIS_USER && REDIS_PASSWORD ? { username: REDIS_USER, password: REDIS_PASSWORD } : undefined

const config: KuaiConfig = {
const config = {
port: 3000,
redisPort: REDIS_HOST ? +REDIS_HOST : undefined,
redisHost: REDIS_PORT,
network: process.env.NETWORK || 'testnet',
redisOpt:
redisOpt || redisAuth
? {
...redisOpt,
...redisAuth,
}
: undefined,
network: 'testnet',
ckbChain: {
rpcUrl: 'http://127.0.0.1:8114',
prefix: 'ckt',
},
};
}

export default config;
export default config
24 changes: 15 additions & 9 deletions packages/samples/sudt/src/actors/omnilock.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ export class OmnilockModel extends JSONStore<Record<string, never>> {
}
}

loadCapacity = (capacity: BI) => {
const cells = Object.values(this.chainData)
let currentTotalCapacity = BI.from(0)
const inputs = cells.filter((v) => {
if (currentTotalCapacity.gte(capacity)) return false
currentTotalCapacity = currentTotalCapacity.add(BI.from(v.cell.cellOutput.capacity))
return true
})

if (currentTotalCapacity.lt(capacity)) throw new InternalServerError('not enough capacity')

return { inputs, currentTotalCapacity }
}

mint(
lockScript: Script,
amount: BI,
Expand All @@ -82,17 +96,9 @@ export class OmnilockModel extends JSONStore<Record<string, never>> {
},
data: bytes.hexify(number.Uint128LE.pack(amount.toHexString())),
}
const cells = Object.values(this.chainData)
let currentTotalCapacity: BI = BI.from(0)
// additional 0.001 ckb for tx fee
const needCapacity = BI.from(sudtCell.cellOutput.capacity).add(TX_FEE)
const inputs = cells.filter((v) => {
if (v.cell.cellOutput.type) return false
if (currentTotalCapacity.gte(needCapacity)) return false
currentTotalCapacity = currentTotalCapacity.add(BI.from(v.cell.cellOutput.capacity))
return true
})
if (currentTotalCapacity.lt(needCapacity)) throw new InternalServerError('not enough capacity')
const { inputs, currentTotalCapacity } = this.loadCapacity(needCapacity)

return {
typeScript,
Expand Down
32 changes: 15 additions & 17 deletions packages/samples/sudt/src/actors/sudt.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import { number, bytes } from '@ckb-lumos/codec'
import { InternalServerError } from 'http-errors'
import { BI, utils, config } from '@ckb-lumos/lumos'
import { MIN_SUDT_WITH_OMINILOCK, TX_FEE } from '../const'
import { OmnilockModel } from './omnilock.model'

/**
* add business logic in an actor
*/
@ActorProvider({ ref: { name: 'sudt', path: `/:args/` } })
@ActorProvider({ ref: { name: 'sudt', path: `/:typeArgs/:lockArgs/` } })
@TypeFilter()
@LockFilter()
@Omnilock()
Expand All @@ -44,30 +45,27 @@ export class SudtModel extends JSONStore<Record<string, never>> {
schemaPattern?: SchemaPattern
},
) {
super(undefined, { ...params, ref: ActorReference.newWithFilter(SudtModel, `/${lockArgs}/${typeArgs}/`) })
super(undefined, { ...params, ref: ActorReference.newWithFilter(SudtModel, `/${typeArgs}/${lockArgs}/`) })
if (!this.typeScript) {
throw new Error('type script is required')
}
this.registerResourceBinding()
}

getSudtBalance(lockScripts: Script[]): Record<'capacity' | 'sudtBalance', string> {
let capacity = BigInt(0)
let sudtBalance = BigInt(0)
const filterScriptHashes = new Set(lockScripts.map((v) => utils.computeScriptHash(v)))
getSudtBalance(): Record<'capacity' | 'sudtBalance', BI> {
let capacity = BI.from(0)
let sudtBalance = BI.from(0)
Object.values(this.chainData).forEach((v) => {
if (filterScriptHashes.has(utils.computeScriptHash(v.cell.cellOutput.lock))) {
capacity += BigInt(v.cell.cellOutput.capacity ?? 0)
sudtBalance += number.Uint128LE.unpack(v.cell.data.slice(0, 34)).toBigInt()
}
capacity = capacity.add(v.cell.cellOutput.capacity ?? 0)
sudtBalance = sudtBalance.add(number.Uint128LE.unpack(v.cell.data.slice(0, 34)))
})
return {
capacity: capacity.toString(),
sudtBalance: sudtBalance.toString(),
capacity: capacity,
sudtBalance: sudtBalance,
}
}

send(from: Script[], lockScript: Script, amount: HexString) {
send(omnilock: OmnilockModel, lockScript: Script, amount: HexString) {
const CONFIG = config.getConfig()
const sudtCell: Cell = {
cellOutput: {
Expand All @@ -86,15 +84,15 @@ export class SudtModel extends JSONStore<Record<string, never>> {
let currentTotalCapacity: BI = BI.from(0)
// additional 0.001 ckb for tx fee
const needCapacity = BI.from(sudtCell.cellOutput.capacity).add(TX_FEE)
const fromScriptHashes = new Set(from.map((v) => utils.computeScriptHash(v)))
const inputs = cells.filter((v) => {
if (!fromScriptHashes.has(utils.computeScriptHash(v.cell.cellOutput.lock))) return false
let inputs = cells.filter((v) => {
if (currentTotalCapacity.gte(needCapacity) && currentTotalSudt.gte(amount)) return false
currentTotalCapacity = currentTotalCapacity.add(BI.from(v.cell.cellOutput.capacity))
currentTotalSudt = currentTotalSudt.add(number.Uint128LE.unpack(v.cell.data.slice(0, 34)))
return true
})
if (currentTotalCapacity.lt(needCapacity)) throw new InternalServerError('not enough capacity')
if (currentTotalCapacity.lt(needCapacity)) {
inputs = inputs.concat(omnilock.loadCapacity(needCapacity.sub(currentTotalCapacity)))
}
if (currentTotalSudt.lt(amount)) throw new InternalServerError('not enough sudt balance')

const leftSudt = currentTotalSudt.sub(amount)
Expand Down
44 changes: 27 additions & 17 deletions packages/samples/sudt/src/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { config } from '@ckb-lumos/lumos'
import { DataSource } from 'typeorm'
import { AccountController } from './controllers/account.controller'
import { ExplorerService } from './services/explorer.service'
import { BalanceTask } from './tasks/balance.task'

const initiateDataSource = async () => {
const dataSource = new DataSource({
Expand Down Expand Up @@ -67,9 +68,12 @@ export const bootstrap = async () => {

const dataSource = await initiateDataSource()

const balanceTask = new BalanceTask(dataSource)
balanceTask.run()

// init kuai io
const cor = new CoR()
const sudtController = new SudtController(dataSource, new ExplorerService())
const sudtController = new SudtController(dataSource, new ExplorerService(process.env.EXPLORER_API_HOST))
const accountController = new AccountController(dataSource)
cor.use(sudtController.middleware())
cor.use(accountController.middleware())
Expand All @@ -78,20 +82,26 @@ export const bootstrap = async () => {

app.use(koaRouterAdapter.routes())

const server = app.listen(port, function () {
const address = (() => {
const _address = server.address()
if (!_address) {
return ''
}

if (typeof _address === 'string') {
return _address
}

return `http://${_address.address}:${_address.port}`
})()

console.log(`kuai app listening at ${address}`)
})
// while (true) {
try {
const server = app.listen(port, function () {
const address = (() => {
const _address = server.address()
if (!_address) {
return ''
}

if (typeof _address === 'string') {
return _address
}

return `http://${_address.address}:${_address.port}`
})()

console.log(`kuai app listening at ${address}`)
})
} catch (e) {
console.error(e)
}
// }
}
41 changes: 35 additions & 6 deletions packages/samples/sudt/src/controllers/account.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BaseController, Body, Controller, Get, Param, Post } from '@ckb-js/kuai
import { DataSource } from 'typeorm'
import { Transaction } from '../entities/transaction.entity'
import { Account } from '../entities/account.entity'
import { OmnilockModel, SudtModel, appRegistry } from '../actors'
import { OmnilockModel, appRegistry } from '../actors'
import { ActorReference } from '@ckb-js/kuai-models'
import { getLock } from '../utils'
import { SudtResponse } from '../response'
Expand All @@ -11,6 +11,7 @@ import { BadRequest } from 'http-errors'
import { Tx } from '../views/tx.view'
import { MintRequest } from '../dto/mint.dto'
import { BI } from '@ckb-lumos/lumos'
import { Asset } from '../entities/asset.entity'

@Controller('/account')
export class AccountController extends BaseController {
Expand Down Expand Up @@ -81,18 +82,46 @@ export class AccountController extends BaseController {
@Get('/:address/assets')
async accountAssets(@Param('address') address: string) {
const tokens = await this._dataSource.getRepository(Token).find()
const lock = getLock(address)
const account = await this._dataSource.getRepository(Account).findOneBy({ address })
if (!account) {
throw SudtResponse.err(404, 'account not found')
}

const assets = await this._dataSource.getRepository(Asset).findBy({ accountId: account.id })
const assetsMap = assets.reduce((acc, cur) => {
acc.set(cur.tokenId, cur)
return acc
}, new Map<number, Asset>())
console.log(assetsMap)
return tokens.map((token) => {
try {
return {
...token,
...appRegistry
.findOrBind<SudtModel>(new ActorReference('sudt', `/${lock.args}/${token.args}/`))
.getSudtBalance([getLock(address)]),
uan: token.name,
displayName: token.name,
decimal: token.decimal,
amount: assetsMap.get(token.id)?.balance ?? '0',
}
} catch (e) {
console.error(e)
}
})
}

// @Post('/transfer/:typeId')
// async transfer(@Param('tokenId') typeId: string, @Body() { from, to, amount }: MintRequest) {
// if (!from || from.length === 0 || !to || !amount) {
// throw new BadRequest('undefined body field: from, to or amount')
// }

// const token = await this._dataSource.getRepository(Token).findOneBy({ typeId })
// if (!token) {
// return SudtResponse.err(404, 'token not found')
// }

// const sudt = appRegistry.findOrBind<SudtModel>(new ActorReference('sudt', `/${token.args}/`))

// const result = sudt.send(getLock(to), amount)

// return Tx.toJsonString(result)
// }
}
Loading

0 comments on commit ef557cb

Please sign in to comment.