@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
448 lines (445 loc) • 15.8 kB
text/typescript
import { BigNumber } from "bignumber.js";
import {
InvalidAddress,
InvalidAddressBecauseDestinationIsAlsoSource,
NotEnoughBalance,
AmountRequired,
RecommendUndelegation,
} from "@ledgerhq/errors";
import invariant from "invariant";
import type { CosmosAccount, Transaction } from "@ledgerhq/coin-cosmos/types/index";
import { AccountRaw, CurrenciesData } from "@ledgerhq/types-live";
import { fromTransactionRaw } from "@ledgerhq/coin-cosmos/transaction";
import { CosmosTooManyRedelegations } from "@ledgerhq/coin-cosmos/errors";
import { COSMOS_MAX_REDELEGATIONS } from "@ledgerhq/coin-cosmos/logic";
const dataset: CurrenciesData<Transaction> = {
FIXME_ignoreAccountFields: ["cosmosResources", "operationsCount", "operations"],
FIXME_ignorePreloadFields: ["validators"], // the APY of validators changes over time
scanAccounts: [
{
name: "cosmos seed 1",
apdus: `
=> 550400001b06636f736d6f732c00008076000080000000800000000000000000
<= 0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a636f736d6f73316738343933346a70753376356465357971756b6b6b68786d63767377337532616a787670646c9000
=> 550400001b06636f736d6f732c00008076000080000000800000000000000000
<= 0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a636f736d6f73316738343933346a70753376356465357971756b6b6b68786d63767377337532616a787670646c9000
=> 550400001b06636f736d6f732c00008076000080010000800000000000000000
<= 02624ac83690d5ef627927104767d679aef73d3d3c9544abe4206b1d0c463c94ff636f736d6f7331303875793571396a743539677775677135797264686b7a6364396a7279736c6d706373746b359000
=> 550400001b06636f736d6f732c00008076000080020000800000000000000000
<= 038ff98278402aa3e46ccfd020561dc9724ab63d7179ca507c8154b5257c7d5200636f736d6f733163676336393661793270673664346763656a656b3279386c6136366a376535793363376b79779000
=> 550400001b06636f736d6f732c00008076000080030000800000000000000000
<= 02ecca2a8c647b50bcea2cb4667bb8b2c5f5b2b8439d51c842bc9fd20c4185a95c636f736d6f73313474673476736430713734356678687a6e333239706b78306b727174737a6378797a6c356b759000
=> 550400001b06636f736d6f732c00008076000080040000800000000000000000
<= 03f9f9ddeefd1b762648af6b3ba3e14b0054d7137cf5ed67f501f01524a773c8d0636f736d6f733138713661643239346477357766376e7565713763746c656568786839666774386463656372399000
=> 550400001b06636f736d6f732c00008076000080050000800000000000000000
<= 03b036c68d156c82e550bf5c0b015313221d618cf4022b96d23f1b559b557ed3ef636f736d6f7331756833396a7830647661387570767171716836393072687432766e36387a7439776d6d6871349000
`,
},
],
accounts: [
{
FIXME_tests: ["balance is sum of ops"],
raw: {
id: "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:",
seedIdentifier: "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a",
xpub: "cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4",
derivationMode: "",
index: 0,
freshAddress: "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl",
freshAddressPath: "44'/118'/0'/0/0",
name: "Cosmos 1",
balance: "2180673",
spendableBalance: "2180673",
blockHeight: 1615299,
currencyId: "cosmos",
operationsCount: 85,
operations: [],
pendingOperations: [],
lastSyncDate: "",
},
transactions: [
{
name: "Same as Recipient",
transaction: t => ({
...t,
amount: new BigNumber(100),
recipient: "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl",
}),
expectedStatus: {
errors: {
recipient: new InvalidAddressBecauseDestinationIsAlsoSource(),
},
warnings: {},
},
},
{
name: "Invalid Address",
transaction: t => ({
...t,
amount: new BigNumber(100),
recipient: "dsadasdasdasdas",
}),
expectedStatus: {
errors: {
recipient: new InvalidAddress(),
},
warnings: {},
},
},
{
name: "send max",
transaction: fromTransactionRaw({
amount: "0",
recipient: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5",
useAllAmount: true,
family: "cosmos",
networkInfo: null,
validators: [],
sourceValidator: null,
fees: null,
gas: null,
memo: null,
mode: "send",
}),
expectedStatus: (account, t) => {
const { cosmosResources } = account as CosmosAccount;
if (!cosmosResources) throw new Error("Should exist because it's cosmos");
const estimatedFees = t.fees || new BigNumber(0);
const spendableBalance = account.balance.minus(
cosmosResources.unbondingBalance.plus(cosmosResources.delegatedBalance),
);
const amount = BigNumber.max(0, spendableBalance.minus(estimatedFees));
const totalSpent = amount.plus(estimatedFees);
const errors: Record<string, Error> = {};
const warnings: Record<string, Error> = {};
if (amount.lte(0)) {
errors.amount = new NotEnoughBalance();
}
if (cosmosResources.delegations.length > 0) {
warnings.amount = new RecommendUndelegation();
}
return {
errors,
warnings,
totalSpent,
};
},
},
{
name: "send with memo",
transaction: fromTransactionRaw({
amount: "0",
recipient: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5",
useAllAmount: true,
family: "cosmos",
networkInfo: null,
validators: [],
sourceValidator: null,
fees: null,
gas: null,
memo: "test",
mode: "send",
}),
expectedStatus: (account, t) => {
const { cosmosResources } = account as CosmosAccount;
if (!cosmosResources) throw new Error("Should exist because it's cosmos");
invariant(t.memo === "test", "Should have a memo");
const estimatedFees = t.fees || new BigNumber(0);
const spendableBalance = account.balance.minus(
cosmosResources.unbondingBalance.plus(cosmosResources.delegatedBalance),
);
const amount = BigNumber.max(0, spendableBalance.minus(estimatedFees));
const totalSpent = amount.plus(estimatedFees);
const errors: Record<string, Error> = {};
const warnings: Record<string, Error> = {};
if (amount.lte(0)) {
errors.amount = new NotEnoughBalance();
}
if (cosmosResources.delegations.length > 0) {
warnings.amount = new RecommendUndelegation();
}
return {
errors,
warnings,
totalSpent,
};
},
},
{
name: "Not Enough balance",
transaction: t => ({
...t,
amount: new BigNumber("99999999999999999"),
recipient: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5",
}),
expectedStatus: {
errors: {
amount: new NotEnoughBalance(),
},
warnings: {},
},
},
{
name: "Redelegation - success",
transaction: t => ({
...t,
amount: new BigNumber(100),
validators: [
{
address: "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7",
amount: new BigNumber(100),
},
],
sourceValidator: "cosmosvaloper1sd4tl9aljmmezzudugs7zlaya7pg2895ws8tfs",
mode: "redelegate",
}),
expectedStatus: (a, t) => {
invariant(t.memo === "Ledger Live", "Should have a memo");
const { cosmosResources } = a as CosmosAccount;
const errors: Record<string, Error> = {};
if (
cosmosResources &&
cosmosResources.redelegations.length >= COSMOS_MAX_REDELEGATIONS
) {
errors.redelegation = new CosmosTooManyRedelegations();
}
return {
errors,
warnings: {},
};
},
},
{
name: "redelegation - AmountRequired",
transaction: t => ({
...t,
mode: "redelegate",
validators: [
{
address: "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7",
amount: new BigNumber(0),
},
],
sourceValidator: "cosmosvaloper1sd4tl9aljmmezzudugs7zlaya7pg2895ws8tfs",
}),
expectedStatus: a => {
const { cosmosResources } = a as CosmosAccount;
const errors: Record<string, Error> = {};
if (
cosmosResources &&
cosmosResources.redelegations.length >= COSMOS_MAX_REDELEGATIONS
) {
errors.redelegation = new CosmosTooManyRedelegations();
} else {
errors.amount = new AmountRequired();
}
return {
errors,
warnings: {},
};
},
},
{
name: "redelegation - Source is Destination",
transaction: t => ({
...t,
mode: "redelegate",
validators: [
{
address: "cosmosvaloper1sd4tl9aljmmezzudugs7zlaya7pg2895ws8tfs",
amount: new BigNumber(100),
},
],
sourceValidator: "cosmosvaloper1sd4tl9aljmmezzudugs7zlaya7pg2895ws8tfs",
}),
expectedStatus: a => {
const { cosmosResources } = a as CosmosAccount;
const errors: Record<string, Error> = {};
if (
cosmosResources &&
cosmosResources.redelegations.length >= COSMOS_MAX_REDELEGATIONS
) {
errors.redelegation = new CosmosTooManyRedelegations();
} else {
errors.redelegation = new InvalidAddressBecauseDestinationIsAlsoSource();
}
return {
errors,
warnings: {},
};
},
},
{
name: "Unbonding - success",
transaction: t => ({
...t,
mode: "undelegate",
validators: [
{
address: "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7",
amount: new BigNumber(100),
},
],
}),
expectedStatus: (a, t) => {
invariant(t.memo === "Ledger Live", "Should have a memo");
return {
errors: {},
warnings: {},
};
},
},
{
name: "Unbonding - AmountRequired",
transaction: t => ({
...t,
mode: "undelegate",
validators: [
{
address: "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7",
amount: new BigNumber(0),
},
],
}),
expectedStatus: {
errors: {
amount: new AmountRequired(),
},
warnings: {},
},
},
{
name: "Delegate - success",
transaction: t => ({
...t,
mode: "delegate",
validators: [
{
address: "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7",
amount: new BigNumber(100),
},
],
}),
expectedStatus: (a, t) => {
invariant(t.memo === "Ledger Live", "Should have a memo");
return {
errors: { amount: new AmountRequired() },
warnings: {},
};
},
},
{
name: "Delegate - not a valid",
transaction: t => ({
...t,
mode: "delegate",
validators: [
{
address: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5",
amount: new BigNumber(100),
},
],
}),
expectedStatus: {
errors: {
recipient: new InvalidAddress(),
},
warnings: {},
},
},
{
name: "ClaimReward - success",
transaction: t => ({
...t,
validators: [
{
address: "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7",
amount: new BigNumber(0),
},
],
mode: "claimReward",
}),
expectedStatus: (a, t) => {
invariant(t.memo === "Ledger Live", "Should have a memo");
return {
errors: {},
warnings: {},
};
},
},
{
name: "ClaimReward - not a cosmosvaloper",
transaction: t => ({
...t,
validators: [
{
address: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5",
amount: new BigNumber(0),
},
],
mode: "claimReward",
}),
expectedStatus: {
errors: {
recipient: new InvalidAddress(),
},
warnings: {},
},
},
{
name: "claimRewardCompound - success",
transaction: t => ({
...t,
validators: [
{
address: "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7",
amount: new BigNumber(100),
},
],
mode: "claimRewardCompound",
}),
expectedStatus: (a, t) => {
invariant(t.memo === "Ledger Live", "Should have a memo");
return {
errors: {},
warnings: {},
};
},
},
],
},
{
FIXME_tests: ["balance is sum of ops"],
raw: {
id: "libcore:1:cosmos:cosmospub1addwnpepqd3nvwwx39pqqvw88sg409675u6wyt4wtzqyt2t0e9y4629t50cdzftxnvz:",
seedIdentifier:
"cosmospub1addwnpepqd3nvwwx39pqqvw88sg409675u6wyt4wtzqyt2t0e9y4629t50cdzftxnvz",
name: "Cosmos Static Account",
derivationMode: "",
index: 0,
freshAddress: "cosmos10l6h3qw05u7qduqgafj8wlrx3fjhr8523sm0c3",
freshAddressPath: "44'/118'/0'/0/0",
blockHeight: 2220318,
creationDate: "2020-04-01T13:51:08.000Z",
operationsCount: 0,
operations: [],
pendingOperations: [],
currencyId: "cosmos",
lastSyncDate: "2020-06-11T07:44:10.266Z",
balance: "1000000",
spendableBalance: "0",
xpub: "cosmospub1addwnpepqd3nvwwx39pqqvw88sg409675u6wyt4wtzqyt2t0e9y4629t50cdzftxnvz",
cosmosResources: {
delegations: [],
redelegations: [],
unbondings: [],
delegatedBalance: "0",
pendingRewardsBalance: "0",
unbondingBalance: "1000000",
withdrawAddress: "",
},
} as AccountRaw,
},
],
};
export default dataset;