Skip to content

Commit

Permalink
wip on sdk, fixing package publishing
Browse files Browse the repository at this point in the history
  • Loading branch information
Ptroger committed Apr 4, 2024
1 parent 8b6eece commit 7577344
Show file tree
Hide file tree
Showing 14 changed files with 909 additions and 17 deletions.
483 changes: 479 additions & 4 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@faker-js/faker": "^8.4.0",
"@nestjs/schematics": "^10.0.1",
"@nestjs/testing": "^10.0.2",
"@nrwl/node": "^18.2.2",
"@nx-plus/docusaurus": "^15.0.0-rc.0",
"@nx/devkit": "17.1.3",
"@nx/eslint": "18.1.3",
Expand Down
5 changes: 4 additions & 1 deletion packages/engine-request/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"dependencies": {
"tslib": "^2.3.0",
"uuid": "^9.0.1",
"zod": "^3.22.4"
"zod": "^3.22.4",
"@narval-xyz/policy-engine-domain": "*",
"@narval-xyz/signature": "*",
"axios": "^1.6.7"
}
}
8 changes: 6 additions & 2 deletions packages/engine-request/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export { buildRequest } from './lib/builders/request-builder'
export * from './lib/engine-request'
import EvaluationRequestBuilder from './lib/builders/evaluation-request'

export { buildRequest } from './lib/builders/request/request-builder'
export { ClientConfig, Endpoints, NarvalSdkConfig, PolicyEngineConfig } from './lib/domain'
export { NarvalSdk } from './lib/narval-sdk'
export { EvaluationRequestBuilder }
61 changes: 51 additions & 10 deletions packages/engine-request/src/lib/builders/evaluation-request.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,63 @@
import { Feed, JwtString, Prices } from '@narval/policy-engine-shared'
import { EvaluationRequest, Feed, JwtString, Prices, Request } from '@narval/policy-engine-shared'
import { Jwk, Payload, SignConfig, hash, signJwt } from '@narval/signature'
import axios from 'axios'
import { Endpoints, NarvalSdkConfig } from '../domain'

export type EvaluationRequestConfig = {
approvals?: JwtString[]
prices?: Prices
feeds?: Feed<unknown>[]
}

const buildPayloadFromRequest = (request: Request, jwk: Jwk, orgId: string): Payload => {
return {
requestHash: hash(request),
sub: jwk.kid,
iss: orgId,
iat: new Date().getTime()
}
}

export default class EvaluationRequestBuilder {
private approvals?: JwtString[]
private prices?: Prices
private feeds?: Feed<unknown>[]

setApprovals(approvals: JwtString[]) {
this.approvals = approvals
return this
constructor(config?: EvaluationRequestConfig) {
this.approvals = config?.approvals
this.prices = config?.prices
this.feeds = config?.feeds
}

setPrices(prices: Prices) {
this.prices = prices
return this
async sign(
request: Request,
opts: {
signConfig: SignConfig
jwk: Jwk
clientId: string
}
): Promise<EvaluationRequest> {
const payload = buildPayloadFromRequest(request, opts.jwk, opts.clientId)
const { signer, opts: signingOpts } = opts.signConfig
const authentication = signer
? await signJwt(payload, opts.jwk, signingOpts, signer)
: await signJwt(payload, opts.jwk, signingOpts)
return {
authentication,
request,
approvals: this.approvals,
prices: this.prices,
feeds: this.feeds
}
}

setFeeds(feeds: Feed<unknown>[]) {
this.feeds = feeds
return this
async send(request: EvaluationRequest, config: NarvalSdkConfig): Promise<EvaluationRequest> {
const response = await axios.post(`${config.engine.url}${Endpoints.engine.evaluations}`, request, {
headers: {
'x-client-id': config.client.id,
'x-client-secret': config.client.secret
}
})
return response.data
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { OrganizationAction } from '../../domain'

export default class OrganizationBuilder {
private action: OrganizationAction | null = null
private nonce: string

setAction(action: OrganizationAction) {
this.action = action
switch (action) {
case OrganizationAction.CREATE_ORGANIZATION:
return new CreateOrganizationBuilder()
}
}

setNonce(nonce: string) {
this.nonce = nonce
return this
}
}

class CreateOrganizationBuilder extends OrganizationBuilder {
private organizationId: string

setOrganization(organizationId: string) {
this.organizationId = organizationId
return this
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Category } from '../../domain'
import OrganizationBuilder from './organization-request'
import WalletRequestBuilder from './wallet-request'

/**
* Creates a request builder for either Wallet or Organization categories.
* @param category - The category of the request to build. Either 'wallet' or 'organization'.
* @returns A builder instance specific to the provided category, either WalletRequestBuilder or OrganizationBuilder.
* @example
* // For wallet-related requests
* const walletBuilder = buildRequest('wallet');
*
* // For organization-related requests
* const organizationBuilder = buildRequest('organization');
*/
export function buildRequest(category: 'wallet'): WalletRequestBuilder
export function buildRequest(category: 'organization'): OrganizationBuilder
export function buildRequest(category: Category): OrganizationBuilder | WalletRequestBuilder {
if (category === Category.WALLET) {
return new WalletRequestBuilder()
} else {
return new OrganizationBuilder()
}
}
220 changes: 220 additions & 0 deletions packages/engine-request/src/lib/builders/request/wallet-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import {
Eip712TypedData,
SignMessageAction,
SignRawAction,
SignTransactionAction,
SignTypedDataAction,
TransactionRequest
} from '@narval/policy-engine-shared'
import { v4 } from 'uuid'
import { BuildResponse, WalletAction } from '../../domain'

/**
* Builder class for creating wallet-related requests, such as signing transactions, raw data, messages, or typed data.
*/
export default class WalletRequestBuilder {
private action: WalletAction
private resourceId: string
private nonce: string

/**
* Mandatory. Sets the action to be performed by the request.
* @param action - The specific wallet action to perform.
* @returns The builder instance specific to the action for method chaining.
* @example
* const request = buildRequest('wallet')
* .setAction('signTransaction')
* .setTransactionRequest(transactionRequest)
* @example
* const request = buildRequest('wallet')
* .setAction('signRaw')
* .setRawMessage(rawMessage)
*/
setAction(action: 'signTransaction'): SignTransactionBuilder
setAction(action: 'signRaw'): SignRawBuilder
setAction(action: 'signMessage'): SignMessageBuilder
setAction(action: 'signTypedData'): SignTypedDataBuilder
setAction(action: WalletAction) {
switch (action) {
case WalletAction.SIGN_TRANSACTION:
return new SignTransactionBuilder()
case WalletAction.SIGN_RAW:
return new SignRawBuilder()
case WalletAction.SIGN_MESSAGE:
return new SignMessageBuilder()
case WalletAction.SIGN_TYPED_DATA:
return new SignTypedDataBuilder()
default:
throw new Error(`Invalid action: ${action}`)
}
}

/**
* Optional. Sets the unique identifier for the transaction nonce.
* @default v4()
* @optional
* @param nonce - The nonce value as a string.
* @returns The builder instance for method chaining.
*/
setNonce(nonce: string) {
this.nonce = nonce
return this
}

protected getNonce() {
return this.nonce
}

protected getResourceId() {
return this.resourceId
}

/**
* Mandatory. Sets the resource identifier involved in the request, usually the wallet address.
* @param resourceId - The Ethereum wallet address as a string.
* @mandatory
* @returns The builder instance for method chaining.
*/
setResourceId(resourceId: string) {
this.resourceId = resourceId
return this
}
}

class SignTransactionBuilder extends WalletRequestBuilder {
private transactionRequest: TransactionRequest

setTransactionRequest(transactionRequest: TransactionRequest) {
this.transactionRequest = transactionRequest
return this
}

/**
* Constructs and returns the final request object based on the previously provided configurations.
* @returns {BuildResponse<SignTransactionAction>} The final request object if all required configurations are valid, otherwise returns an error object.
*/
build(): BuildResponse<SignTransactionAction> {
const nonce = this.getNonce() || v4()
const request = {
action: WalletAction.SIGN_TRANSACTION,
nonce,
resourceId: this.getResourceId(),
transactionRequest: this.transactionRequest
}
const res = SignTransactionAction.safeParse(request)
return res.success
? {
success: true,
request: res.data
}
: {
success: false,
error: res.error
}
}
}

class SignRawBuilder extends WalletRequestBuilder {
private rawMessage: string
private alg: string

setRawMessage(rawMessage: string) {
this.rawMessage = rawMessage
return this
}

setAlg(alg: string) {
this.alg = alg
return this
}

/**
* Constructs and returns the final request object based on the previously provided configurations.
* @returns {BuildResponse<SignRawAction>} The final request object if all required configurations are valid, otherwise returns an error object.
*/
build(): BuildResponse<SignRawAction> {
const nonce = this.getNonce() || v4()
const request = {
action: WalletAction.SIGN_RAW,
nonce,
resourceId: this.getResourceId(),
rawMessage: this.rawMessage
}
const res = SignRawAction.safeParse(request)
return res.success
? {
success: true,
request: res.data
}
: {
success: false,
error: res.error
}
}
}

class SignMessageBuilder extends WalletRequestBuilder {
private message: string

setMessage(message: string) {
this.message = message
return this
}

/**
* Constructs and returns the final request object based on the previously provided configurations.
* @returns {BuildResponse<SignMessageAction>} The final request object if all required configurations are valid, otherwise returns an error object.
*/
build(): BuildResponse<SignMessageAction> {
const nonce = this.getNonce() || v4()
const request = {
action: WalletAction.SIGN_MESSAGE,
nonce,
resourceId: this.getResourceId(),
message: this.message
}
const res = SignMessageAction.safeParse(request)
return res.success
? {
success: true,
request: res.data
}
: {
success: false,
error: res.error
}
}
}

class SignTypedDataBuilder extends WalletRequestBuilder {
private typedData: Eip712TypedData

setTypedData(typedData: Eip712TypedData) {
this.typedData = typedData
return this
}

/**
* Constructs and returns the final request object based on the previously provided configurations.
* @returns {BuildResponse<SignTypedDataAction>} The final request object if all required configurations are valid, otherwise returns an error object.
*/
build(): BuildResponse<SignTypedDataAction> {
const nonce = this.getNonce() || v4()
const request = {
action: WalletAction.SIGN_TYPED_DATA,
nonce,
resourceId: this.getResourceId(),
typedData: this.typedData
}
const res = SignTypedDataAction.safeParse(request)
return res.success
? {
success: true,
request: res.data
}
: {
success: false,
error: res.error
}
}
}
Loading

0 comments on commit 7577344

Please sign in to comment.