Skip to content

Commit 7a49003

Browse files
committed
fix: clone should return value if properties are in different order
1 parent 88c21f6 commit 7a49003

File tree

4 files changed

+32
-8
lines changed

4 files changed

+32
-8
lines changed

src/impl/encoded-types/encoded-types.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1314,7 +1314,21 @@ export const getArc4Encoded = (value: DeliberateAny, sourceTypeInfoString?: stri
13141314
name: `Struct<${value.constructor.name}>`,
13151315
genericArgs: Object.fromEntries(Object.keys(value).map((x, i) => [x, genericArgs[i]])),
13161316
}
1317-
return new Struct(typeInfo, Object.fromEntries(Object.keys(value).map((x, i) => [x, result[i]])))
1317+
let s = Object.fromEntries(Object.keys(typeInfo.genericArgs).map((x, i) => [x, result[i]]))
1318+
if (propTypeInfos) {
1319+
// if source type info is provided, reorder the struct properties to match the expected type schema
1320+
s = Object.fromEntries(Object.keys(propTypeInfos).map((x) => [x, s[x]]))
1321+
typeInfo.genericArgs = propTypeInfos
1322+
} else {
1323+
// if not, reorder the struct properties alphabetically
1324+
typeInfo.genericArgs = Object.fromEntries(
1325+
Object.keys(typeInfo.genericArgs)
1326+
.sort()
1327+
.map((key) => [key, typeInfo.genericArgs[key]]),
1328+
)
1329+
s = Object.fromEntries(Object.keys(typeInfo.genericArgs).map((key) => [key, s[key]]))
1330+
}
1331+
return new Struct(typeInfo, s)
13181332
}
13191333

13201334
throw new CodeError(`Unsupported type for encoding: ${typeof value}`)

tests/arc4/encode-decode-arc4.algo.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ const testData = [
8989
new Tuple<[Uint<512>, DynamicBytes, Swapped1]>(
9090
abiUint512,
9191
abiBytes,
92-
new Swapped1({ b: abiUint64, c: abiBool, d: abiString, a: new Tuple<[Uint<64>, Bool, Bool]>(abiUint64, abiBool, abiBool) }),
92+
new Swapped1({ a: new Tuple<[Uint<64>, Bool, Bool]>(abiUint64, abiBool, abiBool), b: abiUint64, c: abiBool, d: abiString }),
9393
),
9494
] as readonly [Tuple<[Bool, Tuple<[Str, Bool]>]>, Tuple<[Uint<64>, Uint<64>]>, Tuple<[Uint<512>, DynamicBytes, Swapped1]>]
9595
},
@@ -122,15 +122,15 @@ const testData = [
122122
},
123123
{
124124
nativeValues() {
125-
return { b: nativeNumber, c: nativeBool, d: nativeString, a: [nativeNumber, nativeBool, nativeBool] } as {
125+
return { a: [nativeNumber, nativeBool, nativeBool], b: nativeNumber, c: nativeBool, d: nativeString } as {
126126
b: uint64
127127
c: boolean
128128
d: string
129129
a: [uint64, boolean, boolean]
130130
}
131131
},
132132
abiValues() {
133-
return { b: abiUint64, c: abiBool, d: abiString, a: new Tuple<[Uint<64>, Bool, Bool]>(abiUint64, abiBool, abiBool) }
133+
return { a: new Tuple<[Uint<64>, Bool, Bool]>(abiUint64, abiBool, abiBool), b: abiUint64, c: abiBool, d: abiString }
134134
},
135135
arc4Value() {
136136
return new Swapped1(this.abiValues())
@@ -167,7 +167,7 @@ describe('decodeArc4', () => {
167167
...encodingUtil.utf8ToUint8Array('hello world'),
168168
]),
169169
)
170-
const e = { a: new arc4.Uint64(50n), b: new DynamicBytes(asBytes(new Uint8Array([1, 2, 3, 4, 5]))) }
170+
const e = { b: new DynamicBytes(asBytes(new Uint8Array([1, 2, 3, 4, 5]))), a: new arc4.Uint64(50n) }
171171
const eBytes = asBytes(new Uint8Array([...encodingUtil.bigIntToUint8Array(50n, 8), 0, 10, 0, 5, 1, 2, 3, 4, 5]))
172172
const f = new Address(Bytes.fromHex(`${'00'.repeat(31)}ff`))
173173
const fBytes = Bytes.fromHex(`${'00'.repeat(31)}ff`)

tests/native-mutable-object.algo.spec.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ describe('native mutable object', () => {
589589
c: arc4.Str
590590
d: arc4.DynamicBytes
591591
}> {}
592-
const obj: SimpleObj = { a: 1, b: true, c: 'hello', d: Bytes('world') }
592+
const obj: SimpleObj = { a: 1, c: 'hello', b: true, d: Bytes('world') }
593593
const encoded = encodeArc4(obj)
594594
const interpreted = convertBytes<SimpleObjStruct>(encoded, { strategy: 'unsafe-cast' })
595595
const decoded = decodeArc4<SimpleObj>(encoded)
@@ -789,4 +789,14 @@ describe('native mutable object', () => {
789789
assertMatch(decoded, obj)
790790
})
791791
})
792+
793+
describe('clone', () => {
794+
it('should work when property order is different', () => {
795+
const obj1: NestedObj = { a: 1, b: true, c: 'hello', d: { x: 10, y: 'world', z: true } }
796+
const obj2: NestedObj = { d: { x: 10, z: true, y: 'world' }, a: 1, c: 'hello', b: true }
797+
798+
expect(clone(obj2)).toEqual(obj2)
799+
expect(clone(obj2)).toEqual(obj1)
800+
})
801+
})
792802
})

tests/references/box.algo.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ describe('Box', () => {
131131
},
132132
},
133133
{
134-
value: { a: 'hello', b: Bytes('world'), c: true },
134+
value: { a: 'hello', c: true, b: Bytes('world') },
135135
newValue: { a: 'world', b: Bytes('hello'), c: false },
136136
emptyValue: {},
137137
withBoxContext: (test: (boxMap: Box<MyObject>) => void) => {
@@ -143,7 +143,7 @@ describe('Box', () => {
143143
},
144144
{
145145
value: { a: 'hello', b: Bytes('world'), c: true },
146-
newValue: { a: 'world', b: Bytes('hello'), c: false },
146+
newValue: { a: 'world', c: false, b: Bytes('hello') },
147147
emptyValue: {},
148148
withBoxContext: (test: (boxMap: Box<Readonly<MyObject>>) => void) => {
149149
ctx.txn.createScope([ctx.any.txn.applicationCall()]).execute(() => {

0 commit comments

Comments
 (0)