Skip to content

feat: support create method in AppClient #401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: fix/deploy_error_parsing
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 69 additions & 5 deletions src/types/app-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import { AppSpec, arc32ToArc56 } from './app-spec'
import {
AppCallMethodCall,
AppCallParams,
AppCreateMethodCall,
AppDeleteMethodCall,
AppDeleteParams,
AppMethodCall,
Expand Down Expand Up @@ -260,9 +261,9 @@ export interface FundAppAccountParams {
/** Source maps for an Algorand app */
export interface AppSourceMaps {
/** The source map of the approval program */
approvalSourceMap: SourceMapExport
approvalSourceMap: algosdk.ProgramSourceMap
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be extending reliance on algosdk types externally which I'm not sure we want to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good catch. Didn't realize the SDK used a class

/** The source map of the clear program */
clearSourceMap: SourceMapExport
clearSourceMap: algosdk.ProgramSourceMap
}

export interface SourceMapExport {
Expand Down Expand Up @@ -353,7 +354,14 @@ export type CallOnComplete = {

/** AppClient common parameters for a bare app call */
export type AppClientBareCallParams = Expand<
Omit<CommonAppCallParams, 'appId' | 'sender' | 'onComplete'> & {
Omit<CommonAppCallParams, 'appId' | 'sender'> & {
/** The address of the account sending the transaction, if undefined then the app client's defaultSender is used. */
sender?: Address | string
}
>

export type AppClientBareCreateParams = Expand<
Omit<CommonAppCallParams, 'appId' | 'sender'> & {
/** The address of the account sending the transaction, if undefined then the app client's defaultSender is used. */
sender?: Address | string
}
Expand Down Expand Up @@ -1193,6 +1201,15 @@ export class AppClient {
OnApplicationComplete.UpdateApplicationOC,
) as AppUpdateParams
},
create: async (params?: AppClientBareCallParams & AppClientCompilationParams) => {
return this.getBareParams(
{
...params,
...(await this.compile(params)),
},
params?.onComplete,
) as AppUpdateParams
},
/** Return params for an opt-in call */
optIn: (params?: AppClientBareCallParams) => {
return this.getBareParams(params, OnApplicationComplete.OptInOC) as AppCallParams
Expand Down Expand Up @@ -1222,6 +1239,9 @@ export class AppClient {
update: async (params?: AppClientBareCallParams & AppClientCompilationParams) => {
return this._algorand.createTransaction.appUpdate(await this.params.bare.update(params))
},
create: async (params?: AppClientBareCallParams & AppClientCompilationParams) => {
return this._algorand.createTransaction.appCreate(await this.params.bare.update(params))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should that be create rather than update?

},
/** Returns a transaction for an opt-in call */
optIn: (params?: AppClientBareCallParams) => {
return this._algorand.createTransaction.appCall(this.params.bare.optIn(params))
Expand Down Expand Up @@ -1255,6 +1275,16 @@ export class AppClient {
...(compiled as Partial<AppCompilationResult>),
}
},
create: async (params?: AppClientBareCallParams & AppClientCompilationParams & SendParams) => {
const compiled = await this.compile(params)
const results = {
...(await this._algorand.send.appCreate(await this.params.bare.create(params))),
...(compiled as Partial<AppCompilationResult>),
}

this._appId = results.appId
return results
},
/** Signs and sends an opt-in call */
optIn: (params?: AppClientBareCallParams & SendParams) => {
return this._algorand.send.appCall(this.params.bare.optIn(params))
Expand Down Expand Up @@ -1307,6 +1337,19 @@ export class AppClient {
OnApplicationComplete.UpdateApplicationOC,
)) satisfies AppUpdateMethodCall
},
create: async (params: AppClientMethodCallParams & AppClientCompilationParams) => {
if (params.onComplete === OnApplicationComplete.ClearStateOC) {
throw new Error(`Cannot create with an OnComplete value of ${params.onComplete}`)
}

return (await this.getABIParams(
{
...params,
...(await this.compile(params)),
},
params.onComplete,
)) satisfies AppCreateMethodCall
},
/**
* Return params for an opt-in ABI call
* @param params The parameters for the opt-in ABI method call
Expand Down Expand Up @@ -1364,6 +1407,24 @@ export class AppClient {
...(compiled as Partial<AppCompilationResult>),
}
},
/**
* Sign and send transactions for an update ABI call, including deploy-time TEAL template replacements and compilation if provided
* @param params The parameters for the update ABI method call
* @returns The result of sending the update ABI method call
*/
create: async (params: AppClientMethodCallParams & AppClientCompilationParams & SendParams) => {
const compiled = await this.compile(params)
const results = {
...(await this.processMethodCallReturn(
this._algorand.send.appCreateMethodCall(await this.params.create({ ...params })),
getArc56Method(params.method, this._appSpec),
)),
...(compiled as Partial<AppCompilationResult>),
}

this._appId = results.appId
return results
},
/**
* Sign and send transactions for an opt-in ABI call
* @param params The parameters for the opt-in ABI method call
Expand Down Expand Up @@ -1477,6 +1538,9 @@ export class AppClient {
update: async (params: AppClientMethodCallParams & AppClientCompilationParams) => {
return this._algorand.createTransaction.appUpdateMethodCall(await this.params.update(params))
},
create: async (params: AppClientMethodCallParams & AppClientCompilationParams) => {
return this._algorand.createTransaction.appCreateMethodCall(await this.params.update(params))
},
/**
* Return transactions for an opt-in ABI call
* @param params The parameters for the opt-in ABI method call
Expand Down Expand Up @@ -1534,7 +1598,7 @@ export class AppClient {
private getBareParams<
TParams extends { sender?: Address | string; signer?: TransactionSigner | TransactionSignerAccount } | undefined,
TOnComplete extends OnApplicationComplete,
>(params: TParams, onComplete: TOnComplete) {
>(params: TParams, onComplete?: TOnComplete) {
return {
...params,
appId: this._appId,
Expand All @@ -1552,7 +1616,7 @@ export class AppClient {
args?: AppClientMethodCallParams['args']
},
TOnComplete extends OnApplicationComplete,
>(params: TParams, onComplete: TOnComplete) {
>(params: TParams, onComplete?: TOnComplete) {
const sender = this.getSender(params.sender)
const method = getArc56Method(params.method, this._appSpec)
const args = await this.getABIArgsWithDefaultValues(params.method, params.args, sender)
Expand Down
13 changes: 7 additions & 6 deletions src/types/app-deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from './composer'
import { Expand } from './expand'
import { ConfirmedTransactionResult, SendParams } from './transaction'
import { AppClient } from './app-client'

/** Params to specify an update transaction for an app deployment */
export type DeployAppUpdateParams = Expand<Omit<AppUpdateParams, 'appId' | 'approvalProgram' | 'clearStateProgram'>>
Expand Down Expand Up @@ -252,18 +253,18 @@ export class AppDeployer {
)
const result = await ('method' in updateParams
? this._transactionSender.appUpdateMethodCall({
...updateParams,
...sendParams,
appId: existingApp.appId,
approvalProgram,
clearStateProgram,
...updateParams,
...sendParams,
})
: this._transactionSender.appUpdate({
...updateParams,
...sendParams,
appId: existingApp.appId,
approvalProgram,
clearStateProgram,
...updateParams,
...sendParams,
}))
const appMetadata: AppMetadata = {
appId: existingApp.appId,
Expand Down Expand Up @@ -300,9 +301,9 @@ export class AppDeployer {
}
const createIndex = await composer.count()
if ('method' in deleteParams) {
composer.addAppDeleteMethodCall({ appId: existingApp.appId, ...deleteParams })
composer.addAppDeleteMethodCall({ ...deleteParams, appId: existingApp.appId })
} else {
composer.addAppDelete({ appId: existingApp.appId, ...deleteParams })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these changes (in a few places) deliberate? In general I was pretty careful about ordering of spreading but it’s possible I got some wrong

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes because we are now getting the params from the app client's params methods, which include the app ID of the client. Previously there was no app ID being passed in deleteParams. The order needed to be changed to ensure we use the app ID the deployer resolves, not the one in the AppClient

composer.addAppDelete({ ...deleteParams, appId: existingApp.appId })
}
const result = await composer.send({ ...sendParams })
const confirmation = result.confirmations.at(createIndex - 1)!
Expand Down
Loading