1- import { SHA3 } from 'sha3'
21import { AxiosInstance } from "axios"
3- import { getSignPIN } from "../mixin/sign"
4- import { BigNumber } from 'bignumber.js'
52import {
6- Keystore ,
7- CollectiblesClientRequest , CollectiblesRequest , CollectiblesUTXO ,
8- CollectiblesAction , RawTransactionInput , Transaction , GhostInput , GhostKeys ,
3+ Keystore , TransactionInput , GhostInput , GhostKeys ,
4+ CollectiblesClientRequest , CollectiblesParams , CollectibleToken , RawCollectibleInput , Transaction , CollectibleAction , CollectibleRequest , CollectibleOutput ,
95} from "../types"
10- import { dumpTransaction } from '../mixin/dump_transacion'
11-
12- const TxVersion = 0x02
13-
14- const OperatorSum = 0xfe
15- const OperatorCmp = 0xff
6+ import { DumpOutputFromGhostKey , dumpTransaction } from '../mixin/dump_transacion'
7+ import { hashMember } from '../mixin/tools'
8+ import { TxVersion } from '../mixin/encoder'
9+ import { getSignPIN } from '../mixin/sign'
10+ import { buildMintCollectibleMemo } from '../mixin/nfo'
1611
12+ const MintAssetID = "c94ac88f-4671-3976-b60a-09064f1811e8"
13+ const MintMinimumCost = "0.001"
14+ const GroupMembers = [
15+ "4b188942-9fb0-4b99-b4be-e741a06d1ebf" ,
16+ "dd655520-c919-4349-822f-af92fabdbdf4" ,
17+ "047061e6-496d-4c35-b06b-b0424a8a400d" ,
18+ "acf65344-c778-41ee-bacb-eb546bacfb9f" ,
19+ "a51006d0-146b-4b32-a2ce-7defbf0d7735" ,
20+ "cf4abd9c-2cfa-4b5a-b1bd-e2b61a83fabd" ,
21+ "50115496-7247-4e2c-857b-ec8680756bee" ,
22+ ]
23+ const GroupThreshold = 5
1724export class CollectiblesClient implements CollectiblesClientRequest {
1825 keystore ! : Keystore
1926 request ! : AxiosInstance
20- readGhostKeys ! : ( receivers : string [ ] , index : number ) => Promise < GhostKeys >
2127 batchReadGhostKeys ! : ( inputs : GhostInput [ ] ) => Promise < GhostKeys [ ] >
22-
23- readCollectible ( id : string ) : Promise < CollectiblesUTXO [ ] > {
24- return this . request . get ( `/collectibles/tokens/${ id } ` )
28+ newMintCollectibleTransferInput ( p : CollectiblesParams ) : TransactionInput {
29+ const { trace_id, collection_id, token_id, content } = p
30+ if ( ! trace_id || ! collection_id || ! token_id || ! content ) throw new Error ( "Missing parameters" )
31+ let input : TransactionInput = {
32+ asset_id : MintAssetID ,
33+ amount : MintMinimumCost ,
34+ trace_id,
35+ memo : buildMintCollectibleMemo ( collection_id , token_id , content ) ,
36+ opponent_multisig : {
37+ receivers : GroupMembers ,
38+ threshold : GroupThreshold ,
39+ }
40+ }
41+ return input
42+ }
43+ readCollectibleToken ( id : string ) : Promise < CollectibleToken > {
44+ return this . request . get ( `/collectibles/tokens/` + id )
2545 }
26- readCollectibleOutputs ( members : string [ ] , threshold : number , offset : string , limit : number ) : Promise < CollectiblesUTXO [ ] > {
27- if ( members . length > 0 && threshold < 1 || threshold > members . length ) return Promise . reject ( new Error ( "Invalid threshold or members" ) )
28- const params : any = { threshold : Number ( threshold ) , offset, limit }
29- params . members = hashMember ( members )
30- return this . request . get ( `/collectibles/outputs` , { params } )
46+ readCollectibleOutputs ( _members : string [ ] , threshold : number , offset : string , limit : number ) : Promise < CollectibleOutput [ ] > {
47+ const members = hashMember ( _members )
48+ return this . request . get ( `/collectibles/outputs` , { params : { members, threshold, offset, limit } } )
49+ }
50+ async makeCollectibleTransactionRaw ( txInput : RawCollectibleInput ) : Promise < string > {
51+ const { token, output, receivers, threshold } = txInput
52+ const tx : Transaction = {
53+ version : TxVersion ,
54+ asset : token . mixin_id ! ,
55+ extra : token . nfo ! ,
56+ inputs : [
57+ {
58+ hash : output . transaction_hash ! ,
59+ index : output . output_index !
60+ }
61+ ]
62+ }
63+ const ghostInputs = await this . batchReadGhostKeys ( [ {
64+ receivers,
65+ index : 0 ,
66+ hint : output . output_id !
67+ } ] )
68+ tx . outputs = [ DumpOutputFromGhostKey ( ghostInputs [ 0 ] , output . amount ! , threshold ) ]
69+ return dumpTransaction ( tx )
3170 }
32- createCollectible ( action : CollectiblesAction , raw : string ) : Promise < CollectiblesRequest > {
71+ createCollectibleRequest ( action : CollectibleAction , raw : string ) : Promise < CollectibleRequest > {
3372 return this . request . post ( `/collectibles/requests` , { action, raw } )
3473 }
35- signCollectible ( request_id : string , pin ?: string ) : Promise < CollectiblesRequest > {
74+ signCollectibleRequest ( requestId : string , pin ?: string ) : Promise < CollectibleRequest > {
3675 pin = getSignPIN ( this . keystore , pin )
37- return this . request . post ( `/collectibles/requests/${ request_id } /sign` , { pin } )
76+ return this . request . post ( `/collectibles/requests/${ requestId } /sign` , { pin } )
3877 }
39- cancelCollectible ( request_id : string ) : Promise < void > {
40- return this . request . post ( `/collectibles/requests/${ request_id } /cancel` )
78+ cancelCollectibleRequest ( requestId : string ) : Promise < void > {
79+ return this . request . post ( `/collectibles/requests/${ requestId } /cancel` )
4180 }
42- unlockCollectible ( request_id : string , pin : string ) : Promise < void > {
81+ unlockCollectibleRequest ( requestId : string , pin ? : string ) : Promise < void > {
4382 pin = getSignPIN ( this . keystore , pin )
44- return this . request . post ( `/collectibles/requests/${ request_id } /unlock` , { pin } )
83+ return this . request . post ( `/collectibles/requests/${ requestId } /unlock` , { pin } )
4584 }
46- async makeCollectiblesTransaction ( txInput : RawTransactionInput ) : Promise < string > {
47- // validate ...
48- let { inputs, memo, outputs } = txInput
49- const tx : Transaction = {
50- version : TxVersion ,
51- asset : newHash ( inputs [ 0 ] . asset_id ) ,
52- extra : Buffer . from ( memo ) . toString ( 'base64' ) ,
53- inputs : [ ] ,
54- outputs : [ ]
55- }
56- // add input
57- for ( const input of inputs ) {
58- tx . inputs ! . push ( {
59- hash : input . transaction_hash ,
60- index : input . output_index
61- } )
62- }
63- let change = inputs . reduce ( ( sum , input ) => sum . plus ( input . amount ) , new BigNumber ( 0 ) )
64- for ( const output of outputs ) change = change . minus ( output . amount )
65- if ( change . isGreaterThan ( 0 ) ) outputs . push ( { receivers : inputs [ 0 ] . members , threshold : inputs [ 0 ] . threshold , amount : change . toString ( ) } )
66- const ghostInputs : GhostInput [ ] = [ ]
67- outputs . forEach ( ( output , idx ) => ghostInputs . push ( { receivers : output . receivers , index : idx , hint : txInput . hint } ) )
68- // get ghost keys
69- let ghosts = await this . batchReadGhostKeys ( ghostInputs )
70- outputs . forEach ( ( output , idx ) => {
71- const { mask, keys } = ghosts [ idx ]
72- tx . outputs ! . push ( {
73- mask,
74- keys,
75- amount : Number ( output . amount ) . toFixed ( 8 ) ,
76- script : Buffer . from ( [ OperatorCmp , OperatorSum , output . threshold ] ) . toString ( 'hex' )
77- } )
78- } )
79- return dumpTransaction ( tx )
80- }
81- }
82-
83- function hashMember ( ids : string [ ] ) {
84- ids = ids . sort ( ( a , b ) => a > b ? 1 : - 1 )
85- return newHash ( ids . join ( '' ) )
8685}
8786
88- function newHash ( str : string ) {
89- return new SHA3 ( 256 ) . update ( str ) . digest ( 'hex' )
90- }
0 commit comments