Skip to content

Commit 6a75d13

Browse files
committed
implement Scratch and add test for it
1 parent d13f0e0 commit 6a75d13

File tree

6 files changed

+84
-10
lines changed

6 files changed

+84
-10
lines changed

src/impl/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ 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'

src/impl/inner-transactions.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,20 @@ export class ApplicationInnerTxn extends ApplicationTransaction implements itxn.
158158
}
159159
}
160160

161-
export const createInnerTxn = <TFields extends InnerTxnFields, T extends InnerTxn>(fields: TFields): T => {
161+
export const createInnerTxn = <TFields extends InnerTxnFields>(fields: TFields) => {
162162
switch (fields.type) {
163163
case TransactionType.Payment:
164-
return new PaymentInnerTxn(fields) as T
164+
return new PaymentInnerTxn(fields)
165165
case TransactionType.AssetConfig:
166-
return new AssetConfigInnerTxn(fields) as T
166+
return new AssetConfigInnerTxn(fields)
167167
case TransactionType.AssetTransfer:
168-
return new AssetTransferInnerTxn(fields as itxn.AssetTransferFields) as T
168+
return new AssetTransferInnerTxn(fields as itxn.AssetTransferFields)
169169
case TransactionType.AssetFreeze:
170-
return new AssetFreezeInnerTxn(fields as itxn.AssetFreezeFields) as T
170+
return new AssetFreezeInnerTxn(fields as itxn.AssetFreezeFields)
171171
case TransactionType.ApplicationCall:
172-
return new ApplicationInnerTxn(fields) as unknown as T
172+
return new ApplicationInnerTxn(fields)
173173
case TransactionType.KeyRegistration:
174-
return new KeyRegistrationInnerTxn(fields) as T
174+
return new KeyRegistrationInnerTxn(fields)
175175
default:
176176
throw new internal.errors.InternalError(`Invalid inner transaction type: ${fields.type}`)
177177
}
@@ -211,7 +211,7 @@ export class ItxnParams<TFields extends InnerTxnFields, TTransaction extends Inn
211211
this.#fields = { ...fields, type }
212212
}
213213
submit(): TTransaction {
214-
return createInnerTxn<InnerTxnFields, TTransaction>(this.#fields)
214+
return createInnerTxn<InnerTxnFields>(this.#fields) as unknown as TTransaction
215215
}
216216

217217
set(p: Partial<TFields>) {

src/impl/scratch.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { bytes, internal, uint64 } from '@algorandfoundation/algo-ts'
2+
import { lazyContext } from '../context-helpers/internal-context'
3+
4+
export const Scratch: internal.opTypes.ScratchType = {
5+
loadBytes: function (a: internal.primitives.StubUint64Compat): bytes {
6+
const result = lazyContext.activeGroup.activeTransaction.getScratchSlot(a)
7+
if (result instanceof internal.primitives.BytesCls) {
8+
return result as bytes
9+
}
10+
throw new internal.errors.InternalError('Invalid scratch slot type')
11+
},
12+
loadUint64: function (a: internal.primitives.StubUint64Compat): uint64 {
13+
const result = lazyContext.activeGroup.activeTransaction.getScratchSlot(a)
14+
if (result instanceof internal.primitives.Uint64Cls) {
15+
return result as uint64
16+
}
17+
throw new internal.errors.InternalError('Invalid scratch slot type')
18+
},
19+
store: function (
20+
a: internal.primitives.StubUint64Compat,
21+
b: internal.primitives.StubUint64Compat | internal.primitives.StubBytesCompat,
22+
): void {
23+
lazyContext.activeGroup.activeTransaction.setScratchSlot(a, b)
24+
},
25+
}

src/impl/transactions.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { MAX_ITEMS_IN_LOG } from '../constants'
1515
import { lazyContext } from '../context-helpers/internal-context'
1616
import { Mutable, ObjectKeys } from '../typescript-helpers'
17-
import { asBytes, asNumber, asUint64Cls, combineIntoMaxBytePages, getRandomBytes } from '../util'
17+
import { asBytes, asMaybeBytesCls, asMaybeUint64Cls, asNumber, asUint64Cls, combineIntoMaxBytePages, getRandomBytes } from '../util'
1818

1919
const baseDefaultFields = () => ({
2020
sender: lazyContext.defaultSender,
@@ -44,6 +44,7 @@ abstract class TransactionBase {
4444
this.groupIndex = fields.groupIndex ?? baseDefaults.groupIndex
4545
this.txnId = fields.txnId ?? baseDefaults.txnId
4646
this.rekeyTo = fields.rekeyTo ?? baseDefaults.rekeyTo
47+
this.scratchSpace = Array(256).fill(Uint64(0))
4748
}
4849

4950
readonly sender: Account
@@ -56,6 +57,28 @@ abstract class TransactionBase {
5657
readonly groupIndex: uint64
5758
readonly txnId: bytes
5859
readonly rekeyTo: Account
60+
readonly scratchSpace: Array<bytes | uint64>
61+
62+
setScratchSlot(
63+
index: internal.primitives.StubUint64Compat,
64+
value: internal.primitives.StubBytesCompat | internal.primitives.StubUint64Compat,
65+
): void {
66+
const i = asNumber(index)
67+
if (i >= this.scratchSpace.length) {
68+
throw internal.errors.internalError('invalid scratch slot')
69+
}
70+
const bytesValue = asMaybeBytesCls(value)
71+
const uint64Value = asMaybeUint64Cls(value)
72+
this.scratchSpace[i] = bytesValue?.asAlgoTs() ?? uint64Value?.asAlgoTs() ?? Uint64(0)
73+
}
74+
75+
getScratchSlot(index: internal.primitives.StubUint64Compat): bytes | uint64 {
76+
const i = asNumber(index)
77+
if (i >= this.scratchSpace.length) {
78+
throw internal.errors.internalError('invalid scratch slot')
79+
}
80+
return this.scratchSpace[i]
81+
}
5982
}
6083

6184
export class PaymentTransaction extends TransactionBase implements gtxn.PaymentTxn {
@@ -201,6 +224,7 @@ export type ApplicationTransactionFields = TxnFields<gtxn.ApplicationTxn> &
201224
approvalProgramPages: Array<bytes>
202225
clearStateProgramPages: Array<bytes>
203226
appLogs: Array<bytes>
227+
scratchSpace: Array<bytes | uint64>
204228
}>
205229

206230
export class ApplicationTransaction extends TransactionBase implements gtxn.ApplicationTxn {
@@ -233,6 +257,7 @@ export class ApplicationTransaction extends TransactionBase implements gtxn.Appl
233257
this.#apps = fields.apps ?? []
234258
this.#approvalProgramPages = fields.approvalProgramPages ?? (fields.approvalProgram ? [fields.approvalProgram] : [])
235259
this.#clearStateProgramPages = fields.clearStateProgramPages ?? (fields.clearStateProgram ? [fields.clearStateProgram] : [])
260+
fields.scratchSpace?.forEach((v, i) => this.setScratchSlot(i, v))
236261
}
237262

238263
readonly appId: Application

src/subcontexts/transaction-context.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { internal, TransactionType, uint64 } from '@algorandfoundation/algo-ts'
1+
import { bytes, internal, TransactionType, uint64 } from '@algorandfoundation/algo-ts'
22
import algosdk from 'algosdk'
33
import { lazyContext } from '../context-helpers/internal-context'
44
import { DecodedLogs, decodeLogs, LogDecoding } from '../decode-logs'
@@ -162,6 +162,10 @@ export class TransactionGroup {
162162
return this.constructingItxnGroup.at(-1)!
163163
}
164164

165+
getScratchSlot(index: internal.primitives.StubUint64Compat): bytes | uint64 {
166+
return this.activeTransaction.getScratchSlot(index)
167+
}
168+
165169
patchActiveTransactionFields(fields: AllTransactionFields) {
166170
const activeTransaction = this.activeTransaction as unknown as AllTransactionFields
167171
const filteredFields = Object.fromEntries(Object.entries(fields).filter(([_, value]) => value !== undefined))

tests/state-op-codes.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,4 +367,23 @@ describe('State op codes', async () => {
367367
})
368368
})
369369
})
370+
371+
describe('Scratch', async () => {
372+
test.each([
373+
[0, Bytes('test_bytes')],
374+
[1, Uint64(42)],
375+
[2, Bytes('test_bytes')],
376+
[3, Uint64(42)],
377+
[255, Bytes('max_index')],
378+
])('should return the correct field value of the scratch slot', async (index: number, value: bytes | uint64) => {
379+
const newScratchSpace = Array(256).fill(Uint64(0))
380+
newScratchSpace[index] = value
381+
382+
ctx.txn.createScope([ctx.any.txn.applicationCall({ scratchSpace: newScratchSpace })]).execute(() => {})
383+
384+
expect(ctx.txn.lastGroup.getScratchSlot(index)).toEqual(value)
385+
386+
expect(() => ctx.txn.lastGroup.getScratchSlot(256)).toThrow('invalid scratch slot')
387+
})
388+
})
370389
})

0 commit comments

Comments
 (0)