Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

“Add Token” via freighter-api #1815

Open
wants to merge 29 commits into
base: release/5.28.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ed916e1
First cut on data flow
CassioMG Jan 24, 2025
1aa2a0f
Fix lint warnings
CassioMG Jan 24, 2025
167c6ed
Clean up
CassioMG Jan 24, 2025
891402c
Only enable button if asset found
CassioMG Jan 24, 2025
8870fcf
If there is an asset, display it right away
CassioMG Jan 24, 2025
74a7398
Add missing network param
CassioMG Jan 24, 2025
f1de591
Guard against missing contract id
CassioMG Jan 24, 2025
fdb38e6
Make sure popup is always closed
CassioMG Jan 24, 2025
2188240
Use publicKeySelector instead
CassioMG Jan 28, 2025
04c21cd
No need for backwards compatibility on new method
CassioMG Jan 28, 2025
69f3b42
Default to tx's network passphrase
CassioMG Jan 28, 2025
1addc55
Return contractId on success
CassioMG Jan 28, 2025
8e0e204
Default to Mainnet passphrase
CassioMG Jan 29, 2025
f6b4f54
Add tests
CassioMG Jan 29, 2025
3cc5b01
Use Type definition for input and output
CassioMG Jan 29, 2025
49aaacc
Disable rule only on necessary spots
CassioMG Jan 29, 2025
38d05a1
Let optional params be optional
CassioMG Jan 30, 2025
22d06f0
First cut on UI
CassioMG Jan 31, 2025
71042df
only display info when it exists
CassioMG Jan 31, 2025
33fe94e
Display token image when available
CassioMG Jan 31, 2025
1b167fc
Display "Asset on your lists" notification
CassioMG Jan 31, 2025
edc64f6
Fetch asset name from TOML
CassioMG Jan 31, 2025
46d1785
Prevent infinity spinner
CassioMG Jan 31, 2025
d8a7b75
Fetch and display balance
CassioMG Feb 3, 2025
45b0698
Display error if id not valid
CassioMG Feb 3, 2025
b351f28
Add/update docs
CassioMG Feb 4, 2025
5578efb
Missing addToken documentation
CassioMG Feb 6, 2025
4fcde95
Missing getNetworkDetails documentation
CassioMG Feb 6, 2025
156cc27
"Simulated Balance Changes" => "Balance Info"
CassioMG Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions @shared/api/external.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ export const requestPublicKey = async (): Promise<{
return { publicKey: response?.publicKey || "", error: response?.apiError };
};

export const submitToken = async (args: {
contractId: string;
networkPassphrase?: string;
}): Promise<{
error?: FreighterApiError;
}> => {
let response;
try {
response = await sendMessageToContentScript({
contractId: args.contractId,
networkPassphrase: args.networkPassphrase,
type: EXTERNAL_SERVICE_TYPES.SUBMIT_TOKEN,
});
} catch (e) {
return {
error: FreighterApiInternalError,
};
}

return { error: response?.apiError };
};

export const submitTransaction = async (
transactionXdr: string,
opts?:
Expand Down
10 changes: 10 additions & 0 deletions @shared/api/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,16 @@ export const handleSignedHwPayload = async ({
}
};

export const addToken = async (): Promise<void> => {
try {
await sendMessageToBackground({
type: SERVICE_TYPES.ADD_TOKEN,
});
} catch (e) {
console.error(e);
}
};

export const signTransaction = async (): Promise<void> => {
try {
await sendMessageToBackground({
Expand Down
9 changes: 7 additions & 2 deletions @shared/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,19 @@ export interface MemoRequiredAccount {
}

export interface ExternalRequestBase {
network: string;
networkPassphrase: string;
accountToSign?: string;
address?: string;
networkPassphrase?: string;
type: EXTERNAL_SERVICE_TYPES;
}

export interface ExternalRequestToken extends ExternalRequestBase {
contractId: string;
}

export interface ExternalRequestTx extends ExternalRequestBase {
transactionXdr: string;
network?: string;
}

export interface ExternalRequestBlob extends ExternalRequestBase {
Expand All @@ -124,6 +128,7 @@ export interface ExternalRequestAuthEntry extends ExternalRequestBase {
}

export type ExternalRequest =
| ExternalRequestToken
| ExternalRequestTx
| ExternalRequestBlob
| ExternalRequestAuthEntry;
Expand Down
2 changes: 2 additions & 0 deletions @shared/constants/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum SERVICE_TYPES {
CONFIRM_PASSWORD = "CONFIRM_PASSWORD",
REJECT_ACCESS = "REJECT_ACCESS",
GRANT_ACCESS = "GRANT_ACCESS",
ADD_TOKEN = "ADD_TOKEN",
SIGN_TRANSACTION = "SIGN_TRANSACTION",
SIGN_BLOB = "SIGN_BLOB",
SIGN_AUTH_ENTRY = "SIGN_AUTH_ENTRY",
Expand Down Expand Up @@ -53,6 +54,7 @@ export enum SERVICE_TYPES {
export enum EXTERNAL_SERVICE_TYPES {
REQUEST_ACCESS = "REQUEST_ACCESS",
REQUEST_PUBLIC_KEY = "REQUEST_PUBLIC_KEY",
SUBMIT_TOKEN = "SUBMIT_TOKEN",
SUBMIT_TRANSACTION = "SUBMIT_TRANSACTION",
SUBMIT_BLOB = "SUBMIT_BLOB",
SUBMIT_AUTH_ENTRY = "SUBMIT_AUTH_ENTRY",
Expand Down
23 changes: 23 additions & 0 deletions @stellar/freighter-api/src/addToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { submitToken } from "@shared/api/external";
import { FreighterApiError } from "@shared/api/types";
import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging";
import { isBrowser } from ".";

export const addToken = async (args: {
contractId: string;
networkPassphrase?: string;
CassioMG marked this conversation as resolved.
Show resolved Hide resolved
}): Promise<{
error?: FreighterApiError;
}> => {
if (isBrowser) {
const req = await submitToken(args);

if (req.error) {
return { error: req.error };
}

return {};
CassioMG marked this conversation as resolved.
Show resolved Hide resolved
}

return { error: FreighterApiNodeError };
};
3 changes: 3 additions & 0 deletions @stellar/freighter-api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getAddress } from "./getAddress";
import { addToken } from "./addToken";
import { signTransaction } from "./signTransaction";
import { signMessage } from "./signMessage";
import { signAuthEntry } from "./signAuthEntry";
Expand All @@ -14,6 +15,7 @@ export const isBrowser = typeof window !== "undefined";

export {
getAddress,
addToken,
signTransaction,
signMessage,
signAuthEntry,
Expand All @@ -27,6 +29,7 @@ export {
};
export default {
getAddress,
addToken,
signTransaction,
signMessage,
signAuthEntry,
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ That should launch your project in xcode. You should run the project, with a tar

[The `signTransaction` playground](http://localhost:3000/docs/playground/signTransaction)

[The `addToken` playground](http://localhost:3000/docs/playground/addToken)

It's important to note that these last functions won't interact with the _dev server_ popup
UI on `localhost:9000` — you'll need to re-install the unpacked extension each
time you make a change.
Expand Down
11 changes: 11 additions & 0 deletions docs/docs/playground/addToken.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
id: addToken
title: addToken
---

#### `addToken({ contractId: string, networkPassphrase?: string }) -> <Promise<{ error?: string; }>>`

import { AddTokenDemo } from "./components/AddTokenDemo";

<strong>Test Freighter's `addToken` method:</strong>
<AddTokenDemo />
56 changes: 56 additions & 0 deletions docs/docs/playground/components/AddTokenDemo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { addToken } from "@stellar/freighter-api";
import React, { useState } from "react";
import { PlaygroundInput, PlaygroundTextarea } from "./basics/inputs";

export const AddTokenDemo = () => {
const [contractId, setContractId] = useState("");
const [networkPassphrase, setNetworkPassphrase] = useState("");
const [result, setResult] = useState("");

const contractIdOnChangeHandler = (
e: React.ChangeEvent<HTMLInputElement>
) => {
setContractId(e.currentTarget.value);
};

const networkPassphraseOnChangeHandler = (
e: React.ChangeEvent<HTMLInputElement>
) => {
setNetworkPassphrase(e.currentTarget.value);
};

const btnHandler = async () => {
let tokenResult;

tokenResult = await addToken({
contractId,
networkPassphrase,
});

if (tokenResult.error) {
setResult(JSON.stringify(tokenResult.error));
} else {
setResult("Token info successfully sent.");
}
};

return (
<section>
<div>
Enter Token's Contract Id:
<PlaygroundInput onChange={contractIdOnChangeHandler} />
</div>
<div>
Enter network passphrase:
<PlaygroundInput onChange={networkPassphraseOnChangeHandler} />
</div>
<div>
Result:
<PlaygroundTextarea readOnly value={result} />
</div>
<button type="button" onClick={btnHandler}>
Add Token
</button>
</section>
);
};
2 changes: 1 addition & 1 deletion docs/docs/playground/components/SignTransactionDemo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from "react";
import { signTransaction } from "@stellar/freighter-api";
import React, { useState } from "react";
import { PlaygroundInput, PlaygroundTextarea } from "./basics/inputs";

export const SignTransactionDemo = () => {
Expand Down
1 change: 1 addition & 0 deletions docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const playgroundPaths = [
"getAddress",
"getNetwork",
"getNetworkDetails",
"addToken",
"signTransaction",
"signAuthEntry",
"signMessage",
Expand Down
45 changes: 27 additions & 18 deletions extension/src/background/helpers/account.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention */
import {
ACCOUNT_NAME_LIST_ID,
ALLOWLIST_ID,
Expand Down Expand Up @@ -238,11 +239,9 @@ export const subscribeAccount = async (publicKey: string) => {
const options = {
method: "POST",
headers: {
// eslint-disable-next-line
CassioMG marked this conversation as resolved.
Show resolved Hide resolved
"Content-Type": "application/json",
},
body: JSON.stringify({
// eslint-disable-next-line
pub_key: publicKey,
network: networkDetails.network,
}),
Expand All @@ -266,26 +265,28 @@ export const subscribeAccount = async (publicKey: string) => {
return { publicKey };
};

export const subscribeTokenBalance = async (
publicKey: string,
contractId: string,
) => {
export const subscribeTokenBalance = async ({
publicKey,
contractId,
network,
}: {
publicKey: string;
contractId: string;
network: string;
}) => {
try {
const networkDetails = await getNetworkDetails();
const options = {
method: "POST",
headers: {
// eslint-disable-next-line
"Content-Type": "application/json",
},
body: JSON.stringify({
// eslint-disable-next-line
pub_key: publicKey,
// eslint-disable-next-line
contract_id: contractId,
network: networkDetails.network,
network,
}),
};

const res = await fetch(
`${INDEXER_URL}/subscription/token-balance`,
options,
Expand All @@ -301,20 +302,28 @@ export const subscribeTokenBalance = async (
}
};

export const subscribeTokenHistory = async (
publicKey: string,
contractId: string,
) => {
export const subscribeTokenHistory = async ({
publicKey,
contractId,
network,
}: {
publicKey: string;
contractId: string;
network: string;
}) => {
try {
const options = {
method: "POST",
headers: {
// eslint-disable-next-line
CassioMG marked this conversation as resolved.
Show resolved Hide resolved
"Content-Type": "application/json",
},
// eslint-disable-next-line
body: JSON.stringify({ pub_key: publicKey, contract_id: contractId }),
body: JSON.stringify({
pub_key: publicKey,
contract_id: contractId,
network,
}),
};

const res = await fetch(`${INDEXER_URL}/subscription/token`, options);

if (!res.ok) {
Expand Down
Loading
Loading