@cardano-sdk/e2e 0.15.0 → 0.16.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/.env.example +3 -0
- package/CHANGELOG.md +10 -0
- package/dist/cjs/factories.d.ts.map +1 -1
- package/dist/cjs/factories.js +7 -8
- package/dist/cjs/factories.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/factories.d.ts.map +1 -1
- package/dist/esm/factories.js +8 -9
- package/dist/esm/factories.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/docker-compose.yml +20 -4
- package/local-network/scripts/cardano-node-ogmios.sh +13 -0
- package/local-network/scripts/mint-handles.sh +1 -0
- package/local-network/sdk-ipc/README.md +5 -0
- package/package.json +20 -19
- package/src/factories.ts +8 -10
- package/test/artillery/StakePoolSearch.ts +2 -2
- package/test/blockfrost/StakePoolCompare.test.ts +2 -2
- package/test/load-test-custom/stake-pool-search/stake-pool-search.test.ts +2 -2
- package/test/long-running/cache-invalidation.test.ts +1 -1
- package/test/long-running/delegation-rewards.test.ts +0 -8
- package/test/long-running/projector-ogmios-connection.test.ts +132 -0
- package/test/providers/StakePoolProvider.test.ts +2 -2
- package/test/web-extension/extension/background/services.ts +17 -2
- package/test/web-extension/extension/ui.html +11 -0
- package/test/web-extension/extension/ui.ts +87 -0
- package/test/web-extension/extension/util.ts +4 -1
- package/test/web-extension/specs/wallet.spec.ts +51 -2
package/docker-compose.yml
CHANGED
|
@@ -12,6 +12,10 @@ services:
|
|
|
12
12
|
<<: *logging
|
|
13
13
|
build:
|
|
14
14
|
context: ./local-network
|
|
15
|
+
depends_on:
|
|
16
|
+
# We need the file server here in order to calculate the pool metadata hashes
|
|
17
|
+
file-server:
|
|
18
|
+
condition: service_healthy
|
|
15
19
|
environment:
|
|
16
20
|
CARDANO_NODE_LOG_LEVEL: ${CARDANO_NODE_LOG_LEVEL:-Info}
|
|
17
21
|
CARDANO_NODE_CHAINDB_LOG_LEVEL: ${CARDANO_NODE_CHAINDB_LOG_LEVEL:-Notice}
|
|
@@ -20,10 +24,7 @@ services:
|
|
|
20
24
|
volumes:
|
|
21
25
|
- ./local-network/network-files/node-sp1/:/root/network-files/node-sp1
|
|
22
26
|
- ./local-network/config:/root/config
|
|
23
|
-
|
|
24
|
-
# We need the file server here in order to calculate the pool metadata hashes
|
|
25
|
-
file-server:
|
|
26
|
-
condition: service_healthy
|
|
27
|
+
- sdk-ipc:/sdk-ipc
|
|
27
28
|
|
|
28
29
|
file-server:
|
|
29
30
|
<<: *logging
|
|
@@ -39,12 +40,15 @@ services:
|
|
|
39
40
|
timeout: 10s
|
|
40
41
|
|
|
41
42
|
cardano-node-ogmios:
|
|
43
|
+
entrypoint: [ "/tini", "-g", "--", "/scripts/cardano-node-ogmios.sh" ]
|
|
42
44
|
image: cardanosolutions/cardano-node-ogmios:v${OGMIOS_VERSION:-5.6.0}_${CARDANO_NODE_VERSION:-1.35.5}
|
|
43
45
|
depends_on:
|
|
44
46
|
local-testnet:
|
|
45
47
|
condition: service_healthy
|
|
46
48
|
volumes:
|
|
47
49
|
- ./local-network/config/network:/config
|
|
50
|
+
- ./local-network/scripts:/scripts
|
|
51
|
+
- sdk-ipc:/sdk-ipc
|
|
48
52
|
|
|
49
53
|
cardano-db-sync:
|
|
50
54
|
depends_on:
|
|
@@ -72,6 +76,12 @@ services:
|
|
|
72
76
|
condition: service_healthy
|
|
73
77
|
restart: on-failure
|
|
74
78
|
|
|
79
|
+
handle-projector:
|
|
80
|
+
environment:
|
|
81
|
+
HANDLE_POLICY_IDS_FILE: /sdk-ipc/handle_policy_ids
|
|
82
|
+
volumes:
|
|
83
|
+
- sdk-ipc:/sdk-ipc
|
|
84
|
+
|
|
75
85
|
provider-server:
|
|
76
86
|
environment:
|
|
77
87
|
TOKEN_METADATA_SERVER_URL: stub://
|
|
@@ -79,4 +89,10 @@ services:
|
|
|
79
89
|
- ./local-network/config/network:/config
|
|
80
90
|
|
|
81
91
|
volumes:
|
|
92
|
+
sdk-ipc:
|
|
93
|
+
driver: local
|
|
94
|
+
driver_opts:
|
|
95
|
+
device: ./local-network/sdk-ipc
|
|
96
|
+
o: bind
|
|
97
|
+
type: none
|
|
82
98
|
wallet-db:
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Simple scripts which overrides the original cardano-node-ogmios.sh file from the
|
|
4
|
+
# cardano-node-ogmios docker image.
|
|
5
|
+
|
|
6
|
+
# Used to support the e2e test to check the projector is able to
|
|
7
|
+
# connect / reconnect to the ogmios server.
|
|
8
|
+
|
|
9
|
+
# If the test set the file, wait for its removal before starting the container
|
|
10
|
+
while [ -f /sdk-ipc/prevent_ogmios ] ; do sleep 10 ; done
|
|
11
|
+
|
|
12
|
+
# Start the cardano-node-ogmios as normal
|
|
13
|
+
/root/cardano-node-ogmios.sh
|
|
@@ -25,6 +25,7 @@ cat >network-files/utxo-keys/handles-metadata.json <<EOL
|
|
|
25
25
|
}}
|
|
26
26
|
EOL
|
|
27
27
|
|
|
28
|
+
echo $policyid > /sdk-ipc/handle_policy_ids
|
|
28
29
|
|
|
29
30
|
addr=$(cardano-cli address build --payment-verification-key-file network-files/utxo-keys/utxo1.vkey --testnet-magic 888)
|
|
30
31
|
faucetAddr="addr_test1qqen0wpmhg7fhkus45lyv4wju26cecgu6avplrnm6dgvuk6qel5hu3u3q0fht53ly97yx95hkt56j37ch07pesf6s4pqh5gd4e"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardano-sdk/e2e",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "End to end tests for the cardano-js-sdk packages.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16.0"
|
|
@@ -81,18 +81,18 @@
|
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@cardano-foundation/ledgerjs-hw-app-cardano": "^6.0.0",
|
|
83
83
|
"@cardano-ogmios/client": "5.6.0",
|
|
84
|
-
"@cardano-sdk/cardano-services": "~0.
|
|
85
|
-
"@cardano-sdk/cardano-services-client": "~0.
|
|
86
|
-
"@cardano-sdk/core": "~0.15.
|
|
87
|
-
"@cardano-sdk/crypto": "~0.1.
|
|
88
|
-
"@cardano-sdk/hardware-ledger": "~0.2.
|
|
89
|
-
"@cardano-sdk/key-management": "~0.7.
|
|
90
|
-
"@cardano-sdk/ogmios": "~0.12.
|
|
91
|
-
"@cardano-sdk/tx-construction": "~0.8.
|
|
92
|
-
"@cardano-sdk/util": "~0.
|
|
93
|
-
"@cardano-sdk/util-dev": "~0.13.
|
|
94
|
-
"@cardano-sdk/util-rxjs": "~0.
|
|
95
|
-
"@cardano-sdk/wallet": "~0.
|
|
84
|
+
"@cardano-sdk/cardano-services": "~0.14.0",
|
|
85
|
+
"@cardano-sdk/cardano-services-client": "~0.10.0",
|
|
86
|
+
"@cardano-sdk/core": "~0.15.1",
|
|
87
|
+
"@cardano-sdk/crypto": "~0.1.7",
|
|
88
|
+
"@cardano-sdk/hardware-ledger": "~0.2.9",
|
|
89
|
+
"@cardano-sdk/key-management": "~0.7.8",
|
|
90
|
+
"@cardano-sdk/ogmios": "~0.12.6",
|
|
91
|
+
"@cardano-sdk/tx-construction": "~0.8.1",
|
|
92
|
+
"@cardano-sdk/util": "~0.12.0",
|
|
93
|
+
"@cardano-sdk/util-dev": "~0.13.4",
|
|
94
|
+
"@cardano-sdk/util-rxjs": "~0.5.0",
|
|
95
|
+
"@cardano-sdk/wallet": "~0.17.0",
|
|
96
96
|
"@vespaiach/axios-fetch-adapter": "^0.3.0",
|
|
97
97
|
"axios": "^0.27.2",
|
|
98
98
|
"bunyan": "^1.8.15",
|
|
@@ -122,16 +122,17 @@
|
|
|
122
122
|
"@babel/core": "^7.18.2",
|
|
123
123
|
"@babel/preset-env": "^7.18.2",
|
|
124
124
|
"@babel/preset-typescript": "^7.17.12",
|
|
125
|
-
"@cardano-sdk/dapp-connector": "~0.9.
|
|
126
|
-
"@cardano-sdk/projection": "~0.6.
|
|
127
|
-
"@cardano-sdk/projection-typeorm": "~0.3.
|
|
128
|
-
"@cardano-sdk/web-extension": "~0.13.
|
|
125
|
+
"@cardano-sdk/dapp-connector": "~0.9.8",
|
|
126
|
+
"@cardano-sdk/projection": "~0.6.10",
|
|
127
|
+
"@cardano-sdk/projection-typeorm": "~0.3.6",
|
|
128
|
+
"@cardano-sdk/web-extension": "~0.13.1",
|
|
129
129
|
"@dcspark/cardano-multiplatform-lib-browser": "^3.1.1",
|
|
130
130
|
"@emurgo/cardano-message-signing-asmjs": "^1.0.1",
|
|
131
131
|
"@types/bunyan": "^1.8.8",
|
|
132
132
|
"@types/chalk": "^2.2.0",
|
|
133
133
|
"@types/convict": "^6.1.2",
|
|
134
134
|
"@types/delay": "^3.1.0",
|
|
135
|
+
"@types/dockerode": "^3.3.8",
|
|
135
136
|
"@types/jest": "^28.1.2",
|
|
136
137
|
"@types/lodash": "^4.14.182",
|
|
137
138
|
"@types/ora": "^3.2.0",
|
|
@@ -146,7 +147,7 @@
|
|
|
146
147
|
"babel-loader": "^8.2.5",
|
|
147
148
|
"blake2b-no-wasm": "2.1.4",
|
|
148
149
|
"buffer": "^6.0.3",
|
|
149
|
-
"chromedriver": "^
|
|
150
|
+
"chromedriver": "^114.0.0",
|
|
150
151
|
"copy-webpack-plugin": "^10.2.4",
|
|
151
152
|
"eslint": "^7.32.0",
|
|
152
153
|
"events": "^3.3.0",
|
|
@@ -173,5 +174,5 @@
|
|
|
173
174
|
"webpack-cli": "^4.9.2",
|
|
174
175
|
"webpack-merge": "^5.8.0"
|
|
175
176
|
},
|
|
176
|
-
"gitHead": "
|
|
177
|
+
"gitHead": "46029ae65c25ce6cfef394249c7ff5a32259757f"
|
|
177
178
|
}
|
package/src/factories.ts
CHANGED
|
@@ -42,8 +42,7 @@ import {
|
|
|
42
42
|
rewardsHttpProvider,
|
|
43
43
|
stakePoolHttpProvider,
|
|
44
44
|
txSubmitHttpProvider,
|
|
45
|
-
utxoHttpProvider
|
|
46
|
-
version
|
|
45
|
+
utxoHttpProvider
|
|
47
46
|
} from '@cardano-sdk/cardano-services-client';
|
|
48
47
|
import { LedgerKeyAgent } from '@cardano-sdk/hardware-ledger';
|
|
49
48
|
import { Logger } from 'ts-log';
|
|
@@ -105,8 +104,7 @@ assetProviderFactory.register(HTTP_PROVIDER, async (params: any, logger: Logger)
|
|
|
105
104
|
assetInfoHttpProvider({
|
|
106
105
|
adapter: customHttpFetchAdapter,
|
|
107
106
|
baseUrl: params.baseUrl,
|
|
108
|
-
logger
|
|
109
|
-
version
|
|
107
|
+
logger
|
|
110
108
|
})
|
|
111
109
|
);
|
|
112
110
|
});
|
|
@@ -118,7 +116,7 @@ chainHistoryProviderFactory.register(
|
|
|
118
116
|
if (params.baseUrl === undefined) throw new Error(`${chainHistoryHttpProvider.name}: ${MISSING_URL_PARAM}`);
|
|
119
117
|
|
|
120
118
|
return new Promise<ChainHistoryProvider>(async (resolve) => {
|
|
121
|
-
resolve(chainHistoryHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger
|
|
119
|
+
resolve(chainHistoryHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger }));
|
|
122
120
|
});
|
|
123
121
|
}
|
|
124
122
|
);
|
|
@@ -129,7 +127,7 @@ networkInfoProviderFactory.register(
|
|
|
129
127
|
if (params.baseUrl === undefined) throw new Error(`${networkInfoHttpProvider.name}: ${MISSING_URL_PARAM}`);
|
|
130
128
|
|
|
131
129
|
return new Promise<NetworkInfoProvider>(async (resolve) => {
|
|
132
|
-
resolve(networkInfoHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger
|
|
130
|
+
resolve(networkInfoHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger }));
|
|
133
131
|
});
|
|
134
132
|
}
|
|
135
133
|
);
|
|
@@ -138,7 +136,7 @@ rewardsProviderFactory.register(HTTP_PROVIDER, async (params: any, logger: Logge
|
|
|
138
136
|
if (params.baseUrl === undefined) throw new Error(`${rewardsHttpProvider.name}: ${MISSING_URL_PARAM}`);
|
|
139
137
|
|
|
140
138
|
return new Promise<RewardsProvider>(async (resolve) => {
|
|
141
|
-
resolve(rewardsHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger
|
|
139
|
+
resolve(rewardsHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger }));
|
|
142
140
|
});
|
|
143
141
|
});
|
|
144
142
|
|
|
@@ -160,7 +158,7 @@ txSubmitProviderFactory.register(HTTP_PROVIDER, async (params: any, logger: Logg
|
|
|
160
158
|
if (params.baseUrl === undefined) throw new Error(`${txSubmitHttpProvider.name}: ${MISSING_URL_PARAM}`);
|
|
161
159
|
|
|
162
160
|
return new Promise<TxSubmitProvider>(async (resolve) => {
|
|
163
|
-
resolve(txSubmitHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger
|
|
161
|
+
resolve(txSubmitHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger }));
|
|
164
162
|
});
|
|
165
163
|
});
|
|
166
164
|
|
|
@@ -168,7 +166,7 @@ utxoProviderFactory.register(HTTP_PROVIDER, async (params: any, logger: Logger):
|
|
|
168
166
|
if (params.baseUrl === undefined) throw new Error(`${utxoHttpProvider.name}: ${MISSING_URL_PARAM}`);
|
|
169
167
|
|
|
170
168
|
return new Promise<UtxoProvider>(async (resolve) => {
|
|
171
|
-
resolve(utxoHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger
|
|
169
|
+
resolve(utxoHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger }));
|
|
172
170
|
});
|
|
173
171
|
});
|
|
174
172
|
|
|
@@ -195,7 +193,7 @@ stakePoolProviderFactory.register(HTTP_PROVIDER, async (params: any, logger: Log
|
|
|
195
193
|
if (params.baseUrl === undefined) throw new Error(`${stakePoolHttpProvider.name}: ${MISSING_URL_PARAM}`);
|
|
196
194
|
|
|
197
195
|
return new Promise<StakePoolProvider>(async (resolve) => {
|
|
198
|
-
resolve(stakePoolHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger
|
|
196
|
+
resolve(stakePoolHttpProvider({ adapter: customHttpFetchAdapter, baseUrl: params.baseUrl, logger }));
|
|
199
197
|
});
|
|
200
198
|
});
|
|
201
199
|
|
|
@@ -2,7 +2,7 @@ import * as envalid from 'envalid';
|
|
|
2
2
|
import { ArtilleryContext, FunctionHook, WhileTrueHook } from './artillery';
|
|
3
3
|
import { Cardano, Paginated, QueryStakePoolsArgs } from '@cardano-sdk/core';
|
|
4
4
|
import { logger } from '@cardano-sdk/util-dev';
|
|
5
|
-
import { stakePoolHttpProvider
|
|
5
|
+
import { stakePoolHttpProvider } from '@cardano-sdk/cardano-services-client';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The context variables shared between all the hooks.
|
|
@@ -26,7 +26,7 @@ interface StakePoolSearchVars extends Paginated<Cardano.StakePool> {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const env = envalid.cleanEnv(process.env, { STAKE_POOL_PROVIDER_URL: envalid.str() });
|
|
29
|
-
const provider = stakePoolHttpProvider({ baseUrl: env.STAKE_POOL_PROVIDER_URL, logger
|
|
29
|
+
const provider = stakePoolHttpProvider({ baseUrl: env.STAKE_POOL_PROVIDER_URL, logger });
|
|
30
30
|
|
|
31
31
|
const PAGE_SIZE = 20;
|
|
32
32
|
|
|
@@ -4,7 +4,7 @@ import { ChildProcess, fork } from 'child_process';
|
|
|
4
4
|
import { WriteStream, createWriteStream } from 'fs';
|
|
5
5
|
import { getRandomPort } from 'get-port-please';
|
|
6
6
|
import { logger } from '@cardano-sdk/util-dev';
|
|
7
|
-
import { stakePoolHttpProvider
|
|
7
|
+
import { stakePoolHttpProvider } from '@cardano-sdk/cardano-services-client';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
|
|
10
10
|
type StakePoolRecord = Record<string, Cardano.StakePool>;
|
|
@@ -127,7 +127,7 @@ describe('StakePoolCompare', () => {
|
|
|
127
127
|
};
|
|
128
128
|
|
|
129
129
|
const setupProvider = async (args: string[] = []) => {
|
|
130
|
-
const provider = stakePoolHttpProvider({ baseUrl: await startServer(args), logger
|
|
130
|
+
const provider = stakePoolHttpProvider({ baseUrl: await startServer(args), logger });
|
|
131
131
|
|
|
132
132
|
// eslint-disable-next-line no-constant-condition
|
|
133
133
|
while (true) {
|
|
@@ -4,7 +4,7 @@ import { Logger } from 'ts-log';
|
|
|
4
4
|
import { MeasurementUtil, getEnv, getLoadTestScheduler } from '../../../src';
|
|
5
5
|
import { bufferTime, from, tap } from 'rxjs';
|
|
6
6
|
import { logger } from '@cardano-sdk/util-dev';
|
|
7
|
-
import { stakePoolHttpProvider
|
|
7
|
+
import { stakePoolHttpProvider } from '@cardano-sdk/cardano-services-client';
|
|
8
8
|
|
|
9
9
|
// Example call:
|
|
10
10
|
/* STAKE_POOL_PROVIDER_URL="http://mhvm:4000/stake-pool" \
|
|
@@ -14,7 +14,7 @@ import { stakePoolHttpProvider, version } from '@cardano-sdk/cardano-services-cl
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
const { STAKE_POOL_PROVIDER_URL } = envalid.cleanEnv(process.env, { STAKE_POOL_PROVIDER_URL: envalid.str() });
|
|
17
|
-
const provider = stakePoolHttpProvider({ baseUrl: STAKE_POOL_PROVIDER_URL, logger
|
|
17
|
+
const provider = stakePoolHttpProvider({ baseUrl: STAKE_POOL_PROVIDER_URL, logger });
|
|
18
18
|
const testLogger: Logger = console;
|
|
19
19
|
const intermediateResultsInterval = 10_000;
|
|
20
20
|
|
|
@@ -47,7 +47,7 @@ describe('cache invalidation', () => {
|
|
|
47
47
|
|
|
48
48
|
afterAll(() => wallet1.wallet.shutdown());
|
|
49
49
|
|
|
50
|
-
test('cache is invalidated on epoch rollover', async () => {
|
|
50
|
+
test.skip('cache is invalidated on epoch rollover', async () => {
|
|
51
51
|
const wallet = wallet1.wallet;
|
|
52
52
|
|
|
53
53
|
await walletReady(wallet);
|
|
@@ -5,10 +5,8 @@ import {
|
|
|
5
5
|
getEnv,
|
|
6
6
|
getTxConfirmationEpoch,
|
|
7
7
|
getWallet,
|
|
8
|
-
requestCoins,
|
|
9
8
|
runningAgainstLocalNetwork,
|
|
10
9
|
submitAndConfirm,
|
|
11
|
-
transferCoins,
|
|
12
10
|
waitForEpoch,
|
|
13
11
|
walletVariables
|
|
14
12
|
} from '../../src';
|
|
@@ -24,9 +22,6 @@ describe('delegation rewards', () => {
|
|
|
24
22
|
let wallet2: PersonalWallet;
|
|
25
23
|
|
|
26
24
|
const initializeWallets = async () => {
|
|
27
|
-
const amountFromFaucet = 100_000_000_000n;
|
|
28
|
-
const tAdaToSend = 50_000_000n;
|
|
29
|
-
|
|
30
25
|
({ wallet: wallet1, providers } = await getWallet({
|
|
31
26
|
env,
|
|
32
27
|
logger,
|
|
@@ -35,9 +30,6 @@ describe('delegation rewards', () => {
|
|
|
35
30
|
}));
|
|
36
31
|
({ wallet: wallet2 } = await getWallet({ env, logger, name: 'Receiving Wallet', polling: { interval: 50 } }));
|
|
37
32
|
|
|
38
|
-
await requestCoins({ coins: amountFromFaucet, wallet: wallet1 });
|
|
39
|
-
await transferCoins({ coins: tAdaToSend, fromWallet: wallet1, toWallet: wallet2 });
|
|
40
|
-
|
|
41
33
|
await waitForWalletStateSettle(wallet1);
|
|
42
34
|
await waitForWalletStateSettle(wallet2);
|
|
43
35
|
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import * as envalid from 'envalid';
|
|
2
|
+
import { DockerUtil } from '@cardano-sdk/util-dev';
|
|
3
|
+
import { open, rm } from 'fs/promises';
|
|
4
|
+
import Dockerode from 'dockerode';
|
|
5
|
+
import axios, { AxiosResponse } from 'axios';
|
|
6
|
+
import delay from 'delay';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
const preventOgmiosStartFile = path.join(__dirname, '..', '..', 'local-network', 'sdk-ipc', 'prevent_ogmios');
|
|
10
|
+
|
|
11
|
+
const docker = new Dockerode();
|
|
12
|
+
|
|
13
|
+
const ogmiosContainer = docker.getContainer('local-network-e2e-cardano-node-ogmios-1');
|
|
14
|
+
const stakePoolProjectorContainer = docker.getContainer('local-network-e2e-stake-pool-projector-1');
|
|
15
|
+
|
|
16
|
+
const createPrevent = async () => {
|
|
17
|
+
const file = await open(preventOgmiosStartFile, 'a');
|
|
18
|
+
|
|
19
|
+
await file.close();
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const removePrevent = () => rm(preventOgmiosStartFile, { force: true });
|
|
23
|
+
|
|
24
|
+
const killContainer = async (container: DockerUtil.Docker.Container) => {
|
|
25
|
+
// This waits only for the signal to be sent
|
|
26
|
+
await DockerUtil.containerExec(container, ['kill', '1']);
|
|
27
|
+
// Let's wait one more second to ensure the container was shut down
|
|
28
|
+
await delay(1000);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
describe('projector ogmios connection', () => {
|
|
32
|
+
let stakePoolProjectorUrl: string;
|
|
33
|
+
|
|
34
|
+
const fetchProjectorHealth = async () => {
|
|
35
|
+
let result: AxiosResponse;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
result = await axios.post(stakePoolProjectorUrl, {});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (axios.isAxiosError(error)) throw new Error(error.message);
|
|
41
|
+
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const checkProjector = async () => {
|
|
49
|
+
const health = await fetchProjectorHealth();
|
|
50
|
+
|
|
51
|
+
if (!health.data.ok) throw new Error('Projector not healthy');
|
|
52
|
+
|
|
53
|
+
const { blockNo } = health.data.services[0].projectedTip;
|
|
54
|
+
const start = Date.now();
|
|
55
|
+
|
|
56
|
+
// Wait for another block to be projected to ensure projector is working correctly
|
|
57
|
+
while ((await fetchProjectorHealth()).data.services[0].projectedTip.blockNo === blockNo) {
|
|
58
|
+
if (Date.now() - start > 60_000) throw new Error('The projector is not projecting new blocks');
|
|
59
|
+
|
|
60
|
+
await delay(100);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const waitProjector = async (skipOkCheck = false, tolerateConnectionErrors = false) => {
|
|
65
|
+
const start = Date.now();
|
|
66
|
+
let projectorReady = false;
|
|
67
|
+
|
|
68
|
+
do {
|
|
69
|
+
try {
|
|
70
|
+
const health = await fetchProjectorHealth();
|
|
71
|
+
|
|
72
|
+
if ('ok' in health.data && (skipOkCheck || health.data.ok)) projectorReady = true;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error && !tolerateConnectionErrors) throw error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (Date.now() - start > 60_000) throw new Error("The projector can't get ready");
|
|
78
|
+
} while (projectorReady === false);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
beforeAll(() => {
|
|
82
|
+
const env = envalid.cleanEnv(process.env, { STAKE_POOL_PROJECTOR_URL: envalid.url() });
|
|
83
|
+
|
|
84
|
+
stakePoolProjectorUrl = `${env.STAKE_POOL_PROJECTOR_URL}health`;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
beforeEach(async () => {
|
|
88
|
+
await removePrevent();
|
|
89
|
+
await checkProjector();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
afterEach(() => removePrevent());
|
|
93
|
+
|
|
94
|
+
it.skip('projector reconnects after a short delay', async () => {
|
|
95
|
+
await killContainer(ogmiosContainer);
|
|
96
|
+
await waitProjector();
|
|
97
|
+
// This throws because is not able to complete HTTP request
|
|
98
|
+
await checkProjector();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Once the problem which prevent the original test to work is solved
|
|
102
|
+
// the original test can be un-skipped and this one can be removed
|
|
103
|
+
it('projector reconnects after a short delay - alternative', async () => {
|
|
104
|
+
await killContainer(ogmiosContainer);
|
|
105
|
+
// Add a 10" sleep as a quick workaround for HTTP connection issue
|
|
106
|
+
await delay(10_000);
|
|
107
|
+
await waitProjector();
|
|
108
|
+
await checkProjector();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it.skip('projector reconnects after a long delay', async () => {
|
|
112
|
+
await createPrevent();
|
|
113
|
+
await killContainer(ogmiosContainer);
|
|
114
|
+
await delay(120_000);
|
|
115
|
+
await removePrevent();
|
|
116
|
+
await waitProjector();
|
|
117
|
+
await checkProjector();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('projector connects to a later started ogmios', async () => {
|
|
121
|
+
await createPrevent();
|
|
122
|
+
await killContainer(ogmiosContainer);
|
|
123
|
+
await killContainer(stakePoolProjectorContainer);
|
|
124
|
+
await waitProjector(true, true);
|
|
125
|
+
await removePrevent();
|
|
126
|
+
// Once the 'projector reconnects after a short delay' problem is solved
|
|
127
|
+
// also remove following line and uncomment next one
|
|
128
|
+
await waitProjector(false, true);
|
|
129
|
+
// await waitProjector();
|
|
130
|
+
await checkProjector();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as envalid from 'envalid';
|
|
4
4
|
import { Cardano, QueryStakePoolsArgs, StakePoolProvider } from '@cardano-sdk/core';
|
|
5
5
|
import { logger } from '@cardano-sdk/util-dev';
|
|
6
|
-
import { stakePoolHttpProvider
|
|
6
|
+
import { stakePoolHttpProvider } from '@cardano-sdk/cardano-services-client';
|
|
7
7
|
|
|
8
8
|
const stringToRegExEqualsTo = (str: string) => `^${str.replace(/[$()*+.?[\\\]^{|}-]/g, '\\$&')}$`;
|
|
9
9
|
|
|
@@ -140,7 +140,7 @@ describe('StakePoolProvider', () => {
|
|
|
140
140
|
|
|
141
141
|
beforeAll(async () => {
|
|
142
142
|
const env = envalid.cleanEnv(process.env, { STAKE_POOL_PROVIDER_URL: envalid.url() });
|
|
143
|
-
const config = { baseUrl: env.STAKE_POOL_PROVIDER_URL, logger
|
|
143
|
+
const config = { baseUrl: env.STAKE_POOL_PROVIDER_URL, logger };
|
|
144
144
|
|
|
145
145
|
provider = stakePoolHttpProvider(config);
|
|
146
146
|
pools = await fetchAllPools();
|
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
// Expose any additional services to be shared with UIs
|
|
2
|
-
import { BackgroundServices, adaPriceProperties, logger } from '../util';
|
|
2
|
+
import { BackgroundServices, adaPriceProperties, env, logger } from '../util';
|
|
3
|
+
import { Cardano } from '@cardano-sdk/core';
|
|
3
4
|
import { adaPriceServiceChannel, walletName } from '../const';
|
|
4
5
|
import { authenticator } from './authenticator';
|
|
5
6
|
import { exposeApi, exposeSupplyDistributionTracker } from '@cardano-sdk/web-extension';
|
|
6
7
|
import { of } from 'rxjs';
|
|
7
8
|
import { runtime } from 'webextension-polyfill';
|
|
9
|
+
import { stakePoolProviderFactory } from '../../../../src';
|
|
8
10
|
import { supplyDistributionTrackerReady } from './supplyDistributionTracker';
|
|
9
11
|
|
|
10
12
|
const priceService: BackgroundServices = {
|
|
11
13
|
adaUsd$: of(2.99),
|
|
12
|
-
clearAllowList: authenticator.clear.bind(authenticator)
|
|
14
|
+
clearAllowList: authenticator.clear.bind(authenticator),
|
|
15
|
+
getPoolIds: async (count: number): Promise<Cardano.StakePool[]> => {
|
|
16
|
+
const stakePoolProvider = await stakePoolProviderFactory.create(
|
|
17
|
+
env.STAKE_POOL_PROVIDER,
|
|
18
|
+
env.STAKE_POOL_PROVIDER_PARAMS,
|
|
19
|
+
logger
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const activePools = await stakePoolProvider.queryStakePools({
|
|
23
|
+
filters: { status: [Cardano.StakePoolStatus.Active] },
|
|
24
|
+
pagination: { limit: count, startAt: 0 }
|
|
25
|
+
});
|
|
26
|
+
return activePools.pageResults.slice(0, count);
|
|
27
|
+
}
|
|
13
28
|
};
|
|
14
29
|
|
|
15
30
|
exposeApi(
|
|
@@ -27,6 +27,17 @@
|
|
|
27
27
|
<div>Stake Address: <span id="stakeAddress">-</span></div>
|
|
28
28
|
<div>Balance: <span id="balance">-</span></div>
|
|
29
29
|
<div>Network-wide staked: <span id="supplyDistribution">-</span></div>
|
|
30
|
+
<div id="multiDelegation">
|
|
31
|
+
<div class="distribution">
|
|
32
|
+
<h3>Delegation distribution:</h3>
|
|
33
|
+
<ul></ul>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="delegate">
|
|
36
|
+
<p>Available Pools: <span class="pools"></span></p>
|
|
37
|
+
<button>Delegate to multiple pools</button>
|
|
38
|
+
<p class="delegateTxId"></p>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
30
41
|
<button id="buildAndSignTx">Build & Sign TX</button>
|
|
31
42
|
<div>Signature: <span id="signature">-</span></div>
|
|
32
43
|
<script src="ui.js"></script>
|
|
@@ -10,10 +10,16 @@ import {
|
|
|
10
10
|
import { adaPriceServiceChannel, getObservableWalletName, userPromptServiceChannel, walletName } from './const';
|
|
11
11
|
import { bip32Ed25519Factory, keyManagementFactory } from '../../../src';
|
|
12
12
|
|
|
13
|
+
import { Cardano } from '@cardano-sdk/core';
|
|
13
14
|
import { combineLatest, firstValueFrom, of } from 'rxjs';
|
|
14
15
|
import { runtime } from 'webextension-polyfill';
|
|
15
16
|
import { setupWallet } from '@cardano-sdk/wallet';
|
|
16
17
|
|
|
18
|
+
const delegationConfig = {
|
|
19
|
+
count: 3,
|
|
20
|
+
distribution: [10, 30, 60]
|
|
21
|
+
};
|
|
22
|
+
|
|
17
23
|
const api: UserPromptService = {
|
|
18
24
|
allowOrigin(origin) {
|
|
19
25
|
const container = document.querySelector<HTMLDivElement>('#requestAccess')!;
|
|
@@ -66,6 +72,30 @@ combineLatest([supplyDistribution.lovelaceSupply$, supplyDistribution.stake$]).s
|
|
|
66
72
|
(document.querySelector('#supplyDistribution')!.textContent = `${stake.live} out of ${lovelaceSupply.total}`)
|
|
67
73
|
);
|
|
68
74
|
|
|
75
|
+
/** Get pools from background service and assign weights */
|
|
76
|
+
const displayPoolIdsAndPreparePortfolio = async (): Promise<{ pool: Cardano.StakePool; weight: number }[]> => {
|
|
77
|
+
const pools = await backgroundServices.getPoolIds(delegationConfig.count);
|
|
78
|
+
const poolsSpan = document.querySelector('#multiDelegation .delegate .pools');
|
|
79
|
+
poolsSpan!.textContent = pools.map(({ id }) => id).join(' ');
|
|
80
|
+
return pools.map((pool, idx) => ({ pool, weight: delegationConfig.distribution[idx] }));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** Build, sign and submit delegation transaction */
|
|
84
|
+
const sendDelegationTx = async (portfolio: { pool: Cardano.StakePool; weight: number }[]): Promise<void> => {
|
|
85
|
+
const pools = portfolio.map(({ pool: { hexId: id }, weight }) => ({ id, weight }));
|
|
86
|
+
const txBuilder = wallet.createTxBuilder();
|
|
87
|
+
|
|
88
|
+
let msg: string;
|
|
89
|
+
try {
|
|
90
|
+
const signedTx = await txBuilder.delegatePortfolio({ pools }).build().sign();
|
|
91
|
+
const txId = await wallet.submitTx(signedTx);
|
|
92
|
+
msg = `TxId: ${txId}`;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
msg = `ERROR delegating: ${JSON.stringify(error)}`;
|
|
95
|
+
}
|
|
96
|
+
document.querySelector('#multiDelegation .delegateTxId')!.textContent = msg;
|
|
97
|
+
};
|
|
98
|
+
|
|
69
99
|
const setAddresses = ({ address, stakeAddress }: { address: string; stakeAddress: string }): void => {
|
|
70
100
|
document.querySelector('#address')!.textContent = address;
|
|
71
101
|
document.querySelector('#stakeAddress')!.textContent = stakeAddress;
|
|
@@ -100,6 +130,45 @@ const deactivateWallet = async (): Promise<void> => {
|
|
|
100
130
|
clearWalletValues();
|
|
101
131
|
};
|
|
102
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Wallet does not have any active delegations.
|
|
135
|
+
* Show a `<p class="noDelegation">No delegation found</p>`
|
|
136
|
+
*/
|
|
137
|
+
const createEmptyDelegationEl = () => {
|
|
138
|
+
const emptyDistribution = document.createElement('p');
|
|
139
|
+
emptyDistribution.classList.add('noDelegation');
|
|
140
|
+
emptyDistribution.textContent = 'No delegation found';
|
|
141
|
+
return emptyDistribution;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Create a list item for a delegation
|
|
146
|
+
* `<li> <span class="poolId">thePoolId</span> <span class="percent">50</span> </li>`
|
|
147
|
+
*/
|
|
148
|
+
const createDelegationLi = (poolId: string, percent: string) => {
|
|
149
|
+
const delegationLi = document.createElement('li');
|
|
150
|
+
const poolIdSpan = document.createElement('span');
|
|
151
|
+
poolIdSpan.classList.add('poolId');
|
|
152
|
+
poolIdSpan.textContent = poolId;
|
|
153
|
+
const delegationPercentageSpan = document.createElement('span');
|
|
154
|
+
delegationPercentageSpan.classList.add('percent');
|
|
155
|
+
delegationPercentageSpan.textContent = percent;
|
|
156
|
+
const separatorSpan = document.createElement('span');
|
|
157
|
+
separatorSpan.textContent = ' - ';
|
|
158
|
+
delegationLi.append(poolIdSpan);
|
|
159
|
+
delegationLi.append(separatorSpan);
|
|
160
|
+
delegationLi.append(delegationPercentageSpan);
|
|
161
|
+
return delegationLi;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/** Remove empty delegation message or multi-delegation list items to display new data */
|
|
165
|
+
const cleanupMultidelegationInfo = (multiDelegationDiv: Element) => {
|
|
166
|
+
multiDelegationDiv.querySelector('p.noDelegation')?.remove();
|
|
167
|
+
for (const delegationLi of multiDelegationDiv.querySelectorAll('ul > li')) {
|
|
168
|
+
delegationLi.remove();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
103
172
|
const walletManager = new WalletManagerUi({ walletName }, { logger, runtime });
|
|
104
173
|
// Wallet object does not change when wallets are activated/deactivated.
|
|
105
174
|
// Instead, it's observable properties emit from the currently active wallet.
|
|
@@ -108,6 +177,19 @@ const wallet = walletManager.wallet;
|
|
|
108
177
|
// Wallet can be subscribed can be used even before it is actually created.
|
|
109
178
|
wallet.addresses$.subscribe(([{ address, rewardAccount }]) => setAddresses({ address, stakeAddress: rewardAccount }));
|
|
110
179
|
wallet.balance.utxo.available$.subscribe(({ coins }) => setBalance(coins.toString()));
|
|
180
|
+
wallet.delegation.distribution$.subscribe((delegationDistrib) => {
|
|
181
|
+
const multiDelegationDiv = document.querySelector('#multiDelegation .distribution');
|
|
182
|
+
cleanupMultidelegationInfo(multiDelegationDiv!);
|
|
183
|
+
|
|
184
|
+
if (delegationDistrib.size === 0) {
|
|
185
|
+
multiDelegationDiv?.appendChild(createEmptyDelegationEl());
|
|
186
|
+
} else {
|
|
187
|
+
const distributionUl = multiDelegationDiv?.querySelector('ul');
|
|
188
|
+
for (const [poolId, delegation] of delegationDistrib) {
|
|
189
|
+
distributionUl?.appendChild(createDelegationLi(poolId, (delegation.percentage * 100).toString()));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
111
193
|
|
|
112
194
|
const createWallet = async (accountIndex: number) => {
|
|
113
195
|
clearWalletValues();
|
|
@@ -142,6 +224,11 @@ document.querySelector('#activateWallet1')!.addEventListener('click', async () =
|
|
|
142
224
|
document.querySelector('#activateWallet2')!.addEventListener('click', async () => await createWallet(1));
|
|
143
225
|
document.querySelector('#deactivateWallet')!.addEventListener('click', async () => await deactivateWallet());
|
|
144
226
|
document.querySelector('#destroyWallet')!.addEventListener('click', async () => await destroyWallet());
|
|
227
|
+
document.querySelector('#multiDelegation .delegate button')!.addEventListener('click', async () => {
|
|
228
|
+
const poolsAndWeights = await displayPoolIdsAndPreparePortfolio();
|
|
229
|
+
// multi-delegate with 10%, 30%, 60% distribution
|
|
230
|
+
await sendDelegationTx(poolsAndWeights);
|
|
231
|
+
});
|
|
145
232
|
|
|
146
233
|
document.querySelector('#buildAndSignTx')!.addEventListener('click', async () => {
|
|
147
234
|
const [{ address: ownAddress }] = await firstValueFrom(wallet.addresses$);
|