Skip to content

Commit

Permalink
tiered transfers working
Browse files Browse the repository at this point in the history
  • Loading branch information
Ptroger committed Sep 9, 2024
1 parent fbc873b commit e199511
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,6 @@ export class OpenPolicyAgentEngine implements Engine<OpenPolicyAgentEngine> {
// an array of results with an inner result. We perform a typecast here to
// satisfy TypeScript compiler. Later, we parse the schema a few lines
// below to ensure type-safety for data coming from external sources.

console.log('###input ', JSON.stringify(input, null, 2))
const results = (await this.opa.evaluate(input, POLICY_ENTRYPOINT)) as { result: unknown }[]

const parse = z.array(resultSchema).safeParse(results.map(({ result }) => result))
Expand Down
14 changes: 7 additions & 7 deletions examples/approvals-by-spending-limit/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Collect approval before authorizing a request

Setup

`npm i` (run from this directory)
Expand All @@ -7,23 +8,22 @@ Run each command in order

1. Set up Policies & base Entity data (see data.ts) - as the system manager

`tsx 1-setup.ts`
`tsx 1-setup.ts`

2. Add a new Destination to the address book - as the system manager

`tsx 2-add-destination.ts`
`tsx 2-add-destination.ts`

3. Add a second Account to be managed - as the system manager

`tsx 3-add-account.ts`
`tsx 3-add-account.ts`

4. Transfer from Account A to Account B - as the Member User

Run the transfer 4 times in a row; the 4th requires an Approval
Run the transfer 4 times in a row; the 4th requires an Approval

`tsx 4-transfer-a-to-b.ts`
`tsx 4-transfer-a-to-b.ts`

5. Use the ID from the pending request to approve - as the Admin User

`tsx 5-approve-transfer.ts $REQUEST_ID`

`tsx 5-approve-transfer.ts $REQUEST_ID`
2 changes: 1 addition & 1 deletion examples/approvals-by-spending-limit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
"devDependencies": {
"@types/minimist": "^1.2.5"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable jest/consistent-test-it */
import { Action, Decision, entitiesSchema, FIXTURE, policySchema, Request, ValueOperators } from '@narval/policy-engine-shared'
import { Action, Decision, entitiesSchema, FIXTURE, policySchema, Request, toHex } from '@narval/policy-engine-shared'
import { v4 } from 'uuid'
import defaultEntities from '../../../../resource/entity/default.json'
import tieredEthTransfer from '../../../../resource/policy/set/tiered-eth-transfer.json'
Expand Down Expand Up @@ -54,87 +54,62 @@ describe('tiered approvals and spending limits', () => {
clientId,
host: getAuthHost(),
entities,
policies: [ {
"id": "tier1-low-value-transfers",
"description": "Permit members to transfer up to 0.00000000001 ETH per day without approval",
"when": [
{
"criterion": "checkPrincipalRole",
"args": [
"member"
]
},
{
"criterion": "checkIntentToken",
"args": [
"eip155:1/slip44:60"
]
},
{
"criterion": "checkIntentAmount",
"args": {
"value": "1",
"operator": "lte" as ValueOperators
}
}
],
"then": "permit"
}]
policies
})
})

// it('permits member to transfer less than or equal to 1 ETH', async () => {
// const { authClient } = await buildAuthClient(antoinePrivateKey, {
// host: getAuthHost(),
// clientId
// })

// const lowValueRequest = genNonce({
// ...request,
// transactionRequest: {
// ...request.transactionRequest,
// value: '0xDE0B6B3A7640000' // 0.3 ETH
// }
// })

// const response = await authClient.requestAccessToken(lowValueRequest)
// expect(response).toMatchObject({ value: expect.any(String) })
// })

// it('requires manager approval for transfers between 1 and 10 ETH', async () => {
// expect.assertions(2)

// const { authClient: managerClient } = await buildAuthClient(carolPrivateKey, {
// host: getAuthHost(),
// clientId
// })

// const { authClient } = await buildAuthClient(antoinePrivateKey, {
// host: getAuthHost(),
// clientId
// })

// const mediumValueRequest = genNonce({
// ...request,
// transactionRequest: {
// ...request.transactionRequest,
// value: '0x4563918244F40000' // 5 ETH
// }
// })

// const res = await authClient.authorize(mediumValueRequest)
// expect(res.decision).toEqual(Decision.CONFIRM)

// if (res.decision === Decision.CONFIRM) {
// await managerClient.approve(res.authId)

// const accessToken = await authClient.getAccessToken(res.authId)
// expect(accessToken).toMatchObject({ value: expect.any(String) })
// }
// })
it('permits member to transfer less than or equal to 1 ETH', async () => {
const { authClient } = await buildAuthClient(antoinePrivateKey, {
host: getAuthHost(),
clientId
})

const lowValueRequest = genNonce({
...request,
transactionRequest: {
...request.transactionRequest,
value: toHex(1000000000000000000n) // 1 ETH
}
})

const response = await authClient.requestAccessToken(lowValueRequest)
expect(response).toMatchObject({ value: expect.any(String) })
})

it('requires manager approval for transfers between 1 and 10 ETH', async () => {
expect.assertions(2)

const { authClient: managerClient } = await buildAuthClient(carolPrivateKey, {
host: getAuthHost(),
clientId
})

const { authClient } = await buildAuthClient(antoinePrivateKey, {
host: getAuthHost(),
clientId
})

const mediumValueRequest = genNonce({
...request,
transactionRequest: {
...request.transactionRequest,
value: '0x4563918244F40000' // 5 ETH
}
})

const res = await authClient.authorize(mediumValueRequest)
expect(res.decision).toEqual(Decision.CONFIRM)

if (res.decision === Decision.CONFIRM) {
await managerClient.approve(res.authId)

const accessToken = await authClient.getAccessToken(res.authId)
expect(accessToken).toMatchObject({ value: expect.any(String) })
}
})

it('requires admin approval for transfers between 10 and 100 ETH', async () => {
expect.assertions(1)
expect.assertions(2)

const { authClient: adminClient } = await buildAuthClient(alicePrivateKey, {
host: getAuthHost(),
Expand All @@ -147,19 +122,19 @@ describe('tiered approvals and spending limits', () => {
})

const highValueRequest = genNonce({
action: Action.SIGN_TRANSACTION,
nonce: 'test-nonce-4',
transactionRequest: {
from: '0x0301e2724a40E934Cce3345928b88956901aA127',
to: '0x76d1b7f9b3F69C435eeF76a98A415332084A856F',
value: '0x7FFFFFFFFFFFFDFF', // 10 ETH
chainId: 1
},
resourceId: 'eip155:eoa:0x0301e2724a40e934cce3345928b88956901aa127'
})
action: Action.SIGN_TRANSACTION,
nonce: 'test-nonce-4',
transactionRequest: {
from: '0x0301e2724a40E934Cce3345928b88956901aA127',
to: '0x76d1b7f9b3F69C435eeF76a98A415332084A856F',
value: '0x8000000000000000', // 10 ETH
chainId: 1
},
resourceId: 'eip155:eoa:0x0301e2724a40e934cce3345928b88956901aa127'
})

const res = await authClient.authorize(highValueRequest)
expect(res.decision).toEqual(Decision.FORBID)
expect(res.decision).toEqual(Decision.CONFIRM)

if (res.decision === Decision.CONFIRM) {
await adminClient.approve(res.authId)
Expand All @@ -169,42 +144,41 @@ describe('tiered approvals and spending limits', () => {
}
})

it('requires two admin approvals for transfers above 100 ETH', async () => {
expect.assertions(2)

// it('requires two admin approvals for transfers above 100 ETH', async () => {
// expect.assertions(3)

// const { authClient: adminClient1 } = await buildAuthClient(alicePrivateKey, {
// host: getAuthHost(),
// clientId
// })
const { authClient: adminClient1 } = await buildAuthClient(alicePrivateKey, {
host: getAuthHost(),
clientId
})

// const { authClient: adminClient2 } = await buildAuthClient(bobPrivateKey, {
// host: getAuthHost(),
// clientId
// })
const { authClient: adminClient2 } = await buildAuthClient(bobPrivateKey, {
host: getAuthHost(),
clientId
})

// const { authClient } = await buildAuthClient(antoinePrivateKey, {
// host: getAuthHost(),
// clientId
// })
const { authClient } = await buildAuthClient(antoinePrivateKey, {
host: getAuthHost(),
clientId
})

// const veryHighValueRequest = genNonce({
// ...request,
// transactionRequest: {
// ...request.transactionRequest,
// value: '0x56BC75E2D63100000' // 150 ETH
// }
// })
const veryHighValueRequest = genNonce({
...request,
transactionRequest: {
...request.transactionRequest,
value: '0x56BC75E2D63100000' // 150 ETH
}
})

// const res = await authClient.authorize(veryHighValueRequest)
// expect(res.decision).toEqual(Decision.CONFIRM)
const res = await authClient.authorize(veryHighValueRequest)
expect(res.decision).toEqual(Decision.CONFIRM)

// if (res.decision === Decision.CONFIRM) {
// await adminClient1.approve(res.authId)
// await adminClient2.approve(res.authId)
if (res.decision === Decision.CONFIRM) {
await adminClient1.approve(res.authId)
await adminClient2.approve(res.authId)

// const accessToken = await authClient.getAccessToken(res.authId)
// expect(accessToken).toMatchObject({ value: expect.any(String) })
// }
// })
const accessToken = await authClient.getAccessToken(res.authId)
expect(accessToken).toMatchObject({ value: expect.any(String) })
}
})
})
1 change: 0 additions & 1 deletion packages/armory-sdk/src/lib/__test__/util/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { AuthAdminClient, AuthClient, AuthConfig } from '../../auth'
import { createHttpDataStore, DataStoreConfig, EntityStoreClient, PolicyStoreClient } from '../../data-store'
import { VaultAdminClient, VaultConfig } from '../../vault'


export const getAuthHost = () => 'http://localhost:3005'
export const getAuthAdminApiKey = () => 'armory-admin-api-key'

Expand Down
Loading

0 comments on commit e199511

Please sign in to comment.