Skip to content

Commit

Permalink
feat: add subintents support
Browse files Browse the repository at this point in the history
  • Loading branch information
dawidsowardx committed Oct 15, 2024
1 parent 566da1c commit 94e543a
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 42 deletions.
22 changes: 22 additions & 0 deletions examples/simple-dapp/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ content.innerHTML = `
<div class="mt-25">
<button id="proof-of-ownership-request">Send proof of ownership request</button>
</div>
<hr/>
<textarea id="subintentManifest">YIELD_TO_PARENT;</textarea>
<button id="subintent">Send Pre Authorization</button>
<hr/>
<pre id="sessions"></pre>
<pre id="requests"></pre>
Expand All @@ -48,6 +52,10 @@ const sendTxButton = document.getElementById('sendTx')!
const sessions = document.getElementById('sessions')!
const removeCb = document.getElementById('removeCb')!
const addCb = document.getElementById('addCb')!
const subintentButton = document.getElementById('subintent')!
const subintentManifest = document.getElementById(
'subintentManifest',
)! as HTMLTextAreaElement
const requests = document.getElementById('requests')!
const logs = document.getElementById('logs')!
const state = document.getElementById('state')!
Expand Down Expand Up @@ -75,6 +83,20 @@ removeCb.onclick = () => {
document.querySelector('radix-connect-button')?.remove()
}

subintentButton.onclick = async () => {
console.log(subintentManifest.value)
const result = await dAppToolkit.walletApi.sendPreAuthorizationRequest({
transactionManifest: subintentManifest.value,
childSubintentHashes: [],
expiration: {
discriminator: 'expireAfterSignature',
value: '3600',
},
})

console.log(result);
}

addCb.onclick = () => {
const connectButton = document.createElement('radix-connect-button')
const header = document.querySelector('header')!
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const RequestItemType = {
dataRequest: 'dataRequest',
sendTransaction: 'sendTransaction',
proofRequest: 'proofRequest',
preAuthorizationRequest: 'preAuthorizationRequest',
} as const

export type RequestItemType = typeof RequestItemType
Expand Down
12 changes: 11 additions & 1 deletion packages/connect-button/src/components/card/request-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,17 @@ export class RadixRequestCard extends LitElement {
content: this.getRequestContentTemplate(
'Open Your Radix Wallet App to complete the request',
),
}
},
preAuthorizationRequest: {
pending: 'Preauthorization Request Pending',
fail: 'Preauthorization Request Rejected',
cancelled: 'Preauthorization Request Rejected',
success: 'Preauthorization Request',
ignored: '',
content: this.getRequestContentTemplate(
'Open Your Radix Wallet App to complete the request',
),
},
}

return html`<radix-card
Expand Down
18 changes: 17 additions & 1 deletion packages/dapp-toolkit/src/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type { Observable } from 'rxjs'
import type { RadixButtonStatus, RadixButtonTheme } from 'radix-connect-common'
import {
CallbackFns,
ExpireAfterSignature,
ExpireAtTime,
Persona,
PersonaDataName,
WalletInteraction,
Expand Down Expand Up @@ -39,7 +41,9 @@ export type ExplorerConfig = {
accountsPath: string
}

export type WalletDataRequest = Parameters<WalletRequestSdk['request']>[0]
export type WalletDataRequest = Parameters<
WalletRequestSdk['sendInteraction']
>[0]

export type WalletRequest =
| { type: 'sendTransaction'; payload: WalletInteraction }
Expand Down Expand Up @@ -96,6 +100,15 @@ export type SendTransactionInput = {
onTransactionId?: (transactionId: string) => void
}

export type SendPreAuthorizationRequestInput = {
transactionManifest: string
version?: number
blobs?: string[]
message?: string
childSubintentHashes: string[]
expiration: ExpireAtTime | ExpireAfterSignature
}

export type ButtonApi = {
setMode: (value: 'light' | 'dark') => void
setTheme: (value: RadixButtonTheme) => void
Expand Down Expand Up @@ -128,6 +141,9 @@ export type WalletApi = {
dataRequestControl: (fn: (walletResponse: WalletData) => Promise<any>) => void
updateSharedAccounts: () => WalletDataRequestResult
sendTransaction: (input: SendTransactionInput) => SendTransactionResult
sendPreAuthorizationRequest: (
input: SendPreAuthorizationRequestInput,
) => ResultAsync<{ signedPartialTransaction: string }, SdkError>
setRequestData: (...dataRequestBuilderItem: DataRequestBuilderItem[]) => void
sendRequest: () => WalletDataRequestResult
sendOneTimeRequest: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ export const WalletRequestSdk = (input: WalletRequestSdkInput) => {
})
}

const request = (
const sendInteraction = (
{
interactionId = uuidV4(),
items,
}: Pick<WalletInteraction, 'items'> & { interactionId?: string },
}: { interactionId?: string; items: WalletInteraction['items'] },
callbackFns: Partial<CallbackFns> = {},
): ResultAsync<WalletInteractionResponse, SdkError> =>
withInterceptor({
Expand All @@ -99,28 +99,8 @@ export const WalletRequestSdk = (input: WalletRequestSdkInput) => {
),
)

const sendTransaction = (
{
interactionId = uuidV4(),
items,
}: { interactionId?: string; items: WalletInteraction['items'] },
callbackFns: Partial<CallbackFns> = {},
): ResultAsync<WalletInteractionResponse, SdkError> =>
withInterceptor({
interactionId,
items,
metadata,
}).andThen((walletInteraction) =>
getTransport(interactionId).asyncAndThen((transport) =>
transport
.send(walletInteraction, callbackFns)
.andThen(validateWalletResponse),
),
)

return {
request,
sendTransaction,
sendInteraction,
createWalletInteraction,
getTransport,
}
Expand Down
59 changes: 42 additions & 17 deletions packages/dapp-toolkit/src/modules/wallet-request/wallet-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import {
import { validateRolaChallenge, type Logger } from '../../helpers'
import { TransactionStatus } from '../gateway'
import { ResultAsync, err, ok, okAsync } from 'neverthrow'
import type { MessageLifeCycleEvent, WalletInteraction } from '../../schemas'
import type {
ExpireAfterSignature,
ExpireAtTime,
MessageLifeCycleEvent,
WalletInteraction,
} from '../../schemas'
import { SdkError } from '../../error'
import {
DataRequestBuilderItem,
Expand All @@ -25,6 +30,8 @@ import { StorageModule } from '../storage'
import type { StateModule, WalletData } from '../state'
import {
AwaitedWalletDataRequestResult,
SendPreAuthorizationRequestInput,
SendTransactionInput,
TransportProvider,
WalletDataRequestResult,
} from '../../_types'
Expand All @@ -37,14 +44,7 @@ import {
failedResponseResolver,
sendTransactionResponseResolver,
} from './request-resolver'

type SendTransactionInput = {
transactionManifest: string
version?: number
blobs?: string[]
message?: string
onTransactionId?: (transactionId: string) => void
}
import { RequestItemTypes } from 'radix-connect-common'

export type WalletRequestModule = ReturnType<typeof WalletRequestModule>
export const WalletRequestModule = (input: {
Expand Down Expand Up @@ -199,7 +199,7 @@ export const WalletRequestModule = (input: {
),
)
},
} satisfies Parameters<WalletRequestSdk['request']>[1]
} satisfies Parameters<WalletRequestSdk['sendInteraction']>[1]
}

let challengeGeneratorFn: () => Promise<string> = () => Promise.resolve('')
Expand Down Expand Up @@ -249,13 +249,10 @@ export const WalletRequestModule = (input: {

const sendRequestAndAwaitResponse = (
walletInteraction: WalletInteraction,
type: 'transaction' | 'data',
) => {
updateConnectButtonStatus('pending')
return ResultAsync.combine([
(type === 'data'
? walletRequestSdk.request
: walletRequestSdk.sendTransaction)(
walletRequestSdk.sendInteraction(
walletInteraction,
cancelRequestControl(walletInteraction.interactionId),
),
Expand All @@ -273,7 +270,7 @@ export const WalletRequestModule = (input: {
})

const sendDataRequest = (walletInteraction: WalletInteraction) =>
sendRequestAndAwaitResponse(walletInteraction, 'data')
sendRequestAndAwaitResponse(walletInteraction)
.andThen((response) => {
logger?.debug({ method: 'sendDataRequest.successResponse', response })
return ok(response.walletData! as WalletData)
Expand All @@ -287,7 +284,7 @@ export const WalletRequestModule = (input: {
stateModule.getState().mapErr(() => SdkError('FailedToReadRdtState', ''))

const addNewRequest = (
type: 'loginRequest' | 'dataRequest' | 'proofRequest',
type: RequestItemTypes,
walletInteraction: WalletInteraction,
isOneTimeRequest: boolean,
) =>
Expand All @@ -305,6 +302,33 @@ export const WalletRequestModule = (input: {
),
)

const sendPreAuthorizationRequest = (
value: SendPreAuthorizationRequestInput,
): ResultAsync<
{
signedPartialTransaction: string
},
SdkError
> => {
const walletInteraction = walletRequestSdk.createWalletInteraction({
discriminator: 'preAuthorizedRequest',
subintent: {
discriminator: 'subintent',
blobs: value.blobs,
transactionManifest: value.transactionManifest,
message: value.message,
version: value.version ?? 1,
},
})

return addNewRequest('preAuthorizationRequest', walletInteraction, false)
.andThen(() => sendRequestAndAwaitResponse(walletInteraction))
.map((requestItem) => ({
signedPartialTransaction:
requestItem.walletResponse.signedPartialTransaction,
}))
}

const sendRequest = ({
isConnect,
oneTime,
Expand Down Expand Up @@ -423,7 +447,7 @@ export const WalletRequestModule = (input: {

return createTransactionRequest()
.andThen((walletInteraction) =>
sendRequestAndAwaitResponse(walletInteraction, 'transaction'),
sendRequestAndAwaitResponse(walletInteraction),
)
.andThen(({ status, transactionIntentHash, metadata, interactionId }) => {
const output = {
Expand Down Expand Up @@ -502,6 +526,7 @@ export const WalletRequestModule = (input: {
return err(error)
}),
sendTransaction,
sendPreAuthorizationRequest,
cancelRequest,
ignoreTransaction,
requestItemModule,
Expand Down
1 change: 1 addition & 0 deletions packages/dapp-toolkit/src/radix-dapp-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export const RadixDappToolkit = (
walletRequestModule.provideConnectResponseCallback,
updateSharedAccounts: () => walletRequestModule.updateSharedAccounts(),
sendOneTimeRequest: walletRequestModule.sendOneTimeRequest,
sendPreAuthorizationRequest: walletRequestModule.sendPreAuthorizationRequest,
sendTransaction: (input: SendTransactionInput) =>
walletRequestModule.sendTransaction(input),
walletData$: stateModule.walletData$,
Expand Down
38 changes: 38 additions & 0 deletions packages/dapp-toolkit/src/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,49 @@ export const CancelRequest = object({
discriminator: literal('cancelRequest'),
})

export type ExpireAtTime = InferOutput<typeof ExpireAtTime>
export const ExpireAtTime = object({
discriminator: literal('expireAtTime'),
value: string(),
})

export type ExpireAfterSignature = InferOutput<typeof ExpireAfterSignature>
export const ExpireAfterSignature = object({
discriminator: literal('expireAfterSignature'),
value: string(),
})

export type SubintentRequestItem = InferOutput<typeof SubintentRequestItem>
export const SubintentRequestItem = object({
discriminator: literal('subintent'),
version: number(),
transactionManifest: string(),
blobs: optional(array(string())),
message: optional(string()),
childSubintentHashes: optional(array(string())),
expiration: optional(union([ExpireAtTime, ExpireAfterSignature])),
})

export type SubintentResponseItem = InferOutput<typeof SubintentResponseItem>
export const SubintentResponseItem = object({
discriminator: literal('subintent'),
signedPartialTransaction: string(),
})

export type WalletPreAuthorizationItems = InferOutput<
typeof WalletPreAuthorizationItems
>
export const WalletPreAuthorizationItems = object({
discriminator: literal('preAuthorizedRequest'),
subintent: optional(SubintentRequestItem),
})

export type WalletInteractionItems = InferOutput<typeof WalletInteractionItems>
export const WalletInteractionItems = union([
WalletRequestItems,
WalletTransactionItems,
CancelRequest,
WalletPreAuthorizationItems,
])

export type Metadata = InferOutput<typeof Metadata>
Expand Down

0 comments on commit 94e543a

Please sign in to comment.