Skip to content

More tests #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Tests

on: [push, pull_request]
on: [push]

jobs:
test:
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ There are lots of other reasons you may get a bad request error, such as TEAL ex
There are a few interrelated account reference quirks to keep in mind:
* `Txn.accounts[0]` will always evaluate to `Txn.sender()`
* `Txn.accounts[1]` is the first `--app-account` item.
* If no `--app-account` items are included, Txn.accounts.length() will be 0 but `Txn.accounts[0]` still resolves to the sender.
* `Txn.accounts[n]` for n > 0 will evaluate to the element at the n-1th index of the --app-account/ForeignAccounts transaction field.
* 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.
* If no `--app-account` items are included, `Txn.accounts.length()` will be 0 but `Txn.accounts[0]` still resolves to the sender.
* `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.
* 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`.

## Teal contract size

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

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

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...
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...

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

Expand Down
16 changes: 11 additions & 5 deletions tests/grant_roles.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { describe } = require('yargs')
const server = "http://127.0.0.1"
const port = 8080

var adminAccount, receiverAccount, token, clientV2, appId
var adminAccount, receiverAccount, token, clientV2, appId, localState

beforeEach(async () => {
await privateTestNetSetup(appId)
Expand All @@ -23,17 +23,23 @@ beforeEach(async () => {
await util.optInApp(clientV2, receiverAccount, appId)
})

test('contract admin role can be granted by contract admin', async () => {
async function grantRoles(roleId, from=adminAccount, target=receiverAccount) {
appArgs = [
EncodeBytes("grantRoles"),
EncodeUint('8')
EncodeUint(roleId)
]
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
await util.appCall(clientV2, from, appId, appArgs, [target.addr])
}

test('contract admin role can be granted by contract admin', async () => {
await grantRoles(8, adminAccount, receiverAccount)

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

await util.appCall(clientV2, receiverAccount, appId, appArgs, [receiverAccount.addr])
await grantRoles(15, receiverAccount, receiverAccount)
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["roles"]["ui"]).toEqual(15)
})

test('contract admin role can be revoked by contract admin', async () => {
Expand Down
22 changes: 22 additions & 0 deletions tests/max_token_balance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,28 @@ test('blocks transfers that exceed the addresses maxBalance but not lesser amoun
appArgs = [EncodeBytes("transfer"), EncodeUint('10')]
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])

// tokens sent back to admin
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(undefined)
})

test('maxBalance of 0 is treated as no max balance', async () => {
let maxTokenBalance = 0
appArgs = [EncodeBytes("setAddressPermissions"), EncodeUint('0'), EncodeUint(`${maxTokenBalance}`), EncodeUint('0'), EncodeUint('1')]
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])

// allow token transfers to address with 0 maxBalance
appArgs = [EncodeBytes("transfer"), EncodeUint('10')]
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])

// tokens sent to receiver
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(10)

// allow tokens to be transferred out of the account
appArgs = [EncodeBytes("transfer"), EncodeUint('10')]
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])

// tokens sent back to admin
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(undefined)
Expand Down
159 changes: 145 additions & 14 deletions tests/transfer_restrictions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,25 @@ test('has expected starting test state', async () => {
expect(localState["transfer admin"]).toEqual(undefined)
})

test('simple transfer', async () => {
test('cannot transfer by default from and to the default group 1 -> 1', async () => {
try {
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
} catch (e) {
expect(e.message).toEqual("Bad Request")
}
// check first receiver got no tokens and is in group 1
let localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["transferGroup"]["ui"]).toEqual(1)
expect(localState["balance"]["ui"]).toEqual(undefined)

// check sender sent no tokens and is in group 1
localState = await util.readLocalState(clientV2, adminAccount, appId)
expect(localState["transferGroup"]["ui"]).toEqual(1)
expect(localState["balance"]["ui"]).toEqual(27)
})

test('simple transfer back and forth: with group 1 -> 1 permitted', async () => {
let fromGroupId = 1
let toGroupId = 1
let earliestPermittedTime = 1
Expand Down Expand Up @@ -80,6 +98,24 @@ test('simple transfer', async () => {
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
expect(globalState['cap']['ui'].toString()).toEqual('80000000000000000')
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')

// ======
//transfer back
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])

// check original sender got tokens back
localState = await util.readLocalState(clientV2, adminAccount, appId)
expect(localState["balance"]["ui"]).toEqual(27)

// check tokens deducted
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(undefined)

// check global supply is same
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
expect(globalState['cap']['ui'].toString()).toEqual('80000000000000000')
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')
})

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

test('simple transfer from group 0 -> 0 works when permitted', async () => {
let fromGroupId = 0
let toGroupId = 0
let earliestPermittedTime = 1

let transferGroupLock =
`goal app call --app-id ${appId} --from ${adminAccount.addr} ` +
`--app-arg 'str:setTransferRule' ` +
`--app-arg "int:${fromGroupId}" --app-arg "int:${toGroupId}" ` +
`--app-arg "int:${earliestPermittedTime}" -d devnet/Primary`

appArgs = [EncodeBytes("setAddressPermissions"), EncodeUint('0'), EncodeUint('0'), EncodeUint('0'), EncodeUint('0')]
await util.appCall(clientV2, adminAccount, appId, appArgs, [adminAccount.addr])
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])

await shell.exec(transferGroupLock, {async: false, silent: false})

globalState = await util.readGlobalState(clientV2, adminAccount, appId)
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')

//transfer
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])

// check receiver got tokens
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(11)
expect(localState["transferGroup"]["ui"]).toEqual(undefined)

// check sender has less tokens
localState = await util.readLocalState(clientV2, adminAccount, appId)
expect(localState["balance"]["ui"]).toEqual(16)
expect(localState["transferGroup"]["ui"]).toEqual(undefined)

// check global supply is same
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
expect(globalState['cap']['ui'].toString()).toEqual('80000000000000000')
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')

// ======
//transfer back
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])

// check original sender got tokens back
localState = await util.readLocalState(clientV2, adminAccount, appId)
expect(localState["balance"]["ui"]).toEqual(27)

// check tokens deducted
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(undefined)

// check global supply is same
globalState = await util.readGlobalState(clientV2, adminAccount, appId)
expect(globalState['cap']['ui'].toString()).toEqual('80000000000000000')
expect(globalState['reserve']['ui'].toString()).toEqual('79999999999999973')
})

test('can transfer to an account if the transfer rule lock has expired', async () => {
let fromGroupId = 1
let toGroupId = 1
Expand All @@ -128,20 +222,7 @@ test('can transfer to an account if the transfer rule lock has expired', async (
expect(localState["balance"]["ui"]).toEqual(11)
})

test('cannot transfer by default', async () => {
try {
appArgs = [EncodeBytes("transfer"), EncodeUint('11')]
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])
} catch (e) {
expect(e.message).toEqual("Bad Request")
}
// check first receiver got tokens
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(undefined)
})

test('can transfer between permitted account groups', async () => {

let earliestPermittedTime = 1
// from group 1 -> 1 is allowed
let transferGroupLock1 =
Expand Down Expand Up @@ -191,4 +272,54 @@ test('can transfer between permitted account groups', async () => {
// first account no longer has the transferred tokens
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(4)
})

test('transferRule allowing transfer from group 1 to 2 does not allow transfers from 2 to 1 (the reverse rule)', async () => {
let earliestPermittedTime = 1

// from group 1 -> 2 is allowed
let transferGroupLock2 =
`goal app call --app-id ${appId} --from ${adminAccount.addr} ` +
`--app-arg 'str:setTransferRule' ` +
`--app-arg "int:1" --app-arg "int:2" ` +
`--app-arg "int:${earliestPermittedTime}" -d devnet/Primary`

await shell.exec(transferGroupLock2, {async: false, silent: false})

// put receiver in group 2
appArgs = [EncodeBytes("setAddressPermissions"), EncodeUint('0'), EncodeUint('0'), EncodeUint('0'), EncodeUint('2')]
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])

let localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(undefined)
expect(localState["transferGroup"]["ui"].toString()).toEqual('2')

//transfer to receiver (group 1 -> 2)
appArgs = [EncodeBytes("transfer"), EncodeUint('7')]
await util.appCall(clientV2, adminAccount, appId, appArgs, [receiverAccount.addr])

// check receiver got tokens
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(7)

// first sender adminAccount no longer has the transferred tokens
localState = await util.readLocalState(clientV2, adminAccount, appId)
expect(localState["balance"]["ui"]).toEqual(20)

// transfer back from receiver account (group 2 -> 1) FAILS!
let error = null
try {
appArgs = [EncodeBytes("transfer"), EncodeUint('7')]
await util.appCall(clientV2, receiverAccount, appId, appArgs, [adminAccount.addr])
} catch(e) {
error = e
}
expect(error.message).toBe("Bad Request")

//balances remain unchanged before and after the failed transfer
localState = await util.readLocalState(clientV2, receiverAccount, appId)
expect(localState["balance"]["ui"]).toEqual(7)

localState = await util.readLocalState(clientV2, adminAccount, appId)
expect(localState["balance"]["ui"]).toEqual(20)
})