diff --git a/examples/abstracted-account.html b/examples/abstracted-account.html
new file mode 100644
index 000000000..343d6ec21
--- /dev/null
+++ b/examples/abstracted-account.html
@@ -0,0 +1,494 @@
+
+
+
+
+
+
+ Beacon Example Abstracted account
+
+
+
+
+
+
+
+
+
+
+
+ Beacon Example Abstracted Account
+
+
+
+
+
+ ---
+
+
+
+
+ Active account:
+
+
+
+
+
+
+
+ ---
+
+
+
+ ---
+
+
+
+
+ Test input:
+
+
+
+
+
diff --git a/examples/dapp.html b/examples/dapp.html
index 17825fc56..d9c6fa6fd 100644
--- a/examples/dapp.html
+++ b/examples/dapp.html
@@ -39,12 +39,12 @@
-
-
-
+
@@ -201,6 +201,7 @@
],
[beacon.Regions.NORTH_AMERICA_EAST]: []
},
+ preferredNetwork: beacon.NetworkType.GHOSTNET,
featuredWallets: ['airgap', 'metamask']
// network: {
// type: beacon.NetworkType.GHOSTNET
@@ -218,6 +219,16 @@
document.getElementById('activeAccount').innerText = activeAccount.address
document.getElementById('activeAccountNetwork').innerText = activeAccount.network.type
document.getElementById('activeAccountTransport').innerText = activeAccount.origin.type
+
+ if (
+ activeAccount.walletType !== 'account_abstracted' &&
+ activeAccount.verificationType !== 'proof_of_event'
+ )
+ return
+
+ const btn = document.getElementById('requestProofOfEventChallenge')
+ btn.style.opacity = '1'
+ btn.disabled = false
} else {
document.getElementById('activeAccount').innerText = ''
document.getElementById('activeAccountNetwork').innerText = ''
@@ -268,7 +279,7 @@
// send contract call
const sendContractCall = () => {
return client.getActiveAccount().then(async (activeAccount) => {
- const TZ_BUTTON_COLORS_CONTRACT = 'KT1RPW5kTX6WFxg8JK34rGEU24gqEEudyfvz'
+ const TZ_BUTTON_COLORS_CONTRACT = 'KT1GSdrLzGAorWKoe2z7qV3P5Df6Vmo7neZt'
const tokenId = '925'
// Setting the color of TzButton is only possible if you are currently the leader and own a color
@@ -313,6 +324,27 @@
})
}
+ // Initiate a Proof Of Event Request
+ const requestProofOfEventChallenge = () => {
+ client
+ .requestProofOfEventChallenge({
+ dAppChallengeId: 'my-id',
+ payload: JSON.stringify({ timestamp: Date.now() })
+ })
+ .then(() => {
+ console.log('ProofOfEventChallenge approved')
+
+ const promise = client.getProofOfEventChallenge('my-id')
+
+ promise.then(() => {
+ alert('Proof of event verified')
+ })
+ })
+ .catch((error) => {
+ console.log('error during Proof Of Event Challenge', error)
+ })
+ }
+
document.getElementById('connect').addEventListener('click', () => {
// Check if we have an active account
client.getActiveAccount().then((activeAccount) => {
@@ -339,6 +371,11 @@
requestPermission()
})
+ // Add event listener to the button
+ document.getElementById('requestProofOfEventChallenge').addEventListener('click', () => {
+ requestProofOfEventChallenge()
+ })
+
// Add event listener to the button
document.getElementById('reset').addEventListener('click', () => {
client.destroy().then(() => {
diff --git a/examples/index.html b/examples/index.html
index 785be45de..6c6ad1a4b 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -1,3 +1,4 @@
-DApp
Wallet
Events
+DApp
+Wallet
+Events
+Abstracted account
diff --git a/packages/beacon-core/__tests__/managers/AccountManager.spec.ts b/packages/beacon-core/__tests__/managers/AccountManager.spec.ts
index 3ff1bf810..3030176e1 100644
--- a/packages/beacon-core/__tests__/managers/AccountManager.spec.ts
+++ b/packages/beacon-core/__tests__/managers/AccountManager.spec.ts
@@ -17,11 +17,12 @@ const account1: AccountInfo = {
type: Origin.P2P,
id: 'o1'
},
- address: 'tz1',
- publicKey: 'pubkey1',
+ address: 'KT1',
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN],
- connectedAt: new Date().getTime()
+ connectedAt: new Date().getTime(),
+ walletType: 'abstracted_account',
+ hasVerifiedChallenge: false
}
const account2: AccountInfo = {
@@ -35,7 +36,8 @@ const account2: AccountInfo = {
publicKey: 'pubkey2',
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN],
- connectedAt: new Date().getTime()
+ connectedAt: new Date().getTime(),
+ walletType: 'implicit'
}
const account3: AccountInfo = {
@@ -49,7 +51,8 @@ const account3: AccountInfo = {
publicKey: 'pubkey3',
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN],
- connectedAt: new Date().getTime()
+ connectedAt: new Date().getTime(),
+ walletType: 'implicit'
}
describe(`AccountManager`, () => {
@@ -87,6 +90,24 @@ describe(`AccountManager`, () => {
expect(accountsAfterReplacing[0].scopes, 'after replacing').to.deep.equal(newAccount1.scopes)
})
+ it(`updates an existing account`, async () => {
+ const accountsBefore: AccountInfo[] = await manager.getAccounts()
+ expect(accountsBefore.length, 'before').to.equal(0)
+
+ await manager.addAccount(account1)
+ const accountsAfterAdding: AccountInfo[] = await manager.getAccounts()
+
+ expect(accountsAfterAdding.length, 'after adding').to.equal(1)
+
+ await manager.updateAccount(account1.accountIdentifier, {
+ hasVerifiedChallenge: true
+ })
+ const accountsAfterReplacing: AccountInfo[] = await manager.getAccounts()
+
+ expect(accountsAfterReplacing.length, 'after replacing').to.equal(1)
+ expect(accountsAfterReplacing[0].hasVerifiedChallenge, 'after replacing').to.equal(true)
+ })
+
it(`reads and adds multiple accounts`, async () => {
const accountsBefore: AccountInfo[] = await manager.getAccounts()
expect(accountsBefore.length, 'before').to.equal(0)
diff --git a/packages/beacon-core/src/managers/AccountManager.ts b/packages/beacon-core/src/managers/AccountManager.ts
index 723678fad..7f0dba95b 100644
--- a/packages/beacon-core/src/managers/AccountManager.ts
+++ b/packages/beacon-core/src/managers/AccountManager.ts
@@ -29,6 +29,24 @@ export class AccountManager {
)
}
+ public async updateAccount(
+ accountIdentifier: string,
+ accountInfo: Partial
+ ): Promise {
+ const account = await this.getAccount(accountIdentifier)
+
+ if (!account) return undefined
+
+ const newAccount = { ...account, ...accountInfo }
+ await this.storageManager.addOne(
+ newAccount,
+ (account) => account.accountIdentifier === accountIdentifier,
+ true
+ )
+
+ return newAccount
+ }
+
public async removeAccount(accountIdentifier: string): Promise {
return this.storageManager.remove((account) => account.accountIdentifier === accountIdentifier)
}
diff --git a/packages/beacon-dapp/__tests__/dapp-client.spec.ts b/packages/beacon-dapp/__tests__/dapp-client.spec.ts
index b38b3e153..05e15047a 100644
--- a/packages/beacon-dapp/__tests__/dapp-client.spec.ts
+++ b/packages/beacon-dapp/__tests__/dapp-client.spec.ts
@@ -20,7 +20,8 @@ import {
StorageKey,
TezosOperationType,
TransportStatus,
- ExtendedP2PPairingRequest
+ ExtendedP2PPairingRequest,
+ ProofOfEventChallengeResponse
} from '@airgap/beacon-types'
import { MockTransport } from '../../../test/test-utils/MockTransport'
@@ -71,11 +72,13 @@ const account1: AccountInfo = {
type: Origin.P2P,
id: peer1.publicKey
},
- address: 'tz1',
- publicKey: 'pubkey1',
+ address: 'KT1',
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN],
- connectedAt: new Date().getTime()
+ connectedAt: new Date().getTime(),
+ walletType: 'abstracted_account',
+ verificationType: 'proof_of_event',
+ hasVerifiedChallenge: false
}
const account2: AccountInfo = {
@@ -89,7 +92,8 @@ const account2: AccountInfo = {
publicKey: 'pubkey2',
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN],
- connectedAt: new Date().getTime()
+ connectedAt: new Date().getTime(),
+ walletType: 'implicit'
}
/**
@@ -161,7 +165,8 @@ describe(`DAppClient`, () => {
type: BeaconMessageType.PermissionResponse,
publicKey: 'pubkey1',
network: { type: NetworkType.MAINNET },
- scopes: []
+ scopes: [],
+ walletType: 'implicit'
}
const contextInfo: ConnectionContext = {
origin: Origin.P2P,
@@ -317,7 +322,8 @@ describe(`DAppClient`, () => {
publicKey: 'pubkey1',
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN],
- connectedAt: new Date().getTime()
+ connectedAt: new Date().getTime(),
+ walletType: 'implicit'
}
const getPeersStub = sinon.stub(DAppClient.prototype, 'getPeer').resolves(peer1)
@@ -565,7 +571,8 @@ describe(`DAppClient`, () => {
senderId: 'sender-id',
publicKey: '444e1f4ab90c304a5ac003d367747aab63815f583ff2330ce159d12c1ecceba1',
network: { type: NetworkType.MAINNET },
- scopes: [PermissionScope.SIGN, PermissionScope.OPERATION_REQUEST]
+ scopes: [PermissionScope.SIGN, PermissionScope.OPERATION_REQUEST],
+ walletType: 'implicit'
}
const connectionInfo: ConnectionContext = {
@@ -622,6 +629,60 @@ describe(`DAppClient`, () => {
})
})
+ it(`should prepare a proof of event challenge request`, async () => {
+ const dAppClient = new DAppClient({ name: 'Test', storage: new LocalStorage() })
+
+ const permissionResponse: ProofOfEventChallengeResponse = {
+ id: 'my-id',
+ type: BeaconMessageType.ProofOfEventChallengeResponse,
+ version: BEACON_VERSION,
+ senderId: 'sender-id',
+ dAppChallengeId: 'my-id',
+ isAccepted: true
+ }
+
+ const connectionInfo: ConnectionContext = {
+ origin: Origin.P2P,
+ id: 'KT1'
+ }
+ const makeRequestStub = sinon
+ .stub(dAppClient, 'makeRequest')
+ .resolves({ message: permissionResponse, connectionInfo })
+
+ const notifySuccessStub = sinon.stub(dAppClient, 'notifySuccess').resolves()
+
+ const recordProofOfEventChallengeStub = sinon
+ .stub(dAppClient, 'recordProofOfEventChallenge')
+ .resolves()
+
+ const getActiveAccountStub = sinon.stub(dAppClient, 'getActiveAccount').resolves(account1)
+
+ const input = {
+ dAppChallengeId: 'my-id',
+ payload: 'my-payload'
+ }
+ const response = await dAppClient.requestProofOfEventChallenge(input)
+
+ expect(notifySuccessStub.callCount, 'notifySuccessStub').to.equal(1)
+ expect(recordProofOfEventChallengeStub.callCount, 'recordProofOfEventChallengeStub').to.equal(1)
+ expect(getActiveAccountStub.callCount, 'getActiveAccountStub').to.equal(2)
+ expect(makeRequestStub.callCount).to.equal(1)
+ expect(makeRequestStub.firstCall.args[0]).to.deep.equal({
+ type: BeaconMessageType.ProofOfEventChallengeRequest,
+ contractAddress: 'KT1',
+ ...input
+ })
+ delete (response as any).accountInfo
+ expect(response).to.deep.equal({
+ id: 'my-id',
+ type: 'proof_of_event_challenge_response',
+ version: BEACON_VERSION,
+ senderId: 'sender-id',
+ dAppChallengeId: 'my-id',
+ isAccepted: true
+ })
+ })
+
it(`should prepare a sign payload request (RAW)`, async () => {
const dAppClient = new DAppClient({ name: 'Test', storage: new LocalStorage() })
@@ -637,7 +698,8 @@ describe(`DAppClient`, () => {
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN, PermissionScope.OPERATION_REQUEST],
threshold: undefined,
- connectedAt: 1599142450653
+ connectedAt: 1599142450653,
+ walletType: 'implicit'
}
const getPeerStub = sinon.stub(DAppClient.prototype, 'getPeer').resolves(peer1)
@@ -739,7 +801,8 @@ describe(`DAppClient`, () => {
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN, PermissionScope.OPERATION_REQUEST],
threshold: undefined,
- connectedAt: 1599142450653
+ connectedAt: 1599142450653,
+ walletType: 'implicit'
}
const getPeerStub = sinon.stub(DAppClient.prototype, 'getPeer').resolves(peer1)
@@ -807,7 +870,8 @@ describe(`DAppClient`, () => {
network: { type: NetworkType.MAINNET },
scopes: [PermissionScope.SIGN, PermissionScope.OPERATION_REQUEST],
threshold: undefined,
- connectedAt: 1599142450653
+ connectedAt: 1599142450653,
+ walletType: 'implicit'
}
const getPeerStub = sinon.stub(DAppClient.prototype, 'getPeer').resolves(peer1)
diff --git a/packages/beacon-dapp/src/beacon-message-events.ts b/packages/beacon-dapp/src/beacon-message-events.ts
index b6d55acf7..2710b9acc 100644
--- a/packages/beacon-dapp/src/beacon-message-events.ts
+++ b/packages/beacon-dapp/src/beacon-message-events.ts
@@ -24,6 +24,21 @@ export const messageEvents: {
success: BeaconEvent.UNKNOWN,
error: BeaconEvent.UNKNOWN
},
+ [BeaconMessageType.ProofOfEventChallengeRequest]: {
+ sent: BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT,
+ success: BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS,
+ error: BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR
+ },
+ [BeaconMessageType.ProofOfEventChallengeResponse]: {
+ sent: BeaconEvent.UNKNOWN,
+ success: BeaconEvent.UNKNOWN,
+ error: BeaconEvent.UNKNOWN
+ },
+ [BeaconMessageType.ProofOfEventChallengeRecorded]: {
+ sent: BeaconEvent.UNKNOWN,
+ success: BeaconEvent.UNKNOWN,
+ error: BeaconEvent.UNKNOWN
+ },
[BeaconMessageType.OperationRequest]: {
sent: BeaconEvent.OPERATION_REQUEST_SENT,
success: BeaconEvent.OPERATION_REQUEST_SUCCESS,
diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts
index 3388da746..c87f88a12 100644
--- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts
+++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts
@@ -59,6 +59,11 @@ import {
ExtensionApp,
WebApp,
ExtendedWalletConnectPairingResponse,
+ ProofOfEventChallengeRequest,
+ ProofOfEventChallengeResponse,
+ ProofOfEventChallengeRequestInput,
+ RequestProofOfEventChallengeInput,
+ ProofOfEventChallengeRecordedMessageInput,
ChangeAccountRequest,
PeerInfoType
// PermissionRequestV3
@@ -84,7 +89,10 @@ import {
ExposedPromise,
generateGUID,
toHex,
- prefixPublicKey
+ signMessage,
+ CONTRACT_PREFIX,
+ prefixPublicKey,
+ isValidAddress
} from '@airgap/beacon-utils'
import { messageEvents } from '../beacon-message-events'
import { BlockExplorer } from '../utils/block-explorer'
@@ -110,7 +118,6 @@ import {
getWebList,
getiOSList
} from '@airgap/beacon-ui'
-import { signMessage } from '@airgap/beacon-utils'
import { WalletConnectTransport } from '@airgap/beacon-transport-walletconnect'
const logger = new Logger('DAppClient')
@@ -769,7 +776,13 @@ export class DAppClient extends Client {
* @param type The type of the message
*/
public async checkPermissions(type: BeaconMessageType): Promise {
- if (type === BeaconMessageType.PermissionRequest) {
+ if (
+ [
+ BeaconMessageType.PermissionRequest,
+ BeaconMessageType.ProofOfEventChallengeRequest,
+ BeaconMessageType.ProofOfEventChallengeRecorded
+ ].includes(type)
+ ) {
return true
}
@@ -1005,8 +1018,18 @@ export class DAppClient extends Client {
throw await this.handleRequestError(request, requestError)
})
+ console.log('######## MESSAGE #######')
+ console.log(message)
+
const accountInfo = await this.onNewAccount(message, connectionInfo)
+ console.log('######## ACCOUNT INFO #######')
+
+ console.log(JSON.stringify(accountInfo))
+
+ await this.accountManager.addAccount(accountInfo)
+ await this.setActiveAccount(accountInfo)
+
const output: PermissionResponseOutput = {
...message,
walletKey: accountInfo.walletKey,
@@ -1029,6 +1052,85 @@ export class DAppClient extends Client {
return output
}
+ /**
+ * Send a proof of event request to the wallet. The wallet will either accept or decline the challenge.
+ * If it is accepted, the challenge will be stored, meaning that even if the user refresh the page, the DAppClient will keep checking if the challenge has been fulfilled.
+ * Once the challenge is stored, a challenge stored message will be sent to the wallet.
+ * It's **highly recommended** to run a proof of event challenge to check the identity of an abstracted account
+ *
+ * @param input The message details we need to prepare the ProofOfEventChallenge message.
+ */
+ public async requestProofOfEventChallenge(input: RequestProofOfEventChallengeInput) {
+ const activeAccount = await this.getActiveAccount()
+
+ if (!activeAccount)
+ throw new Error('Please request permissions before doing a proof of event challenge')
+ if (
+ activeAccount.walletType !== 'abstracted_account' &&
+ activeAccount.verificationType !== 'proof_of_event'
+ )
+ throw new Error(
+ 'This wallet is not an abstracted account and thus cannot perform proof of event'
+ )
+
+ const request: ProofOfEventChallengeRequestInput = {
+ type: BeaconMessageType.ProofOfEventChallengeRequest,
+ contractAddress: activeAccount.address,
+ ...input
+ }
+
+ const { message, connectionInfo } = await this.makeRequest<
+ ProofOfEventChallengeRequest,
+ ProofOfEventChallengeResponse
+ >(request).catch(async (requestError: ErrorResponse) => {
+ throw await this.handleRequestError(request, requestError)
+ })
+
+ this.analytics.track(
+ 'event',
+ 'DAppClient',
+ `Proof of event challenge ${message.isAccepted ? 'accepted' : 'refused'}`,
+ { address: activeAccount.address }
+ )
+
+ if (message.isAccepted) {
+ await this.recordProofOfEventChallenge(input)
+ }
+
+ await this.notifySuccess(request, {
+ account: activeAccount,
+ output: message,
+ blockExplorer: this.blockExplorer,
+ connectionContext: connectionInfo,
+ walletInfo: await this.getWalletInfo()
+ })
+
+ return message
+ }
+
+ private async recordProofOfEventChallenge(input: RequestProofOfEventChallengeInput) {
+ const activeAccount = await this.getActiveAccount()
+
+ if (!activeAccount)
+ throw new Error(
+ 'Active account is undefined. Please request permissions before recording a proof of event challenge'
+ )
+
+ let success = true
+ let errorMessage = ''
+
+ const recordedRequest: ProofOfEventChallengeRecordedMessageInput = {
+ type: BeaconMessageType.ProofOfEventChallengeRecorded,
+ dAppChallengeId: input.dAppChallengeId,
+ success,
+ errorMessage
+ }
+
+ await this.makeRequest(recordedRequest, true).catch(async (requestError: ErrorResponse) => {
+ throw await this.handleRequestError(recordedRequest, requestError)
+ })
+ }
+
/**
* This method will send a "SignPayloadRequest" to the wallet. This method is meant to be used to sign
* arbitrary data (eg. a string). It will return the signature in the format of "edsig..."
@@ -1416,6 +1518,13 @@ export class DAppClient extends Client {
connectionContext: ConnectionContext
walletInfo: WalletInfo
}
+ | {
+ account: AccountInfo
+ output: ProofOfEventChallengeResponse
+ blockExplorer: BlockExplorer
+ connectionContext: ConnectionContext
+ walletInfo: WalletInfo
+ }
| {
account: AccountInfo
output: OperationResponseOutput
@@ -1541,13 +1650,24 @@ export class DAppClient extends Client {
*
* @param requestInput The BeaconMessage to be sent to the wallet
* @param account The account that the message will be sent to
+ * @param skipResponse If true, the function return as soon as the message is sent
*/
- private async makeRequest(
- requestInput: Optional
+
+ private makeRequest(
+ requestInput: Optional,
+ skipResponse?: undefined | false
): Promise<{
message: U
connectionInfo: ConnectionContext
- }> {
+ }>
+ private makeRequest(
+ requestInput: Optional,
+ skipResponse: true
+ ): Promise
+ private async makeRequest(
+ requestInput: Optional,
+ skipResponse?: boolean
+ ) {
const messageId = await generateGUID()
logger.time(true, messageId)
@@ -1593,15 +1713,19 @@ export class DAppClient extends Client {
...requestInput
}
- const exposed = new ExposedPromise<
- {
- message: BeaconMessage | BeaconMessageWrapper
- connectionInfo: ConnectionContext
- },
- ErrorResponse
- >()
+ let exposed
- this.addOpenRequest(request.id, exposed)
+ if (!skipResponse) {
+ exposed = new ExposedPromise<
+ {
+ message: BeaconMessage | BeaconMessageWrapper
+ connectionInfo: ConnectionContext
+ },
+ ErrorResponse
+ >()
+
+ this.addOpenRequest(request.id, exposed)
+ }
const payload = await new Serializer().serialize(request)
@@ -1648,7 +1772,7 @@ export class DAppClient extends Client {
.catch((emitError) => console.warn(emitError))
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- return exposed.promise as any // TODO: fix type
+ return exposed?.promise as any // TODO: fix type
}
/**
@@ -1857,10 +1981,29 @@ export class DAppClient extends Client {
connectionInfo: ConnectionContext
): Promise {
// TODO: Migration code. Remove sometime after 1.0.0 release.
- const publicKey = await prefixPublicKey(
+ const tempPK: string | undefined =
message.publicKey || (message as any).pubkey || (message as any).pubKey
- )
- const address = await getAddressFromPublicKey(publicKey)
+
+ const publicKey = !!tempPK ? await prefixPublicKey(tempPK) : undefined
+
+ if (!publicKey && !message.address) {
+ throw new Error('PublicKey or Address must be defined')
+ }
+
+ const address = message.address ?? (await getAddressFromPublicKey(publicKey!))
+
+ if (isValidAddress(address)) {
+ throw new Error(`Invalid address: "${address}"`)
+ }
+
+ if (
+ message.walletType === 'abstracted_account' &&
+ address.substring(0, 3) !== CONTRACT_PREFIX
+ ) {
+ throw new Error(
+ `Invalid abstracted account address "${address}", it should be a ${CONTRACT_PREFIX} address`
+ )
+ }
logger.log('######## MESSAGE #######')
logger.log('onNewAccount', message)
@@ -1881,7 +2024,10 @@ export class DAppClient extends Client {
scopes: message.scopes,
threshold: message.threshold,
notification: message.notification,
- connectedAt: new Date().getTime()
+ connectedAt: new Date().getTime(),
+ walletType: message.walletType ?? 'implicit',
+ verificationType: message.verificationType,
+ ...(message.verificationType === 'proof_of_event' ? { hasVerifiedChallenge: false } : {})
}
logger.log('accountInfo', '######## ACCOUNT INFO #######')
diff --git a/packages/beacon-dapp/src/events.ts b/packages/beacon-dapp/src/events.ts
index a37781bd7..41280a929 100644
--- a/packages/beacon-dapp/src/events.ts
+++ b/packages/beacon-dapp/src/events.ts
@@ -39,6 +39,7 @@ import {
} from '@airgap/beacon-core'
import { shortenString } from './utils/shorten-string'
import { isMobile } from '@airgap/beacon-ui'
+import { ProofOfEventChallengeResponseOutput } from '@airgap/beacon-types'
const logger = new Logger('BeaconEvents')
@@ -61,6 +62,9 @@ export enum BeaconEvent {
PERMISSION_REQUEST_SENT = 'PERMISSION_REQUEST_SENT',
PERMISSION_REQUEST_SUCCESS = 'PERMISSION_REQUEST_SUCCESS',
PERMISSION_REQUEST_ERROR = 'PERMISSION_REQUEST_ERROR',
+ PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT = 'PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT',
+ PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS = 'PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS',
+ PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR = 'PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR',
OPERATION_REQUEST_SENT = 'OPERATION_REQUEST_SENT',
OPERATION_REQUEST_SUCCESS = 'OPERATION_REQUEST_SUCCESS',
OPERATION_REQUEST_ERROR = 'OPERATION_REQUEST_ERROR',
@@ -118,6 +122,18 @@ export interface BeaconEventType {
walletInfo: WalletInfo
}
[BeaconEvent.PERMISSION_REQUEST_ERROR]: { errorResponse: ErrorResponse; walletInfo: WalletInfo }
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT]: RequestSentInfo
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS]: {
+ account: AccountInfo
+ output: ProofOfEventChallengeResponseOutput
+ blockExplorer: BlockExplorer
+ connectionContext: ConnectionContext
+ walletInfo: WalletInfo
+ }
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR]: {
+ errorResponse: ErrorResponse
+ walletInfo: WalletInfo
+ }
[BeaconEvent.OPERATION_REQUEST_SENT]: RequestSentInfo
[BeaconEvent.OPERATION_REQUEST_SUCCESS]: {
account: AccountInfo
@@ -482,6 +498,25 @@ const showPermissionSuccessAlert = async (
})
}
+const showProofOfEventChallengeSuccessAlert = async (
+ data: BeaconEventType[BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS]
+): Promise => {
+ const { output } = data
+
+ await openToast({
+ body: `{{wallet}}\u00A0 has ${output.isAccepted ? 'accepted' : 'refused'} the challenge`,
+ timer: SUCCESS_TIMER,
+ walletInfo: data.walletInfo,
+ state: 'finished',
+ actions: [
+ {
+ text: 'Challenge Id',
+ actionText: output.dAppChallengeId
+ }
+ ]
+ })
+}
+
/**
* Show an "Operation Broadcasted" alert
*
@@ -639,6 +674,9 @@ export const defaultEventCallbacks: {
[BeaconEvent.PERMISSION_REQUEST_SENT]: showSentToast,
[BeaconEvent.PERMISSION_REQUEST_SUCCESS]: showPermissionSuccessAlert,
[BeaconEvent.PERMISSION_REQUEST_ERROR]: showErrorToast,
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT]: showSentToast,
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS]: showProofOfEventChallengeSuccessAlert,
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR]: showErrorToast,
[BeaconEvent.OPERATION_REQUEST_SENT]: showSentToast,
[BeaconEvent.OPERATION_REQUEST_SUCCESS]: showOperationSuccessAlert,
[BeaconEvent.OPERATION_REQUEST_ERROR]: showErrorToast,
@@ -679,6 +717,15 @@ export class BeaconEventHandler {
[BeaconEvent.PERMISSION_REQUEST_SENT]: [defaultEventCallbacks.PERMISSION_REQUEST_SENT],
[BeaconEvent.PERMISSION_REQUEST_SUCCESS]: [defaultEventCallbacks.PERMISSION_REQUEST_SUCCESS],
[BeaconEvent.PERMISSION_REQUEST_ERROR]: [defaultEventCallbacks.PERMISSION_REQUEST_ERROR],
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT]: [
+ defaultEventCallbacks.PERMISSION_REQUEST_SENT
+ ],
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS]: [
+ defaultEventCallbacks.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS
+ ],
+ [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR]: [
+ defaultEventCallbacks.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR
+ ],
[BeaconEvent.OPERATION_REQUEST_SENT]: [defaultEventCallbacks.OPERATION_REQUEST_SENT],
[BeaconEvent.OPERATION_REQUEST_SUCCESS]: [defaultEventCallbacks.OPERATION_REQUEST_SUCCESS],
[BeaconEvent.OPERATION_REQUEST_ERROR]: [defaultEventCallbacks.OPERATION_REQUEST_ERROR],
diff --git a/packages/beacon-transport-walletconnect/src/communication-client/WalletConnectCommunicationClient.ts b/packages/beacon-transport-walletconnect/src/communication-client/WalletConnectCommunicationClient.ts
index b1e77db9b..a1e49e680 100644
--- a/packages/beacon-transport-walletconnect/src/communication-client/WalletConnectCommunicationClient.ts
+++ b/packages/beacon-transport-walletconnect/src/communication-client/WalletConnectCommunicationClient.ts
@@ -252,7 +252,8 @@ export class WalletConnectCommunicationClient extends CommunicationClient {
publicKey,
network: message.network,
scopes: [PermissionScope.SIGN, PermissionScope.OPERATION_REQUEST],
- id: this.currentMessageId!
+ id: this.currentMessageId!,
+ walletType: 'implicit'
}
this.notifyListeners(session.pairingTopic, permissionResponse)
@@ -475,7 +476,8 @@ export class WalletConnectCommunicationClient extends CommunicationClient {
type: BeaconMessageType.ChangeAccountRequest,
publicKey,
network: { type: chainId as NetworkType },
- scopes: [PermissionScope.SIGN, PermissionScope.OPERATION_REQUEST]
+ scopes: [PermissionScope.SIGN, PermissionScope.OPERATION_REQUEST],
+ walletType: 'implicit'
})
}
} catch {}
diff --git a/packages/beacon-types/src/index.ts b/packages/beacon-types/src/index.ts
index 52d946b48..f2bc74dda 100644
--- a/packages/beacon-types/src/index.ts
+++ b/packages/beacon-types/src/index.ts
@@ -4,6 +4,9 @@
*/
import { AppMetadata } from './types/beacon/AppMetadata'
import { PermissionRequest } from './types/beacon/messages/PermissionRequest'
+import { ProofOfEventChallengeRequest } from './types/beacon/messages/ProofOfEventChallengeRequest'
+import { ProofOfEventChallengeResponse } from './types/beacon/messages/ProofOfEventChallengeResponse'
+import { ProofOfEventChallengeRecordedRequest } from './types/beacon/messages/ProofOfEventChallengeRecordedRequest'
import { Network } from './types/beacon/Network'
import { BeaconBaseMessage } from './types/beacon/BeaconBaseMessage'
import { BeaconMessageType } from './types/beacon/BeaconMessageType'
@@ -49,6 +52,7 @@ import { StorageKeyReturnType } from './types/storage/StorageKeyReturnType'
import { ExtendedP2PPairingRequest, P2PPairingRequest } from './types/P2PPairingRequest'
import { BeaconMessage } from './types/beacon/BeaconMessage'
import { RequestPermissionInput } from './types/RequestPermissionInput'
+import { RequestProofOfEventChallengeInput } from './types/RequestProofOfEventChallengeInput'
import { RequestSignPayloadInput } from './types/RequestSignPayloadInput'
// import { RequestEncryptPayloadInput } from './types/RequestEncryptPayloadInput'
import { RequestOperationInput } from './types/RequestOperationInput'
@@ -70,7 +74,8 @@ import {
// EncryptPayloadResponseOutput,
OperationResponseOutput,
BroadcastResponseOutput,
- BeaconResponseOutputMessage
+ BeaconResponseOutputMessage,
+ ProofOfEventChallengeResponseOutput
} from './types/beacon/messages/BeaconResponseOutputMessage'
import {
PermissionRequestInput,
@@ -79,7 +84,9 @@ import {
OperationRequestInput,
BroadcastRequestInput,
BeaconRequestInputMessage,
- IgnoredRequestInputProperties
+ IgnoredRequestInputProperties,
+ ProofOfEventChallengeRecordedMessageInput,
+ ProofOfEventChallengeRequestInput
} from './types/beacon/messages/BeaconRequestInputMessage'
import {
PermissionRequestOutput,
@@ -87,7 +94,9 @@ import {
// EncryptPayloadRequestOutput,
OperationRequestOutput,
BroadcastRequestOutput,
- BeaconRequestOutputMessage
+ BeaconRequestOutputMessage,
+ ProofOfEventChallengeRequestOutput,
+ ProofOfEventChallengeRecordedMessageOutput
} from './types/beacon/messages/BeaconRequestOutputMessage'
import { PermissionInfo } from './types/PermissionInfo'
import { ConnectionContext } from './types/ConnectionContext'
@@ -202,12 +211,17 @@ export {
Extension,
EncryptedExtensionMessage,
RequestPermissionInput,
+ RequestProofOfEventChallengeInput,
RequestSignPayloadInput,
+ ProofOfEventChallengeRecordedMessageInput,
// RequestEncryptPayloadInput,
RequestOperationInput,
RequestBroadcastInput,
PermissionInfo,
- PermissionEntity
+ PermissionEntity,
+ ProofOfEventChallengeRequest,
+ ProofOfEventChallengeResponse,
+ ProofOfEventChallengeRecordedRequest
}
export {
@@ -219,17 +233,21 @@ export {
AcknowledgeResponseInput,
ErrorResponseInput,
PermissionResponseOutput,
+ ProofOfEventChallengeResponseOutput,
SignPayloadResponseOutput,
// EncryptPayloadResponseOutput,
OperationResponseOutput,
BroadcastResponseOutput,
PermissionRequestInput,
SignPayloadRequestInput,
+ ProofOfEventChallengeRequestInput,
// EncryptPayloadRequestInput,
OperationRequestInput,
BroadcastRequestInput,
PermissionRequestOutput,
SignPayloadRequestOutput,
+ ProofOfEventChallengeRequestOutput,
+ ProofOfEventChallengeRecordedMessageOutput,
// EncryptPayloadRequestOutput,
OperationRequestOutput,
BroadcastRequestOutput,
diff --git a/packages/beacon-types/src/types/AccountInfo.ts b/packages/beacon-types/src/types/AccountInfo.ts
index d74926eb8..45a0ab538 100644
--- a/packages/beacon-types/src/types/AccountInfo.ts
+++ b/packages/beacon-types/src/types/AccountInfo.ts
@@ -12,7 +12,10 @@ export interface AccountInfo extends PermissionEntity {
id: string
}
walletKey?: string
- publicKey: string
+ publicKey?: string
connectedAt: number
notification?: Notification
+ hasVerifiedChallenge?: boolean
+ walletType: 'implicit' | 'abstracted_account'
+ verificationType?: 'proof_of_event'
}
diff --git a/packages/beacon-types/src/types/PermissionInfo.ts b/packages/beacon-types/src/types/PermissionInfo.ts
index 55c81f44e..82b39e786 100644
--- a/packages/beacon-types/src/types/PermissionInfo.ts
+++ b/packages/beacon-types/src/types/PermissionInfo.ts
@@ -6,6 +6,6 @@ export interface PermissionInfo extends PermissionEntity {
senderId: string
appMetadata: AppMetadata
website: string
- publicKey: string
+ publicKey?: string
connectedAt: number
}
diff --git a/packages/beacon-types/src/types/RequestProofOfEventChallengeInput.ts b/packages/beacon-types/src/types/RequestProofOfEventChallengeInput.ts
new file mode 100644
index 000000000..a74011fa2
--- /dev/null
+++ b/packages/beacon-types/src/types/RequestProofOfEventChallengeInput.ts
@@ -0,0 +1,7 @@
+/**
+ * @category DApp
+ */
+export interface RequestProofOfEventChallengeInput {
+ dAppChallengeId: string
+ payload: string
+}
diff --git a/packages/beacon-types/src/types/beacon/BeaconMessage.ts b/packages/beacon-types/src/types/beacon/BeaconMessage.ts
index f05733e63..daf1b45bc 100644
--- a/packages/beacon-types/src/types/beacon/BeaconMessage.ts
+++ b/packages/beacon-types/src/types/beacon/BeaconMessage.ts
@@ -12,6 +12,9 @@ import {
AcknowledgeResponse,
DisconnectMessage,
ErrorResponse,
+ ProofOfEventChallengeRequest,
+ ProofOfEventChallengeResponse,
+ ProofOfEventChallengeRecordedRequest,
ChangeAccountRequest
} from '@airgap/beacon-types'
@@ -21,6 +24,9 @@ import {
export type BeaconMessage =
| PermissionRequest
| PermissionResponse
+ | ProofOfEventChallengeRequest
+ | ProofOfEventChallengeResponse
+ | ProofOfEventChallengeRecordedRequest
| OperationRequest
| OperationResponse
| SignPayloadRequest
diff --git a/packages/beacon-types/src/types/beacon/BeaconMessageType.ts b/packages/beacon-types/src/types/beacon/BeaconMessageType.ts
index 6eb8faa17..d71f92734 100644
--- a/packages/beacon-types/src/types/beacon/BeaconMessageType.ts
+++ b/packages/beacon-types/src/types/beacon/BeaconMessageType.ts
@@ -10,6 +10,9 @@ export enum BeaconMessageType {
PermissionResponse = 'permission_response',
SignPayloadResponse = 'sign_payload_response',
// EncryptPayloadResponse = 'encrypt_payload_response',
+ ProofOfEventChallengeRequest = 'proof_of_event_challenge_request',
+ ProofOfEventChallengeResponse = 'proof_of_event_challenge_response',
+ ProofOfEventChallengeRecorded = 'proof_of_event_challenge_recorded',
OperationResponse = 'operation_response',
BroadcastResponse = 'broadcast_response',
Acknowledge = 'acknowledge',
diff --git a/packages/beacon-types/src/types/beacon/BeaconRequestMessage.ts b/packages/beacon-types/src/types/beacon/BeaconRequestMessage.ts
index f0cedfec7..43062f7a3 100644
--- a/packages/beacon-types/src/types/beacon/BeaconRequestMessage.ts
+++ b/packages/beacon-types/src/types/beacon/BeaconRequestMessage.ts
@@ -2,7 +2,9 @@ import {
PermissionRequest,
OperationRequest,
SignPayloadRequest,
- BroadcastRequest
+ BroadcastRequest,
+ ProofOfEventChallengeRequest,
+ ProofOfEventChallengeRecordedRequest
// EncryptPayloadRequest
} from '@airgap/beacon-types'
@@ -15,3 +17,5 @@ export type BeaconRequestMessage =
| SignPayloadRequest
// | EncryptPayloadRequest
| BroadcastRequest
+ | ProofOfEventChallengeRequest
+ | ProofOfEventChallengeRecordedRequest
diff --git a/packages/beacon-types/src/types/beacon/messages/BeaconRequestInputMessage.ts b/packages/beacon-types/src/types/beacon/messages/BeaconRequestInputMessage.ts
index 393020803..90111708d 100644
--- a/packages/beacon-types/src/types/beacon/messages/BeaconRequestInputMessage.ts
+++ b/packages/beacon-types/src/types/beacon/messages/BeaconRequestInputMessage.ts
@@ -1,8 +1,9 @@
-import { Optional } from '@airgap/beacon-types'
+import { Optional, ProofOfEventChallengeRecordedRequest } from '@airgap/beacon-types'
import {
PermissionRequest,
OperationRequest,
SignPayloadRequest,
+ ProofOfEventChallengeRequest,
// EncryptPayloadRequest,
BroadcastRequest
} from '@airgap/beacon-types'
@@ -18,6 +19,22 @@ export type IgnoredRequestInputProperties = 'id' | 'senderId' | 'version'
* @category DApp
*/
export type PermissionRequestInput = Optional
+/**
+ * @internalapi
+ * @category DApp
+ */
+export type ProofOfEventChallengeRequestInput = Optional<
+ ProofOfEventChallengeRequest,
+ IgnoredRequestInputProperties
+>
+/**
+ * @internalapi
+ * @category DApp
+ */
+export type ProofOfEventChallengeRecordedMessageInput = Optional<
+ ProofOfEventChallengeRecordedRequest,
+ IgnoredRequestInputProperties
+>
/**
* @internalapi
* @category DApp
@@ -52,3 +69,5 @@ export type BeaconRequestInputMessage =
// | EncryptPayloadRequestInput
| SignPayloadRequestInput
| BroadcastRequestInput
+ | ProofOfEventChallengeRequestInput
+ | ProofOfEventChallengeRecordedMessageInput
diff --git a/packages/beacon-types/src/types/beacon/messages/BeaconRequestOutputMessage.ts b/packages/beacon-types/src/types/beacon/messages/BeaconRequestOutputMessage.ts
index 394fc9361..65659f6e5 100644
--- a/packages/beacon-types/src/types/beacon/messages/BeaconRequestOutputMessage.ts
+++ b/packages/beacon-types/src/types/beacon/messages/BeaconRequestOutputMessage.ts
@@ -1,4 +1,8 @@
-import { Optional } from '@airgap/beacon-types'
+import {
+ Optional,
+ ProofOfEventChallengeRecordedRequest,
+ ProofOfEventChallengeRequest
+} from '@airgap/beacon-types'
import {
AppMetadata,
PermissionRequest,
@@ -28,6 +32,22 @@ export type PermissionRequestOutput = Optional &
+ ExtraResponseOutputProperties
+/**
+ * @category Wallet
+ */
+export type ProofOfEventChallengeRecordedMessageOutput = Optional<
+ ProofOfEventChallengeRecordedRequest,
+ IgnoredRequestOutputProperties
+> &
+ ExtraResponseOutputProperties
+/**
+ * @category Wallet
+ */
export type OperationRequestOutput = Optional &
ExtraResponseOutputProperties
/**
@@ -62,3 +82,5 @@ export type BeaconRequestOutputMessage =
| SignPayloadRequestOutput
// | EncryptPayloadRequestOutput
| BroadcastRequestOutput
+ | ProofOfEventChallengeRequestOutput
+ | ProofOfEventChallengeRecordedMessageOutput
diff --git a/packages/beacon-types/src/types/beacon/messages/BeaconResponseInputMessage.ts b/packages/beacon-types/src/types/beacon/messages/BeaconResponseInputMessage.ts
index 0259ebf82..67dab30a9 100644
--- a/packages/beacon-types/src/types/beacon/messages/BeaconResponseInputMessage.ts
+++ b/packages/beacon-types/src/types/beacon/messages/BeaconResponseInputMessage.ts
@@ -1,4 +1,4 @@
-import { Optional } from '@airgap/beacon-types'
+import { Optional, ProofOfEventChallengeResponse } from '@airgap/beacon-types'
import {
PermissionResponse,
OperationResponse,
@@ -21,6 +21,13 @@ export type PermissionResponseInput = Optional<
PermissionResponse,
IgnoredResponseInputProperties | 'appMetadata'
>
+/**
+ * @category Wallet
+ */
+export type ProofOfEventChallengeResponseInput = Optional<
+ ProofOfEventChallengeResponse,
+ IgnoredResponseInputProperties
+>
/**
* @category Wallet
*/
@@ -61,3 +68,4 @@ export type BeaconResponseInputMessage =
| BroadcastResponseInput
| AcknowledgeResponseInput
| ErrorResponseInput
+ | ProofOfEventChallengeResponseInput
diff --git a/packages/beacon-types/src/types/beacon/messages/BeaconResponseOutputMessage.ts b/packages/beacon-types/src/types/beacon/messages/BeaconResponseOutputMessage.ts
index 8d71fa2a7..7b8fe476a 100644
--- a/packages/beacon-types/src/types/beacon/messages/BeaconResponseOutputMessage.ts
+++ b/packages/beacon-types/src/types/beacon/messages/BeaconResponseOutputMessage.ts
@@ -4,7 +4,8 @@ import {
SignPayloadResponse,
// EncryptPayloadResponse,
BroadcastResponse,
- AccountInfo
+ AccountInfo,
+ ProofOfEventChallengeResponse
} from '@airgap/beacon-types'
/**
@@ -20,6 +21,12 @@ export type PermissionResponseOutput = PermissionResponse & {
accountInfo: AccountInfo
walletKey?: string | undefined // Last selected wallet key
}
+
+/**
+ * @category DApp
+ */
+export type ProofOfEventChallengeResponseOutput = ProofOfEventChallengeResponse
+
/**
* @category DApp
*/
@@ -47,3 +54,4 @@ export type BeaconResponseOutputMessage =
| SignPayloadResponseOutput
// | EncryptPayloadResponseOutput
| BroadcastResponseOutput
+ | ProofOfEventChallengeResponseOutput
diff --git a/packages/beacon-types/src/types/beacon/messages/ChangeAccountRequest.ts b/packages/beacon-types/src/types/beacon/messages/ChangeAccountRequest.ts
index d3817c7fa..9eb31ed93 100644
--- a/packages/beacon-types/src/types/beacon/messages/ChangeAccountRequest.ts
+++ b/packages/beacon-types/src/types/beacon/messages/ChangeAccountRequest.ts
@@ -1,11 +1,20 @@
-import { BeaconBaseMessage, BeaconMessageType, Network, PermissionScope, Threshold } from "@airgap/beacon-types"
+import {
+ BeaconBaseMessage,
+ BeaconMessageType,
+ Network,
+ PermissionScope,
+ Threshold
+} from '@airgap/beacon-types'
import { Notification } from '../../Notification'
export interface ChangeAccountRequest extends BeaconBaseMessage {
- type: BeaconMessageType.ChangeAccountRequest
- publicKey: string
- network: Network
- scopes: PermissionScope[]
- threshold?: Threshold
- notification?: Notification
-}
\ No newline at end of file
+ type: BeaconMessageType.ChangeAccountRequest
+ address?: string
+ walletType: 'implicit' | 'abstracted_account'
+ verificationType?: 'proof_of_event'
+ publicKey?: string
+ network: Network
+ scopes: PermissionScope[]
+ threshold?: Threshold
+ notification?: Notification
+}
diff --git a/packages/beacon-types/src/types/beacon/messages/PermissionResponse.ts b/packages/beacon-types/src/types/beacon/messages/PermissionResponse.ts
index 2042a52d3..b2128ef82 100644
--- a/packages/beacon-types/src/types/beacon/messages/PermissionResponse.ts
+++ b/packages/beacon-types/src/types/beacon/messages/PermissionResponse.ts
@@ -12,9 +12,12 @@ import { Notification } from '../../Notification'
* @category Message
*/
export interface PermissionResponse extends BeaconBaseMessage {
+ address?: string // If wallet is an abstracted_account, address should be defined
+ walletType: 'implicit' | 'abstracted_account'
+ verificationType?: 'proof_of_event'
type: BeaconMessageType.PermissionResponse
appMetadata: AppMetadata // Some additional information about the Wallet
- publicKey: string // Public Key, because it can be used to verify signatures
+ publicKey?: string // Public Key, because it can be used to verify signatures. Undefined if wallet is an abstracted_account
network: Network // Network on which the permissions have been granted
scopes: PermissionScope[] // Permissions that have been granted for this specific address / account
threshold?: Threshold
diff --git a/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeRecordedRequest.ts b/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeRecordedRequest.ts
new file mode 100644
index 000000000..4a99f3763
--- /dev/null
+++ b/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeRecordedRequest.ts
@@ -0,0 +1,8 @@
+import { BeaconBaseMessage, BeaconMessageType } from '@airgap/beacon-types'
+
+export interface ProofOfEventChallengeRecordedRequest extends BeaconBaseMessage {
+ type: BeaconMessageType.ProofOfEventChallengeRecorded
+ dAppChallengeId: string // dApp decided challenge identifier
+ success: boolean // Indicating whether the challenge is recorded successfully
+ errorMessage: string // Optional, error message incase of failure
+}
diff --git a/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeRequest.ts b/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeRequest.ts
new file mode 100644
index 000000000..8fbf82ea1
--- /dev/null
+++ b/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeRequest.ts
@@ -0,0 +1,8 @@
+import { BeaconBaseMessage, BeaconMessageType } from '@airgap/beacon-types'
+
+export interface ProofOfEventChallengeRequest extends BeaconBaseMessage {
+ type: BeaconMessageType.ProofOfEventChallengeRequest
+ payload: string // The payload that will be emitted.
+ contractAddress: string // The contract address of the abstracted account
+ dAppChallengeId: string // dApp decided challenge identifier
+}
diff --git a/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeResponse.ts b/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeResponse.ts
new file mode 100644
index 000000000..68ea89221
--- /dev/null
+++ b/packages/beacon-types/src/types/beacon/messages/ProofOfEventChallengeResponse.ts
@@ -0,0 +1,7 @@
+import { BeaconBaseMessage, BeaconMessageType } from '@airgap/beacon-types'
+
+export interface ProofOfEventChallengeResponse extends BeaconBaseMessage {
+ type: BeaconMessageType.ProofOfEventChallengeResponse
+ dAppChallengeId: string // dApp decided challenge identifier
+ isAccepted: boolean // Indicating whether the challenge is accepted
+}
diff --git a/packages/beacon-types/src/types/storage/StorageKey.ts b/packages/beacon-types/src/types/storage/StorageKey.ts
index 0e0193e44..bc3aba932 100644
--- a/packages/beacon-types/src/types/storage/StorageKey.ts
+++ b/packages/beacon-types/src/types/storage/StorageKey.ts
@@ -14,6 +14,7 @@ export enum StorageKey {
BEACON_SDK_SECRET_SEED = 'beacon:sdk-secret-seed',
APP_METADATA_LIST = 'beacon:app-metadata-list',
PERMISSION_LIST = 'beacon:permissions',
+ ONGOING_PROOF_OF_EVENT_CHALLENGES = 'beacon:ongoing-proof-of-event-challenges',
BEACON_SDK_VERSION = 'beacon:sdk_version',
MATRIX_PRESERVED_STATE = 'beacon:sdk-matrix-preserved-state',
MATRIX_PEER_ROOM_IDS = 'beacon:matrix-peer-rooms',
diff --git a/packages/beacon-types/src/types/storage/StorageKeyReturnDefaults.ts b/packages/beacon-types/src/types/storage/StorageKeyReturnDefaults.ts
index e98842d34..43c72b64f 100644
--- a/packages/beacon-types/src/types/storage/StorageKeyReturnDefaults.ts
+++ b/packages/beacon-types/src/types/storage/StorageKeyReturnDefaults.ts
@@ -21,6 +21,7 @@ export const defaultValues: StorageKeyReturnDefaults = {
[StorageKey.BEACON_SDK_SECRET_SEED]: undefined,
[StorageKey.APP_METADATA_LIST]: [],
[StorageKey.PERMISSION_LIST]: [],
+ [StorageKey.ONGOING_PROOF_OF_EVENT_CHALLENGES]: [],
[StorageKey.BEACON_SDK_VERSION]: undefined,
[StorageKey.MATRIX_PRESERVED_STATE]: {},
[StorageKey.MATRIX_PEER_ROOM_IDS]: {},
diff --git a/packages/beacon-types/src/types/storage/StorageKeyReturnType.ts b/packages/beacon-types/src/types/storage/StorageKeyReturnType.ts
index 08450234b..2cb4f7d67 100644
--- a/packages/beacon-types/src/types/storage/StorageKeyReturnType.ts
+++ b/packages/beacon-types/src/types/storage/StorageKeyReturnType.ts
@@ -5,7 +5,8 @@ import {
P2PPairingRequest,
AppMetadata,
PermissionInfo,
- ExtendedWalletConnectPairingResponse
+ ExtendedWalletConnectPairingResponse,
+ RequestProofOfEventChallengeInput
} from '../..'
// TODO: MOVE TYPE import { MatrixState } from '../../matrix-client/MatrixClientStore'
import { ExtendedP2PPairingResponse } from '../P2PPairingResponse'
@@ -29,6 +30,10 @@ export interface StorageKeyReturnType {
[StorageKey.BEACON_SDK_SECRET_SEED]: string | undefined
[StorageKey.APP_METADATA_LIST]: AppMetadata[]
[StorageKey.PERMISSION_LIST]: PermissionInfo[]
+ [StorageKey.ONGOING_PROOF_OF_EVENT_CHALLENGES]: ({
+ contractAddress: string
+ accountIdentifier: string
+ } & RequestProofOfEventChallengeInput)[]
[StorageKey.BEACON_SDK_VERSION]: string | undefined
[StorageKey.MATRIX_PRESERVED_STATE]: { [key: string]: unknown } // TODO: TYPE Partial
[StorageKey.MATRIX_PEER_ROOM_IDS]: { [key: string]: string | undefined }
diff --git a/packages/beacon-utils/src/index.ts b/packages/beacon-utils/src/index.ts
index 5d3197b11..a79f06d0d 100644
--- a/packages/beacon-utils/src/index.ts
+++ b/packages/beacon-utils/src/index.ts
@@ -11,9 +11,11 @@ export {
openCryptobox,
recipientString,
signMessage,
+ isValidAddress,
prefixPublicKey
} from './utils/crypto'
export { generateGUID } from './utils/generate-uuid'
+export const CONTRACT_PREFIX = 'KT1'
export const secretbox_NONCEBYTES = 24 // crypto_secretbox_NONCEBYTES
export const secretbox_MACBYTES = 16 // crypto_secretbox_MACBYTES
diff --git a/packages/beacon-utils/src/utils/crypto.ts b/packages/beacon-utils/src/utils/crypto.ts
index 9c5c03dad..a6834c488 100644
--- a/packages/beacon-utils/src/utils/crypto.ts
+++ b/packages/beacon-utils/src/utils/crypto.ts
@@ -262,4 +262,20 @@ export const signMessage = async (
return signature
}
+export const isValidAddress = (address: string): boolean => {
+ const prefixes = ['tz1', 'tz2', 'tz3', 'tz4', 'KT1', 'txr1', 'sr1']
+
+ if (!prefixes.some((p) => address.toLowerCase().startsWith(p.toLowerCase()))) {
+ return false
+ }
+
+ try {
+ bs58check.decode(address)
+ } catch (error) {
+ return false
+ }
+
+ return true
+}
+
/* eslint-enable prefer-arrow/prefer-arrow-functions */
diff --git a/packages/beacon-wallet/src/client/WalletClient.ts b/packages/beacon-wallet/src/client/WalletClient.ts
index d99a2107d..eb2bac255 100644
--- a/packages/beacon-wallet/src/client/WalletClient.ts
+++ b/packages/beacon-wallet/src/client/WalletClient.ts
@@ -84,7 +84,6 @@ export class WalletClient extends Client {
public async init(): Promise {
const keyPair = await this.keyPair // We wait for keypair here so the P2P Transport creation is not delayed and causing issues
-
const p2pTransport = new WalletP2PTransport(
this.name,
keyPair,
diff --git a/packages/beacon-wallet/src/interceptors/IncomingRequestInterceptor.ts b/packages/beacon-wallet/src/interceptors/IncomingRequestInterceptor.ts
index 9b55a320e..18ee73f73 100644
--- a/packages/beacon-wallet/src/interceptors/IncomingRequestInterceptor.ts
+++ b/packages/beacon-wallet/src/interceptors/IncomingRequestInterceptor.ts
@@ -6,12 +6,14 @@ import {
OperationRequestOutput,
SignPayloadRequestOutput,
BroadcastRequestOutput,
+ ProofOfEventChallengeRequestOutput,
ConnectionContext,
BeaconRequestMessage,
BeaconMessageWrapper,
BlockchainRequestV3,
PermissionRequestV3,
- BeaconBaseMessage
+ BeaconBaseMessage,
+ ProofOfEventChallengeRecordedMessageOutput
// EncryptPayloadRequestOutput
} from '@airgap/beacon-types'
import { AppMetadataManager, Logger } from '@airgap/beacon-core'
@@ -141,7 +143,32 @@ export class IncomingRequestInterceptor {
interceptorCallback(request, connectionInfo)
}
break
-
+ case BeaconMessageType.ProofOfEventChallengeRequest:
+ {
+ const appMetadata: AppMetadata = await IncomingRequestInterceptor.getAppMetadata(
+ appMetadataManager,
+ message.senderId
+ )
+ const request: ProofOfEventChallengeRequestOutput = {
+ appMetadata,
+ ...message
+ }
+ interceptorCallback(request, connectionInfo)
+ }
+ break
+ case BeaconMessageType.ProofOfEventChallengeRecorded:
+ {
+ const appMetadata: AppMetadata = await IncomingRequestInterceptor.getAppMetadata(
+ appMetadataManager,
+ message.senderId
+ )
+ const request: ProofOfEventChallengeRecordedMessageOutput = {
+ appMetadata,
+ ...message
+ }
+ interceptorCallback(request, connectionInfo)
+ }
+ break
default:
logger.log('intercept', 'Message not handled')
assertNever(message)
diff --git a/packages/beacon-wallet/src/interceptors/OutgoingResponseInterceptor.ts b/packages/beacon-wallet/src/interceptors/OutgoingResponseInterceptor.ts
index 9528e1f8c..bcbac30d5 100644
--- a/packages/beacon-wallet/src/interceptors/OutgoingResponseInterceptor.ts
+++ b/packages/beacon-wallet/src/interceptors/OutgoingResponseInterceptor.ts
@@ -21,10 +21,11 @@ import {
BeaconMessageWrapper,
BlockchainResponseV3,
PermissionResponseV3,
- BeaconBaseMessage
+ BeaconBaseMessage,
+ ProofOfEventChallengeResponse
// EncryptPayloadResponse
} from '@airgap/beacon-types'
-import { getAddressFromPublicKey } from '@airgap/beacon-utils'
+import { getAddressFromPublicKey, CONTRACT_PREFIX, isValidAddress } from '@airgap/beacon-utils'
interface OutgoingResponseInterceptorOptions {
senderId: string
@@ -183,10 +184,29 @@ export class OutgoingResponseInterceptor {
...message
}
+ if (!response.address && !response.publicKey) {
+ throw new Error('Address or PublicKey must be defined')
+ }
+
const publicKey = response.publicKey
- const address: string = await getAddressFromPublicKey(publicKey)
+ const address: string = response.address ?? (await getAddressFromPublicKey(publicKey!))
+
+ if (isValidAddress(address)) {
+ throw new Error(`Invalid address: "${address}"`)
+ }
+
+ if (
+ message.walletType === 'abstracted_account' &&
+ address.substring(0, 3) !== CONTRACT_PREFIX
+ ) {
+ throw new Error(
+ `Invalid abstracted account address "${address}", it should be a ${CONTRACT_PREFIX} address`
+ )
+ }
+
const appMetadata = await appMetadataManager.getAppMetadata(request.senderId)
+
if (!appMetadata) {
throw new Error('AppMetadata not found')
}
@@ -249,7 +269,16 @@ export class OutgoingResponseInterceptor {
interceptorCallback(response)
}
break
-
+ case BeaconMessageType.ProofOfEventChallengeResponse:
+ {
+ const response: ProofOfEventChallengeResponse = {
+ senderId,
+ version: '2',
+ ...message
+ }
+ interceptorCallback(response)
+ }
+ break
default:
logger.log('intercept', 'Message not handled')
assertNever(message)