Skip to content

Commit

Permalink
Feat/ton jetton integration (#7672)
Browse files Browse the repository at this point in the history
* feat(ton): Jetton integration

* feat(ton): Adjust integration test

* feat(ton): Add changeset

* feat(ton): add bot tests for jettons and clean code

* feat(ton): adjust comment input width and error field name

* feat(ton): fix a test

* feat(ton): move a function and adjust a value

* feat(ton): adjust test

* fix: integration test

* fix: ton integration test

---------

Co-authored-by: Maria Ayelen Murano <[email protected]>
  • Loading branch information
hzheng-ledger and ayelenmurano authored Aug 29, 2024
1 parent 094468c commit fb9466a
Show file tree
Hide file tree
Showing 67 changed files with 1,785 additions and 121 deletions.
23 changes: 23 additions & 0 deletions .changeset/fair-berries-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@actions/live-common-affected": patch
"@ledgerhq/types-live": patch
"@ledgerhq/errors": patch
"@ledgerhq/live-countervalues-react": patch
"@ledgerhq/crypto-icons-ui": patch
"@actions/turbo-affected": patch
"@ledgerhq/coin-icon": patch
"@ledgerhq/webpack.js-example": patch
"@ledgerhq/coin-ton": patch
"@actions/build-checks": patch
"ledger-live-desktop": patch
"@ledgerhq/next.js-example": patch
"live-mobile": patch
"@ledgerhq/live-common": patch
"@ledgerhq/live-countervalues": patch
"@ledgerhq/speculos-transport": patch
"@ledgerhq/native-ui": patch
"@ledgerhq/icons-ui": patch
"@ledgerhq/react-ui": patch
---

Add support for jettons
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const CommentField = ({
// on the ledger-live mobile
return (
<Input
warning={status.warnings.comment}
warning={status.warnings.transaction}
error={status.errors.transaction}
value={transaction.comment.text}
placeholder={t("families.ton.commentPlaceholder")}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,16 @@ const Root = (props: {
</LabelInfoTooltip>
</Label>
</Box>
<Box mb={15} horizontal grow alignItems="center" justifyContent="space-between">
<Box grow={1}>
<Box
mb={15}
horizontal
grow
alignItems="center"
justifyContent="space-between"
maxWidth={"100%"}
id="testinggg"
>
<Box grow={1} maxWidth={"100%"}>
<CommentField {...props} />
</Box>
</Box>
Expand Down
9 changes: 9 additions & 0 deletions apps/ledger-live-desktop/static/i18n/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6213,6 +6213,15 @@
},
"BitcoinInfrastructureError": {
"title": "We are experiencing an issue with our Bitcoin infrastructure. Please try again later."
},
"TonExcessFee": {
"title": "It constitutes a token transfer. You will pay 0.1 TON in fees, and any extra will be returned to you."
},
"TonNotEnoughBalanceInParentAccount": {
"title": "Insufficient funds in main account (TON) to calculate fees. The minimum required balance is 0.1 TON."
},
"TonMinimumRequired": {
"title": "Insufficient funds. The minimum required balance is 0.02 TON."
}
},
"cryptoOrg": {
Expand Down
9 changes: 9 additions & 0 deletions apps/ledger-live-mobile/src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,15 @@
"TrustchainNotFound": {
"title": "Something went wrong while fetching your data",
"description": "Please try again or contact Ledger Support."
},
"TonExcessFee": {
"title": "It constitutes a token transfer. You will pay 0.1 TON in fees, and any extra will be returned to you."
},
"TonNotEnoughBalanceInParentAccount": {
"title": "Insufficient funds in main account (TON) to calculate fees. The minimum required balance is 0.1 TON."
},
"TonMinimumRequired": {
"title": "Insufficient funds. The minimum required balance is 0.02 TON."
}
},
"crash": {
Expand Down
2 changes: 2 additions & 0 deletions libs/coin-modules/coin-ton/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@
"@ton/crypto": "^3.3.0",
"bignumber.js": "^9.1.2",
"expect": "^27.4.6",
"imurmurhash": "^0.1.4",
"invariant": "^2.2.2",
"lodash": "^4.17.21",
"msw": "^2.0.11",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@types/imurmurhash": "^0.1.4",
"@types/invariant": "^2.2.2",
"@types/jest": "^29.5.10",
"@types/lodash": "^4.14.191",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { HttpResponse, http } from "msw";
import { setupServer } from "msw/node";
import {
jettonTransferResponse,
jettonWallets,
lastBlockNumber,
tonAccount,
tonEstimateFee,
Expand Down Expand Up @@ -29,6 +31,11 @@ const handlers = [
http.get(`${API_TON_ENDPOINT}/wallet`, () => {
return HttpResponse.json(tonWallet);
}),
// Handle GET request for jetton transfers endpoint
http.get(`${API_TON_ENDPOINT}/jetton/transfers`, () => HttpResponse.json(jettonTransferResponse)),
// Handle GET request for jetton wallets endpoint
http.get(`${API_TON_ENDPOINT}/jetton/wallets`, () => HttpResponse.json(jettonWallets)),

// Handle POST request for estimate fee endpoint
http.post(`${API_TON_ENDPOINT}/estimateFee`, () => HttpResponse.json(tonEstimateFee)),
];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
import { Account } from "@ledgerhq/types-live";
import { Account, TokenAccount } from "@ledgerhq/types-live";
import BigNumber from "bignumber.js";
import {
TonAccountInfo,
TonResponseEstimateFee,
TonResponseJettonTransfer,
TonResponseJettonWallets,
TonResponseWalletInfo,
TonTransactionsList,
} from "../../bridge/bridgeHelpers/api.types";
Expand All @@ -13,6 +15,15 @@ export const mockAddress = "UQDzd8aeBOU-jqYw_ZSuZjceI5p-F4b7HMprAsUJAtRPbMol";
export const mockAccountId =
"js:2:ton:b19891a06654f21c64147550b3321bef63acd25b5dd61b688b022c42fac4831d:ton";

export const tokenAccount = {
id: "subAccountId",
type: "TokenAccount",
spendableBalance: new BigNumber("5000000"),
token: {
contractAddress: "0:A2CC9B938389950125001F6B8AF280CACA23BE045714AD69387DD546588D667E",
},
} as TokenAccount;

export const account = {
id: mockAccountId,
freshAddress: mockAddress,
Expand All @@ -23,6 +34,7 @@ export const account = {
spendableBalance: new BigNumber("1000000000"),
balance: new BigNumber("1000000000"),
seedIdentifier: "seedIdentifier",
subAccounts: [tokenAccount],
} as Account;

export const transaction = {
Expand All @@ -35,6 +47,11 @@ export const transaction = {
family: "ton",
} as unknown as Transaction;

export const jettonTransaction = {
...transaction,
subAccountId: "subAccountId",
} as Transaction;

export const fees = {
in_fwd_fee: 10000,
storage_fee: 10000,
Expand Down Expand Up @@ -73,11 +90,45 @@ export const tonWallet: TonResponseWalletInfo = {
status: "active",
};

export const jettonWallets: TonResponseJettonWallets = {
jetton_wallets: [
{
address: "0:495AB6C978E3C0AE7FCF863A2D4504E37CE8D2D04A5E59048301BA29EC372F79",
balance: "1200000000000",
owner: "0:D02D314791CB10EF3F964CC7421E4F46348C262444946F7A64C2374700E3ED19",
jetton: "0:3C52A0A732A83F022E517E5C2715E0EE458A4B9772580E903FF491526C3E9137",
last_transaction_lt: "30345242000008",
code_hash: "3axDia4eCUnTVixqU0/BUA4i8id5BtVw1pt/yayZd6k=",
data_hash: "P8j0kENM5s4zE2w5IpD8NrrSneGQ7d0mzs5yTBNPlqo=",
},
],
};

export const tonEstimateFee: TonResponseEstimateFee = {
source_fees: fees,
destination_fees: [],
};

export const jettonTransferResponse: TonResponseJettonTransfer = {
jetton_transfers: [
{
query_id: "1",
source: "UQDnqcVSV4S9m2Y9gLAQrDerQktKSx2I1uhs6r5o_H8VT4x7",
destination: mockAddress,
amount: "",
source_wallet: "",
jetton_master: "0:729C13B6DF2C07CBF0A06AB63D34AF454F3D320EC1BCD8FB5C6D24D0806A17C2",
transaction_hash: "",
transaction_lt: "",
transaction_now: 0,
response_destination: "",
custom_payload: null,
forward_ton_amount: "",
forward_payload: null,
},
],
};

export const tonTransactionResponse: TonTransactionsList = {
transactions: [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { findSubAccountById } from "@ledgerhq/coin-framework/account/index";
import { InvalidAddress, NotEnoughBalance } from "@ledgerhq/errors";
import { CurrenciesData, DatasetTest } from "@ledgerhq/types-live";
import BigNumber from "bignumber.js";
Expand All @@ -9,8 +10,11 @@ const PUBKEY = "86196cb40cd25e9e696bc808e3f2c074ce0b39f2a2a9d482a68eafef86e4a060
const ADDRESS = "UQCOvQLYvTcbi5tL9MaDNzuVl3-J3vATimNm9yO5XPafLfV4";
const ADDRESS_2 = "UQAui6M4jOYOezUGfmeONA22Ars9yjd34YIGdAR1Pcpp4sgR";
const PATH = "44'/607'/0'/0'/0'/0'";
const SUBACCOUNT =
"js:2:ton:86196cb40cd25e9e696bc808e3f2c074ce0b39f2a2a9d482a68eafef86e4a060:ton+ton%2Fjetton%2Feqbynbo23ywhy~!underscore!~cgary9nk9ftz0ydsg82ptcbstqggoxwiua";

const ton: CurrenciesData<Transaction> = {
IgnorePrepareTransactionFields: ["fees"],
scanAccounts: [
{
name: "ton seed 1",
Expand Down Expand Up @@ -85,13 +89,13 @@ const ton: CurrenciesData<Transaction> = {
transaction: fromTransactionRaw({
family: "ton",
recipient: ADDRESS_2,
fees: "10000000",
amount: (1 * 1e9).toString(),
fees: "1",
amount: (1 * 1e2).toString(),
comment: { isEncrypted: false, text: "😀" },
}),
expectedStatus: {
errors: {
comment: new TonCommentInvalid(),
transaction: new TonCommentInvalid(),
},
warnings: {},
},
Expand All @@ -111,6 +115,77 @@ const ton: CurrenciesData<Transaction> = {
warnings: {},
},
},
// sub account tests
{
name: "Subaccount Not a valid address",
transaction: fromTransactionRaw({
family: "ton",
recipient: "novalidaddress",
fees: "10000000",
amount: "1000",
comment: { isEncrypted: false, text: "" },
subAccountId: SUBACCOUNT,
}),
expectedStatus: {
errors: {
recipient: new InvalidAddress(),
},
warnings: {},
},
},
{
name: "Subaccount Not enough balance",
transaction: fromTransactionRaw({
family: "ton",
recipient: ADDRESS_2,
fees: "10000000",
amount: (300 * 1e9).toString(),
comment: { isEncrypted: false, text: "" },
subAccountId: SUBACCOUNT,
}),
expectedStatus: {
errors: {
amount: new NotEnoughBalance(),
},
warnings: {},
},
},
{
name: "Subaccount New account and sufficient amount",
transaction: fromTransactionRaw({
family: "ton",
recipient: ADDRESS_2,
fees: "10000000",
amount: "10000000",
comment: { isEncrypted: false, text: "Valid" },
subAccountId: SUBACCOUNT,
}),
expectedStatus: {
amount: new BigNumber("10000000"),
errors: {},
warnings: {},
},
},
{
name: "Subaccount Send max",
transaction: fromTransactionRaw({
family: "ton",
recipient: ADDRESS_2,
fees: "10000000",
amount: "10000000",
comment: { isEncrypted: false, text: "Valid" },
useAllAmount: true,
subAccountId: SUBACCOUNT,
}),
expectedStatus: (account, tx) => {
const subAccount = findSubAccountById(account, tx.subAccountId ?? "");
return {
amount: subAccount?.spendableBalance,
errors: {},
warnings: {},
};
},
},
],
},
],
Expand Down
14 changes: 14 additions & 0 deletions libs/coin-modules/coin-ton/src/__tests__/unit/api.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import {
estimateFee,
fetchAccountInfo,
fetchJettonTransactions,
fetchJettonWallets,
fetchLastBlockNumber,
fetchTransactions,
} from "../../bridge/bridgeHelpers/api";
import { setCoinConfig } from "../../config";
import mockServer, { API_TON_ENDPOINT } from "../fixtures/api.fixtures";
import {
jettonTransferResponse,
jettonWallets,
lastBlockNumber,
mockAddress,
tonAccount,
Expand Down Expand Up @@ -53,6 +57,16 @@ describe("getAccount", () => {
});
});

it("should return the jetton transactions", async () => {
const result = await fetchJettonTransactions(mockAddress);
expect(result).toEqual(jettonTransferResponse.jetton_transfers);
});

it("should return the jetton wallets", async () => {
const result = await fetchJettonWallets();
expect(result).toEqual(jettonWallets.jetton_wallets);
});

it("should return the estimated fees", async () => {
const result = await estimateFee(mockAddress, "");
expect(result).toEqual(tonEstimateFee.source_fees);
Expand Down
Loading

0 comments on commit fb9466a

Please sign in to comment.