Skip to content

Commit c2b789d

Browse files
authored
Merge pull request #5 from algorandfoundation/feat-gload-block
feat: implement gloadBytes, gloadUint64, and Block
2 parents bb21d69 + 228d2cb commit c2b789d

File tree

12 files changed

+192
-21
lines changed

12 files changed

+192
-21
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { arc4, assert, BaseContract, Bytes, op, uint64, Uint64 } from '@algorandfoundation/algorand-typescript'
2+
3+
export class ScratchSlotsContract extends arc4.Contract {
4+
@arc4.abimethod()
5+
public storeData(): boolean {
6+
op.Scratch.store(1, Uint64(5))
7+
op.Scratch.store(2, Bytes('Hello World'))
8+
assert(op.Scratch.loadUint64(1) === Uint64(5))
9+
assert(op.Scratch.loadBytes(2) === Bytes('Hello World'))
10+
return true
11+
}
12+
}
13+
14+
export class SimpleScratchSlotsContract extends BaseContract {
15+
approvalProgram(): boolean | uint64 {
16+
assert(op.Scratch.loadUint64(1) === Uint64(5))
17+
assert(op.Scratch.loadBytes(2) === Bytes('Hello World'))
18+
return Uint64(1)
19+
}
20+
clearStateProgram(): boolean | uint64 {
21+
return Uint64(1)
22+
}
23+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing'
2+
import { afterEach, describe, expect, it } from 'vitest'
3+
import { ScratchSlotsContract, SimpleScratchSlotsContract } from './contract.algo'
4+
import { Bytes, Uint64 } from '@algorandfoundation/algorand-typescript'
5+
6+
describe('ScratchSlotsContract', () => {
7+
const ctx = new TestExecutionContext()
8+
afterEach(() => {
9+
ctx.reset()
10+
})
11+
it('should be able to store data', async () => {
12+
const contract = ctx.contract.create(ScratchSlotsContract)
13+
const result = contract.storeData()
14+
expect(result).toBe(true)
15+
16+
const scratchSpace = ctx.txn.lastGroup.getScratchSpace()
17+
18+
expect(scratchSpace[1]).toEqual(Uint64(5))
19+
expect(scratchSpace[2]).toEqual(Bytes('Hello World'))
20+
})
21+
22+
it('should be able to load stored data', async () => {
23+
const contract = ctx.contract.create(SimpleScratchSlotsContract)
24+
ctx.txn.createScope([ctx.any.txn.applicationCall({ scratchSpace: [Uint64(0), Uint64(5), Bytes('Hello World')] })]).execute(() => {
25+
const result = contract.approvalProgram()
26+
expect(result).toEqual(Uint64(1))
27+
})
28+
})
29+
})

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"postinstall": "patch-package",
1010
"audit": "better-npm-audit audit",
1111
"format": "prettier --write .",
12-
"lint": "eslint \"src/**/*.ts\"",
13-
"lint:fix": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix",
12+
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"examples/**/*.ts\"",
13+
"lint:fix": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"examples/**/*.ts\" --fix",
1414
"build": "run-s build:*",
1515
"build:0-clean": "rimraf dist coverage",
1616
"build:1-lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" --max-warnings 0",

src/impl/block.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { bytes, internal, uint64 } from '@algorandfoundation/algorand-typescript'
2+
import { lazyContext } from '../context-helpers/internal-context'
3+
import { itob } from './pure'
4+
import { asUint64 } from '../util'
5+
6+
export const Block: internal.opTypes.BlockType = {
7+
blkSeed: function (a: internal.primitives.StubUint64Compat): bytes {
8+
return itob(lazyContext.ledger.getBlockContent(a).seed)
9+
},
10+
blkTimestamp: function (a: internal.primitives.StubUint64Compat): uint64 {
11+
return asUint64(lazyContext.ledger.getBlockContent(a).timestamp)
12+
},
13+
}

src/impl/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export { GTxn } from './gtxn'
88
export * from './pure'
99
export { Txn, gaid } from './txn'
1010
export { GITxn, ITxn, ITxnCreate } from './itxn'
11-
export { Scratch } from './scratch'
11+
export { Scratch, gloadBytes, gloadUint64 } from './scratch'
12+
export { Block } from './block'

src/impl/scratch.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
11
import { bytes, internal, uint64 } from '@algorandfoundation/algorand-typescript'
22
import { lazyContext } from '../context-helpers/internal-context'
33

4+
export const gloadUint64: internal.opTypes.GloadUint64Type = (
5+
a: internal.primitives.StubUint64Compat,
6+
b: internal.primitives.StubUint64Compat,
7+
): uint64 => {
8+
const txn = lazyContext.activeGroup.getTransaction(a)
9+
const result = txn.getScratchSlot(b)
10+
if (result instanceof internal.primitives.Uint64Cls) {
11+
return result.asAlgoTs()
12+
}
13+
throw new internal.errors.InternalError('invalid scratch slot type')
14+
}
15+
16+
export const gloadBytes: internal.opTypes.GloadBytesType = (
17+
a: internal.primitives.StubUint64Compat,
18+
b: internal.primitives.StubUint64Compat,
19+
): bytes => {
20+
const txn = lazyContext.activeGroup.getTransaction(a)
21+
const result = txn.getScratchSlot(b)
22+
if (result instanceof internal.primitives.BytesCls) {
23+
return result.asAlgoTs()
24+
}
25+
throw new internal.errors.InternalError('invalid scratch slot type')
26+
}
27+
428
export const Scratch: internal.opTypes.ScratchType = {
529
loadBytes: function (a: internal.primitives.StubUint64Compat): bytes {
630
const result = lazyContext.activeGroup.activeTransaction.getScratchSlot(a)
731
if (result instanceof internal.primitives.BytesCls) {
832
return result as bytes
933
}
10-
throw new internal.errors.InternalError('Invalid scratch slot type')
34+
throw new internal.errors.InternalError('invalid scratch slot type')
1135
},
1236
loadUint64: function (a: internal.primitives.StubUint64Compat): uint64 {
1337
const result = lazyContext.activeGroup.activeTransaction.getScratchSlot(a)
1438
if (result instanceof internal.primitives.Uint64Cls) {
1539
return result as uint64
1640
}
17-
throw new internal.errors.InternalError('Invalid scratch slot type')
41+
throw new internal.errors.InternalError('invalid scratch slot type')
1842
},
1943
store: function (
2044
a: internal.primitives.StubUint64Compat,

src/impl/transactions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export type ApplicationTransactionFields = TxnFields<gtxn.ApplicationTxn> &
233233
approvalProgramPages: Array<bytes>
234234
clearStateProgramPages: Array<bytes>
235235
appLogs: Array<bytes>
236-
scratchSpace: Array<bytes | uint64>
236+
scratchSpace: Record<number, bytes | uint64>
237237
}>
238238

239239
export class ApplicationTransaction extends TransactionBase implements gtxn.ApplicationTxn {
@@ -266,7 +266,7 @@ export class ApplicationTransaction extends TransactionBase implements gtxn.Appl
266266
this.#apps = fields.apps ?? []
267267
this.#approvalProgramPages = fields.approvalProgramPages ?? (fields.approvalProgram ? [fields.approvalProgram] : [])
268268
this.#clearStateProgramPages = fields.clearStateProgramPages ?? (fields.clearStateProgram ? [fields.clearStateProgram] : [])
269-
fields.scratchSpace?.forEach((v, i) => this.setScratchSlot(i, v))
269+
Object.entries(fields.scratchSpace ?? {}).forEach(([k, v]) => this.setScratchSlot(Number(k), v))
270270
}
271271

272272
readonly appId: Application

src/impl/txn.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
11
import { Account, Application, arc4, Asset, bytes, internal, TransactionType, uint64 } from '@algorandfoundation/algorand-typescript'
22
import { lazyContext } from '../context-helpers/internal-context'
33
import { asNumber, asUint64, asUint64Cls } from '../util'
4-
// import {
5-
// getApplicationTransaction,
6-
// getAssetConfigTransaction,
7-
// getAssetFreezeTransaction,
8-
// getAssetTransferTransaction,
9-
// getKeyRegistrationTransaction,
10-
// getPaymentTransaction,
11-
// getTransaction,
12-
// } from './gtxn'
134

145
export const gaid = (a: internal.primitives.StubUint64Compat): uint64 => {
156
const group = lazyContext.activeGroup
16-
const transaction = group.transactions[asNumber(a)]
7+
const transaction = group.getTransaction(a)
178
if (transaction.type === TransactionType.ApplicationCall) {
189
return transaction.createdApp.id
1910
} else if (transaction.type === TransactionType.AssetConfig) {

src/subcontexts/ledger-context.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ import { AssetData } from '../impl/asset'
77
import { GlobalData } from '../impl/global'
88
import { asBigInt, asMaybeUint64Cls, asUint64, asUint64Cls, iterBigInt } from '../util'
99

10+
interface BlockData {
11+
seed: bigint
12+
timestamp: bigint
13+
}
14+
1015
export class LedgerContext {
1116
appIdIter = iterBigInt(1001n, MAX_UINT64)
1217
assetIdIter = iterBigInt(1001n, MAX_UINT64)
1318
applicationDataMap = new Map<bigint, ApplicationData>()
1419
appIdContractMap = new Map<bigint, BaseContract>()
1520
accountDataMap = new AccountMap<AccountData>()
1621
assetDataMap = new Map<bigint, AssetData>()
22+
blocks = new Map<bigint, BlockData>()
1723
globalData = new GlobalData()
1824

1925
addAppIdContractMap(appId: internal.primitives.StubUint64Compat, contract: BaseContract): void {
@@ -76,4 +82,24 @@ export class LedgerContext {
7682
...data,
7783
}
7884
}
85+
86+
setBlock(
87+
index: internal.primitives.StubUint64Compat,
88+
seed: internal.primitives.StubUint64Compat,
89+
timestamp: internal.primitives.StubUint64Compat,
90+
): void {
91+
const i = asBigInt(index)
92+
const s = asBigInt(seed)
93+
const t = asBigInt(timestamp)
94+
95+
this.blocks.set(i, { seed: s, timestamp: t })
96+
}
97+
98+
getBlockContent(index: internal.primitives.StubUint64Compat): BlockData {
99+
const i = asBigInt(index)
100+
if (this.blocks.has(i)) {
101+
return this.blocks.get(i)!
102+
}
103+
throw internal.errors.internalError(`Block ${i} not set`)
104+
}
79105
}

0 commit comments

Comments
 (0)