Skip to content

Commit 36ba523

Browse files
committed
feat: generate dao transactions
1 parent f96bf68 commit 36ba523

2 files changed

Lines changed: 386 additions & 6 deletions

File tree

packages/neuron-wallet/src/services/tx/transaction-generator.ts

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import CellsService, { MIN_CELL_CAPACITY } from 'services/cells'
33
import LockUtils from 'models/lock-utils'
44
import { CapacityTooSmall } from 'exceptions'
55
import { TargetOutput } from './params'
6+
import DaoUtils from 'models/dao-utils'
67

78
export 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

135263
export default TransactionGenerator

0 commit comments

Comments
 (0)