@alephium/ledger-app 0.2.0 → 0.3.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/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +29 -15
- package/dist/test/release.test.js +115 -9
- package/dist/test/speculos.test.js +326 -32
- package/docker/devnet.conf +55 -0
- package/docker/docker-compose.yaml +22 -0
- package/package.json +9 -9
- package/src/index.ts +30 -15
- package/test/release.test.ts +134 -10
- package/test/speculos.test.ts +344 -33
package/dist/src/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare const CLA = 128;
|
|
|
5
5
|
export declare enum INS {
|
|
6
6
|
GET_VERSION = 0,
|
|
7
7
|
GET_PUBLIC_KEY = 1,
|
|
8
|
-
|
|
8
|
+
SIGN_TX = 2
|
|
9
9
|
}
|
|
10
10
|
export declare const GROUP_NUM = 4;
|
|
11
11
|
export declare const HASH_LEN = 32;
|
|
@@ -15,5 +15,5 @@ export default class AlephiumApp {
|
|
|
15
15
|
close(): Promise<void>;
|
|
16
16
|
getVersion(): Promise<string>;
|
|
17
17
|
getAccount(startPath: string, targetGroup?: number, keyType?: KeyType): Promise<readonly [Account, number]>;
|
|
18
|
-
|
|
18
|
+
signUnsignedTx(path: string, unsignedTx: Buffer): Promise<string>;
|
|
19
19
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var INS;
|
|
|
34
34
|
(function (INS) {
|
|
35
35
|
INS[INS["GET_VERSION"] = 0] = "GET_VERSION";
|
|
36
36
|
INS[INS["GET_PUBLIC_KEY"] = 1] = "GET_PUBLIC_KEY";
|
|
37
|
-
INS[INS["
|
|
37
|
+
INS[INS["SIGN_TX"] = 2] = "SIGN_TX";
|
|
38
38
|
})(INS = exports.INS || (exports.INS = {}));
|
|
39
39
|
exports.GROUP_NUM = 4;
|
|
40
40
|
exports.HASH_LEN = 32;
|
|
@@ -67,21 +67,35 @@ class AlephiumApp {
|
|
|
67
67
|
const hdIndex = response.slice(65, 69).readUInt32BE(0);
|
|
68
68
|
return [{ publicKey: publicKey, address: address, group: group, keyType: keyType ?? 'default' }, hdIndex];
|
|
69
69
|
}
|
|
70
|
-
async
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
async signUnsignedTx(path, unsignedTx) {
|
|
71
|
+
console.log(`unsigned tx size: ${unsignedTx.length}`);
|
|
72
|
+
const encodedPath = serde.serializePath(path);
|
|
73
|
+
const firstFrameTxLength = 256 - 25;
|
|
74
|
+
const txData = unsignedTx.slice(0, unsignedTx.length > firstFrameTxLength ? firstFrameTxLength : unsignedTx.length);
|
|
75
|
+
const data = Buffer.concat([encodedPath, txData]);
|
|
76
|
+
let response = await this.transport.send(exports.CLA, INS.SIGN_TX, 0x00, 0x00, data, [hw_transport_1.StatusCodes.OK]);
|
|
77
|
+
if (unsignedTx.length <= firstFrameTxLength) {
|
|
78
|
+
return decodeSignature(response);
|
|
73
79
|
}
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return (0, web3_1.encodeHexSignature)(r.toString('hex'), s.toString('hex'));
|
|
80
|
+
const frameLength = 256 - 5;
|
|
81
|
+
let fromIndex = firstFrameTxLength;
|
|
82
|
+
while (fromIndex < unsignedTx.length) {
|
|
83
|
+
const remain = unsignedTx.length - fromIndex;
|
|
84
|
+
const toIndex = remain > frameLength ? (fromIndex + frameLength) : unsignedTx.length;
|
|
85
|
+
const data = unsignedTx.slice(fromIndex, toIndex);
|
|
86
|
+
response = await this.transport.send(exports.CLA, INS.SIGN_TX, 0x01, 0x00, data, [hw_transport_1.StatusCodes.OK]);
|
|
87
|
+
fromIndex = toIndex;
|
|
88
|
+
}
|
|
89
|
+
return decodeSignature(response);
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
exports.default = AlephiumApp;
|
|
93
|
+
function decodeSignature(response) {
|
|
94
|
+
// Decode signature: https://bitcoin.stackexchange.com/a/12556
|
|
95
|
+
const rLen = response.slice(3, 4)[0];
|
|
96
|
+
const r = response.slice(4, 4 + rLen);
|
|
97
|
+
const sLen = response.slice(5 + rLen, 6 + rLen)[0];
|
|
98
|
+
const s = response.slice(6 + rLen, 6 + rLen + sLen);
|
|
99
|
+
console.log(`${rLen} - ${r.toString('hex')}\n${sLen} - ${s.toString('hex')}`);
|
|
100
|
+
return (0, web3_1.encodeHexSignature)(r.toString('hex'), s.toString('hex'));
|
|
101
|
+
}
|
|
@@ -5,22 +5,128 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const hw_transport_node_hid_1 = __importDefault(require("@ledgerhq/hw-transport-node-hid"));
|
|
7
7
|
const logs_1 = require("@ledgerhq/logs");
|
|
8
|
-
const blakejs_1 = __importDefault(require("blakejs"));
|
|
9
8
|
const web3_1 = require("@alephium/web3");
|
|
10
9
|
const src_1 = __importDefault(require("../src"));
|
|
10
|
+
const web3_test_1 = require("@alephium/web3-test");
|
|
11
|
+
const web3_wallet_1 = require("@alephium/web3-wallet");
|
|
11
12
|
describe.skip('Integration', () => {
|
|
12
13
|
const path = `m/44'/1234'/0'/0/0`;
|
|
14
|
+
const nodeProvider = new web3_1.NodeProvider("http://127.0.0.1:22973");
|
|
15
|
+
web3_1.web3.setCurrentNodeProvider(nodeProvider);
|
|
16
|
+
function randomP2PKHAddress(groupIndex) {
|
|
17
|
+
return web3_wallet_1.PrivateKeyWallet.Random(groupIndex, nodeProvider).address;
|
|
18
|
+
}
|
|
19
|
+
async function getALPHBalance(address) {
|
|
20
|
+
const balances = await nodeProvider.addresses.getAddressesAddressBalance(address);
|
|
21
|
+
return BigInt(balances.balance);
|
|
22
|
+
}
|
|
23
|
+
async function transferToTestAccount(address) {
|
|
24
|
+
const fromAccount = await (0, web3_test_1.getSigner)();
|
|
25
|
+
const transferResult = await (0, web3_test_1.transfer)(fromAccount, address, web3_1.ALPH_TOKEN_ID, web3_1.ONE_ALPH * 10n);
|
|
26
|
+
await (0, web3_1.waitForTxConfirmation)(transferResult.txId, 1, 1000);
|
|
27
|
+
}
|
|
13
28
|
// enable this for integration test
|
|
14
|
-
it('should
|
|
29
|
+
it('should transfer ALPH', async () => {
|
|
15
30
|
const transport = await hw_transport_node_hid_1.default.open('');
|
|
16
31
|
(0, logs_1.listen)((log) => console.log(log));
|
|
17
32
|
const app = new src_1.default(transport);
|
|
18
|
-
const [
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
const [testAccount] = await app.getAccount(path);
|
|
34
|
+
await transferToTestAccount(testAccount.address);
|
|
35
|
+
const balance0 = await getALPHBalance(testAccount.address);
|
|
36
|
+
const buildTxResult = await nodeProvider.transactions.postTransactionsBuild({
|
|
37
|
+
fromPublicKey: testAccount.publicKey,
|
|
38
|
+
destinations: [
|
|
39
|
+
{
|
|
40
|
+
address: randomP2PKHAddress(0),
|
|
41
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 2n).toString(),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
address: randomP2PKHAddress(0),
|
|
45
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 3n).toString(),
|
|
46
|
+
},
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
const signature = await app.signUnsignedTx(path, Buffer.from(buildTxResult.unsignedTx, 'hex'));
|
|
50
|
+
expect((0, web3_1.transactionVerifySignature)(buildTxResult.txId, testAccount.publicKey, signature)).toBe(true);
|
|
51
|
+
const submitResult = await nodeProvider.transactions.postTransactionsSubmit({
|
|
52
|
+
unsignedTx: buildTxResult.unsignedTx,
|
|
53
|
+
signature: signature
|
|
54
|
+
});
|
|
55
|
+
await (0, web3_1.waitForTxConfirmation)(submitResult.txId, 1, 1000);
|
|
56
|
+
const balance1 = await getALPHBalance(testAccount.address);
|
|
57
|
+
const gasFee = BigInt(buildTxResult.gasAmount) * BigInt(buildTxResult.gasPrice);
|
|
58
|
+
expect(balance1).toEqual(balance0 - gasFee - web3_1.ONE_ALPH * 5n);
|
|
24
59
|
await app.close();
|
|
25
|
-
},
|
|
60
|
+
}, 120000);
|
|
61
|
+
it('should transfer token', async () => {
|
|
62
|
+
const transport = await hw_transport_node_hid_1.default.open('');
|
|
63
|
+
(0, logs_1.listen)((log) => console.log(log));
|
|
64
|
+
const app = new src_1.default(transport);
|
|
65
|
+
const [testAccount] = await app.getAccount(path);
|
|
66
|
+
await transferToTestAccount(testAccount.address);
|
|
67
|
+
const tokenAmount = 2222222222222222222222222n;
|
|
68
|
+
const tokenInfo = await (0, web3_test_1.mintToken)(testAccount.address, tokenAmount);
|
|
69
|
+
const balances0 = await nodeProvider.addresses.getAddressesAddressBalance(testAccount.address);
|
|
70
|
+
const transferAmount = 2222222222222222222222222n / 2n;
|
|
71
|
+
const buildTxResult = await nodeProvider.transactions.postTransactionsBuild({
|
|
72
|
+
fromPublicKey: testAccount.publicKey,
|
|
73
|
+
destinations: [
|
|
74
|
+
{
|
|
75
|
+
address: '1BmVCLrjttchZMW7i6df7mTdCKzHpy38bgDbVL1GqV6P7',
|
|
76
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 2n).toString(),
|
|
77
|
+
tokens: [{ id: tokenInfo.contractId, amount: transferAmount.toString() }]
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
const signature = await app.signUnsignedTx(path, Buffer.from(buildTxResult.unsignedTx, 'hex'));
|
|
82
|
+
expect((0, web3_1.transactionVerifySignature)(buildTxResult.txId, testAccount.publicKey, signature)).toBe(true);
|
|
83
|
+
const submitResult = await nodeProvider.transactions.postTransactionsSubmit({
|
|
84
|
+
unsignedTx: buildTxResult.unsignedTx,
|
|
85
|
+
signature: signature
|
|
86
|
+
});
|
|
87
|
+
await (0, web3_1.waitForTxConfirmation)(submitResult.txId, 1, 1000);
|
|
88
|
+
const balances1 = await nodeProvider.addresses.getAddressesAddressBalance(testAccount.address);
|
|
89
|
+
const gasFee = BigInt(buildTxResult.gasAmount) * BigInt(buildTxResult.gasPrice);
|
|
90
|
+
const alphBalance = BigInt(balances1.balance);
|
|
91
|
+
expect(alphBalance).toEqual(BigInt(balances0.balance) - gasFee - web3_1.ONE_ALPH * 2n);
|
|
92
|
+
const tokenBalance = balances1.tokenBalances.find((t) => t.id === tokenInfo.contractId);
|
|
93
|
+
expect(tokenBalance.amount).toEqual((tokenAmount - transferAmount).toString());
|
|
94
|
+
await app.close();
|
|
95
|
+
}, 120000);
|
|
96
|
+
it('should transfer to multisig address', async () => {
|
|
97
|
+
const transport = await hw_transport_node_hid_1.default.open('');
|
|
98
|
+
(0, logs_1.listen)((log) => console.log(log));
|
|
99
|
+
const app = new src_1.default(transport);
|
|
100
|
+
const [testAccount] = await app.getAccount(path);
|
|
101
|
+
await transferToTestAccount(testAccount.address);
|
|
102
|
+
const tokenAmount = 2222222222222222222222222n;
|
|
103
|
+
const tokenInfo = await (0, web3_test_1.mintToken)(testAccount.address, tokenAmount);
|
|
104
|
+
const balances0 = await nodeProvider.addresses.getAddressesAddressBalance(testAccount.address);
|
|
105
|
+
const transferAmount = 2222222222222222222222222n / 2n;
|
|
106
|
+
const multiSigAddress = 'X3KYVteDjsKuUP1F68Nv9iEUecnnkMuwjbC985AnA6MvciDFJ5bAUEso2Sd7sGrwZ5rfNLj7Rp4n9XjcyzDiZsrPxfhNkPYcDm3ce8pQ9QasNFByEufMi3QJ3cS9Vk6cTpqNcq';
|
|
107
|
+
const buildTxResult = await nodeProvider.transactions.postTransactionsBuild({
|
|
108
|
+
fromPublicKey: testAccount.publicKey,
|
|
109
|
+
destinations: [
|
|
110
|
+
{
|
|
111
|
+
address: multiSigAddress,
|
|
112
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 2n).toString(),
|
|
113
|
+
tokens: [{ id: tokenInfo.contractId, amount: transferAmount.toString() }]
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
});
|
|
117
|
+
const signature = await app.signUnsignedTx(path, Buffer.from(buildTxResult.unsignedTx, 'hex'));
|
|
118
|
+
expect((0, web3_1.transactionVerifySignature)(buildTxResult.txId, testAccount.publicKey, signature)).toBe(true);
|
|
119
|
+
const submitResult = await nodeProvider.transactions.postTransactionsSubmit({
|
|
120
|
+
unsignedTx: buildTxResult.unsignedTx,
|
|
121
|
+
signature: signature
|
|
122
|
+
});
|
|
123
|
+
await (0, web3_1.waitForTxConfirmation)(submitResult.txId, 1, 1000);
|
|
124
|
+
const balances1 = await nodeProvider.addresses.getAddressesAddressBalance(testAccount.address);
|
|
125
|
+
const gasFee = BigInt(buildTxResult.gasAmount) * BigInt(buildTxResult.gasPrice);
|
|
126
|
+
const alphBalance = BigInt(balances1.balance);
|
|
127
|
+
expect(alphBalance).toEqual(BigInt(balances0.balance) - gasFee - web3_1.ONE_ALPH * 2n);
|
|
128
|
+
const tokenBalance = balances1.tokenBalances.find((t) => t.id === tokenInfo.contractId);
|
|
129
|
+
expect(tokenBalance.amount).toEqual((tokenAmount - transferAmount).toString());
|
|
130
|
+
await app.close();
|
|
131
|
+
}, 120000);
|
|
26
132
|
});
|
|
@@ -28,9 +28,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
const hw_transport_node_speculos_1 = __importDefault(require("@ledgerhq/hw-transport-node-speculos"));
|
|
30
30
|
const src_1 = __importStar(require("../src"));
|
|
31
|
-
const blakejs_1 = __importDefault(require("blakejs"));
|
|
32
31
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
33
32
|
const web3_1 = require("@alephium/web3");
|
|
33
|
+
const web3_test_1 = require("@alephium/web3-test");
|
|
34
|
+
const web3_wallet_1 = require("@alephium/web3-wallet");
|
|
34
35
|
function sleep(ms) {
|
|
35
36
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
37
|
}
|
|
@@ -48,6 +49,8 @@ function getRandomInt(min, max) {
|
|
|
48
49
|
}
|
|
49
50
|
describe('sdk', () => {
|
|
50
51
|
const apduPort = 9999;
|
|
52
|
+
const nodeProvider = new web3_1.NodeProvider("http://127.0.0.1:22973");
|
|
53
|
+
web3_1.web3.setCurrentNodeProvider(nodeProvider);
|
|
51
54
|
let pathIndex;
|
|
52
55
|
let path;
|
|
53
56
|
beforeEach(() => {
|
|
@@ -58,7 +61,7 @@ describe('sdk', () => {
|
|
|
58
61
|
const transport = await hw_transport_node_speculos_1.default.open({ apduPort });
|
|
59
62
|
const app = new src_1.default(transport);
|
|
60
63
|
const version = await app.getVersion();
|
|
61
|
-
expect(version).toBe('0.
|
|
64
|
+
expect(version).toBe('0.2.0');
|
|
62
65
|
await app.close();
|
|
63
66
|
});
|
|
64
67
|
it('should get public key', async () => {
|
|
@@ -72,52 +75,343 @@ describe('sdk', () => {
|
|
|
72
75
|
it('should get public key for group', async () => {
|
|
73
76
|
const transport = await hw_transport_node_speculos_1.default.open({ apduPort });
|
|
74
77
|
const app = new src_1.default(transport);
|
|
75
|
-
|
|
78
|
+
for (let group = 0; group < src_1.GROUP_NUM; group++) {
|
|
76
79
|
const [account, hdIndex] = await app.getAccount(path, group);
|
|
77
80
|
expect(hdIndex >= pathIndex).toBe(true);
|
|
78
81
|
expect((0, web3_1.groupOfAddress)(account.address)).toBe(group);
|
|
79
82
|
expect(account.keyType).toBe('default');
|
|
80
|
-
}
|
|
83
|
+
}
|
|
81
84
|
await app.close();
|
|
82
85
|
});
|
|
83
86
|
it('should get public key for group for Schnorr signature', async () => {
|
|
84
87
|
const transport = await hw_transport_node_speculos_1.default.open({ apduPort });
|
|
85
88
|
const app = new src_1.default(transport);
|
|
86
|
-
|
|
89
|
+
for (let group = 0; group < src_1.GROUP_NUM; group++) {
|
|
87
90
|
await expect(app.getAccount(path, group, 'bip340-schnorr')).rejects.toThrow('BIP340-Schnorr is not supported yet');
|
|
88
|
-
}
|
|
91
|
+
}
|
|
89
92
|
await app.close();
|
|
90
93
|
});
|
|
91
|
-
|
|
94
|
+
async function transferToAddress(address, amount = web3_1.ONE_ALPH * 10n) {
|
|
95
|
+
const balance0 = await getALPHBalance(address);
|
|
96
|
+
const fromAccount = await (0, web3_test_1.getSigner)();
|
|
97
|
+
const transferResult = await (0, web3_test_1.transfer)(fromAccount, address, web3_1.ALPH_TOKEN_ID, amount);
|
|
98
|
+
await (0, web3_1.waitForTxConfirmation)(transferResult.txId, 1, 1000);
|
|
99
|
+
const balance1 = await getALPHBalance(address);
|
|
100
|
+
expect(balance1 - balance0).toEqual(amount);
|
|
101
|
+
}
|
|
102
|
+
async function getALPHBalance(address) {
|
|
103
|
+
const balances = await nodeProvider.addresses.getAddressesAddressBalance(address);
|
|
104
|
+
return BigInt(balances.balance);
|
|
105
|
+
}
|
|
106
|
+
async function clickAndApprove(times) {
|
|
107
|
+
for (let i = 0; i < times; i++) {
|
|
108
|
+
await pressButton('right');
|
|
109
|
+
}
|
|
110
|
+
await pressButton('both');
|
|
111
|
+
}
|
|
112
|
+
it('should transfer alph to p2pkh address', async () => {
|
|
92
113
|
const transport = await hw_transport_node_speculos_1.default.open({ apduPort });
|
|
93
114
|
const app = new src_1.default(transport);
|
|
94
|
-
const [
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
const [testAccount] = await app.getAccount(path);
|
|
116
|
+
await transferToAddress(testAccount.address);
|
|
117
|
+
const buildTxResult = await nodeProvider.transactions.postTransactionsBuild({
|
|
118
|
+
fromPublicKey: testAccount.publicKey,
|
|
119
|
+
destinations: [
|
|
120
|
+
{
|
|
121
|
+
address: '1BmVCLrjttchZMW7i6df7mTdCKzHpy38bgDbVL1GqV6P7',
|
|
122
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 2n).toString(),
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
address: '1BmVCLrjttchZMW7i6df7mTdCKzHpy38bgDbVL1GqV6P7',
|
|
126
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 3n).toString(),
|
|
127
|
+
},
|
|
128
|
+
]
|
|
129
|
+
});
|
|
130
|
+
function approve(index) {
|
|
131
|
+
if (index >= 7)
|
|
132
|
+
return;
|
|
133
|
+
if (index >= 3) { // outputs and signature
|
|
134
|
+
setTimeout(async () => {
|
|
135
|
+
await clickAndApprove(6);
|
|
136
|
+
approve(index + 1);
|
|
137
|
+
}, 1000);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
setTimeout(async () => {
|
|
141
|
+
await clickAndApprove(3);
|
|
142
|
+
approve(index + 1);
|
|
143
|
+
}, 1000);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
approve(0);
|
|
147
|
+
const signature = await app.signUnsignedTx(path, Buffer.from(buildTxResult.unsignedTx, 'hex'));
|
|
148
|
+
expect((0, web3_1.transactionVerifySignature)(buildTxResult.txId, testAccount.publicKey, signature)).toBe(true);
|
|
149
|
+
const submitResult = await nodeProvider.transactions.postTransactionsSubmit({
|
|
150
|
+
unsignedTx: buildTxResult.unsignedTx,
|
|
151
|
+
signature: signature
|
|
152
|
+
});
|
|
153
|
+
await (0, web3_1.waitForTxConfirmation)(submitResult.txId, 1, 1000);
|
|
154
|
+
const balance1 = await getALPHBalance(testAccount.address);
|
|
155
|
+
expect(balance1 < (web3_1.ONE_ALPH * 5n)).toEqual(true);
|
|
105
156
|
await app.close();
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
it('should reject signing', async () => {
|
|
157
|
+
}, 120000);
|
|
158
|
+
it('should transfer alph to multisig address', async () => {
|
|
109
159
|
const transport = await hw_transport_node_speculos_1.default.open({ apduPort });
|
|
110
160
|
const app = new src_1.default(transport);
|
|
111
|
-
const [
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
161
|
+
const [testAccount] = await app.getAccount(path);
|
|
162
|
+
await transferToAddress(testAccount.address);
|
|
163
|
+
const multiSigAddress = 'X3KYVteDjsKuUP1F68Nv9iEUecnnkMuwjbC985AnA6MvciDFJ5bAUEso2Sd7sGrwZ5rfNLj7Rp4n9XjcyzDiZsrPxfhNkPYcDm3ce8pQ9QasNFByEufMi3QJ3cS9Vk6cTpqNcq';
|
|
164
|
+
const buildTxResult = await nodeProvider.transactions.postTransactionsBuild({
|
|
165
|
+
fromPublicKey: testAccount.publicKey,
|
|
166
|
+
destinations: [
|
|
167
|
+
{
|
|
168
|
+
address: multiSigAddress,
|
|
169
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 2n).toString(),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
address: multiSigAddress,
|
|
173
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 3n).toString(),
|
|
174
|
+
},
|
|
175
|
+
]
|
|
176
|
+
});
|
|
177
|
+
function approve(index) {
|
|
178
|
+
if (index >= 7)
|
|
179
|
+
return;
|
|
180
|
+
if (index == 3 || index == 4) { // multi-sig outputs
|
|
181
|
+
setTimeout(async () => {
|
|
182
|
+
await clickAndApprove(11);
|
|
183
|
+
approve(index + 1);
|
|
184
|
+
}, 1000);
|
|
185
|
+
}
|
|
186
|
+
else if (index > 4) { // change output and signature
|
|
187
|
+
setTimeout(async () => {
|
|
188
|
+
await clickAndApprove(6);
|
|
189
|
+
approve(index + 1);
|
|
190
|
+
}, 1000);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
setTimeout(async () => {
|
|
194
|
+
await clickAndApprove(3);
|
|
195
|
+
approve(index + 1);
|
|
196
|
+
}, 1000);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
approve(0);
|
|
200
|
+
const signature = await app.signUnsignedTx(path, Buffer.from(buildTxResult.unsignedTx, 'hex'));
|
|
201
|
+
expect((0, web3_1.transactionVerifySignature)(buildTxResult.txId, testAccount.publicKey, signature)).toBe(true);
|
|
202
|
+
const submitResult = await nodeProvider.transactions.postTransactionsSubmit({
|
|
203
|
+
unsignedTx: buildTxResult.unsignedTx,
|
|
204
|
+
signature: signature
|
|
205
|
+
});
|
|
206
|
+
await (0, web3_1.waitForTxConfirmation)(submitResult.txId, 1, 1000);
|
|
207
|
+
const balance1 = await getALPHBalance(testAccount.address);
|
|
208
|
+
expect(balance1 < (web3_1.ONE_ALPH * 5n)).toEqual(true);
|
|
209
|
+
await app.close();
|
|
210
|
+
}, 120000);
|
|
211
|
+
it('should transfer token to multisig address', async () => {
|
|
212
|
+
const transport = await hw_transport_node_speculos_1.default.open({ apduPort });
|
|
213
|
+
const app = new src_1.default(transport);
|
|
214
|
+
const [testAccount] = await app.getAccount(path);
|
|
215
|
+
await transferToAddress(testAccount.address);
|
|
216
|
+
const tokenInfo = await (0, web3_test_1.mintToken)(testAccount.address, 2222222222222222222222222n);
|
|
217
|
+
const multiSigAddress = 'X3KYVteDjsKuUP1F68Nv9iEUecnnkMuwjbC985AnA6MvciDFJ5bAUEso2Sd7sGrwZ5rfNLj7Rp4n9XjcyzDiZsrPxfhNkPYcDm3ce8pQ9QasNFByEufMi3QJ3cS9Vk6cTpqNcq';
|
|
218
|
+
const buildTxResult = await nodeProvider.transactions.postTransactionsBuild({
|
|
219
|
+
fromPublicKey: testAccount.publicKey,
|
|
220
|
+
destinations: [
|
|
221
|
+
{
|
|
222
|
+
address: multiSigAddress,
|
|
223
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 5n).toString(),
|
|
224
|
+
tokens: [
|
|
225
|
+
{
|
|
226
|
+
id: tokenInfo.contractId,
|
|
227
|
+
amount: "1111111111111111111111111",
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
});
|
|
233
|
+
function approve(index) {
|
|
234
|
+
if (index > 7)
|
|
235
|
+
return;
|
|
236
|
+
if (index <= 2) {
|
|
237
|
+
setTimeout(async () => {
|
|
238
|
+
await clickAndApprove(3);
|
|
239
|
+
approve(index + 1);
|
|
240
|
+
}, 1000);
|
|
241
|
+
}
|
|
242
|
+
else if (index === 3) { // multi-sig token output
|
|
243
|
+
setTimeout(async () => {
|
|
244
|
+
await clickAndApprove(17);
|
|
245
|
+
approve(index + 1);
|
|
246
|
+
}, 1000);
|
|
247
|
+
}
|
|
248
|
+
else if (index === 4) { // multi-sig alph output
|
|
249
|
+
setTimeout(async () => {
|
|
250
|
+
await clickAndApprove(11);
|
|
251
|
+
approve(index + 1);
|
|
252
|
+
}, 1000);
|
|
253
|
+
}
|
|
254
|
+
else if (index === 5) { // token change output
|
|
255
|
+
setTimeout(async () => {
|
|
256
|
+
await clickAndApprove(12);
|
|
257
|
+
approve(index + 1);
|
|
258
|
+
}, 1000);
|
|
259
|
+
}
|
|
260
|
+
else if (index >= 6) { // alph change output and signature
|
|
261
|
+
setTimeout(async () => {
|
|
262
|
+
await clickAndApprove(6);
|
|
263
|
+
approve(index + 1);
|
|
264
|
+
}, 1000);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
approve(0);
|
|
268
|
+
const signature = await app.signUnsignedTx(path, Buffer.from(buildTxResult.unsignedTx, 'hex'));
|
|
269
|
+
expect((0, web3_1.transactionVerifySignature)(buildTxResult.txId, testAccount.publicKey, signature)).toBe(true);
|
|
270
|
+
const submitResult = await nodeProvider.transactions.postTransactionsSubmit({
|
|
271
|
+
unsignedTx: buildTxResult.unsignedTx,
|
|
272
|
+
signature: signature
|
|
273
|
+
});
|
|
274
|
+
await (0, web3_1.waitForTxConfirmation)(submitResult.txId, 1, 1000);
|
|
275
|
+
const balances = await nodeProvider.addresses.getAddressesAddressBalance(testAccount.address);
|
|
276
|
+
const alphBalance = BigInt(balances.balance);
|
|
277
|
+
expect(alphBalance < (web3_1.ONE_ALPH * 5n)).toEqual(true);
|
|
278
|
+
expect(balances.tokenBalances.length).toEqual(1);
|
|
279
|
+
const token = balances.tokenBalances[0];
|
|
280
|
+
expect(token.id).toEqual(tokenInfo.contractId);
|
|
281
|
+
expect(token.amount).toEqual('1111111111111111111111111');
|
|
282
|
+
await app.close();
|
|
283
|
+
}, 120000);
|
|
284
|
+
it('should transfer from multiple inputs', async () => {
|
|
285
|
+
const transport = await hw_transport_node_speculos_1.default.open({ apduPort });
|
|
286
|
+
const app = new src_1.default(transport);
|
|
287
|
+
const [testAccount] = await app.getAccount(path);
|
|
288
|
+
for (let i = 0; i < 20; i += 1) {
|
|
289
|
+
await transferToAddress(testAccount.address, web3_1.ONE_ALPH);
|
|
290
|
+
}
|
|
291
|
+
const buildTxResult = await nodeProvider.transactions.postTransactionsBuild({
|
|
292
|
+
fromPublicKey: testAccount.publicKey,
|
|
293
|
+
destinations: [
|
|
294
|
+
{
|
|
295
|
+
address: '1BmVCLrjttchZMW7i6df7mTdCKzHpy38bgDbVL1GqV6P7',
|
|
296
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 19n).toString(),
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
});
|
|
300
|
+
function approve(index) {
|
|
301
|
+
if (index >= 6)
|
|
302
|
+
return;
|
|
303
|
+
if (index >= 3) { // outputs and signature
|
|
304
|
+
setTimeout(async () => {
|
|
305
|
+
await clickAndApprove(6);
|
|
306
|
+
approve(index + 1);
|
|
307
|
+
}, 1000);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
setTimeout(async () => {
|
|
311
|
+
await clickAndApprove(3);
|
|
312
|
+
approve(index + 1);
|
|
313
|
+
}, 1000);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
approve(0);
|
|
317
|
+
const signature = await app.signUnsignedTx(path, Buffer.from(buildTxResult.unsignedTx, 'hex'));
|
|
318
|
+
expect((0, web3_1.transactionVerifySignature)(buildTxResult.txId, testAccount.publicKey, signature)).toBe(true);
|
|
319
|
+
const submitResult = await nodeProvider.transactions.postTransactionsSubmit({
|
|
320
|
+
unsignedTx: buildTxResult.unsignedTx,
|
|
321
|
+
signature: signature
|
|
322
|
+
});
|
|
323
|
+
await (0, web3_1.waitForTxConfirmation)(submitResult.txId, 1, 1000);
|
|
324
|
+
const balance = await getALPHBalance(testAccount.address);
|
|
325
|
+
expect(balance < (web3_1.ONE_ALPH * 1n)).toEqual(true);
|
|
326
|
+
await app.close();
|
|
327
|
+
}, 120000);
|
|
328
|
+
function getAccount(groupIndex) {
|
|
329
|
+
const useDefaultKeyType = Math.random() >= 0.5;
|
|
330
|
+
if (useDefaultKeyType) {
|
|
331
|
+
const account = web3_wallet_1.PrivateKeyWallet.Random(groupIndex);
|
|
332
|
+
return { account, unlockScript: '00' + account.publicKey };
|
|
333
|
+
}
|
|
334
|
+
const account = web3_wallet_1.PrivateKeyWallet.Random(groupIndex, nodeProvider, 'bip340-schnorr');
|
|
335
|
+
const unlockScript = '02' + `0101000000000458144020${account.publicKey}8685` + '00';
|
|
336
|
+
return { account, unlockScript };
|
|
337
|
+
}
|
|
338
|
+
it('should transfer from different input addresses', async () => {
|
|
339
|
+
const transport = await hw_transport_node_speculos_1.default.open({ apduPort });
|
|
340
|
+
const app = new src_1.default(transport);
|
|
341
|
+
const [testAccount] = await app.getAccount(path);
|
|
342
|
+
const { account: newAccount, unlockScript: unlockScript0 } = getAccount(testAccount.group);
|
|
343
|
+
for (let i = 0; i < 2; i += 1) {
|
|
344
|
+
await transferToAddress(testAccount.address, web3_1.ONE_ALPH);
|
|
345
|
+
await transferToAddress(newAccount.address, web3_1.ONE_ALPH);
|
|
346
|
+
}
|
|
347
|
+
const utxos0 = await nodeProvider.addresses.getAddressesAddressUtxos(newAccount.address);
|
|
348
|
+
expect(utxos0.utxos.length).toEqual(2);
|
|
349
|
+
const utxos1 = await nodeProvider.addresses.getAddressesAddressUtxos(testAccount.address);
|
|
350
|
+
expect(utxos1.utxos.length).toEqual(2);
|
|
351
|
+
const useSameAsPrevious = Math.random() >= 0.5;
|
|
352
|
+
const inputs0 = utxos0.utxos.map((utxo, index) => {
|
|
353
|
+
const unlockScript = index > 0 && useSameAsPrevious ? '03' : unlockScript0;
|
|
354
|
+
return { outputRef: utxo.ref, unlockScript };
|
|
355
|
+
});
|
|
356
|
+
const unlockScript1 = '00' + testAccount.publicKey;
|
|
357
|
+
const inputs1 = utxos1.utxos.map((utxo, index) => {
|
|
358
|
+
const unlockScript = index > 0 && useSameAsPrevious ? '03' : unlockScript1;
|
|
359
|
+
return { outputRef: utxo.ref, unlockScript };
|
|
360
|
+
});
|
|
361
|
+
const unsignedTx = {
|
|
362
|
+
txId: '',
|
|
363
|
+
version: 0,
|
|
364
|
+
networkId: 4,
|
|
365
|
+
gasAmount: 100000,
|
|
366
|
+
gasPrice: (web3_1.ONE_ALPH / 100000n).toString(),
|
|
367
|
+
inputs: inputs0.concat(inputs1),
|
|
368
|
+
fixedOutputs: [{
|
|
369
|
+
hint: 0,
|
|
370
|
+
key: '',
|
|
371
|
+
attoAlphAmount: (web3_1.ONE_ALPH * 3n).toString(),
|
|
372
|
+
address: testAccount.address,
|
|
373
|
+
tokens: [],
|
|
374
|
+
lockTime: 0,
|
|
375
|
+
message: ''
|
|
376
|
+
}]
|
|
377
|
+
};
|
|
378
|
+
const txBytes = web3_1.codec.unsignedTxCodec.encodeApiUnsignedTx(unsignedTx);
|
|
379
|
+
const signResult0 = await newAccount.signUnsignedTx({
|
|
380
|
+
signerAddress: newAccount.address,
|
|
381
|
+
unsignedTx: (0, web3_1.binToHex)(txBytes)
|
|
382
|
+
});
|
|
383
|
+
function approve(index) {
|
|
384
|
+
if (index > 6)
|
|
385
|
+
return;
|
|
386
|
+
if (index === 3 || index === 4) { // inputs
|
|
387
|
+
setTimeout(async () => {
|
|
388
|
+
await clickAndApprove(5);
|
|
389
|
+
approve(index + 1);
|
|
390
|
+
}, 1000);
|
|
391
|
+
}
|
|
392
|
+
else if (index >= 5) { // outputs and tx id
|
|
393
|
+
setTimeout(async () => {
|
|
394
|
+
await clickAndApprove(6);
|
|
395
|
+
approve(index + 1);
|
|
396
|
+
}, 1000);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
setTimeout(async () => {
|
|
400
|
+
await clickAndApprove(3);
|
|
401
|
+
approve(index + 1);
|
|
402
|
+
}, 1000);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
approve(0);
|
|
406
|
+
const signature1 = await app.signUnsignedTx(path, Buffer.from(txBytes));
|
|
407
|
+
expect((0, web3_1.transactionVerifySignature)(signResult0.txId, testAccount.publicKey, signature1)).toBe(true);
|
|
408
|
+
const submitResult = await nodeProvider.multisig.postMultisigSubmit({
|
|
409
|
+
unsignedTx: (0, web3_1.binToHex)(txBytes),
|
|
410
|
+
signatures: [signResult0.signature, signature1]
|
|
411
|
+
});
|
|
412
|
+
await (0, web3_1.waitForTxConfirmation)(submitResult.txId, 1, 1000);
|
|
413
|
+
const balance = await getALPHBalance(testAccount.address);
|
|
414
|
+
expect(balance).toEqual(web3_1.ONE_ALPH * 3n);
|
|
121
415
|
await app.close();
|
|
122
|
-
},
|
|
416
|
+
}, 120000);
|
|
123
417
|
});
|