Skip to content

Commit fce81c6

Browse files
authored
Merge pull request #21 from CoMakery/more-tests
More tests
2 parents 22353f7 + 03b2db9 commit fce81c6

File tree

5 files changed

+183
-24
lines changed

5 files changed

+183
-24
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Tests
22

3-
on: [push, pull_request]
3+
on: [push]
44

55
jobs:
66
test:

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ There are lots of other reasons you may get a bad request error, such as TEAL ex
122122
There are a few interrelated account reference quirks to keep in mind:
123123
* `Txn.accounts[0]` will always evaluate to `Txn.sender()`
124124
* `Txn.accounts[1]` is the first `--app-account` item.
125-
* If no `--app-account` items are included, Txn.accounts.length() will be 0 but `Txn.accounts[0]` still resolves to the sender.
126-
* `Txn.accounts[n]` for n > 0 will evaluate to the element at the n-1th index of the --app-account/ForeignAccounts transaction field.
127-
* Some versions of `tealdbg` show the Txn.Accounts array incorrectly. If n accounts are present in the transaction’s ForeignAccounts array, the debugger will show the sender’s account following by the first n-1 elements from ForeignAccounts.
125+
* If no `--app-account` items are included, `Txn.accounts.length()` will be 0 but `Txn.accounts[0]` still resolves to the sender.
126+
* `Txn.accounts[n]` for n > 0 will evaluate to the element at the n-1th index of the `--app-account` or `ForeignAccounts` transaction field. For example `Txn.accounts[2]` would refer to `appAccount[1]`. Another way to put it is the `--from` address is shifted into Txn.accounts[0] and `--app-accounts` are shifted right by 1 position.
127+
* Some versions of `tealdbg` show the `Txn.accounts` array incorrectly. If n accounts are present in the transaction’s `ForeignAccounts` array, the debugger will show the sender’s account following by the first n-1 elements from `ForeignAccounts`.
128128

129129
## Teal contract size
130130

@@ -208,7 +208,7 @@ It is recommended that all admin actions should be performed by accounts other t
208208

209209
## QSP-8 Do Algorand Smart Contracts Lack A Standard Like the Ethereum ERC20 Token?
210210

211-
Yes, to accommodate this we use functionality with the same function names and behavior as the Ethereum ERC20 token standard - with the notable exception that the contract does not implement the `approve()` and `transferFrom` functions. See next question...
211+
Yes, to accommodate this we use functionality with the same function names and behavior as the Ethereum ERC20 token standard - with the notable exception that the contract does not implement the `approve()` and `transferFrom()` functions. See next question...
212212

213213
## QSP-9 Why doesn't the contract implement the approve() and transferFrom() functions from the ERC20 standard?
214214

tests/grant_roles.test.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const { describe } = require('yargs')
88
const server = "http://127.0.0.1"
99
const port = 8080
1010

11-
var adminAccount, receiverAccount, token, clientV2, appId
11+
var adminAccount, receiverAccount, token, clientV2, appId, localState
1212

1313
beforeEach(async () => {
1414
await privateTestNetSetup(appId)
@@ -23,17 +23,23 @@ beforeEach(async () => {
2323
await util.optInApp(clientV2, receiverAccount, appId)
2424
})
2525

26-
test('contract admin role can be granted by contract admin', async () => {
26+
async function grantRoles(roleId, from=adminAccount, target=receiverAccount) {
2727
appArgs = [
2828
EncodeBytes("grantRoles"),
29-
EncodeUint('8')
29+
EncodeUint(roleId)
3030
]
31-
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
31+
await util.appCall(clientV2, from, appId, appArgs, [target.addr])
32+
}
33+
34+
test('contract admin role can be granted by contract admin', async () => {
35+
await grantRoles(8, adminAccount, receiverAccount)
3236

3337
localState = await util.readLocalState(clientV2, receiverAccount, appId)
3438
expect(localState["roles"]["ui"]).toEqual(8)
3539

36-
await util.appCall(clientV2, receiverAccount, appId, appArgs, [receiverAccount.addr])
40+
await grantRoles(15, receiverAccount, receiverAccount)
41+
localState = await util.readLocalState(clientV2, receiverAccount, appId)
42+
expect(localState["roles"]["ui"]).toEqual(15)
3743
})
3844

3945
test('contract admin role can be revoked by contract admin', async () => {

tests/max_token_balance.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,28 @@ test('blocks transfers that exceed the addresses maxBalance but not lesser amoun
6969
appArgs = [EncodeBytes("transfer"), EncodeUint('10')]
7070
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])
7171

72+
// tokens sent back to admin
73+
localState = await util.readLocalState(clientV2, receiverAccount, appId)
74+
expect(localState["balance"]["ui"]).toEqual(undefined)
75+
})
76+
77+
test('maxBalance of 0 is treated as no max balance', async () => {
78+
let maxTokenBalance = 0
79+
appArgs = [EncodeBytes("setAddressPermissions"), EncodeUint('0'), EncodeUint(`${maxTokenBalance}`), EncodeUint('0'), EncodeUint('1')]
80+
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
81+
82+
// allow token transfers to address with 0 maxBalance
83+
appArgs = [EncodeBytes("transfer"), EncodeUint('10')]
84+
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
85+
86+
// tokens sent to receiver
87+
localState = await util.readLocalState(clientV2, receiverAccount, appId)
88+
expect(localState["balance"]["ui"]).toEqual(10)
89+
90+
// allow tokens to be transferred out of the account
91+
appArgs = [EncodeBytes("transfer"), EncodeUint('10')]
92+
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])
93+
7294
// tokens sent back to admin
7395
localState = await util.readLocalState(clientV2, receiverAccount, appId)
7496
expect(localState["balance"]["ui"]).toEqual(undefined)

tests/transfer_restrictions.test.js

Lines changed: 145 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,25 @@ test('has expected starting test state', async () => {
4848
expect(localState["transfer admin"]).toEqual(undefined)
4949
})
5050

51-
test('simple transfer', async () => {
51+
test('cannot transfer by default from and to the default group 1 -> 1', async () => {
52+
try {
53+
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
54+
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
55+
} catch (e) {
56+
expect(e.message).toEqual("Bad Request")
57+
}
58+
// check first receiver got no tokens and is in group 1
59+
let localState = await util.readLocalState(clientV2, receiverAccount, appId)
60+
expect(localState["transferGroup"]["ui"]).toEqual(1)
61+
expect(localState["balance"]["ui"]).toEqual(undefined)
62+
63+
// check sender sent no tokens and is in group 1
64+
localState = await util.readLocalState(clientV2, adminAccount, appId)
65+
expect(localState["transferGroup"]["ui"]).toEqual(1)
66+
expect(localState["balance"]["ui"]).toEqual(27)
67+
})
68+
69+
test('simple transfer back and forth: with group 1 -> 1 permitted', async () => {
5270
let fromGroupId = 1
5371
let toGroupId = 1
5472
let earliestPermittedTime = 1
@@ -80,6 +98,24 @@ test('simple transfer', async () => {
8098
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
8199
expect(globalState['cap']['ui'].toString()).toEqual('80000000000000000')
82100
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')
101+
102+
// ======
103+
//transfer back
104+
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
105+
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])
106+
107+
// check original sender got tokens back
108+
localState = await util.readLocalState(clientV2, adminAccount, appId)
109+
expect(localState["balance"]["ui"]).toEqual(27)
110+
111+
// check tokens deducted
112+
localState = await util.readLocalState(clientV2, receiverAccount, appId)
113+
expect(localState["balance"]["ui"]).toEqual(undefined)
114+
115+
// check global supply is same
116+
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
117+
expect(globalState['cap']['ui'].toString()).toEqual('80000000000000000')
118+
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')
83119
})
84120

85121
test('can lock the default address category for transfers', async () => {
@@ -107,6 +143,64 @@ test('can lock the default address category for transfers', async () => {
107143
expect(localState["balance"]["ui"]).toEqual(undefined)
108144
})
109145

146+
test('simple transfer from group 0 -> 0 works when permitted', async () => {
147+
let fromGroupId = 0
148+
let toGroupId = 0
149+
let earliestPermittedTime = 1
150+
151+
let transferGroupLock =
152+
`goal app call --app-id ${appId} --from ${adminAccount.addr} ` +
153+
`--app-arg 'str:setTransferRule' ` +
154+
`--app-arg "int:${fromGroupId}" --app-arg "int:${toGroupId}" ` +
155+
`--app-arg "int:${earliestPermittedTime}" -d devnet/Primary`
156+
157+
appArgs = [EncodeBytes("setAddressPermissions"), EncodeUint('0'), EncodeUint('0'), EncodeUint('0'), EncodeUint('0')]
158+
await util.appCall(clientV2, adminAccount, appId, appArgs, [adminAccount.addr])
159+
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
160+
161+
await shell.exec(transferGroupLock, {async: false, silent: false})
162+
163+
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
164+
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')
165+
166+
//transfer
167+
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
168+
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
169+
170+
// check receiver got tokens
171+
localState = await util.readLocalState(clientV2, receiverAccount, appId)
172+
expect(localState["balance"]["ui"]).toEqual(11)
173+
expect(localState["transferGroup"]["ui"]).toEqual(undefined)
174+
175+
// check sender has less tokens
176+
localState = await util.readLocalState(clientV2, adminAccount, appId)
177+
expect(localState["balance"]["ui"]).toEqual(16)
178+
expect(localState["transferGroup"]["ui"]).toEqual(undefined)
179+
180+
// check global supply is same
181+
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
182+
expect(globalState['cap']['ui'].toString()).toEqual('80000000000000000')
183+
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')
184+
185+
// ======
186+
//transfer back
187+
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
188+
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])
189+
190+
// check original sender got tokens back
191+
localState = await util.readLocalState(clientV2, adminAccount, appId)
192+
expect(localState["balance"]["ui"]).toEqual(27)
193+
194+
// check tokens deducted
195+
localState = await util.readLocalState(clientV2, receiverAccount, appId)
196+
expect(localState["balance"]["ui"]).toEqual(undefined)
197+
198+
// check global supply is same
199+
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
200+
expect(globalState['cap']['ui'].toString()).toEqual('80000000000000000')
201+
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')
202+
})
203+
110204
test('can transfer to an account if the transfer rule lock has expired', async () => {
111205
let fromGroupId = 1
112206
let toGroupId = 1
@@ -128,20 +222,7 @@ test('can transfer to an account if the transfer rule lock has expired', async (
128222
expect(localState["balance"]["ui"]).toEqual(11)
129223
})
130224

131-
test('cannot transfer by default', async () => {
132-
try {
133-
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
134-
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
135-
} catch (e) {
136-
expect(e.message).toEqual("Bad Request")
137-
}
138-
// check first receiver got tokens
139-
localState = await util.readLocalState(clientV2, receiverAccount, appId)
140-
expect(localState["balance"]["ui"]).toEqual(undefined)
141-
})
142-
143225
test('can transfer between permitted account groups', async () => {
144-
145226
let earliestPermittedTime = 1
146227
// from group 1 -> 1 is allowed
147228
let transferGroupLock1 =
@@ -191,4 +272,54 @@ test('can transfer between permitted account groups', async () => {
191272
// first account no longer has the transferred tokens
192273
localState = await util.readLocalState(clientV2, receiverAccount, appId)
193274
expect(localState["balance"]["ui"]).toEqual(4)
275+
})
276+
277+
test('transferRule allowing transfer from group 1 to 2 does not allow transfers from 2 to 1 (the reverse rule)', async () => {
278+
let earliestPermittedTime = 1
279+
280+
// from group 1 -> 2 is allowed
281+
let transferGroupLock2 =
282+
`goal app call --app-id ${appId} --from ${adminAccount.addr} ` +
283+
`--app-arg 'str:setTransferRule' ` +
284+
`--app-arg "int:1" --app-arg "int:2" ` +
285+
`--app-arg "int:${earliestPermittedTime}" -d devnet/Primary`
286+
287+
await shell.exec(transferGroupLock2, {async: false, silent: false})
288+
289+
// put receiver in group 2
290+
appArgs = [EncodeBytes("setAddressPermissions"), EncodeUint('0'), EncodeUint('0'), EncodeUint('0'), EncodeUint('2')]
291+
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
292+
293+
let localState = await util.readLocalState(clientV2, receiverAccount, appId)
294+
expect(localState["balance"]["ui"]).toEqual(undefined)
295+
expect(localState["transferGroup"]["ui"].toString()).toEqual('2')
296+
297+
//transfer to receiver (group 1 -> 2)
298+
appArgs = [EncodeBytes("transfer"), EncodeUint('7')]
299+
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
300+
301+
// check receiver got tokens
302+
localState = await util.readLocalState(clientV2, receiverAccount, appId)
303+
expect(localState["balance"]["ui"]).toEqual(7)
304+
305+
// first sender adminAccount no longer has the transferred tokens
306+
localState = await util.readLocalState(clientV2, adminAccount, appId)
307+
expect(localState["balance"]["ui"]).toEqual(20)
308+
309+
// transfer back from receiver account (group 2 -> 1) FAILS!
310+
let error = null
311+
try {
312+
appArgs = [EncodeBytes("transfer"), EncodeUint('7')]
313+
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])
314+
} catch(e) {
315+
error = e
316+
}
317+
expect(error.message).toBe("Bad Request")
318+
319+
//balances remain unchanged before and after the failed transfer
320+
localState = await util.readLocalState(clientV2, receiverAccount, appId)
321+
expect(localState["balance"]["ui"]).toEqual(7)
322+
323+
localState = await util.readLocalState(clientV2, adminAccount, appId)
324+
expect(localState["balance"]["ui"]).toEqual(20)
194325
})

0 commit comments

Comments
 (0)