@@ -3,6 +3,7 @@ import CellsService, { MIN_CELL_CAPACITY } from 'services/cells'
33import LockUtils from 'models/lock-utils'
44import { CapacityTooSmall } from 'exceptions'
55import { TargetOutput } from './params'
6+ import DaoUtils from 'models/dao-utils'
67
78export class TransactionGenerator {
89 private static txSerializedSizeInBlockWithoutInputs = ( outputLength : number ) : number => {
@@ -16,7 +17,34 @@ export class TransactionGenerator {
1617 return 4 + 68 + 37 * 1 + ( 4 + 97 + 4 + 4 ) * outputLength
1718 }
1819
19- private static txFee = ( size : number , feeRate : bigint ) => {
20+ public static txSerializedSizeInBlockWithoutInputsForDeposit = ( ) : number => {
21+ /*
22+ * add a transaction to block need 4 Bytes for offset
23+ * a transaction with empty inputs/outputs/cellDeps/header/outputs_data/witnesses need 68 Bytes
24+ * every cellDep need 37 Bytes, transaction for deposit needs 2 cellDeps(lock / type)
25+ * every output without typeScript & with lock in secp need 97 Bytes and 4 Bytes for offset (add to transaction)
26+ * every output for deposit with typeScript in dao & with lock in secp need 130 Bytes and 4 Bytes for offset (add to transaction)
27+ * every outputsData in "0x" need 4 Bytes and 4 Bytes for offset
28+ * every outputsData in "0x0000000000000000" need 12 Bytes and 4 Bytes for offset
29+ */
30+ return 4 + 68 + 37 * 2 + ( 4 + 97 + 4 + 4 ) + ( 130 + 4 + 12 + 4 )
31+ }
32+
33+ public static txSerializedSizeInBlockForWithdraw = ( ) : number => {
34+ /*
35+ * add a transaction to block need 4 Bytes for offset
36+ * a transaction with empty inputs/outputs/cellDeps/header/outputs_data/witnesses need 68 Bytes
37+ * every cellDep need 37 Bytes, transaction for withdraw step2 needs 2 cellDeps(lock / type)
38+ * every headerDep need 32 Bytes, transaction for withdraw step2 need 2 headerDeps
39+ * every output without typeScript & with lock in secp need 97 Bytes and 4 Bytes for offset (add to transaction)
40+ * every outputsData in "0x" need 4 Bytes and 4 Bytes for offset
41+ * only one input, for 44 Bytes
42+ * one witness with extra inputType "0x0000000000000000", 101 Bytes, extra 4 Bytes for add to transaction
43+ */
44+ return 4 + 68 + 37 * 2 + 32 * 2 + ( 4 + 97 + 4 + 4 ) + ( 101 + 4 )
45+ }
46+
47+ public static txFee = ( size : number , feeRate : bigint ) => {
2048 const ratio = BigInt ( 1000 )
2149 const base = BigInt ( size ) * feeRate
2250 const fee = base / ratio
@@ -130,6 +158,106 @@ export class TransactionGenerator {
130158 witnesses : [ ] ,
131159 }
132160 }
161+
162+ public static generateDepositTx = async (
163+ lockHashes : string [ ] ,
164+ capacity : string ,
165+ receiveAddress : string ,
166+ changeAddress : string ,
167+ fee : string = '0' ,
168+ feeRate : string = '0'
169+ ) => {
170+ const { codeHash, outPoint, hashType } = await LockUtils . systemScript ( )
171+ const blake160 : string = LockUtils . addressToBlake160 ( receiveAddress )
172+ const daoScriptInfo = await DaoUtils . daoScript ( )
173+
174+ const feeInt = BigInt ( fee )
175+ const feeRateInt = BigInt ( feeRate )
176+ let mode : 'fee' | 'feeRate' = 'fee'
177+ if ( feeRateInt > 0 ) {
178+ mode = 'feeRate'
179+ }
180+
181+ const capacityInt : bigint = BigInt ( capacity )
182+
183+ const sizeWithoutInputs : number = TransactionGenerator . txSerializedSizeInBlockWithoutInputsForDeposit ( )
184+ const feeWithoutInputs : bigint = TransactionGenerator . txFee ( sizeWithoutInputs , feeRateInt )
185+
186+ const output : Cell = {
187+ capacity : capacity ,
188+ lock : {
189+ codeHash,
190+ hashType,
191+ args : blake160 ,
192+ } ,
193+ type : {
194+ codeHash : daoScriptInfo . codeHash ,
195+ hashType : daoScriptInfo . hashType ,
196+ args : '0x' ,
197+ } ,
198+ data : '0x0000000000000000'
199+ }
200+
201+ const outputs : Cell [ ] = [ output ]
202+
203+ let gatherCapacities = capacityInt
204+ if ( mode === 'feeRate' ) {
205+ gatherCapacities = capacityInt + feeWithoutInputs
206+ }
207+
208+ const {
209+ inputs,
210+ capacities,
211+ needFee
212+ } = await CellsService . gatherInputs (
213+ gatherCapacities . toString ( ) ,
214+ lockHashes ,
215+ fee ,
216+ feeRate ,
217+ )
218+ const needFeeInt = BigInt ( needFee )
219+ const totalFee = feeWithoutInputs + needFeeInt
220+
221+ // change
222+ if ( mode === 'fee' && BigInt ( capacities ) > capacityInt + feeInt || mode === 'feeRate' && BigInt ( capacities ) > capacityInt + feeWithoutInputs + needFeeInt ) {
223+ const changeBlake160 : string = LockUtils . addressToBlake160 ( changeAddress )
224+ let changeCapacity = BigInt ( capacities ) - capacityInt - feeInt
225+ if ( mode === 'feeRate' ) {
226+ changeCapacity = BigInt ( capacities ) - capacityInt - totalFee
227+ }
228+
229+ const changeOutput : Cell = {
230+ capacity : changeCapacity . toString ( ) ,
231+ data : '0x' ,
232+ lock : {
233+ codeHash,
234+ args : changeBlake160 ,
235+ hashType
236+ } ,
237+ }
238+
239+ outputs . push ( changeOutput )
240+ }
241+
242+ return {
243+ version : '0' ,
244+ cellDeps : [
245+ {
246+ outPoint,
247+ depType : DepType . DepGroup ,
248+ } ,
249+ {
250+ outPoint : daoScriptInfo . outPoint ,
251+ depType : DepType . Code ,
252+ } ,
253+ ] ,
254+ headerDeps : [ ] ,
255+ inputs,
256+ outputs,
257+ outputsData : outputs . map ( output => output . data || '0x' ) ,
258+ witnesses : [ ] ,
259+ }
260+ }
133261}
134262
135263export default TransactionGenerator
0 commit comments