@ledgerhq/coin-algorand 0.2.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +57 -0
- package/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +18 -0
- package/jest.config.js +6 -0
- package/package.json +92 -0
- package/src/account.ts +76 -0
- package/src/api/algodv2.ts +71 -0
- package/src/api/algodv2.types.ts +27 -0
- package/src/api/index.ts +41 -0
- package/src/api/indexer.ts +102 -0
- package/src/api/indexer.types.ts +41 -0
- package/src/bridge/js.ts +92 -0
- package/src/bridge.integration.test.ts +312 -0
- package/src/buildTransaction.ts +98 -0
- package/src/cli-transaction.ts +124 -0
- package/src/deviceTransactionConfig.ts +146 -0
- package/src/errors.ts +5 -0
- package/src/hw-getAddress.ts +14 -0
- package/src/initAccount.ts +10 -0
- package/src/js-broadcast.ts +21 -0
- package/src/js-createTransaction.ts +21 -0
- package/src/js-estimateMaxSpendable.ts +58 -0
- package/src/js-getFeesForTransaction.ts +28 -0
- package/src/js-getTransactionStatus.ts +189 -0
- package/src/js-prepareTransaction.ts +40 -0
- package/src/js-signOperation.ts +164 -0
- package/src/js-synchronization.ts +429 -0
- package/src/logic.ts +46 -0
- package/src/mock.ts +131 -0
- package/src/serialization.ts +48 -0
- package/src/specs.ts +292 -0
- package/src/speculos-deviceActions.ts +111 -0
- package/src/tokens.ts +5 -0
- package/src/transaction.ts +87 -0
- package/src/types.ts +65 -0
- package/tsconfig.json +19 -0
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
parser: "@typescript-eslint/parser",
|
|
3
|
+
env: {
|
|
4
|
+
browser: true,
|
|
5
|
+
es6: true,
|
|
6
|
+
node: true,
|
|
7
|
+
jest: true,
|
|
8
|
+
},
|
|
9
|
+
extends: [
|
|
10
|
+
"eslint:recommended",
|
|
11
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
12
|
+
"plugin:@typescript-eslint/recommended",
|
|
13
|
+
"prettier",
|
|
14
|
+
],
|
|
15
|
+
globals: {
|
|
16
|
+
Atomics: "readonly",
|
|
17
|
+
SharedArrayBuffer: "readonly",
|
|
18
|
+
},
|
|
19
|
+
plugins: ["@typescript-eslint", "prettier"],
|
|
20
|
+
rules: {
|
|
21
|
+
"no-console": ["error", { allow: ["warn", "error"] }],
|
|
22
|
+
"linebreak-style": ["error", "unix"],
|
|
23
|
+
semi: ["error", "always"],
|
|
24
|
+
"no-unused-vars": "off",
|
|
25
|
+
"import/prefer-default-export": 0,
|
|
26
|
+
"no-plusplus": 0,
|
|
27
|
+
"no-underscore-dangle": 0,
|
|
28
|
+
"prefer-template": 0,
|
|
29
|
+
"no-await-in-loop": 0,
|
|
30
|
+
"no-restricted-syntax": 0,
|
|
31
|
+
"consistent-return": 0,
|
|
32
|
+
"no-lonely-if": 0,
|
|
33
|
+
"no-use-before-define": 0,
|
|
34
|
+
"no-nested-ternary": 0,
|
|
35
|
+
"import/no-cycle": 0,
|
|
36
|
+
"no-multi-assign": 0,
|
|
37
|
+
"guard-for-in": 0,
|
|
38
|
+
"no-continue": 0,
|
|
39
|
+
"lines-between-class-members": 0,
|
|
40
|
+
"prefer-destructuring": 0,
|
|
41
|
+
"prettier/prettier": "error",
|
|
42
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
43
|
+
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
|
44
|
+
"@typescript-eslint/no-empty-function": "off",
|
|
45
|
+
"@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }],
|
|
46
|
+
"@typescript-eslint/no-explicit-any": 0,
|
|
47
|
+
"@typescript-eslint/ban-types": [
|
|
48
|
+
"error",
|
|
49
|
+
{
|
|
50
|
+
extendDefaults: true,
|
|
51
|
+
types: {
|
|
52
|
+
"{}": false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
};
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @ledgerhq/coin-algorand
|
|
2
|
+
|
|
3
|
+
## 0.2.0-next.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#3306](https://github.com/LedgerHQ/ledger-live/pull/3306) [`77f990e207`](https://github.com/LedgerHQ/ledger-live/commit/77f990e2075c7c9a4be69b364e3754b449c7a546) Thanks [@chabroA](https://github.com/chabroA)! - Move algorand coin logic in own package
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`77f990e207`](https://github.com/LedgerHQ/ledger-live/commit/77f990e2075c7c9a4be69b364e3754b449c7a546), [`817a8dd811`](https://github.com/LedgerHQ/ledger-live/commit/817a8dd8112ff7c4640852ab4e47ea0436df2ec1), [`77f990e207`](https://github.com/LedgerHQ/ledger-live/commit/77f990e2075c7c9a4be69b364e3754b449c7a546), [`22491091f7`](https://github.com/LedgerHQ/ledger-live/commit/22491091f7b4e06ee6a0cdf964498aa5b08d6eb0), [`745fbf2a54`](https://github.com/LedgerHQ/ledger-live/commit/745fbf2a54cdc34aea938d7fbe4c8b198dc36b54), [`7cf49e1919`](https://github.com/LedgerHQ/ledger-live/commit/7cf49e1919466836e9025693ed07b18ebf99041a)]:
|
|
12
|
+
- @ledgerhq/errors@6.12.6-next.0
|
|
13
|
+
- @ledgerhq/cryptoassets@9.6.0-next.0
|
|
14
|
+
- @ledgerhq/coin-framework@0.3.4-next.0
|
|
15
|
+
- @ledgerhq/live-env@0.3.0-next.0
|
|
16
|
+
- @ledgerhq/types-live@6.34.1-next.0
|
|
17
|
+
- @ledgerhq/devices@8.0.3-next.0
|
|
18
|
+
- @ledgerhq/hw-app-algorand@6.27.15-next.0
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ledgerhq/coin-algorand",
|
|
3
|
+
"version": "0.2.0-next.0",
|
|
4
|
+
"description": "Ledger Algorand Coin integration",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"Ledger",
|
|
7
|
+
"LedgerWallet",
|
|
8
|
+
"algo",
|
|
9
|
+
"Algorand",
|
|
10
|
+
"Hardware Wallet"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/LedgerHQ/ledger-live.git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/LedgerHQ/ledger-live/issues"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/LedgerHQ/ledger-live/tree/develop/libs/coin-algorand",
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"typesVersions": {
|
|
24
|
+
"*": {
|
|
25
|
+
"lib/*": [
|
|
26
|
+
"lib/*"
|
|
27
|
+
],
|
|
28
|
+
"lib-es/*": [
|
|
29
|
+
"lib-es/*"
|
|
30
|
+
],
|
|
31
|
+
"*": [
|
|
32
|
+
"lib/*"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"exports": {
|
|
37
|
+
"./lib/*": "./lib/*.js",
|
|
38
|
+
"./lib-es/*": "./lib-es/*.js",
|
|
39
|
+
"./*": {
|
|
40
|
+
"require": "./lib/*.js",
|
|
41
|
+
"default": "./lib-es/*.js"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"license": "Apache-2.0",
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"algo-msgpack-with-bigint": "^2.1.1",
|
|
48
|
+
"algosdk": "1.13.0",
|
|
49
|
+
"bignumber.js": "^9.1.0",
|
|
50
|
+
"expect": "^27.4.6",
|
|
51
|
+
"invariant": "^2.2.2",
|
|
52
|
+
"lodash": "^4.17.21",
|
|
53
|
+
"prando": "^6.0.1",
|
|
54
|
+
"rxjs": "^6.6.7",
|
|
55
|
+
"@ledgerhq/coin-framework": "^0.3.4-next.0",
|
|
56
|
+
"@ledgerhq/cryptoassets": "^9.6.0-next.0",
|
|
57
|
+
"@ledgerhq/devices": "^8.0.3-next.0",
|
|
58
|
+
"@ledgerhq/errors": "^6.12.6-next.0",
|
|
59
|
+
"@ledgerhq/hw-app-algorand": "^6.27.15-next.0",
|
|
60
|
+
"@ledgerhq/live-env": "^0.3.0-next.0",
|
|
61
|
+
"@ledgerhq/live-promise": "^0.0.1",
|
|
62
|
+
"@ledgerhq/types-cryptoassets": "^7.2.0",
|
|
63
|
+
"@ledgerhq/types-live": "^6.34.1-next.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/invariant": "^2.2.2",
|
|
67
|
+
"@types/jest": "^29.2.4",
|
|
68
|
+
"@types/lodash": "^4.14.191",
|
|
69
|
+
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
|
70
|
+
"@typescript-eslint/parser": "^5.46.1",
|
|
71
|
+
"eslint": "^7.32.0",
|
|
72
|
+
"eslint-config-prettier": "^8.3.0",
|
|
73
|
+
"eslint-config-typescript": "^3.0.0",
|
|
74
|
+
"eslint-formatter-pretty": "^3.0.1",
|
|
75
|
+
"eslint-plugin-prettier": "^3.4.0",
|
|
76
|
+
"eslint-plugin-typescript": "^0.14.0",
|
|
77
|
+
"jest": "^28.1.1",
|
|
78
|
+
"prettier": "^2.8.1",
|
|
79
|
+
"ts-jest": "^28.0.5",
|
|
80
|
+
"typescript": "^4.9.5"
|
|
81
|
+
},
|
|
82
|
+
"scripts": {
|
|
83
|
+
"clean": "rimraf lib lib-es",
|
|
84
|
+
"build": "tsc && tsc -m ES6 --outDir lib-es",
|
|
85
|
+
"prewatch": "pnpm build",
|
|
86
|
+
"watch": "tsc --watch",
|
|
87
|
+
"doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
|
|
88
|
+
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx",
|
|
89
|
+
"lint:fix": "pnpm lint --fix",
|
|
90
|
+
"test": "jest"
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/account.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { getAccountUnit } from "@ledgerhq/coin-framework/account/index";
|
|
2
|
+
import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
|
|
3
|
+
import type { Unit } from "@ledgerhq/types-cryptoassets";
|
|
4
|
+
import type { Account, Operation } from "@ledgerhq/types-live";
|
|
5
|
+
import { BigNumber } from "bignumber.js";
|
|
6
|
+
import invariant from "invariant";
|
|
7
|
+
import type { AlgorandAccount, AlgorandResources } from "./types";
|
|
8
|
+
|
|
9
|
+
function formatOperationSpecifics(
|
|
10
|
+
op: Operation,
|
|
11
|
+
unit: Unit | null | undefined
|
|
12
|
+
): string {
|
|
13
|
+
const { rewards } = op.extra;
|
|
14
|
+
return rewards
|
|
15
|
+
? " REWARDS : " +
|
|
16
|
+
`${
|
|
17
|
+
unit
|
|
18
|
+
? formatCurrencyUnit(unit, new BigNumber(rewards), {
|
|
19
|
+
showCode: true,
|
|
20
|
+
disableRounding: true,
|
|
21
|
+
}).padEnd(16)
|
|
22
|
+
: rewards
|
|
23
|
+
}`
|
|
24
|
+
: "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function formatAccountSpecifics(account: Account): string {
|
|
28
|
+
const { algorandResources } = account as AlgorandAccount;
|
|
29
|
+
invariant(algorandResources, "algorand account expected");
|
|
30
|
+
const unit = getAccountUnit(account);
|
|
31
|
+
const formatConfig = {
|
|
32
|
+
disableRounding: true,
|
|
33
|
+
alwaysShowSign: false,
|
|
34
|
+
showCode: true,
|
|
35
|
+
};
|
|
36
|
+
let str = " ";
|
|
37
|
+
str +=
|
|
38
|
+
formatCurrencyUnit(unit, account.spendableBalance, formatConfig) +
|
|
39
|
+
" spendable. ";
|
|
40
|
+
|
|
41
|
+
if ((algorandResources as AlgorandResources).rewards.gt(0)) {
|
|
42
|
+
str +=
|
|
43
|
+
formatCurrencyUnit(
|
|
44
|
+
unit,
|
|
45
|
+
(algorandResources as AlgorandResources).rewards,
|
|
46
|
+
formatConfig
|
|
47
|
+
) + " rewards. ";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return str;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function fromOperationExtraRaw(
|
|
54
|
+
extra: Record<string, any> | null | undefined
|
|
55
|
+
) {
|
|
56
|
+
if (extra && extra.rewards) {
|
|
57
|
+
return { ...extra, rewards: new BigNumber(extra.rewards) };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return extra;
|
|
61
|
+
}
|
|
62
|
+
export function toOperationExtraRaw(
|
|
63
|
+
extra: Record<string, any> | null | undefined
|
|
64
|
+
) {
|
|
65
|
+
if (extra && extra.rewards) {
|
|
66
|
+
return { ...extra, rewards: extra.rewards.toString() };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return extra;
|
|
70
|
+
}
|
|
71
|
+
export default {
|
|
72
|
+
formatAccountSpecifics,
|
|
73
|
+
formatOperationSpecifics,
|
|
74
|
+
fromOperationExtraRaw,
|
|
75
|
+
toOperationExtraRaw,
|
|
76
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { NetworkRequestCall } from "@ledgerhq/coin-framework/network";
|
|
2
|
+
import { getEnv } from "@ledgerhq/live-env";
|
|
3
|
+
import { BigNumber } from "bignumber.js";
|
|
4
|
+
import {
|
|
5
|
+
AlgoAccount,
|
|
6
|
+
AlgoAsset,
|
|
7
|
+
AlgoTransactionBroadcastResponse,
|
|
8
|
+
AlgoTransactionParams,
|
|
9
|
+
} from "./algodv2.types";
|
|
10
|
+
|
|
11
|
+
const BASE_URL = getEnv("API_ALGORAND_BLOCKCHAIN_EXPLORER_API_ENDPOINT");
|
|
12
|
+
const NODE_URL = `${BASE_URL}/ps2/v2`;
|
|
13
|
+
|
|
14
|
+
const fullUrl = (route: string): string => `${NODE_URL}${route}`;
|
|
15
|
+
|
|
16
|
+
export const getAccount =
|
|
17
|
+
(network: NetworkRequestCall) =>
|
|
18
|
+
async (address: string): Promise<AlgoAccount> => {
|
|
19
|
+
const { data } = await network({
|
|
20
|
+
method: "GET",
|
|
21
|
+
url: fullUrl(`/accounts/${address}`),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const assets: AlgoAsset[] = data.assets
|
|
25
|
+
? // FIXME: what is the type of `a`?
|
|
26
|
+
data.assets.map((a: any): AlgoAsset => {
|
|
27
|
+
return {
|
|
28
|
+
assetId: a["asset-id"].toString(),
|
|
29
|
+
balance: new BigNumber(a.amount),
|
|
30
|
+
};
|
|
31
|
+
})
|
|
32
|
+
: [];
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
round: data.round,
|
|
36
|
+
address: data.address,
|
|
37
|
+
balance: new BigNumber(data.amount),
|
|
38
|
+
pendingRewards: new BigNumber(data["pending-rewards"]),
|
|
39
|
+
assets,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const getTransactionParams =
|
|
44
|
+
(network: NetworkRequestCall) => async (): Promise<AlgoTransactionParams> => {
|
|
45
|
+
const { data } = await network({
|
|
46
|
+
method: "GET",
|
|
47
|
+
url: fullUrl(`/transactions/params`),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
fee: data["fee"],
|
|
52
|
+
minFee: data["min-fee"],
|
|
53
|
+
firstRound: data["first-round"] ?? 0,
|
|
54
|
+
lastRound: data["last-round"],
|
|
55
|
+
genesisID: data["genesis-id"],
|
|
56
|
+
genesisHash: data["genesis-hash"],
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const broadcastTransaction =
|
|
61
|
+
(network: NetworkRequestCall) =>
|
|
62
|
+
async (payload: Buffer): Promise<string> => {
|
|
63
|
+
const { data }: { data: AlgoTransactionBroadcastResponse } = await network({
|
|
64
|
+
method: "POST",
|
|
65
|
+
url: fullUrl(`/transactions`),
|
|
66
|
+
data: payload,
|
|
67
|
+
headers: { "Content-Type": "application/x-binary" },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return data.txId;
|
|
71
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BigNumber } from "bignumber.js";
|
|
2
|
+
|
|
3
|
+
export interface AlgoAccount {
|
|
4
|
+
round: number;
|
|
5
|
+
address: string;
|
|
6
|
+
balance: BigNumber;
|
|
7
|
+
pendingRewards: BigNumber;
|
|
8
|
+
assets: AlgoAsset[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AlgoAsset {
|
|
12
|
+
assetId: string;
|
|
13
|
+
balance: BigNumber;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AlgoTransactionParams {
|
|
17
|
+
fee: number;
|
|
18
|
+
minFee: number;
|
|
19
|
+
firstRound: number;
|
|
20
|
+
lastRound: number;
|
|
21
|
+
genesisHash: string;
|
|
22
|
+
genesisID: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AlgoTransactionBroadcastResponse {
|
|
26
|
+
txId: string;
|
|
27
|
+
}
|
package/src/api/index.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NetworkRequestCall } from "@ledgerhq/coin-framework/network";
|
|
2
|
+
import { AlgoAccount, AlgoTransactionParams } from "./algodv2.types";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
broadcastTransaction,
|
|
6
|
+
getAccount,
|
|
7
|
+
getTransactionParams,
|
|
8
|
+
} from "./algodv2";
|
|
9
|
+
|
|
10
|
+
import { getAccountTransactions } from "./indexer";
|
|
11
|
+
import { AlgoTransaction } from "./indexer.types";
|
|
12
|
+
|
|
13
|
+
export * from "./algodv2.types";
|
|
14
|
+
export * from "./indexer.types";
|
|
15
|
+
|
|
16
|
+
export class AlgorandAPI {
|
|
17
|
+
network: NetworkRequestCall;
|
|
18
|
+
|
|
19
|
+
constructor(network: NetworkRequestCall) {
|
|
20
|
+
this.network = network;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async getAccount(address: string): Promise<AlgoAccount> {
|
|
24
|
+
return getAccount(this.network)(address);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async getTransactionParams(): Promise<AlgoTransactionParams> {
|
|
28
|
+
return getTransactionParams(this.network)();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async broadcastTransaction(payload: Buffer): Promise<string> {
|
|
32
|
+
return broadcastTransaction(this.network)(payload);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async getAccountTransactions(
|
|
36
|
+
address: string,
|
|
37
|
+
startAt?: number
|
|
38
|
+
): Promise<AlgoTransaction[]> {
|
|
39
|
+
return getAccountTransactions(this.network)(address, startAt);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { NetworkRequestCall } from "@ledgerhq/coin-framework/network";
|
|
2
|
+
import { getEnv } from "@ledgerhq/live-env";
|
|
3
|
+
import { BigNumber } from "bignumber.js";
|
|
4
|
+
import {
|
|
5
|
+
AlgoAssetTransferInfo,
|
|
6
|
+
AlgoPaymentInfo,
|
|
7
|
+
AlgoTransaction,
|
|
8
|
+
AlgoTransactionDetails,
|
|
9
|
+
} from "./indexer.types";
|
|
10
|
+
|
|
11
|
+
const LIMIT = 100; // Max nb of transactions per request
|
|
12
|
+
|
|
13
|
+
const BASE_URL = getEnv("API_ALGORAND_BLOCKCHAIN_EXPLORER_API_ENDPOINT");
|
|
14
|
+
const INDEXER_URL = `${BASE_URL}/idx2/v2`;
|
|
15
|
+
|
|
16
|
+
const fullUrl = (route: string): string =>
|
|
17
|
+
`${INDEXER_URL}${route}?limit=${LIMIT}`;
|
|
18
|
+
|
|
19
|
+
export const getAccountTransactions =
|
|
20
|
+
(network: NetworkRequestCall) =>
|
|
21
|
+
async (address: string, startAt?: number): Promise<AlgoTransaction[]> => {
|
|
22
|
+
const url = fullUrl(`/accounts/${address}/transactions`);
|
|
23
|
+
|
|
24
|
+
let nextToken: string | undefined;
|
|
25
|
+
let newRawTxs: any[] = [];
|
|
26
|
+
const mergedTxs: AlgoTransaction[] = [];
|
|
27
|
+
do {
|
|
28
|
+
let nextUrl: string = url;
|
|
29
|
+
if (startAt) {
|
|
30
|
+
nextUrl = nextUrl.concat(`&min-round=${startAt}`);
|
|
31
|
+
}
|
|
32
|
+
if (nextToken) {
|
|
33
|
+
nextUrl = nextUrl.concat(`&next=${nextToken}`);
|
|
34
|
+
}
|
|
35
|
+
const { data }: { data: { transactions: any[] } } = await network({
|
|
36
|
+
method: "GET",
|
|
37
|
+
url: nextUrl,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// FIXME: what is the correct type? Properly type response from api above (data)
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
nextToken = data["next-token"];
|
|
44
|
+
newRawTxs = data.transactions;
|
|
45
|
+
newRawTxs.map(parseRawTransaction).forEach((tx) => mergedTxs.push(tx));
|
|
46
|
+
} while (newRawTxs.length >= LIMIT);
|
|
47
|
+
|
|
48
|
+
return mergedTxs;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const parseRawTransaction = (tx: any): AlgoTransaction => {
|
|
52
|
+
let details: AlgoTransactionDetails | undefined = undefined;
|
|
53
|
+
if (tx["tx-type"] === "pay") {
|
|
54
|
+
const info = tx["payment-transaction"];
|
|
55
|
+
const paymentInfo: AlgoPaymentInfo = {
|
|
56
|
+
amount: new BigNumber(info.amount),
|
|
57
|
+
recipientAddress: info.receiver,
|
|
58
|
+
closeAmount:
|
|
59
|
+
info["close-amount"] === undefined
|
|
60
|
+
? undefined
|
|
61
|
+
: new BigNumber(info["close-amount"]),
|
|
62
|
+
closeToAddress: info["close-remainder-to"],
|
|
63
|
+
};
|
|
64
|
+
details = paymentInfo;
|
|
65
|
+
} else if (tx["tx-type"] === "axfer") {
|
|
66
|
+
const info = tx["asset-transfer-transaction"];
|
|
67
|
+
const assetTransferInfo: AlgoAssetTransferInfo = {
|
|
68
|
+
assetAmount: new BigNumber(info.amount),
|
|
69
|
+
assetId: info["asset-id"],
|
|
70
|
+
assetRecipientAddress: info.receiver,
|
|
71
|
+
assetSenderAddress: info.sender,
|
|
72
|
+
assetCloseAmount:
|
|
73
|
+
info["close-amount"] === undefined
|
|
74
|
+
? undefined
|
|
75
|
+
: new BigNumber(info["close-amount"]),
|
|
76
|
+
assetCloseToAddress: tx["close-to"],
|
|
77
|
+
};
|
|
78
|
+
details = assetTransferInfo;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
id: tx.id,
|
|
83
|
+
timestamp: tx["round-time"],
|
|
84
|
+
round: tx["confirmed-round"],
|
|
85
|
+
senderAddress: tx.sender,
|
|
86
|
+
senderRewards: new BigNumber(tx["sender-rewards"]),
|
|
87
|
+
recipientRewards: new BigNumber(tx["receiver-rewards"]),
|
|
88
|
+
closeRewards:
|
|
89
|
+
tx["close-rewards"] === undefined
|
|
90
|
+
? undefined
|
|
91
|
+
: new BigNumber(tx["close-rewards"]),
|
|
92
|
+
closeAmount:
|
|
93
|
+
tx["closing-amount"] === undefined
|
|
94
|
+
? undefined
|
|
95
|
+
: new BigNumber(tx["closing-amount"]),
|
|
96
|
+
fee: new BigNumber(tx.fee),
|
|
97
|
+
note: tx.note,
|
|
98
|
+
|
|
99
|
+
type: tx["tx-type"],
|
|
100
|
+
details,
|
|
101
|
+
};
|
|
102
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BigNumber } from "bignumber.js";
|
|
2
|
+
|
|
3
|
+
export type AlgoTransactionDetails = AlgoPaymentInfo | AlgoAssetTransferInfo;
|
|
4
|
+
|
|
5
|
+
// Define only types we explicitely support
|
|
6
|
+
export enum AlgoTransactionType {
|
|
7
|
+
PAYMENT = "pay",
|
|
8
|
+
ASSET_TRANSFER = "axfer",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AlgoTransaction {
|
|
12
|
+
id: string;
|
|
13
|
+
timestamp: string; // 'round-time': 1597933539,
|
|
14
|
+
round: number; // 'confirmed-round': 8575676,
|
|
15
|
+
senderAddress: string; // sender: 'RGX5XA7DWZOZ5SLG4WQSNIFKIG4CNX4VOH23YCEX56523DQEAL3QL56XZM',
|
|
16
|
+
senderRewards: BigNumber; // 'sender-rewards': 0,
|
|
17
|
+
recipientRewards: BigNumber; //'receiver-rewards': 0,
|
|
18
|
+
closeRewards?: BigNumber; // 'close-rewards': 0,
|
|
19
|
+
closeAmount?: BigNumber; //'closing-amount': 0,
|
|
20
|
+
fee: BigNumber;
|
|
21
|
+
note: string;
|
|
22
|
+
|
|
23
|
+
type: string; // 'tx-type': 'pay'
|
|
24
|
+
details?: AlgoTransactionDetails;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AlgoPaymentInfo {
|
|
28
|
+
amount: BigNumber;
|
|
29
|
+
recipientAddress: string; // receiver
|
|
30
|
+
closeAmount?: BigNumber; // close-amount
|
|
31
|
+
closeToAddress?: string; // close-remainder-to
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AlgoAssetTransferInfo {
|
|
35
|
+
assetId: string; // asset-id
|
|
36
|
+
assetAmount: BigNumber; // amount
|
|
37
|
+
assetRecipientAddress: string; // receiver
|
|
38
|
+
assetSenderAddress?: string; // sender
|
|
39
|
+
assetCloseAmount?: BigNumber; // close-amount
|
|
40
|
+
assetCloseToAddress?: string; // close-to
|
|
41
|
+
}
|
package/src/bridge/js.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper";
|
|
2
|
+
import {
|
|
3
|
+
DeviceCommunication,
|
|
4
|
+
makeAccountBridgeReceive,
|
|
5
|
+
makeScanAccounts,
|
|
6
|
+
makeSync,
|
|
7
|
+
} from "@ledgerhq/coin-framework/bridge/jsHelpers";
|
|
8
|
+
import type { NetworkRequestCall } from "@ledgerhq/coin-framework/network";
|
|
9
|
+
|
|
10
|
+
import type { AccountBridge, CurrencyBridge } from "@ledgerhq/types-live";
|
|
11
|
+
import { AlgorandAPI } from "../api";
|
|
12
|
+
import getAddress from "../hw-getAddress";
|
|
13
|
+
import { initAccount } from "../initAccount";
|
|
14
|
+
import { broadcast } from "../js-broadcast";
|
|
15
|
+
import createTransaction from "../js-createTransaction";
|
|
16
|
+
import { estimateMaxSpendable } from "../js-estimateMaxSpendable";
|
|
17
|
+
import { getTransactionStatus } from "../js-getTransactionStatus";
|
|
18
|
+
import prepareTransaction from "../js-prepareTransaction";
|
|
19
|
+
import { buildSignOperation } from "../js-signOperation";
|
|
20
|
+
import { makeGetAccountShape } from "../js-synchronization";
|
|
21
|
+
import { assignFromAccountRaw, assignToAccountRaw } from "../serialization";
|
|
22
|
+
import type { Transaction } from "../types";
|
|
23
|
+
|
|
24
|
+
const updateTransaction = (t: Transaction, patch: Partial<Transaction>) => ({
|
|
25
|
+
...t,
|
|
26
|
+
...patch,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export function buildCurrencyBridge(
|
|
30
|
+
deviceCommunication: DeviceCommunication,
|
|
31
|
+
network: NetworkRequestCall
|
|
32
|
+
): CurrencyBridge {
|
|
33
|
+
const algorandAPI = new AlgorandAPI(network);
|
|
34
|
+
|
|
35
|
+
const getAccountShape = makeGetAccountShape(algorandAPI);
|
|
36
|
+
const scanAccounts = makeScanAccounts({
|
|
37
|
+
getAccountShape,
|
|
38
|
+
deviceCommunication,
|
|
39
|
+
getAddressFn: getAddress,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
preload: async () => Promise.resolve({}),
|
|
44
|
+
hydrate: () => {},
|
|
45
|
+
scanAccounts,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function buildAccountBridge(
|
|
50
|
+
deviceCommunication: DeviceCommunication,
|
|
51
|
+
network: NetworkRequestCall
|
|
52
|
+
): AccountBridge<Transaction> {
|
|
53
|
+
const algorandAPI = new AlgorandAPI(network);
|
|
54
|
+
|
|
55
|
+
const receive = makeAccountBridgeReceive(
|
|
56
|
+
getAddressWrapper(getAddress),
|
|
57
|
+
deviceCommunication
|
|
58
|
+
);
|
|
59
|
+
const signOperation = buildSignOperation(deviceCommunication, algorandAPI);
|
|
60
|
+
const getAccountShape = makeGetAccountShape(algorandAPI);
|
|
61
|
+
const sync = makeSync({ getAccountShape });
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
createTransaction,
|
|
65
|
+
updateTransaction,
|
|
66
|
+
prepareTransaction: prepareTransaction(algorandAPI),
|
|
67
|
+
getTransactionStatus: getTransactionStatus(algorandAPI),
|
|
68
|
+
sync,
|
|
69
|
+
receive,
|
|
70
|
+
assignToAccountRaw,
|
|
71
|
+
assignFromAccountRaw,
|
|
72
|
+
initAccount,
|
|
73
|
+
signOperation,
|
|
74
|
+
broadcast: broadcast(algorandAPI),
|
|
75
|
+
estimateMaxSpendable: estimateMaxSpendable(algorandAPI),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* FIXME: an unsued cacheFn is passed to createBridges because of how the
|
|
81
|
+
* libs/ledger-live-common/scripts/sync-families-dispatch.mjs script works.
|
|
82
|
+
*/
|
|
83
|
+
export function createBridges(
|
|
84
|
+
deviceCommunication: DeviceCommunication,
|
|
85
|
+
network: NetworkRequestCall,
|
|
86
|
+
_cacheFn: unknown
|
|
87
|
+
) {
|
|
88
|
+
return {
|
|
89
|
+
currencyBridge: buildCurrencyBridge(deviceCommunication, network),
|
|
90
|
+
accountBridge: buildAccountBridge(deviceCommunication, network),
|
|
91
|
+
};
|
|
92
|
+
}
|