Skip to content

Commit 6c987a4

Browse files
authored
Merge pull request #6 from algorandfoundation/feat-app-global
feat: implement AppGlobal and add tests for it
2 parents c2b789d + b3b8405 commit 6c987a4

32 files changed

+1111
-106
lines changed

.editorconfig

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[*]
2+
charset = utf-8
3+
insert_final_newline = true
4+
end_of_line = lf
5+
indent_style = space
6+
indent_size = 2
7+
tab_width = 2
8+
max_line_length = 140
9+
trim_trailing_whitespace = true

.gitattributes

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Set the repository to show as TypeScript rather than JS in GitHub
2+
*.js linguist-detectable=false
3+
4+
# Treat text as lf
5+
* text=auto
6+
* eol=lf

.vscode/settings.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"editor.formatOnSave": true,
3+
"editor.defaultFormatter": "esbenp.prettier-vscode",
4+
"editor.codeActionsOnSave": {
5+
"source.fixAll.eslint": "explicit",
6+
"source.organizeImports": "explicit"
7+
},
8+
"eslint.validate": ["typescript"],
9+
"eslint.options": {
10+
"extensions": [".ts"]
11+
},
12+
"typescript.preferences.quoteStyle": "single",
13+
"yaml.schemas": {
14+
"https://json.schemastore.org/github-workflow.json": "/.github/workflows/*.yml",
15+
"https://json.schemastore.org/github-action.json": "/.github/actions/**/*.yml"
16+
},
17+
"files.associations": {
18+
"*.mdx": "markdown"
19+
}
20+
}

package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
},
6464
"dependencies": {
6565
"@algorandfoundation/algokit-utils": "^6.2.1",
66-
"@algorandfoundation/algorand-typescript": "^0.0.1-alpha.9",
66+
"@algorandfoundation/algorand-typescript": "^0.0.1-alpha.12",
6767
"@algorandfoundation/puya-ts": "^1.0.0-alpha.14",
6868
"algosdk": "^2.9.0",
6969
"elliptic": "^6.5.7",

src/collections/custom-key-map.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Account, internal } from '@algorandfoundation/algorand-typescript'
2-
import { encodingUtil } from '@algorandfoundation/puya-ts'
32
import { DeliberateAny } from '../typescript-helpers'
3+
import { asBytesCls, asUint64Cls } from '../util'
44

55
type Primitive = number | bigint | string | boolean
66
export abstract class CustomKeyMap<TKey, TValue> implements Map<TKey, TValue> {
@@ -25,6 +25,13 @@ export abstract class CustomKeyMap<TKey, TValue> implements Map<TKey, TValue> {
2525
get(key: TKey): TValue | undefined {
2626
return this.#map.get(this.#keySerializer(key))?.[1]
2727
}
28+
getOrFail(key: TKey): TValue {
29+
const value = this.get(key)
30+
if (value === undefined) {
31+
throw internal.errors.internalError('Key not found')
32+
}
33+
return value
34+
}
2835
has(key: TKey): boolean {
2936
return this.#map.has(this.#keySerializer(key))
3037
}
@@ -62,6 +69,18 @@ export class AccountMap<TValue> extends CustomKeyMap<Account, TValue> {
6269
}
6370

6471
private static getAddressStrFromAccount = (acc: Account) => {
65-
return encodingUtil.uint8ArrayToHex(internal.primitives.BytesCls.fromCompat(acc.bytes).asUint8Array())
72+
return asBytesCls(acc.bytes).valueOf()
73+
}
74+
}
75+
76+
export class BytesMap<TValue> extends CustomKeyMap<internal.primitives.StubBytesCompat, TValue> {
77+
constructor() {
78+
super((bytes) => asBytesCls(bytes).valueOf())
79+
}
80+
}
81+
82+
export class Uint64Map<TValue> extends CustomKeyMap<internal.primitives.StubUint64Compat, TValue> {
83+
constructor() {
84+
super((uint64) => asUint64Cls(uint64).valueOf())
6685
}
6786
}

src/context-helpers/internal-context.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ class InternalContext {
6161
}
6262

6363
getApplicationData(id: internal.primitives.StubUint64Compat): ApplicationData {
64-
const key = internal.primitives.Uint64Cls.fromCompat(id)
65-
const data = this.ledger.applicationDataMap.get(key.asBigInt())
64+
const data = this.ledger.applicationDataMap.get(id)
6665
if (!data) {
6766
throw internal.errors.internalError('Unknown application, check correct testing context is active')
6867
}

src/impl/account.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Account, Application, Asset, bytes, internal, uint64 } from '@algorandfoundation/algorand-typescript'
2+
import { Uint64Map } from '../collections/custom-key-map'
23
import { DEFAULT_ACCOUNT_MIN_BALANCE, ZERO_ADDRESS } from '../constants'
34
import { lazyContext } from '../context-helpers/internal-context'
45
import { Mutable } from '../typescript-helpers'
@@ -17,8 +18,8 @@ export class AssetHolding {
1718
}
1819

1920
export class AccountData {
20-
optedAssets = new Map<bigint, AssetHolding>()
21-
optedApplications = new Map<bigint, Application>()
21+
optedAssets = new Uint64Map<AssetHolding>()
22+
optedApplications = new Uint64Map<Application>()
2223
account: Mutable<Omit<Account, 'bytes' | 'isOptedIn'>>
2324

2425
constructor() {
@@ -91,7 +92,7 @@ export class AccountCls extends BytesBackedCls implements Account {
9192

9293
isOptedIn(assetOrApp: Asset | Application): boolean {
9394
if (assetOrApp instanceof AssetCls) {
94-
return this.data.optedAssets.has(asUint64Cls(assetOrApp.id).asBigInt())
95+
return this.data.optedAssets.has(assetOrApp.id)
9596
}
9697
if (assetOrApp instanceof ApplicationCls) {
9798
return this.data.optedApplications.has(asUint64Cls(assetOrApp.id).asBigInt())

src/impl/acct-params.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -22,51 +22,51 @@ export const minBalance = (a: Account | internal.primitives.StubUint64Compat): u
2222
}
2323

2424
export const AcctParams: internal.opTypes.AcctParamsType = {
25-
acctBalance: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
25+
acctBalance(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
2626
const acct = getAccount(a)
2727
return [acct.balance, acct.balance !== 0]
2828
},
29-
acctMinBalance: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
29+
acctMinBalance(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
3030
const acct = getAccount(a)
3131
return [acct.minBalance, acct.balance !== 0]
3232
},
33-
acctAuthAddr: function (a: Account | internal.primitives.StubUint64Compat): readonly [Account, boolean] {
33+
acctAuthAddr(a: Account | internal.primitives.StubUint64Compat): readonly [Account, boolean] {
3434
const acct = getAccount(a)
3535
return [acct.authAddress, acct.balance !== 0]
3636
},
37-
acctTotalNumUint: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
37+
acctTotalNumUint(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
3838
const acct = getAccount(a)
3939
return [acct.totalNumUint, acct.balance !== 0]
4040
},
41-
acctTotalNumByteSlice: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
41+
acctTotalNumByteSlice(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
4242
const acct = getAccount(a)
4343
return [acct.totalNumByteSlice, acct.balance !== 0]
4444
},
45-
acctTotalExtraAppPages: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
45+
acctTotalExtraAppPages(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
4646
const acct = getAccount(a)
4747
return [acct.totalExtraAppPages, acct.balance !== 0]
4848
},
49-
acctTotalAppsCreated: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
49+
acctTotalAppsCreated(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
5050
const acct = getAccount(a)
5151
return [acct.totalAppsCreated, acct.balance !== 0]
5252
},
53-
acctTotalAppsOptedIn: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
53+
acctTotalAppsOptedIn(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
5454
const acct = getAccount(a)
5555
return [acct.totalAppsOptedIn, acct.balance !== 0]
5656
},
57-
acctTotalAssetsCreated: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
57+
acctTotalAssetsCreated(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
5858
const acct = getAccount(a)
5959
return [acct.totalAssetsCreated, acct.balance !== 0]
6060
},
61-
acctTotalAssets: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
61+
acctTotalAssets(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
6262
const acct = getAccount(a)
6363
return [acct.totalAssets, acct.balance !== 0]
6464
},
65-
acctTotalBoxes: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
65+
acctTotalBoxes(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
6666
const acct = getAccount(a)
6767
return [acct.totalBoxes, acct.balance !== 0]
6868
},
69-
acctTotalBoxBytes: function (a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
69+
acctTotalBoxBytes(a: Account | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
7070
const acct = getAccount(a)
7171
return [acct.totalBoxBytes, acct.balance !== 0]
7272
},

src/impl/app-global.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Application, Bytes, bytes, internal, Uint64, uint64 } from '@algorandfoundation/algorand-typescript'
2+
import { lazyContext } from '../context-helpers/internal-context'
3+
import { asBytes } from '../util'
4+
import { getApp } from './app-params'
5+
6+
export const AppGlobal: internal.opTypes.AppGlobalType = {
7+
delete(a: internal.primitives.StubBytesCompat): void {
8+
lazyContext.ledger.setGlobalState(lazyContext.activeApplication, a, undefined)
9+
},
10+
getBytes(a: internal.primitives.StubBytesCompat): bytes {
11+
return this.getExBytes(0, asBytes(a))[0]
12+
},
13+
getUint64(a: internal.primitives.StubBytesCompat): uint64 {
14+
return this.getExUint64(0, asBytes(a))[0]
15+
},
16+
getExBytes(a: Application | internal.primitives.StubUint64Compat, b: internal.primitives.StubBytesCompat): readonly [bytes, boolean] {
17+
const app = getApp(a)
18+
if (app === undefined) {
19+
return [Bytes(), false]
20+
}
21+
const [state, exists] = lazyContext.ledger.getGlobalState(app, b)
22+
if (!exists) {
23+
return [Bytes(), false]
24+
}
25+
return [state!.value as bytes, exists]
26+
},
27+
getExUint64(a: Application | internal.primitives.StubUint64Compat, b: internal.primitives.StubBytesCompat): readonly [uint64, boolean] {
28+
const app = getApp(a)
29+
if (app === undefined) {
30+
return [Uint64(0), false]
31+
}
32+
const [state, exists] = lazyContext.ledger.getGlobalState(app, b)
33+
if (!exists) {
34+
return [Uint64(0), false]
35+
}
36+
return [state!.value as uint64, exists]
37+
},
38+
put(a: internal.primitives.StubBytesCompat, b: internal.primitives.StubUint64Compat | internal.primitives.StubBytesCompat): void {
39+
lazyContext.ledger.setGlobalState(lazyContext.activeApplication, a, b)
40+
},
41+
}

src/impl/app-params.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -24,39 +24,39 @@ export const getApp = (app: Application | internal.primitives.StubUint64Compat):
2424
}
2525

2626
export const AppParams: internal.opTypes.AppParamsType = {
27-
appApprovalProgram: function (a: Application | internal.primitives.StubUint64Compat): readonly [bytes, boolean] {
27+
appApprovalProgram(a: Application | internal.primitives.StubUint64Compat): readonly [bytes, boolean] {
2828
const app = getApp(a)
2929
return app === undefined ? [Bytes(), false] : [app.approvalProgram, true]
3030
},
31-
appClearStateProgram: function (a: Application | internal.primitives.StubUint64Compat): readonly [bytes, boolean] {
31+
appClearStateProgram(a: Application | internal.primitives.StubUint64Compat): readonly [bytes, boolean] {
3232
const app = getApp(a)
3333
return app === undefined ? [Bytes(), false] : [app.clearStateProgram, true]
3434
},
35-
appGlobalNumUint: function (a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
35+
appGlobalNumUint(a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
3636
const app = getApp(a)
3737
return app === undefined ? [Uint64(0), false] : [app.globalNumUint, true]
3838
},
39-
appGlobalNumByteSlice: function (a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
39+
appGlobalNumByteSlice(a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
4040
const app = getApp(a)
4141
return app === undefined ? [Uint64(0), false] : [app.globalNumBytes, true]
4242
},
43-
appLocalNumUint: function (a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
43+
appLocalNumUint(a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
4444
const app = getApp(a)
4545
return app === undefined ? [Uint64(0), false] : [app.localNumUint, true]
4646
},
47-
appLocalNumByteSlice: function (a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
47+
appLocalNumByteSlice(a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
4848
const app = getApp(a)
4949
return app === undefined ? [Uint64(0), false] : [app.localNumBytes, true]
5050
},
51-
appExtraProgramPages: function (a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
51+
appExtraProgramPages(a: Application | internal.primitives.StubUint64Compat): readonly [uint64, boolean] {
5252
const app = getApp(a)
5353
return app === undefined ? [Uint64(0), false] : [app.extraProgramPages, true]
5454
},
55-
appCreator: function (a: Application | internal.primitives.StubUint64Compat): readonly [Account, boolean] {
55+
appCreator(a: Application | internal.primitives.StubUint64Compat): readonly [Account, boolean] {
5656
const app = getApp(a)
5757
return app === undefined ? [Account(), false] : [app.creator, true]
5858
},
59-
appAddress: function (a: Application | internal.primitives.StubUint64Compat): readonly [Account, boolean] {
59+
appAddress(a: Application | internal.primitives.StubUint64Compat): readonly [Account, boolean] {
6060
const app = getApp(a)
6161
return app === undefined ? [Account(), false] : [app.address, true]
6262
},

src/impl/application.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { Account, Application, Bytes, bytes, uint64 } from '@algorandfoundation/algorand-typescript'
22
import algosdk from 'algosdk'
3+
import { BytesMap } from '../collections/custom-key-map'
34
import { ALWAYS_APPROVE_TEAL_PROGRAM } from '../constants'
45
import { lazyContext } from '../context-helpers/internal-context'
56
import { Mutable } from '../typescript-helpers'
67
import { asBigInt, asUint64 } from '../util'
78
import { Uint64BackedCls } from './base'
9+
import { GlobalStateCls, LocalStateMapCls } from './state'
810

911
export class ApplicationData {
10-
application: Mutable<Omit<Application, 'id' | 'address'>> & { appLogs: bytes[] }
12+
application: Mutable<Omit<Application, 'id' | 'address'>> & {
13+
appLogs: bytes[]
14+
globalStates: BytesMap<GlobalStateCls<unknown>>
15+
localStates: BytesMap<LocalStateMapCls<unknown>>
16+
}
1117
isCreating: boolean = false
1218

1319
get appLogs() {
@@ -25,6 +31,8 @@ export class ApplicationData {
2531
extraProgramPages: 0,
2632
creator: lazyContext.defaultSender,
2733
appLogs: [],
34+
globalStates: new BytesMap(),
35+
localStates: new BytesMap(),
2836
}
2937
}
3038
}

src/impl/asset-holding.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Account, Asset, internal, Uint64, uint64 } from '@algorandfoundation/algorand-typescript'
22
import { lazyContext } from '../context-helpers/internal-context'
3-
import { asBigInt } from '../util'
43
import { AssetHolding as AssetHoldingData } from './account'
54
import { getAccount } from './acct-params'
65
import { getAsset } from './asset-params'
@@ -16,22 +15,22 @@ const getAssetHolding = (
1615
}
1716

1817
const accountData = lazyContext.getAccountData(account)
19-
const holding = accountData.optedAssets.get(asBigInt(asset.id))
18+
const holding = accountData.optedAssets.get(asset.id)
2019
if (holding === undefined) {
2120
return undefined
2221
}
2322
return holding
2423
}
2524

2625
export const AssetHolding: internal.opTypes.AssetHoldingType = {
27-
assetBalance: function (
26+
assetBalance(
2827
a: Account | internal.primitives.StubUint64Compat,
2928
b: Asset | internal.primitives.StubUint64Compat,
3029
): readonly [uint64, boolean] {
3130
const holding = getAssetHolding(a, b)
3231
return holding === undefined ? [Uint64(0), false] : [holding.balance, true]
3332
},
34-
assetFrozen: function (
33+
assetFrozen(
3534
a: Account | internal.primitives.StubUint64Compat,
3635
b: Asset | internal.primitives.StubUint64Compat,
3736
): readonly [boolean, boolean] {

0 commit comments

Comments
 (0)