Skip to content

Commit

Permalink
feat: V7 tweaks (#317)
Browse files Browse the repository at this point in the history
feat: Renamed app client and algorand client properties for more obvious and consistent naming
feat: Make the compile method public in app factory and app client
feat: Improving error stack trace propagation from sendAtomicTransactionComposer
feat: Added `setSigners` on `AccountManager` so you can copy signers from one `AccountManager` to another
feat: Added transaction creation to app factory
refactor: Rename `execute` on `AlgoKitComposer` to `send` for consistency
refactor: Removing use of Expand<> inline in method calls since it results in a sub-par experience in the npm consumed package
feat: Updated to latest ARC-56 spec
docs: Updated docs for v7

BREAKING CHANGES: Various since initial v7 beta
* ExecuteParams -> SendParams
* sendAtomicTransactionComposer takes a params object that extends SendParams now
* algorand.transactions. is now algorand.createTransaction.
* appClient.transactions. is now appClient.createTransaction.
* appFactory.create/deploy now returns the app client in an appClient property rather than an app property
* appFactory.create is now appFactory.send.create and appFactory.send.bare.create
  • Loading branch information
robdmoore committed Sep 29, 2024
1 parent ca4554c commit eebb653
Show file tree
Hide file tree
Showing 83 changed files with 2,121 additions and 1,326 deletions.
14 changes: 7 additions & 7 deletions docs/capabilities/algorand-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ The `AlgorandClient` has a number of manager class instances that help you quick

### Creating transactions

You can compose a transaction via `algorand.transactions...`, which gives you an instance of the [`AlgorandClientTransactionCreator`](../code/classes/types_algorand_client_transaction_creator.AlgorandClientTransactionCreator.md) class. Intellisense will guide you on the different options.
You can compose a transaction via `algorand.createTransaction...`, which gives you an instance of the [`AlgorandClientTransactionCreator`](../code/classes/types_algorand_client_transaction_creator.AlgorandClientTransactionCreator.md) class. Intellisense will guide you on the different options.

The signature for the calls to send a single transaction usually look like:

```
algorand.transactions.{method}(params: {ComposerTransactionTypeParams} & CommonTransactionParams): Promise<Transaction>
algorand.createTransaction.{method}(params: {ComposerTransactionTypeParams} & CommonTransactionParams): Promise<Transaction>
```

- To get intellisense on the params, open an object parenthesis (`{`) and use your IDE's intellisense keyboard shortcut (e.g. ctrl+space).
Expand All @@ -70,7 +70,7 @@ algorand.transactions.{method}(params: {ComposerTransactionTypeParams} & CommonT
The return type for the ABI method call methods are slightly different:

```
algorand.transactions.app{callType}MethodCall(params: {ComposerTransactionTypeParams} & CommonTransactionParams): Promise<BuiltTransactions>
algorand.createTransaction.app{callType}MethodCall(params: {ComposerTransactionTypeParams} & CommonTransactionParams): Promise<BuiltTransactions>
```

Where `BuiltTransactions` looks like this:
Expand Down Expand Up @@ -100,12 +100,12 @@ Further documentation is present in the related capabilities:

The signature for the calls to send a single transaction usually look like:

`algorand.send.{method}(params: {ComposerTransactionTypeParams} & CommonAppCallParams & ExecuteParams): SingleSendTransactionResult`
`algorand.send.{method}(params: {ComposerTransactionTypeParams} & CommonAppCallParams & SendParams): SingleSendTransactionResult`

- To get intellisense on the params, open an object parenthesis (`{`) and use your IDE's intellisense keyboard shortcut (e.g. ctrl+space).
- `{ComposerTransactionTypeParams}` will be the parameters that are specific to that transaction type e.g. `PaymentParams`, [see the full list](../code/modules/types_composer.md#type-aliases)
- [`CommonAppCallParams`](../code/modules/types_composer.md#commonappcallparams) are the [common app call transaction parameters](./app.md#common-app-parameters) that can be specified for every single app transaction
- [`ExecuteParams`](../code/interfaces/types_transaction.ExecuteParams.md) are the [parameters](#transaction-parameters) that control execution semantics when sending transactions to the network
- [`SendParams`](../code/interfaces/types_transaction.SendParams.md) are the [parameters](#transaction-parameters) that control execution semantics when sending transactions to the network
- [`SendSingleTransactionResult`](../code/modules/types_algorand_client.md#sendsingletransactionresult) is all of the information that is relevant when [sending a single transaction to the network](./transaction.md#sending-a-transaction)

Generally, the functions to immediately send a single transaction will emit log messages before and/or after sending the transaction. You can opt-out of this by sending `suppressLog: true`.
Expand All @@ -119,7 +119,7 @@ const result = algorand
.newGroup()
.addPayment({ sender: 'SENDERADDRESS', receiver: 'RECEIVERADDRESS', amount: (1).microAlgo() })
.addAssetOptIn({ sender: 'SENDERADDRESS', assetId: 12345n })
.execute()
.send()
```

`newGroup()` returns a new [`AlgoKitComposer`](./algokit-composer.md) instance, which can also return the group of transactions, simulate them and other things.
Expand All @@ -144,7 +144,7 @@ There are two common base interfaces that get reused:
- `validityWindow?: number` - How many rounds the transaction should be valid for, if not specified then the registered default validity window will be used.
- `firstValidRound?: bigint` - Set the first round this transaction is valid. If left undefined, the value from algod will be used. We recommend you only set this when you intentionally want this to be some time in the future.
- `lastValidRound?: bigint` - The last round this transaction is valid. It is recommended to use `validityWindow` instead.
- [`ExecuteParams`](../code/interfaces/types_transaction.ExecuteParams.md)
- [`SendParams`](../code/interfaces/types_transaction.SendParams.md)
- `maxRoundsToWaitForConfirmation?: number` - The number of rounds to wait for confirmation. By default until the latest lastValid has past.
- `suppressLog?: boolean` - Whether to suppress log messages from transaction send, default: do not suppress.

Expand Down
82 changes: 43 additions & 39 deletions docs/capabilities/app-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,20 @@ As well as allowing you to control creation and deployment of apps, the `AppFact
This is possible via two methods on the app factory:

- [`factory.getAppClientById(params)`](../code/classes/types_app_factory.AppFactory.md#getappclientbyid) - Returns a new `AppClient` client for an app instance of the given ID. Automatically populates appName, defaultSender and source maps from the factory if not specified in the params.
- [`factory.getAppClientByCreatorAddressAndName(params)`](../code/classes/types_app_factory.AppFactory.md#getappclientbycreatoraddressandname) - Returns a new `AppClient` client, resolving the app by creator address and name using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). Automatically populates appName, defaultSender and source maps from the factory if not specified in the params.
- [`factory.getAppClientByCreatorAndName(params)`](../code/classes/types_app_factory.AppFactory.md#getappclientbycreatorandname) - Returns a new `AppClient` client, resolving the app by creator address and name using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note). Automatically populates appName, defaultSender and source maps from the factory if not specified in the params.

```typescript
const app1 = factory.getAppClientById({ appId: 12345n })
const app2 = factory.getAppClientById({ appId: 12346n })
const app3 = factory.getAppClientById({ appId: 12345n, defaultSender: 'SENDER2ADDRESS' })
const app4 = factory.getAppClientByCreatorAddressAndName({
const appClient1 = factory.getAppClientById({ appId: 12345n })
const appClient2 = factory.getAppClientById({ appId: 12346n })
const appClient3 = factory.getAppClientById({ appId: 12345n, defaultSender: 'SENDER2ADDRESS' })
const appClient4 = factory.getAppClientByCreatorAndName({
creatorAddress: 'CREATORADDRESS',
})
const app5 = factory.getAppClientByCreatorAddressAndName({
const appClient5 = factory.getAppClientByCreatorAndName({
creatorAddress: 'CREATORADDRESS',
appName: 'NonDefaultAppName',
})
const app6 = factory.getAppClientByCreatorAddressAndName({
const appClient6 = factory.getAppClientByCreatorAndName({
creatorAddress: 'CREATORADDRESS',
appName: 'NonDefaultAppName',
ignoreCache: true, // Perform fresh indexer lookups
Expand All @@ -108,9 +108,9 @@ The create method is a wrapper over the `appCreate` (bare calls) and `appCreateM

```typescript
// Use no-argument bare-call
const { result, app } = factory.create()
const { result, appClient } = factory.send.bare.create()
// Specify parameters for bare-call and override other parameters
const { result, app } = factory.create({
const { result, appClient } = factory.send.bare.create({
args: [new Uint8Array(1, 2, 3, 4)],
staticFee: (3000).microAlgo(),
onComplete: algosdk.OnApplicationComplete.OptIn,
Expand All @@ -123,13 +123,13 @@ const { result, app } = factory.create({
populateAppCallResources: true,
})
// Specify parameters for ABI method call
const { result, app } = factory.create({
const { result, appClient } = factory.send.create({
method: 'create_application',
args: [1, 'something'],
})
```

If you want to construct a custom create call, use the underlying [`algorand.send.appCreate` / `algorand.transactions.appCreate` / `algorand.send.appCreateMethodCall` / `algorand.transactions.appCreateMethodCall` methods](./app.md#creation) then you can get params objects:
If you want to construct a custom create call, use the underlying [`algorand.send.appCreate` / `algorand.createTransaction.appCreate` / `algorand.send.appCreateMethodCall` / `algorand.createTransaction.appCreateMethodCall` methods](./app.md#creation) then you can get params objects:

- `factory.params.create(params)` - ABI method create call for deploy method or an underlying [`appCreateMethodCall` call](./app.md#creation)
- `factory.params.bare.create(params)` - Bare create call for deploy method or an underlying [`appCreate` call](./app.md#creation)
Expand All @@ -150,9 +150,9 @@ The deploy method is a wrapper over the [`AppDeployer`'s `deploy` method](./app-
```typescript
// Use no-argument bare-calls to deploy with default behaviour
// for when update or schema break detected (fail the deployment)
const { result, app } = factory.deploy({})
const { result, appClient } = factory.deploy({})
// Specify parameters for bare-calls and override the schema break behaviour
const { result, app } = factory.deploy({
const { result, appClient } = factory.deploy({
createParams: {
args: [new Uint8Array(1, 2, 3, 4)],
staticFee: (3000).microAlgo(),
Expand All @@ -174,7 +174,7 @@ const { result, app } = factory.deploy({
deletable: true,
})
// Specify parameters for ABI method calls
const { result, app } = factory.deploy({
const { result, appClient } = factory.deploy({
createParams: {
method: "create_application",
args: [1, "something"],
Expand Down Expand Up @@ -210,8 +210,8 @@ This is done via the following properties:

- `appClient.params.{onComplete}(params)` - Params for an ABI method call
- `appClient.params.bare.{onComplete}(params)` - Params for a bare call
- `appClient.transactions.{onComplete}(params)` - Transaction(s) for an ABI method call
- `appClient.transactions.bare.{onComplete}(params)` - Transaction for a bare call
- `appClient.createTransaction.{onComplete}(params)` - Transaction(s) for an ABI method call
- `appClient.createTransaction.bare.{onComplete}(params)` - Transaction for a bare call
- `appClient.send.{onComplete}(params)` - Sign and send an ABI method call
- `appClient.send.bare.{onComplete}(params)` - Sign and send a bare call

Expand All @@ -229,23 +229,23 @@ The input payload for all of these calls is the same as the [underlying app meth
The return payload for all of these is the same as the [underlying methods](./app.md#calling-apps).

```typescript
const call1 = await app.send.update({
const call1 = await appClient.send.update({
method: 'update_abi',
args: ['string_io'],
deployTimeParams,
})
const call2 = await app.send.delete({
const call2 = await appClient.send.delete({
method: 'delete_abi',
args: ['string_io'],
})
const call3 = await app.send.optIn({ method: 'opt_in' })
const call4 = await app.send.bare.clearState()
const call3 = await appClient.send.optIn({ method: 'opt_in' })
const call4 = await appClient.send.bare.clearState()

const transaction = await app.transactions.bare.closeOut({
const transaction = await appClient.createTransaction.bare.closeOut({
args: [new Uint8Array(1, 2, 3)],
})

const params = app.params.optIn({ method: 'optin' })
const params = appClient.params.optIn({ method: 'optin' })
```

## Funding the app account
Expand All @@ -259,18 +259,18 @@ The input parameters are:
Note: If you are passing the funding payment in as an ABI argument so it can be validated by the ABI method then you'll want to get the funding call as a transaction, e.g.:

```typescript
const result = await app.send.call({
const result = await appClient.send.call({
method: 'bootstrap',
args: [
app.transactions.fundAppAccount({
appClient.createTransaction.fundAppAccount({
amount: microAlgo(200_000),
}),
],
boxReferences: ['Box1'],
})
```

You can also get the funding call as a params object via `app.params.fundAppAccount(params)`.
You can also get the funding call as a params object via `appClient.params.fundAppAccount(params)`.

## Reading state

Expand All @@ -282,9 +282,9 @@ The ARC-56 app spec can specify detailed information about the encoding format o

You can access this functionality via:

- `app.state.global.{method}()` - Global state
- `app.state.local(address).{method}()` - Local state
- `app.state.box.{method}()` - Box storage
- `appClient.state.global.{method}()` - Global state
- `appClient.state.local(address).{method}()` - Local state
- `appClient.state.box.{method}()` - Box storage

Where `{method}` is one of:

Expand All @@ -294,10 +294,10 @@ Where `{method}` is one of:
- `getMap(mapName)` - Returns all map values for the given map in a key=>value record. It's recommended that this is only done when you have a unique `prefix` for the map otherwise there's a high risk that incorrect values will be included in the map.

```typescript
const values = app.state.global.getAll()
const value = app.state.local('ADDRESS').getValue('value1')
const mapValue = app.state.box.getMapValue('map1', 'mapKey')
const map = app.state.global.getMap('myMap')
const values = appClient.state.global.getAll()
const value = appClient.state.local('ADDRESS').getValue('value1')
const mapValue = appClient.state.box.getMapValue('map1', 'mapKey')
const map = appClient.state.global.getMap('myMap')
```

### Generic methods
Expand All @@ -313,17 +313,17 @@ There are various methods defined that let you read state from the smart contrac
- `getBoxValuesFromABIType(type, filter)` - Gets the current values of the boxes from an ABI type using [`algorand.app.getBoxValuesFromABIType`](./app.md#boxes)

```typescript
const globalState = await app.getGlobalState()
const localState = await app.getLocalState('ACCOUNTADDRESS')
const globalState = await appClient.getGlobalState()
const localState = await appClient.getLocalState('ACCOUNTADDRESS')

const boxName: BoxReference = 'my-box'
const boxName2: BoxReference = 'my-box2'

const boxNames = app.getBoxNames()
const boxValue = app.getBoxValue(boxName)
const boxValues = app.getBoxValues([boxName, boxName2])
const boxABIValue = app.getBoxValueFromABIType(boxName, algosdk.ABIStringType)
const boxABIValues = app.getBoxValuesFromABIType([boxName, boxName2], algosdk.ABIStringType)
const boxNames = appClient.getBoxNames()
const boxValue = appClient.getBoxValue(boxName)
const boxValues = appClient.getBoxValues([boxName, boxName2])
const boxABIValue = appClient.getBoxValueFromABIType(boxName, algosdk.ABIStringType)
const boxABIValues = appClient.getBoxValuesFromABIType([boxName, boxName2], algosdk.ABIStringType)
```

## Handling logic errors and diagnosing errors
Expand Down Expand Up @@ -364,3 +364,7 @@ Config.configure({ debug: true })
If you do that then the exception will have the `traces` property within the underlying exception will have key information from the simulation within it and this will get populated into the `led.traces` property of the thrown error.

When this debug flag is set, it will also emit debugging symbols to allow break-point debugging of the calls if the [project root is also configured](./debugging.md).

## Default arguments

If an ABI method call specifies default argument values for any of its arguments you can pass in `undefined` for the value of that argument for the default value to be automatically populated.
6 changes: 2 additions & 4 deletions docs/capabilities/app-deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,7 @@ const deploymentResult = algorand.appDeployer.deploy({
// How to handle a contract code update
onUpdate: OnUpdate.Update,
// Optional execution control parameters
executeParams: {
populateAppCallResources: true,
},
populateAppCallResources: true,
})
```

Expand All @@ -229,9 +227,9 @@ The first parameter `deployment` is an [`AppDeployParams`](../code/interfaces/ty
- [`TealTemplateParams`](../code/interfaces/types_app.TealTemplateParams.md) is a `key => value` object that will result in `TMPL_{key}` being replaced with `value` (where a string or `Uint8Array` will be appropriately encoded as bytes within the TEAL code)
- `onSchemaBreak?: 'replace' | 'fail' | 'append' | OnSchemaBreak` - determines [what should happen](../code/enums/types_app.OnSchemaBreak.md) if a breaking change to the schema is detected (e.g. if you need more global or local state that was previously requested when the contract was originally created)
- `onUpdate?: 'update' | 'replace' | 'fail' | 'append' | OnUpdate` - determines [what should happen](../code/enums/types_app.OnUpdate.md) if an update to the smart contract is detected (e.g. the TEAL code has changed since last deployment)
- `executeParams?: ExecuteParams` - [transaction execution control parameters](./algorand-client.md#transaction-parameters)
- `existingDeployments?: AppLookup` - optionally allows the [app lookup retrieval](#lookup-deployed-apps-by-name) to be skipped if it's already been retrieved outside of this `AppDeployer` instance
- `ignoreCache?: boolean` - optionally allows the [lookup cache](#lookup-deployed-apps-by-name) to be ignored and force retrieval of fresh deployment metadata from indexer
- Everything from [`SendParams`](../code/interfaces/types_transaction.SendParams.md) - [transaction execution control parameters](./algorand-client.md#transaction-parameters)

### Idempotency

Expand Down
Loading

0 comments on commit eebb653

Please sign in to comment.