Skip to content

Commit

Permalink
feat: add proof of ownership request
Browse files Browse the repository at this point in the history
  • Loading branch information
dawidsowardx committed Sep 24, 2024
1 parent 893bce1 commit 8c24675
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 87 deletions.
111 changes: 31 additions & 80 deletions examples/simple-dapp/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,31 @@ const networkId = RadixNetwork.Stokenet
const storageModule = LocalStorageModule(
`rdt:${dAppDefinitionAddress}:${networkId}`,
)
const requestsStore = storageModule.getPartition('requests')
const sessionStore = storageModule.getPartition('sessions')
const identityStore = storageModule.getPartition('identities')
const stateStore = storageModule.getPartition('state')

const content = document.getElementById('app')!

content.innerHTML = `
<button id="reset">Reset</button>
<button id="sendTx">Send Tx</button>
<button id="removeCb">Remove Connect Button</button>
<button id="addCb">Add Connect Button</button>
<div class="mt-25"><button id="one-time-request">Send one time request</button></div>
<pre id="sessions"></pre>
<pre id="requests"></pre>
<pre id="state"></pre>
<pre id="gatewayConfig"></pre>
<pre id="gatewayStatus"></pre>
<pre id="device"></pre>
<button id="proofAccount">Prove Account Ownership</button>
<input id="proofAccountInput" placeholder="Account Address" />
<hr/>
<button id="proofPersona">Prove Persona Ownership</button>
<input id="proofPersonaInput" placeholder="Identity Address" />
<hr/>
<pre id="logs"></pre>
`
const resetButton = document.getElementById('reset')!
const sendTxButton = document.getElementById('sendTx')!
const sessions = document.getElementById('sessions')!
const removeCb = document.getElementById('removeCb')!
const addCb = document.getElementById('addCb')!
const requests = document.getElementById('requests')!

const proofAccount = document.getElementById('proofAccount')!
const proofPersona = document.getElementById('proofPersona')!
const proofAccountInput = document.getElementById(
'proofAccountInput',
) as HTMLInputElement
const proofPersonaInput = document.getElementById(
'proofPersonaInput',
) as HTMLInputElement

const logs = document.getElementById('logs')!
const state = document.getElementById('state')!
const gatewayConfig = document.getElementById('gatewayConfig')!
const gatewayStatus = document.getElementById('gatewayStatus')!
const oneTimeRequest = document.getElementById('one-time-request')!

const logger = Logger()

Expand All @@ -64,15 +55,18 @@ ${logs.innerHTML}`
logs.innerHTML = logEntry
})

removeCb.onclick = () => {
document.querySelector('radix-connect-button')?.remove()
proofAccount.onclick = () => {
dAppToolkit.walletApi.sendProofOfOwnershipRequest({
accountAddresses: [proofAccountInput.value],
})
}

addCb.onclick = () => {
const connectButton = document.createElement('radix-connect-button')
const header = document.querySelector('header')!
header.appendChild(connectButton)
proofPersona.onclick = () => {
dAppToolkit.walletApi.sendProofOfOwnershipRequest({
identityAddress: proofPersonaInput.value,
})
}

const dAppToolkit = RadixDappToolkit({
dAppDefinitionAddress,
networkId,
Expand All @@ -84,56 +78,13 @@ const gatewayApi = GatewayApiClient.initialize(
dAppToolkit.gatewayApi.clientConfig,
)

dAppToolkit.walletApi.provideChallengeGenerator(async () => generateRolaChallenge())
dAppToolkit.walletApi.provideChallengeGenerator(async () =>
generateRolaChallenge(),
)

dAppToolkit.walletApi.setRequestData(
DataRequestBuilder.persona().withProof(),
DataRequestBuilder.accounts().atLeast(1),
)

gatewayConfig.innerHTML = `
[Gateway]
${JSON.stringify(dAppToolkit.gatewayApi.clientConfig, null, 2)}`

resetButton.onclick = () => {
sessionStore.clear()
requestsStore.clear()
stateStore.clear()
identityStore.clear()
localStorage.removeItem('logs')
window.location.hash = ``
window.location.replace(window.location.origin)
}

sendTxButton.onclick = () => {
dAppToolkit.walletApi.sendTransaction({
transactionManifest: `CALL_METHOD
Address("component_tdx_2_1cptxxxxxxxxxfaucetxxxxxxxxx000527798379xxxxxxxxxyulkzl")
"free"
;
CALL_METHOD
Address("account_tdx_2_12yfw30hdc445j4lnepw7dmrkjcqcswsrxlff5r07mrjq9f8mnnn2r5")
"try_deposit_batch_or_abort"
Expression("ENTIRE_WORKTOP")
Enum<0u8>()
;`,
})
}

oneTimeRequest.onclick = () => {
dAppToolkit.walletApi.sendOneTimeRequest(
OneTimeDataRequestBuilder.accounts().exactly(1),
)
}

setInterval(() => {
requestsStore.getState().map((value: any) => {
requests.innerHTML = JSON.stringify({ requests: value ?? {} }, null, 2)
})
stateStore.getState().map((value: any) => {
state.innerHTML = JSON.stringify({ state: value ?? {} }, null, 2)
})
sessionStore.getItemList().map((value: any) => {
sessions.innerHTML = JSON.stringify({ sessions: value }, null, 2)
})
}, 1000)
setInterval(() => {}, 1000)
7 changes: 7 additions & 0 deletions packages/dapp-toolkit/src/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ export type AwaitedWalletDataRequestResult = Result<
WalletDataRequestError
>

export type ProofOfOwnershipInput = {
accountAddresses: string[]
} | {
identityAddress: string
}

export type WalletApi = {
getWalletData: () => WalletDataState | undefined
walletData$: Observable<WalletDataState>
Expand All @@ -130,6 +136,7 @@ export type WalletApi = {
sendTransaction: (input: SendTransactionInput) => SendTransactionResult
setRequestData: (...dataRequestBuilderItem: DataRequestBuilderItem[]) => void
sendRequest: () => WalletDataRequestResult
sendProofOfOwnershipRequest: (input: ProofOfOwnershipInput) => WalletDataRequestResult
sendOneTimeRequest: (
...oneTimeDataRequestBuilderItem: OneTimeDataRequestBuilderItem[]
) => WalletDataRequestResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,25 @@ import {
PersonaDataRequestBuilder,
personaData,
} from './persona-data'
import { proofOfOwnership, ProofOfOwnershipRequest, ProofOfOwnershipRequestBuilder } from './proof-of-ownership'

export type DataRequestBuilderItem =
| AccountsRequestBuilder
| PersonaDataRequestBuilder
| PersonaRequestBuilder
| ProofOfOwnershipRequestBuilder
| ConfigRequestBuilder

export type OneTimeDataRequestBuilderItem =
| OneTimeAccountsRequestBuilder
| OneTimePersonaDataRequestBuilder

export type DataRequestState = Partial<
{ accounts: AccountsDataRequest } & { personaData: PersonaDataRequest } & {
persona: PersonaRequest
}
>
export type DataRequestState = Partial<{
accounts: AccountsDataRequest
personaData: PersonaDataRequest
persona: PersonaRequest
proofOfOwnership: ProofOfOwnershipRequest
}>

export type ConfigRequestBuilder = {}

Expand All @@ -42,13 +45,15 @@ export const config = (data: DataRequestState) => {

export type DataRequestBuilder = {
accounts: () => AccountsRequestBuilder
proofOfOwnership: (input?: ProofOfOwnershipRequest) => ProofOfOwnershipRequestBuilder
personaData: (input?: PersonaDataRequest) => PersonaDataRequestBuilder
persona: (input?: PersonaRequest) => PersonaRequestBuilder
config: (input: DataRequestState) => ConfigRequestBuilder
}

export const DataRequestBuilder: DataRequestBuilder = {
accounts,
proofOfOwnership,
personaData,
persona,
config,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { produce } from 'immer'
import { object, InferOutput, union, string, array, optional } from 'valibot'

export type ProofOfOwnershipRequestBuilder = {
accounts: (value: string[]) => ProofOfOwnershipRequestBuilder
identity: (value: string) => ProofOfOwnershipRequestBuilder
}
export type ProofOfOwnershipRequest = InferOutput<typeof schema>

const schema = object({
accountAddresses: optional(array(string())),
identityAddress: optional(string()),
})

export const proofOfOwnership = (initialData: ProofOfOwnershipRequest = {}) => {
let data: ProofOfOwnershipRequest = produce(initialData, () => {})

const accounts = (value: string[]) => {
if (data.identityAddress) {
throw new Error(
'ProofOfOwnershipRequest: accounts and identity cannot be set at the same time',
)
}
data = produce(data, (draft) => {
draft.accountAddresses = value
})
return methods
}

const identity = (value: string) => {
if (data.accountAddresses) {
throw new Error(
'ProofOfOwnershipRequest: accounts and identity cannot be set at the same time',
)
}
data = produce(data, (draft) => {
draft.identityAddress = value
})
return methods
}

const _toObject = (): { proofOfOwnership: ProofOfOwnershipRequest } => ({
proofOfOwnership: data,
})

const methods = {
accounts,
identity,
_toObject,
}

return methods
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ export const toWalletRequest = ({
transformRdtDataRequestToWalletRequest(
isConnect,
produce({}, (draft: TransformRdtDataRequestToWalletRequestInput) => {
if (dataRequestState.proofOfOwnership) {
draft.proofOfOwnership = {
...dataRequestState.proofOfOwnership,
challenge,
}
}

if (dataRequestState.accounts) {
draft.accounts = {
numberOfAccounts: dataRequestState.accounts.numberOfAccounts || {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@ import { NumberOfValues } from '../../../../schemas'
import { produce } from 'immer'
import type { Result } from 'neverthrow'
import { ok } from 'neverthrow'
import { boolean, object, string, InferOutput, optional } from 'valibot'
import { boolean, object, string, InferOutput, optional, array } from 'valibot'
import { T } from 'vitest/dist/reporters-P7C2ytIv.js'

export type TransformRdtDataRequestToWalletRequestInput = InferOutput<
typeof TransformRdtDataRequestToWalletRequestInput
>
export const TransformRdtDataRequestToWalletRequestInput = object({
proofOfOwnership: optional(
object({
challenge: optional(string()),
accountAddresses: optional(array(string())),
identityAddress: optional(string()),
}),
),
accounts: optional(
object({
numberOfAccounts: NumberOfValues,
Expand Down Expand Up @@ -112,6 +120,45 @@ const withAccountRequestItem =
return updatedRequestItems
}

const withProofOfOwnershipRequestItem =
(input: TransformRdtDataRequestToWalletRequestInput) =>
<T extends WalletUnauthorizedRequestItems | WalletAuthorizedRequestItems>(
requestItems: T,
) => {
const updatedRequestItems = { ...requestItems }

if (input.proofOfOwnership) {
const { challenge, accountAddresses, identityAddress } =
input.proofOfOwnership

const proofOfOwnership = {
challenge,
accountAddresses,
identityAddress,
}

if (challenge) {
if (accountAddresses) {
updatedRequestItems['proofOfOwnership'] = {
discriminator: 'accountsProofOfOwnership',
accountAddresses,
challenge,
}
}

if (identityAddress) {
updatedRequestItems['proofOfOwnership'] = {
discriminator: 'personaProofOfOwnership',
identityAddress,
challenge,
}
}
}
}

return updatedRequestItems
}

const withPersonaDataRequestItem =
(input: TransformRdtDataRequestToWalletRequestInput) =>
<T extends WalletUnauthorizedRequestItems | WalletAuthorizedRequestItems>(
Expand Down Expand Up @@ -170,6 +217,7 @@ const createUnauthorizedRequestItems = (
})
.map(withAccountRequestItem(input))
.map(withPersonaDataRequestItem(input))
.map(withProofOfOwnershipRequestItem(input))

const createAuthorizedRequestItems = (
input: TransformRdtDataRequestToWalletRequestInput,
Expand All @@ -181,6 +229,7 @@ const createAuthorizedRequestItems = (
.map(withAccountRequestItem(input))
.map(withPersonaDataRequestItem(input))
.map(withResetRequestItem(input))
.map(withProofOfOwnershipRequestItem(input))

const transformConnectRequest = (
isConnect: boolean,
Expand Down
Loading

0 comments on commit 8c24675

Please sign in to comment.