Skip to content

Commit 1d574c1

Browse files
authored
Merge pull request #96 from algorandfoundation/fix/empty-bytes
refactor: simplify test transformer as Bytes factory method now always return dynamic bytes
2 parents 45511a9 + e59da62 commit 1d574c1

File tree

12 files changed

+66
-175
lines changed

12 files changed

+66
-175
lines changed

examples/voting/contract.algo.spec.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('VotingRoundApp', () => {
1616

1717
const createContract = () => {
1818
const contract = ctx.contract.create(VotingRoundApp)
19-
const snapshotPublicKey = Bytes<32>(keyPair.publicKey)
19+
const snapshotPublicKey = Bytes(keyPair.publicKey).toFixed({ length: 32 })
2020
const metadataIpfsCid = ctx.any.string(16)
2121
const startTime = ctx.any.uint64(Date.now() - 10_000, Date.now())
2222
const endTime = ctx.any.uint64(Date.now() + 10_000, Date.now() + 100_000)
@@ -52,7 +52,7 @@ describe('VotingRoundApp', () => {
5252
const account = ctx.any.account()
5353
const signature = nacl.sign.detached(toExternalValue(account.bytes), keyPair.secretKey)
5454
ctx.txn.createScope([ctx.any.txn.applicationCall({ sender: account })]).execute(() => {
55-
const preconditions = contract.getPreconditions(Bytes(signature))
55+
const preconditions = contract.getPreconditions(Bytes(signature).toFixed({ length: 64 }))
5656

5757
expect(preconditions.is_allowed_to_vote).toEqual(1)
5858
expect(preconditions.is_voting_open).toEqual(1)
@@ -75,7 +75,11 @@ describe('VotingRoundApp', () => {
7575
)
7676

7777
ctx.txn.createScope([ctx.any.txn.applicationCall({ appId: app, sender: account })]).execute(() => {
78-
contract.vote(ctx.any.txn.payment({ receiver: app.address, amount: voteMinBalanceReq }), Bytes(signature), answerIds)
78+
contract.vote(
79+
ctx.any.txn.payment({ receiver: app.address, amount: voteMinBalanceReq }),
80+
Bytes(signature).toFixed({ length: 64 }),
81+
answerIds,
82+
)
7983

8084
expect(contract.votesByAccount(account).value.bytes).toEqual(answerIds.bytes)
8185
expect(contract.voterCount.value).toEqual(13)

package-lock.json

Lines changed: 9 additions & 9 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
@@ -68,8 +68,8 @@
6868
"vitest": "3.2.4"
6969
},
7070
"dependencies": {
71-
"@algorandfoundation/algorand-typescript": "1.0.0-alpha.84",
72-
"@algorandfoundation/puya-ts": "1.0.0-alpha.84",
71+
"@algorandfoundation/algorand-typescript": "1.0.0-alpha.85",
72+
"@algorandfoundation/puya-ts": "1.0.0-alpha.85",
7373
"elliptic": "^6.6.1",
7474
"js-sha256": "^0.11.0",
7575
"js-sha3": "^0.9.3",

src/constants.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Bytes, FixedBytes } from './impl/primitives'
1+
import { Bytes } from './impl/primitives'
22

33
/** @internal */
44
export const UINT64_SIZE = 64
@@ -40,13 +40,12 @@ export const DEFAULT_ASSET_OPT_IN_MIN_BALANCE = 10_000
4040
/** @internal
4141
* from python code: list(b"\x85Y\xb5\x14x\xfd\x89\xc1vC\xd0]\x15\xa8\xaek\x10\xabG\xbbm\x8a1\x88\x11V\xe6\xbd;\xae\x95\xd1")
4242
*/
43-
export const DEFAULT_GLOBAL_GENESIS_HASH = FixedBytes(
44-
32,
43+
export const DEFAULT_GLOBAL_GENESIS_HASH = Bytes(
4544
new Uint8Array([
4645
133, 89, 181, 20, 120, 253, 137, 193, 118, 67, 208, 93, 21, 168, 174, 107, 16, 171, 71, 187, 109, 138, 49, 136, 17, 86, 230, 189, 59,
4746
174, 149, 209,
4847
]),
49-
)
48+
).toFixed({ length: 32 })
5049

5150
/** @internal
5251
* algorand encoded address of 32 zero bytes

src/impl/crypto.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,37 @@ import { lazyContext } from '../context-helpers/internal-context'
1010
import { InternalError, NotImplementedError } from '../errors'
1111
import { asBytes, asBytesCls, asUint8Array, concatUint8Arrays } from '../util'
1212
import type { StubBytesCompat, StubUint64Compat } from './primitives'
13-
import { Bytes, BytesCls, FixedBytes, Uint64Cls } from './primitives'
13+
import { Bytes, BytesCls, Uint64Cls } from './primitives'
1414

1515
/** @internal */
1616
export const sha256 = (a: StubBytesCompat): bytes<32> => {
1717
const bytesA = BytesCls.fromCompat(a)
1818
const hashArray = js_sha256.sha256.create().update(bytesA.asUint8Array()).digest()
19-
const hashBytes = FixedBytes(32, new Uint8Array(hashArray))
19+
const hashBytes = Bytes(new Uint8Array(hashArray)).toFixed({ length: 32 })
2020
return hashBytes
2121
}
2222

2323
/** @internal */
2424
export const sha3_256 = (a: StubBytesCompat): bytes<32> => {
2525
const bytesA = BytesCls.fromCompat(a)
2626
const hashArray = js_sha3.sha3_256.create().update(bytesA.asUint8Array()).digest()
27-
const hashBytes = FixedBytes(32, new Uint8Array(hashArray))
27+
const hashBytes = Bytes(new Uint8Array(hashArray)).toFixed({ length: 32 })
2828
return hashBytes
2929
}
3030

3131
/** @internal */
3232
export const keccak256 = (a: StubBytesCompat): bytes<32> => {
3333
const bytesA = BytesCls.fromCompat(a)
3434
const hashArray = js_sha3.keccak256.create().update(bytesA.asUint8Array()).digest()
35-
const hashBytes = FixedBytes(32, new Uint8Array(hashArray))
35+
const hashBytes = Bytes(new Uint8Array(hashArray)).toFixed({ length: 32 })
3636
return hashBytes
3737
}
3838

3939
/** @internal */
4040
export const sha512_256 = (a: StubBytesCompat): bytes<32> => {
4141
const bytesA = BytesCls.fromCompat(a)
4242
const hashArray = js_sha512.sha512_256.create().update(bytesA.asUint8Array()).digest()
43-
const hashBytes = FixedBytes(32, new Uint8Array(hashArray))
43+
const hashBytes = Bytes(new Uint8Array(hashArray)).toFixed({ length: 32 })
4444
return hashBytes
4545
}
4646

@@ -114,7 +114,7 @@ export const ecdsaPkRecover = (
114114

115115
const x = pubKey.getX().toArray('be')
116116
const y = pubKey.getY().toArray('be')
117-
return [FixedBytes(32, x), FixedBytes(32, y)]
117+
return [Bytes(x).toFixed({ length: 32 }), Bytes(y).toFixed({ length: 32 })]
118118
}
119119

120120
/** @internal */
@@ -127,7 +127,7 @@ export const ecdsaPkDecompress = (v: Ecdsa, a: StubBytesCompat): readonly [bytes
127127

128128
const x = pubKey.getX().toArray('be')
129129
const y = pubKey.getY().toArray('be')
130-
return [FixedBytes(32, new Uint8Array(x)), FixedBytes(32, new Uint8Array(y))]
130+
return [Bytes(x).toFixed({ length: 32 }), Bytes(y).toFixed({ length: 32 })]
131131
}
132132

133133
/** @internal */

src/impl/primitives.ts

Lines changed: 0 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { biguint, BigUintCompat, bytes, BytesCompat, uint64, Uint64Compat } from '@algorandfoundation/algorand-typescript'
22
import { encodingUtil } from '@algorandfoundation/puya-ts'
33
import { avmError, AvmError, avmInvariant, CodeError, InternalError } from '../errors'
4-
import type { DeliberateAny } from '../typescript-helpers'
54
import { nameOfType } from '../typescript-helpers'
65
import { base32ToUint8Array } from './base-32'
76

@@ -88,97 +87,6 @@ export function BigUint(v?: BigUintCompat | string): biguint {
8887
return BigUintCls.fromCompat(v).asAlgoTs()
8988
}
9089

91-
/**
92-
* @internal
93-
* Create a byte array from a string interpolation template and compatible replacements
94-
* @param value *
95-
* @param replacements *
96-
*/
97-
export function FixedBytes<TLength extends uint64 = uint64>(
98-
length: TLength,
99-
value: TemplateStringsArray,
100-
...replacements: BytesCompat[]
101-
): bytes<TLength>
102-
/**
103-
* @internal
104-
* Create a byte array from a utf8 string
105-
*/
106-
export function FixedBytes<TLength extends uint64 = uint64>(length: TLength, value: string): bytes<TLength>
107-
/**
108-
* @internal
109-
* No op, returns the provided byte array.
110-
*/
111-
export function FixedBytes<TLength extends uint64 = uint64>(length: TLength, value: bytes): bytes<TLength>
112-
/**
113-
* @internal
114-
* Create a byte array from a biguint value encoded as a variable length big-endian number *
115-
*/
116-
export function FixedBytes<TLength extends uint64 = uint64>(length: TLength, value: biguint): bytes<TLength>
117-
/**
118-
* @internal
119-
* Create a byte array from a uint64 value encoded as a fixed length 64-bit number
120-
*/
121-
export function FixedBytes<TLength extends uint64 = uint64>(length: TLength, value: uint64): bytes<TLength>
122-
/**
123-
* @internal
124-
* Create a byte array from an Iterable<uint64> where each item is interpreted as a single byte and must be between 0 and 255 inclusively
125-
*/
126-
export function FixedBytes<TLength extends uint64 = uint64>(length: TLength, value: Iterable<uint64>): bytes<TLength>
127-
/**
128-
* @internal
129-
* Create an empty byte array
130-
*/
131-
export function FixedBytes<TLength extends uint64 = uint64>(length: TLength): bytes<TLength>
132-
export function FixedBytes<TLength extends uint64 = uint64>(
133-
length: TLength,
134-
value?: BytesCompat | TemplateStringsArray | biguint | uint64 | Iterable<number>,
135-
...replacements: BytesCompat[]
136-
): bytes<TLength> {
137-
const result = Bytes((value ?? new Uint8Array(length)) as DeliberateAny, ...replacements)
138-
if (length && length !== getNumber(result.length)) {
139-
throw new CodeError(`Invalid bytes constant length of ${result.length}, expected ${length}`)
140-
}
141-
return result.toFixed({ length })
142-
}
143-
144-
/**
145-
* @internal
146-
* Create a new bytes value from a hexadecimal encoded string
147-
* @param hex
148-
*/
149-
FixedBytes.fromHex = <TLength extends uint64 = uint64>(length: TLength, hex: string): bytes<TLength> => {
150-
const result = BytesCls.fromHex(hex).asAlgoTs()
151-
if (length && length !== getNumber(result.length)) {
152-
throw new CodeError(`Expected decoded bytes value of length ${length}, received ${result.length}`)
153-
}
154-
return result.toFixed({ length })
155-
}
156-
/**
157-
* @internal
158-
* Create a new bytes value from a base 64 encoded string
159-
* @param b64
160-
*/
161-
FixedBytes.fromBase64 = <TLength extends uint64 = uint64>(length: TLength, b64: string): bytes<TLength> => {
162-
const result = BytesCls.fromBase64(b64).asAlgoTs()
163-
if (length && length !== getNumber(result.length)) {
164-
throw new CodeError(`Expected decoded bytes value of length ${length}, received ${result.length}`)
165-
}
166-
return result.toFixed({ length })
167-
}
168-
169-
/**
170-
* @internal
171-
* Create a new bytes value from a base 32 encoded string
172-
* @param b32
173-
*/
174-
FixedBytes.fromBase32 = <TLength extends uint64 = uint64>(length: TLength, b32: string): bytes<TLength> => {
175-
const result = BytesCls.fromBase32(b32).asAlgoTs()
176-
if (length && length !== getNumber(result.length)) {
177-
throw new CodeError(`Expected decoded bytes value of length ${length}, received ${result.length}`)
178-
}
179-
return result.toFixed({ length })
180-
}
181-
18290
/**
18391
* @internal
18492
* Create a byte array from a string interpolation template and compatible replacements

src/runtime-helpers.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import { flattenAsBytes } from './util'
1111

1212
/** @internal */
1313
export { attachAbiMetadata } from './abi-metadata'
14-
/** @internal */
15-
export { FixedBytes } from './impl/primitives'
1614

1715
/** @internal */
1816
export function switchableValue(x: unknown): bigint | string | boolean {

src/test-transformer/node-factory.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,4 @@ export const nodeFactory = {
149149
}
150150
return node
151151
},
152-
153-
callFixedBytesFunction(functionName: string, node: ts.CallExpression, length: number) {
154-
const updatedPropertyAccessExpression = factory.createPropertyAccessExpression(
155-
factory.createIdentifier('runtimeHelpers'),
156-
`FixedBytes${functionName === 'Bytes' ? '' : `.${functionName}`}`,
157-
)
158-
159-
return factory.createCallExpression(
160-
updatedPropertyAccessExpression,
161-
node.typeArguments,
162-
[factory.createNumericLiteral(length), ...(node.arguments ?? [])].filter((arg) => !!arg),
163-
)
164-
},
165152
} satisfies Record<string, (...args: DeliberateAny[]) => ts.Node | ts.Node[]>

src/test-transformer/visitors.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,6 @@ class ExpressionVisitor {
218218
updatedNode = nodeFactory.callAbiCallFunction(updatedNode, typeParams)
219219
} else if (isCallingItxnCompose(stubbedFunctionName)) {
220220
updatedNode = nodeFactory.callItxnComposeFunction(updatedNode)
221-
} else if (isCallingBytes(stubbedFunctionName)) {
222-
if (type instanceof ptypes.BytesPType && type.length)
223-
updatedNode = nodeFactory.callFixedBytesFunction(stubbedFunctionName, updatedNode, Number(type.length))
224221
} else {
225222
updatedNode = nodeFactory.callStubbedFunction(updatedNode, infoArg)
226223
}
@@ -506,7 +503,7 @@ const tryGetStubbedFunctionName = (node: ts.CallExpression, helper: VisitorHelpe
506503
: (node.expression as ts.Identifier)
507504
const functionName = tryGetAlgoTsSymbolName(identityExpression, helper)
508505
if (functionName === undefined) return undefined
509-
const stubbedFunctionNames = ['convertBytes', 'decodeArc4', 'encodeArc4', 'emit', 'methodSelector', 'sizeOf', 'abiCall', 'clone', 'Bytes']
506+
const stubbedFunctionNames = ['convertBytes', 'decodeArc4', 'encodeArc4', 'emit', 'methodSelector', 'sizeOf', 'abiCall', 'clone']
510507

511508
if (stubbedFunctionNames.includes(functionName)) {
512509
if (ts.isPropertyAccessExpression(node.expression)) {
@@ -516,10 +513,10 @@ const tryGetStubbedFunctionName = (node: ts.CallExpression, helper: VisitorHelpe
516513
return functionName
517514
}
518515

519-
if (['begin', 'next', 'fromHex', 'fromBase64', 'fromBase32'].includes(functionName) && ts.isPropertyAccessExpression(node.expression)) {
516+
if (['begin', 'next'].includes(functionName) && ts.isPropertyAccessExpression(node.expression)) {
520517
const objectExpression = node.expression.expression
521518
const objectName = tryGetAlgoTsSymbolName(objectExpression, helper)
522-
if (['itxnCompose', 'Bytes'].includes(objectName || '')) return functionName
519+
if (['itxnCompose'].includes(objectName || '')) return functionName
523520
}
524521

525522
return undefined
@@ -550,5 +547,3 @@ const isCallingMethodSelector = (functionName: string | undefined): boolean => '
550547
const isCallingAbiCall = (functionName: string | undefined): boolean => ['abiCall'].includes(functionName ?? '')
551548
const isCallingItxnCompose = (functionName: string | undefined): boolean => ['begin', 'next'].includes(functionName ?? '')
552549
const isCallingClone = (functionName: string | undefined): boolean => 'clone' === (functionName ?? '')
553-
const isCallingBytes = (functionName: string | undefined): boolean =>
554-
['Bytes', 'fromHex', 'fromBase64', 'fromBase32'].includes(functionName ?? '')

0 commit comments

Comments
 (0)