@@ -8,6 +8,13 @@ import {
88import type { UTxO , MeshTxBuilder } from "@meshsdk/core" ;
99// import { parseDatumCbor } from "@meshsdk/core-cst";
1010import { parseProposalId } from "@/lib/governance" ;
11+ import { buildProxySpendTx } from "@/lib/server/proxyTxBuilders" ;
12+ import {
13+ selectFreeAuthTokenUtxo ,
14+ selectProxyUtxosForOutputs ,
15+ type UtxoRef ,
16+ } from "@/lib/server/proxyUtxos" ;
17+ import { getTxBuilder } from "@/utils/get-tx-builder" ;
1118
1219import { MeshTxInitiator } from "./common" ;
1320import type { MeshTxInitiatorInput } from "./common" ;
@@ -193,6 +200,7 @@ export class MeshProxyContract extends MeshTxInitiator {
193200 outputs : { address : string ; unit : string ; amount : string } [ ] ,
194201 msUtxos ?: UTxO [ ] ,
195202 msWalletAddress ?: string ,
203+ blockedUtxoRefs : UtxoRef [ ] = [ ] ,
196204 ) => {
197205 if ( this . msCbor && ! msUtxos && ! msWalletAddress ) {
198206 throw new Error (
@@ -202,7 +210,6 @@ export class MeshProxyContract extends MeshTxInitiator {
202210 const walletInfo = await this . getWalletInfoForTx ( ) ;
203211 let { utxos, walletAddress } = walletInfo ;
204212 const { collateral } = walletInfo ;
205- // If multisig inputs are provided, use them instead of the wallet inputs
206213 if ( this . msCbor && msUtxos && msWalletAddress ) {
207214 utxos = msUtxos ;
208215 walletAddress = msWalletAddress ;
@@ -219,164 +226,51 @@ export class MeshProxyContract extends MeshTxInitiator {
219226 if ( this . proxyAddress === undefined ) {
220227 throw new Error ( "Proxy address not set. Please setupProxy first." ) ;
221228 }
229+ if ( ! this . paramUtxo . txHash ) {
230+ throw new Error ( "Proxy param UTxO is not set. Please setupProxy first." ) ;
231+ }
222232 const blockchainProvider = this . mesh . fetcher ;
223233 if ( ! blockchainProvider ) {
224234 throw new Error ( "Blockchain provider not found" ) ;
225235 }
226236
227- const proxyUtxos = await blockchainProvider . fetchAddressUTxOs (
228- this . proxyAddress ,
237+ const policyIdAT = resolveScriptHash ( this . getAuthTokenCbor ( ) , "V3" ) ;
238+ const authTokenUtxo = selectFreeAuthTokenUtxo (
239+ utxos ,
240+ policyIdAT ,
241+ blockedUtxoRefs ,
229242 ) ;
230-
231- // Calculate spend requirements and ensure coverage by proxy UTxOs
232- const REQUIRED_FEE_BUFFER = BigInt ( 500_000 ) ; // 0.5 ADA buffer in lovelace
233-
234- const requiredByUnit = new Map < string , bigint > ( ) ;
235- for ( const out of outputs ) {
236- const prev = requiredByUnit . get ( out . unit ) ?? BigInt ( 0 ) ;
237- requiredByUnit . set ( out . unit , prev + BigInt ( out . amount ) ) ;
238- }
239- // Add buffer to lovelace
240- const lovelaceNeed =
241- ( requiredByUnit . get ( "lovelace" ) ?? BigInt ( 0 ) ) + REQUIRED_FEE_BUFFER ;
242- requiredByUnit . set ( "lovelace" , lovelaceNeed ) ;
243-
244- const availableByUnit = new Map < string , bigint > ( ) ;
245- for ( const utxo of proxyUtxos ) {
246- for ( const asset of utxo . output . amount ) {
247- const prev = availableByUnit . get ( asset . unit ) ?? BigInt ( 0 ) ;
248- availableByUnit . set ( asset . unit , prev + BigInt ( asset . quantity ) ) ;
249- }
243+ if ( "error" in authTokenUtxo ) {
244+ throw new Error ( authTokenUtxo . error ) ;
250245 }
251246
252- for ( const [ unit , needed ] of requiredByUnit . entries ( ) ) {
253- const available = availableByUnit . get ( unit ) ?? BigInt ( 0 ) ;
254- if ( available < needed ) {
255- throw new Error (
256- `Insufficient proxy balance for ${ unit } . Needed: ${ needed . toString ( ) } , Available: ${ available . toString ( ) } ` ,
257- ) ;
258- }
259- }
260-
261- // Select as few UTxOs as possible to cover required amounts
262- const remainingByUnit = new Map < string , bigint > ( requiredByUnit ) ;
263- const candidateUtxos = [ ...proxyUtxos ] ;
264- const selectedUtxos : typeof proxyUtxos = [ ] ;
265-
266- const hasRemaining = ( ) => {
267- for ( const value of remainingByUnit . values ( ) ) {
268- if ( value > BigInt ( 0 ) ) return true ;
269- }
270- return false ;
271- } ;
272-
273- const contributionScore = ( utxo : ( typeof proxyUtxos ) [ number ] ) => {
274- let score = BigInt ( 0 ) ;
275- for ( const asset of utxo . output . amount ) {
276- const remaining = remainingByUnit . get ( asset . unit ) ?? BigInt ( 0 ) ;
277- if ( remaining > BigInt ( 0 ) ) {
278- const qty = BigInt ( asset . quantity ) ;
279- score += qty < remaining ? qty : remaining ;
280- }
281- }
282- return score ;
283- } ;
284-
285- while ( hasRemaining ( ) ) {
286- let bestIdx = - 1 ;
287- let bestScore = BigInt ( 0 ) ;
288- for ( let i = 0 ; i < candidateUtxos . length ; i ++ ) {
289- const s = contributionScore ( candidateUtxos [ i ] ! ) ;
290- if ( s > bestScore ) {
291- bestScore = s ;
292- bestIdx = i ;
293- }
294- }
295- if ( bestIdx === - 1 || bestScore === BigInt ( 0 ) ) {
296- throw new Error (
297- "Unable to select proxy UTxOs to cover required amounts." ,
298- ) ;
299- }
300- const chosen = candidateUtxos . splice ( bestIdx , 1 ) [ 0 ] ! ;
301- selectedUtxos . push ( chosen ) ;
302- // Decrease remaining by chosen utxo's amounts
303- for ( const asset of chosen . output . amount ) {
304- const remaining = remainingByUnit . get ( asset . unit ) ?? BigInt ( 0 ) ;
305- if ( remaining > BigInt ( 0 ) ) {
306- const qty = BigInt ( asset . quantity ) ;
307- const newRemaining = remaining - ( qty < remaining ? qty : remaining ) ;
308- remainingByUnit . set ( asset . unit , newRemaining ) ;
309- }
310- }
311- }
312-
313- const freeProxyUtxos = selectedUtxos ;
314- const paramScriptAT = this . getAuthTokenCbor ( ) ;
315- const policyIdAT = resolveScriptHash ( paramScriptAT , "V3" ) ;
316- const authTokenUtxos = utxos . filter ( ( utxo ) =>
317- utxo . output . amount . some ( ( asset ) => asset . unit === policyIdAT ) ,
247+ const proxyUtxos = await blockchainProvider . fetchAddressUTxOs (
248+ this . proxyAddress ,
318249 ) ;
319-
320- if ( ! authTokenUtxos || authTokenUtxos . length === 0 ) {
321- throw new Error ( "No AuthToken found at control wallet address" ) ;
322- }
323- //ToDo check if AuthToken utxo is used in a pending transaction and blocked then use a free AuthToken
324- const authTokenUtxo = authTokenUtxos [ 0 ] ;
325- if ( ! authTokenUtxo ) {
326- throw new Error ( "No AuthToken found" ) ;
327- }
328- const authTokenUtxoAmt = authTokenUtxo . output . amount ;
329- if ( ! authTokenUtxoAmt ) {
330- throw new Error ( "No AuthToken amount found" ) ;
331- }
332-
333- //prepare Proxy spend
334- //1 Get
335- const txHex = this . mesh ;
336-
337- for ( const input of freeProxyUtxos ) {
338- txHex
339- . spendingPlutusScriptV3 ( )
340- . txIn (
341- input . input . txHash ,
342- input . input . outputIndex ,
343- input . output . amount ,
344- input . output . address ,
345- )
346- . txInScript ( this . getProxyCbor ( ) )
347- . txInInlineDatumPresent ( )
348- . txInRedeemerValue ( mConStr0 ( [ ] ) ) ;
349- }
350-
351- txHex
352- . txIn (
353- authTokenUtxo . input . txHash ,
354- authTokenUtxo . input . outputIndex ,
355- authTokenUtxo . output . amount ,
356- authTokenUtxo . output . address ,
357- )
358- . txInCollateral (
359- collateral . input . txHash ,
360- collateral . input . outputIndex ,
361- collateral . output . amount ,
362- collateral . output . address ,
363- )
364- . txOut ( walletAddress , [ { unit : policyIdAT , quantity : "1" } ] ) ;
365-
366- for ( const output of outputs ) {
367- txHex . txOut ( output . address , [
368- { unit : output . unit , quantity : output . amount } ,
369- ] ) ;
370- }
371-
372- txHex . changeAddress ( this . proxyAddress ) ;
373-
374- // Add the multisig script cbor if it exists (like in setupProxy)
375- if ( this . msCbor ) {
376- txHex . txInScript ( this . msCbor ) ;
377- }
378-
379- return txHex ;
250+ const selectedProxyUtxos = selectProxyUtxosForOutputs ( {
251+ proxyUtxos,
252+ outputs,
253+ } ) ;
254+ if ( "error" in selectedProxyUtxos ) {
255+ throw new Error ( selectedProxyUtxos . error ) ;
256+ }
257+
258+ const txBuilder = getTxBuilder ( this . networkId , true ) ;
259+ buildProxySpendTx ( {
260+ txBuilder,
261+ network : this . networkId ,
262+ proxyAddress : this . proxyAddress ,
263+ paramUtxo : this . paramUtxo ,
264+ walletUtxos : [ authTokenUtxo ] ,
265+ proxyUtxos : selectedProxyUtxos ,
266+ authTokenUtxo,
267+ collateral,
268+ outputs,
269+ walletAddress,
270+ multisigScriptCbor : this . msCbor ,
271+ } ) ;
272+
273+ return txBuilder ;
380274 } ;
381275
382276 manageProxyDrep = async (
0 commit comments