@@ -37,6 +37,8 @@ export class ExchangeState extends Struct {
3737export interface RamInfo {
3838 pricePerByte : number
3939 ramBytesNeeded : number
40+ existingContractRam : number // RAM used by existing contract code (will be freed on update)
41+ deltaRamNeeded : number // Actual new RAM needed (ramBytesNeeded - existingContractRam)
4042 costInTokens : Asset
4143 currentRamBytes : number
4244 currentRamAvailable : number
@@ -45,6 +47,7 @@ export interface RamInfo {
4547 hasEnoughTokens : boolean
4648 ramToBuy : number
4749 hasSystemContract : boolean // Whether the chain has full system contracts (RAM market)
50+ isUpdate : boolean // Whether this is an update to existing contract
4851}
4952
5053export interface AccountResources {
@@ -69,6 +72,51 @@ export function calculateRamNeeded(wasmSize: number, abiSize: number): number {
6972 return setcodeRam + setabiRam + buffer
7073}
7174
75+ /**
76+ * Get existing contract RAM usage
77+ * When updating a contract, the existing code RAM will be freed and replaced
78+ * Returns 0 if no contract exists
79+ */
80+ export async function getExistingContractRam ( client : APIClient , accountName : string ) : Promise < number > {
81+ try {
82+ // Get the API URL from the client's provider
83+ const baseUrl = ( client . provider as { url ?: string } ) . url
84+
85+ if ( ! baseUrl ) {
86+ return 0
87+ }
88+
89+ // Use fetch to call get_raw_code_and_abi endpoint directly
90+ // as wharfkit APIClient doesn't have this method built-in
91+ const response = await fetch ( `${ baseUrl } /v1/chain/get_raw_code_and_abi` , {
92+ method : 'POST' ,
93+ headers : { 'Content-Type' : 'application/json' } ,
94+ body : JSON . stringify ( { account_name : accountName } ) ,
95+ } )
96+
97+ if ( ! response . ok ) {
98+ return 0
99+ }
100+
101+ const data = ( await response . json ( ) ) as { wasm ?: string ; abi ?: string }
102+
103+ // Check if there's existing code
104+ if ( ! data . wasm || data . wasm . length === 0 ) {
105+ return 0
106+ }
107+
108+ // Decode base64 to get actual sizes
109+ const wasmBytes = Buffer . from ( data . wasm , 'base64' )
110+ const abiBytes = data . abi ? Buffer . from ( data . abi , 'base64' ) : Buffer . alloc ( 0 )
111+
112+ // Calculate RAM used by existing contract using same formula
113+ return calculateRamNeeded ( wasmBytes . length , abiBytes . length )
114+ } catch {
115+ // Account might not exist or have no code
116+ return 0
117+ }
118+ }
119+
72120/**
73121 * Get the core token symbol for a chain
74122 */
@@ -207,6 +255,8 @@ export function calculateRamCost(bytesNeeded: number, pricePerByte: number, symb
207255
208256/**
209257 * Analyze RAM requirements for deployment
258+ * When updating an existing contract, calculates the delta RAM needed
259+ * (existing contract RAM will be freed when replaced)
210260 */
211261export async function analyzeRamRequirements (
212262 client : APIClient ,
@@ -218,16 +268,27 @@ export async function analyzeRamRequirements(
218268 const { pricePerByte, symbol, hasSystemContract} = await getRamPrice ( client )
219269 const resources = await getAccountResources ( client , accountName , symbol )
220270
221- const ramToBuy = Math . max ( 0 , ramBytesNeeded - resources . ramAvailable )
271+ // Check for existing contract - its RAM will be freed when we update
272+ const existingContractRam = await getExistingContractRam ( client , accountName )
273+ const isUpdate = existingContractRam > 0
274+
275+ // Calculate actual delta RAM needed (new - existing, minimum 0)
276+ // When updating, the existing contract RAM is freed and replaced
277+ const deltaRamNeeded = Math . max ( 0 , ramBytesNeeded - existingContractRam )
278+
279+ // Only need to buy RAM for the delta beyond what's available
280+ const ramToBuy = Math . max ( 0 , deltaRamNeeded - resources . ramAvailable )
222281 const costInTokens = calculateRamCost ( ramToBuy , pricePerByte , symbol )
223282
224283 // On chains without system contracts, RAM is essentially free/unlimited
225- const hasEnoughRam = ! hasSystemContract || resources . ramAvailable >= ramBytesNeeded
284+ const hasEnoughRam = ! hasSystemContract || resources . ramAvailable >= deltaRamNeeded
226285 const hasEnoughTokens = hasEnoughRam || resources . coreBalance . value >= costInTokens . value
227286
228287 return {
229288 pricePerByte,
230289 ramBytesNeeded,
290+ existingContractRam,
291+ deltaRamNeeded,
231292 costInTokens,
232293 currentRamBytes : resources . ramQuota ,
233294 currentRamAvailable : resources . ramAvailable ,
@@ -236,6 +297,7 @@ export async function analyzeRamRequirements(
236297 hasEnoughTokens,
237298 ramToBuy,
238299 hasSystemContract,
300+ isUpdate,
239301 }
240302}
241303
@@ -490,7 +552,17 @@ export function displayRamAnalysis(ramInfo: RamInfo, accountName: string): void
490552 console . log ( '\n📊 RAM Analysis' )
491553 console . log ( '─' . repeat ( 50 ) )
492554 console . log ( `Account: ${ accountName } ` )
493- console . log ( `RAM needed for deployment: ${ formatBytes ( ramInfo . ramBytesNeeded ) } ` )
555+
556+ if ( ramInfo . isUpdate ) {
557+ console . log ( `📦 Updating existing contract` )
558+ console . log ( ` New contract RAM: ${ formatBytes ( ramInfo . ramBytesNeeded ) } ` )
559+ console . log ( ` Existing contract RAM: ${ formatBytes ( ramInfo . existingContractRam ) } ` )
560+ console . log ( ` Delta RAM needed: ${ formatBytes ( ramInfo . deltaRamNeeded ) } ` )
561+ } else {
562+ console . log ( `📦 New contract deployment` )
563+ console . log ( ` RAM needed: ${ formatBytes ( ramInfo . ramBytesNeeded ) } ` )
564+ }
565+
494566 console . log ( `Current RAM available: ${ formatBytes ( ramInfo . currentRamAvailable ) } ` )
495567
496568 if ( ! ramInfo . hasSystemContract ) {
@@ -499,8 +571,10 @@ export function displayRamAnalysis(ramInfo: RamInfo, accountName: string): void
499571 return
500572 }
501573
502- console . log ( `RAM to purchase: ${ formatBytes ( ramInfo . ramToBuy ) } ` )
503- console . log ( `Estimated cost: ${ ramInfo . costInTokens } ` )
574+ if ( ramInfo . ramToBuy > 0 ) {
575+ console . log ( `RAM to purchase: ${ formatBytes ( ramInfo . ramToBuy ) } ` )
576+ console . log ( `Estimated cost: ${ ramInfo . costInTokens } ` )
577+ }
504578 console . log ( `Current balance: ${ ramInfo . tokenBalance } ` )
505579 console . log (
506580 `Price per KB: ${ ( ramInfo . pricePerByte * 1024 ) . toFixed ( 4 ) } ${
0 commit comments