-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #169 from xmtp/nm/add-frames-tests
Ability to POST to a frame
- Loading branch information
Showing
8 changed files
with
205 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@xmtp/frames-client": patch | ||
--- | ||
|
||
Adds ability to post frame to a destination and see an updated response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,17 @@ | ||
# frames-client | ||
|
||
## Usage | ||
|
||
```ts | ||
const xmtpClient = await Client.create(wallet); | ||
const framesClient = new FramesClient(xmtpClient); | ||
|
||
const frameUrl = "https://www.myframe.xyz"; | ||
|
||
// Read data from a frame | ||
const frameMetadata = await readMetadata(frameUrl); | ||
|
||
// Handle a click to button 2 from a conversation with topic "/xmtp/0/123" on messageId "45678" | ||
const payload = await signFrameAction(frameUrl, 2, "/xmtp/0/123", "45678"); | ||
const updatedFrameMetadata = await postToFrame(frameUrl, payload); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Wallet } from "ethers"; | ||
import { it, expect, describe } from "vitest"; | ||
import { PrivateKeyBundleV1, SignedPublicKeyBundle } from "@xmtp/xmtp-js"; | ||
import { v1ToV2Bundle } from "./converters"; | ||
|
||
describe("converters", () => { | ||
it("can convert a valid public key bundle", async () => { | ||
const v1Bundle = await PrivateKeyBundleV1.generate(Wallet.createRandom()); | ||
const publicKeyBundle = v1Bundle.getPublicKeyBundle(); | ||
|
||
const v2Bundle = v1ToV2Bundle(publicKeyBundle); | ||
const v2BundleInstance = new SignedPublicKeyBundle(v2Bundle); | ||
const downgradedBundle = v2BundleInstance.toLegacyBundle(); | ||
|
||
expect(downgradedBundle.equals(publicKeyBundle)).toBe(true); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,9 @@ | ||
// eslint-disable-next-line | ||
const webCrypto: Crypto = | ||
// eslint-disable-next-line | ||
typeof crypto === "undefined" ? require("crypto").webcrypto : crypto; | ||
|
||
export async function sha256(data: Uint8Array): Promise<Uint8Array> { | ||
const digest = await crypto.subtle.digest("SHA-256", data); | ||
const digest = await webCrypto.subtle.digest("SHA-256", data); | ||
return new Uint8Array(digest); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { Client, Signature, SignedPublicKey } from "@xmtp/xmtp-js"; | ||
import { Wallet } from "ethers"; | ||
import { frames, fetcher } from "@xmtp/proto"; | ||
import { it, expect, describe, beforeEach } from "vitest"; | ||
import { FramesClient } from "."; | ||
import { sha256 } from "./crypto"; | ||
|
||
const { b64Decode } = fetcher; | ||
|
||
describe("signFrameAction", () => { | ||
let client: Client; | ||
let framesClient: FramesClient; | ||
beforeEach(async () => { | ||
client = await Client.create(Wallet.createRandom()); | ||
framesClient = new FramesClient(client); | ||
}); | ||
it("should sign a frame action with a valid signature", async () => { | ||
const frameUrl = "https://example.com"; | ||
const buttonIndex = 1; | ||
const conversationIdentifier = "testConversationIdentifier"; | ||
const messageId = "testMessageId"; | ||
|
||
const signedPayload = await framesClient.signFrameAction( | ||
frameUrl, | ||
buttonIndex, | ||
conversationIdentifier, | ||
messageId, | ||
); | ||
|
||
expect(signedPayload.untrustedData.walletAddress).toEqual(client.address); | ||
expect(signedPayload.untrustedData.url).toEqual(frameUrl); | ||
expect(signedPayload.untrustedData.buttonIndex).toEqual(buttonIndex); | ||
expect(signedPayload.untrustedData.conversationIdentifier).toEqual( | ||
conversationIdentifier, | ||
); | ||
expect(signedPayload.untrustedData.timestamp).toBeGreaterThan(0); | ||
|
||
const signedPayloadProto = frames.FrameAction.decode( | ||
b64Decode(signedPayload.trustedData.messageBytes), | ||
); | ||
expect(signedPayloadProto.actionBody).toBeDefined(); | ||
expect(signedPayloadProto.signature).toBeDefined(); | ||
expect(signedPayloadProto.signedPublicKeyBundle).toBeDefined(); | ||
|
||
const signedPayloadBody = frames.FrameActionBody.decode( | ||
signedPayloadProto.actionBody, | ||
); | ||
expect(signedPayloadBody.messageId).toEqual(messageId); | ||
expect(new TextDecoder().decode(signedPayloadBody.buttonIndex)).toEqual( | ||
buttonIndex.toString(), | ||
); | ||
expect(new TextDecoder().decode(signedPayloadBody.frameUrl)).toEqual( | ||
frameUrl, | ||
); | ||
expect(signedPayloadBody.conversationIdentifier).toEqual( | ||
conversationIdentifier, | ||
); | ||
expect(new TextDecoder().decode(signedPayloadBody.frameUrl)).toEqual( | ||
frameUrl, | ||
); | ||
|
||
if ( | ||
!signedPayloadProto.signature || | ||
!signedPayloadProto?.signedPublicKeyBundle?.identityKey | ||
) { | ||
throw new Error("Missing signature"); | ||
} | ||
|
||
const signatureInstance = new Signature(signedPayloadProto.signature); | ||
const digest = await sha256(signedPayloadProto.actionBody); | ||
// Ensure the signature is valid | ||
expect( | ||
signatureInstance | ||
.getPublicKey(digest) | ||
?.equals( | ||
new SignedPublicKey( | ||
signedPayloadProto.signedPublicKeyBundle.identityKey, | ||
).toLegacyKey(), | ||
), | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters