Skip to content
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

Initial implementation of the Armory SDK #241

Merged
merged 16 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ include ./packages/nestjs-shared/Makefile
include ./packages/policy-engine-shared/Makefile
include ./packages/transaction-request-intent/Makefile
include ./packages/signature/Makefile
include ./packages/armory-sdk/Makefile

# For more terminal color codes, head over to
# https://opensource.com/article/19/9/linux-terminal-colors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,7 @@ const PlaygroundEditor = () => {
<div className="pt-4">
<h3 className="pb-4">Examples</h3>
<div className="flex gap-5 ">
<NarButton
label="ERC-20"
onClick={async () => setCodeEditor(JSON.stringify(await erc20(), null, 2))}
/>
<NarButton label="ERC-20" onClick={async () => setCodeEditor(JSON.stringify(await erc20(), null, 2))} />
<NarButton
label="Spending limits"
onClick={async () => setCodeEditor(JSON.stringify(await spendingLimits(), null, 2))}
Expand Down
2 changes: 2 additions & 0 deletions apps/vault/src/shared/guard/authorization.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export class AuthorizationGuard implements CanActivate {
maxTokenAge: client?.maxTokenAge || DEFAULT_MAX_TOKEN_AGE
}

console.log('clientJwk', clientJwk)

wcalderipe marked this conversation as resolved.
Show resolved Hide resolved
// Validate the JWT has a valid signature for the expected client key & the request matches
const { payload } = await verifyJwt(token, clientJwk, {
...opts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class ImportController {

@Post('/private-key')
async create(@ClientId() clientId: string, @Body() body: ImportPrivateKeyDto) {
console.log('importing private key')
wcalderipe marked this conversation as resolved.
Show resolved Hide resolved
let importedKey
if (body.encryptedPrivateKey) {
importedKey = await this.importService.importEncryptedPrivateKey(
Expand All @@ -31,6 +32,7 @@ export class ImportController {
body.walletId
)
} else if (body.privateKey) {
console.log('un-encripted private key')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
console.log('un-encripted private key')

importedKey = await this.importService.importPrivateKey(clientId, body.privateKey, body.walletId)
} else {
throw new ApplicationException({
Expand Down
18 changes: 18 additions & 0 deletions examples/basic/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
11 changes: 11 additions & 0 deletions examples/basic/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'basic-flow',
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }]
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/examples/basic'
}
80 changes: 80 additions & 0 deletions examples/basic/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"name": "basic-flow",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding an example project.

I'm thinking about a better name that describes what it's properly. What do you think armory-sdk-nodejs? I'm adding the runtime in the name of the example because it doesn't solve the problem of using it on the browser.

Remember that the directory structure must reflect the project name (e.g. examples/armory-sdk-nodejs/project.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that is actually a very good naming. I tried multiple ones, and gave up with this one as nothing was satisfying.

I did 'basic' because I'm expecting other examples will complexify with time, and this example felt like the most straightforward way to use our system

"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "examples/basic/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"target": "node",
"compiler": "tsc",
"outputPath": "dist/examples/basic",
"main": "examples/basic/src/index.ts",
"tsConfig": "examples/basic/tsconfig.app.json",
"webpackConfig": "examples/basic/webpack.config.js"
},
"configurations": {
"development": {},
"production": {}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"options": {
"buildTarget": "basic-flow:build"
},
"configurations": {
"development": {
"buildTarget": "basic-flow:build:development"
},
"production": {
"buildTarget": "basic-flow:build:production"
}
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["examples/basic/**/*.ts"]
}
},
"test:type": {
"executor": "nx:run-commands",
"options": {
"command": "make basic-flow/test/type"
}
},
"test:unit": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "examples/basic/jest.unit.ts",
"verbose": true
}
},
"test:integration": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "examples/basic/jest.integration.ts",
"verbose": true,
"runInBand": true
}
},
"test:e2e": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "examples/basic/jest.e2e.ts",
"verbose": true,
"runInBand": true
}
}
},
"tags": ["type:application"]
}
59 changes: 59 additions & 0 deletions examples/basic/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { createArmoryConfig, evaluate, importPrivateKey, signRequest } from '@narval/armory-sdk'
import { Request } from '@narval/policy-engine-shared'
import { privateKeyToJwk } from '@narval/signature'
import { resourceId } from 'packages/armory-sdk/src/lib/utils'
import { UNSAFE_PRIVATE_KEY } from 'packages/policy-engine-shared/src/lib/dev.fixture'
import { v4 } from 'uuid'
import { Hex, createPublicClient, http } from 'viem'
import { privateKeyToAddress } from 'viem/accounts'
import { polygon } from 'viem/chains'

const main = async () => {
const anotherAddress = '0x3f843E606C79312718477F9bC020F3fC5b7264C2'.toLowerCase() as Hex

const config = createArmoryConfig({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have you changed the naming?

Suggested change
const config = createArmoryConfig({
const config = createArmoryClient({

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm expecting that we will have an armory client later, that will be instantiated and extends viem clients. It will have other capabilities.
Also, as we integrate with other tools in web3, when they have a client its almost always something that was instantiated with a transport and that has actual broadcasting capabilities. Let's stay on that path, and keep this naming for when we have something with these capabilities

authClientId: '915ed686-ddcd-4018-95f9-69405d2dd740',
authHost: 'http://localhost:3010',
authSecret: 'fb46a34690a49c41c6093ada79a14fbefe7e158162ee62da1ebcce47ec893c63d3d015f06de8139e29eb',
vaultClientId: '4edb675c-3df1-405b-b495-d93229e57b09',
vaultHost: 'http://localhost:3011',
vaultSecret: '5014c1a89e2224642ba05217d82cb71d6d07e9edf6d48b57a1c6ac8cce5a8e4b33d4c114f6332824b6ee',
signer: privateKeyToJwk(UNSAFE_PRIVATE_KEY.Alice)
})

const privateKey = process.env.USER_PRIVATE_KEY as Hex
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const privateKey = process.env.USER_PRIVATE_KEY as Hex
const privateKey = toHex(process.env.USER_PRIVATE_KEY)

const walletId = 'test-wallet-id'
const vaultWalletAddress = privateKeyToAddress(privateKey)
await importPrivateKey(config, { privateKey, walletId })

const request: Request = {
action: 'signTransaction',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
action: 'signTransaction',
action: Action.SIGN_TRANSACTION,

transactionRequest: {
from: vaultWalletAddress,
chainId: 137,
gas: BigInt(22000),
to: anotherAddress,
value: '0x111',
nonce: 10
},
resourceId: resourceId(walletId),
nonce: v4()
}

const { accessToken } = await evaluate(config, request)
const { signature } = await signRequest(config, { accessToken, request })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signRequest is a confusing/misleading name because of the action of signing the request to send it to the API.

Why haven't you implement a specific function like signTransaction that takes a SignTransactionAction?


console.log(signature)
try {
const publicClient = createPublicClient({
transport: http(),
chain: polygon
})
const hash = await publicClient.sendRawTransaction({ serializedTransaction: signature })
console.log('success', hash)
} catch (error) {
console.error('failed', error)
}
}

main()
10 changes: 10 additions & 0 deletions examples/basic/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["node"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}
16 changes: 16 additions & 0 deletions examples/basic/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"compilerOptions": {
"esModuleInterop": true
}
}
9 changes: 9 additions & 0 deletions examples/basic/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
}
8 changes: 8 additions & 0 deletions examples/basic/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { composePlugins, withNx } = require('@nx/webpack')

// Nx plugins for webpack.
module.exports = composePlugins(withNx(), (config) => {
// Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())`
return config
})
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,8 @@
"viem": "^2.7.16",
"wagmi": "^2.5.7",
"zod": "^3.22.4"
},
"nx": {
"includedScripts": []
Comment on lines +130 to +131
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by Nx when I generated Armory-sdk as a publishable lib

}
}
25 changes: 25 additions & 0 deletions packages/armory-sdk/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error"
}
}
]
}
29 changes: 29 additions & 0 deletions packages/armory-sdk/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
SIGNATURE_PROJECT_NAME := armory-sdk
SIGNATURE_PROJECT_DIR := ./packages/armory-sdk

# == Code format ==

armory-sdk/format:
npx nx format:write --projects ${SIGNATURE_PROJECT_NAME}

armory-sdk/lint:
npx nx lint ${SIGNATURE_PROJECT_NAME} -- --fix

armory-sdk/format/check:
npx nx format:check --projects ${SIGNATURE_PROJECT_NAME}

armory-sdk/lint/check:
npx nx lint ${SIGNATURE_PROJECT_NAME}

# == Testing ==

armory-sdk/test/type:
npx tsc \
--project ${SIGNATURE_PROJECT_DIR}/tsconfig.lib.json \
--noEmit

armory-sdk/test/unit:
npx nx test:unit ${SIGNATURE_PROJECT_NAME} -- ${ARGS}

armory-sdk/test/unit/watch:
make armory-sdk/test/unit ARGS=--watch
11 changes: 11 additions & 0 deletions packages/armory-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# armory-sdk

This library was generated with [Nx](https://nx.dev).

## Building

Run `nx build armory-sdk` to build the library.

## Running unit tests

Run `nx test armory-sdk` to execute the unit tests via [Jest](https://jestjs.io).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a proper README?

18 changes: 18 additions & 0 deletions packages/armory-sdk/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Config } from 'jest'

const config: Config = {
displayName: 'armory-sdk',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]sx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.spec.json'
}
]
}
}

export default config
9 changes: 9 additions & 0 deletions packages/armory-sdk/jest.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Config } from 'jest'
import sharedConfig from './jest.config'

const config: Config = {
...sharedConfig,
testMatch: ['<rootDir>/**/__test__/unit/**/*.spec.ts']
}

export default config
11 changes: 11 additions & 0 deletions packages/armory-sdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@narval/armory-sdk",
"version": "0.0.1",
"dependencies": {
"axios": "^1.6.7",
"tslib": "^2.3.0",
"uuid": "^9.0.1",
"viem": "^2.7.16",
"zod": "^3.22.4"
}
}
Loading
Loading