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 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
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
4 changes: 2 additions & 2 deletions apps/vault/src/vault/http/rest/controller/sign.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Request } from '@narval/policy-engine-shared'
import { Body, Controller, Post, UseGuards } from '@nestjs/common'
import { createZodDto } from 'nestjs-zod'
import {
Expand Down Expand Up @@ -26,7 +25,8 @@ export class SignController {

@Post()
async sign(@ClientId() clientId: string, @Body() body: SignRequestDto) {
const request: Request = body.request
const parsed = SignRequest.parse(body)
const { request } = parsed
const result = await this.signingService.sign(clientId, request)

return { signature: result }
Expand Down
18 changes: 18 additions & 0 deletions examples/armory-sdk-nodejs/.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/armory-sdk-nodejs/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'armory-sdk-nodejs',
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }]
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/examples/armory-sdk-nodejs'
}
80 changes: 80 additions & 0 deletions examples/armory-sdk-nodejs/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"name": "armory-sdk-nodejs",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "examples/armory-sdk-nodejs/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"target": "node",
"compiler": "tsc",
"outputPath": "dist/examples/armory-sdk-nodejs",
"main": "examples/armory-sdk-nodejs/src/index.ts",
"tsConfig": "examples/armory-sdk-nodejs/tsconfig.app.json",
"webpackConfig": "examples/armory-sdk-nodejs/webpack.config.js"
},
"configurations": {
"development": {},
"production": {}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"options": {
"buildTarget": "armory-sdk-nodejs:build"
},
"configurations": {
"development": {
"buildTarget": "armory-sdk-nodejs:build:development"
},
"production": {
"buildTarget": "armory-sdk-nodejs:build:production"
}
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["examples/armory-sdk-nodejs/**/*.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/armory-sdk-nodejs/jest.unit.ts",
"verbose": true
}
},
"test:integration": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "examples/armory-sdk-nodejs/jest.integration.ts",
"verbose": true,
"runInBand": true
}
},
"test:e2e": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "examples/armory-sdk-nodejs/jest.e2e.ts",
"verbose": true,
"runInBand": true
}
}
},
"tags": ["type:application"]
}
133 changes: 133 additions & 0 deletions examples/armory-sdk-nodejs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/* eslint-disable no-console */

import { createArmoryConfig, evaluate, importPrivateKey, signRequest } from '@narval/armory-sdk'
import { SignMessageAction, SignTransactionAction, SignTypedDataAction } 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, TypedData, createPublicClient, http, toHex } from 'viem'
import { privateKeyToAddress } from 'viem/accounts'
import { polygon } from 'viem/chains'

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

const config = createArmoryConfig({
authClientId: 'ad496b05-3a1e-4138-93d0-1505e7a5c8a1',
authHost: 'http://localhost:3010',
authSecret: '27948c192850f36eb0b45285eb1a9ec3490e6ee573dd0ad63a32fe42c317a18be3e29b38bd56663e47bb',
vaultClientId: 'd6369edd-7353-486c-9129-5f667fe8f3fc',
vaultHost: 'http://localhost:3011',
vaultSecret: 'f0e6ad88ba601f0e342e42d945b865d461293c587561a62d8f5cb86d442eff0288dc3e8ae6d98639aadb',
signer: privateKeyToJwk(UNSAFE_PRIVATE_KEY.Alice)
})

const privateKey = '0xcbdb5073d97f2971672e99769d12411fc044dde79b803e9c9e3ad6df5c9a260a'
const walletId = 'test-wallet-id'

console.log('\n\nimporting private key to vault...: ', privateKey)
const vaultWalletAddress = privateKeyToAddress(privateKey)
await importPrivateKey(config, { privateKey, walletId })

const nonce = 11
const transactionRequestAction: SignTransactionAction = {
action: 'signTransaction',
transactionRequest: {
from: vaultWalletAddress,
chainId: 137,
gas: BigInt(22000),
to: anotherAddress,
maxFeePerGas: BigInt(291175227375),
maxPriorityFeePerGas: BigInt(81000000000),
value: toHex(BigInt(50000)),
nonce
// Update it accordingly to USER_PRIVATE_KEY last nonce + 1.
// If you are too low, viem will error on transaction broadcasting, telling you what should be a correct nonce.
},
resourceId: resourceId(walletId),
nonce: v4()
}

const { accessToken } = await evaluate(config, transactionRequestAction)
const { signature } = await signRequest(config, { accessToken, request: transactionRequestAction })

const publicClient = createPublicClient({
chain: polygon,
transport: http()
})
try {
const hash = await publicClient.sendRawTransaction({ serializedTransaction: signature })
console.log('\n\ntransaction request successfully broadcasted !', 'txHash: ', hash)
} catch (error) {
console.error('transaction request failed', error)
}

const signMessageAction: SignMessageAction = {
action: 'signMessage',
message: 'Hello, World!',
resourceId: resourceId(walletId),
nonce: v4()
}

const { accessToken: messageAccessToken } = await evaluate(config, signMessageAction)
const { signature: messageSignature } = await signRequest(config, {
accessToken: messageAccessToken,
request: signMessageAction
})

console.log('\n\nmessage signature:', messageSignature)

try {
const messageVerification = await publicClient.verifyMessage({
message: signMessageAction.message,
signature: messageSignature,
address: vaultWalletAddress
})
console.log('\n\nmessage verification:', messageVerification)
} catch (error) {
console.error('message verification failed', error)
}

const signTypedDataAction: SignTypedDataAction = {
action: 'signTypedData',
typedData: {
types: {
EIP712Domain: [{ name: 'name', type: 'string' }],
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' }
]
},
primaryType: 'Person',
domain: { name: 'Ether Mail', version: '1' },
message: {
name: 'Bob',
wallet: anotherAddress
}
},
resourceId: resourceId(walletId),
nonce: v4()
}

const { accessToken: typedDataAccessToken } = await evaluate(config, signTypedDataAction)

const { signature: typedDataSignature } = await signRequest(config, {
accessToken: typedDataAccessToken,
request: signTypedDataAction
})

const typedDataVerification = await publicClient.verifyTypedData({
signature: typedDataSignature,
address: vaultWalletAddress,
types: signTypedDataAction.typedData.types as unknown as TypedData,
// TODO: @ptroger find a way to make this work without casting or explitly mapping to viem.
primaryType: signTypedDataAction.typedData.primaryType,
domain: signTypedDataAction.typedData.domain,
message: signTypedDataAction.typedData.message
})

console.log('\n\ntyped data verification:', typedDataVerification)
}

main()
10 changes: 10 additions & 0 deletions examples/armory-sdk-nodejs/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/armory-sdk-nodejs/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/armory-sdk-nodejs/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/armory-sdk-nodejs/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?

Loading
Loading