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

ton jettons integration #7513

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d5c1a1a
feat(ton): Jetton integration
ayelenmurano Aug 5, 2024
f50bcd1
feat(ton): Adjust integration test
ayelenmurano Aug 5, 2024
19c0b9d
feat(ton): Add changeset
ayelenmurano Aug 5, 2024
946244b
feat(ton): add bot tests for jettons and clean code
ayelenmurano Aug 15, 2024
ced0453
Merge pull request #7617 from Zondax/feat/jetton-integration
hzheng-ledger Aug 19, 2024
9e8a1f4
feat(ton): Jetton integration
ayelenmurano Aug 5, 2024
a3b8f71
feat(ton): Adjust integration test
ayelenmurano Aug 5, 2024
92e361e
feat(ton): Add changeset
ayelenmurano Aug 5, 2024
d444c05
feat(ton): add bot tests for jettons and clean code
ayelenmurano Aug 15, 2024
db7d8f4
feat(ton): adjust comment input width and error field name
ayelenmurano Aug 19, 2024
f063790
feat(ton): fix a test
ayelenmurano Aug 19, 2024
222a538
feat(ton): move a function and adjust a value
ayelenmurano Aug 20, 2024
cd3e693
Merge branch 'develop' into feat/jetton-integration
hzheng-ledger Aug 26, 2024
cb8f038
feat(ton): adjust test
ayelenmurano Aug 26, 2024
4c99086
Validate transfer notif not to be fake and add subop in jetton transfer
ayelenmurano Aug 23, 2024
fcd1655
feat(ton): adjust bridge integration test
ayelenmurano Aug 28, 2024
46f4959
Merge branch 'subop-jetton-transfer' into feat/jetton-integration
ayelenmurano Aug 28, 2024
e244baa
Revert "Merge branch 'subop-jetton-transfer' into feat/jetton-integra…
ayelenmurano Sep 4, 2024
a7099d5
feat(ton): import jettons from crypto-assets-importer
ayelenmurano Sep 4, 2024
6b3fd9d
feat(ton): adjust tests
ayelenmurano Sep 4, 2024
66760bf
feat(ton): change the used jetton in tests
ayelenmurano Sep 5, 2024
6553d0f
Merge branch 'feat/jetton-integration' into feat/jetton-integration
ayelenmurano Sep 6, 2024
424d7b0
feat(ton): solve rebase conflicts
ayelenmurano Sep 6, 2024
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
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 @@ -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 @@ -6195,6 +6195,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 @@ -948,6 +948,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,6 +10,8 @@ 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> = {
scanAccounts: [
Expand Down Expand Up @@ -85,8 +88,8 @@ 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: {
Expand All @@ -111,6 +114,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
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import BigNumber from "bignumber.js";
import { TOKEN_TRANSFER_MAX_FEE } from "../../constants";
import getDeviceTransactionConfig from "../../deviceTransactionConfig";
import { account, transaction as baseTransaction } from "../fixtures/common.fixtures";
import {
account,
transaction as baseTransaction,
jettonTransaction,
} from "../fixtures/common.fixtures";

const status = {
errors: {},
Expand Down Expand Up @@ -66,4 +71,34 @@ describe("deviceTransactionConfig", () => {
]);
});
});

describe("Jetton transaction", () => {
it("should return the fields for a jetton transaction", async () => {
if (account.subAccounts?.[0]) {
const res = await getDeviceTransactionConfig({
account: account.subAccounts[0],
parentAccount: account,
transaction: jettonTransaction,
status,
});
expect(res).toEqual([
{
type: "address",
label: "To",
address: jettonTransaction.recipient,
},
{
type: "text",
label: "Jetton units",
value: jettonTransaction.amount.toString(),
},
{
type: "text",
label: "Amount",
value: TOKEN_TRANSFER_MAX_FEE,
},
]);
}
});
});
});
Loading
Loading