@shapeshiftoss/hdwallet-vultisig 1.62.5
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/LICENSE.md +21 -0
- package/dist/adapter.d.ts +22 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +84 -0
- package/dist/adapter.js.map +1 -0
- package/dist/bitcoin.d.ts +6 -0
- package/dist/bitcoin.d.ts.map +1 -0
- package/dist/bitcoin.js +184 -0
- package/dist/bitcoin.js.map +1 -0
- package/dist/cosmos.d.ts +6 -0
- package/dist/cosmos.d.ts.map +1 -0
- package/dist/cosmos.js +47 -0
- package/dist/cosmos.js.map +1 -0
- package/dist/ethereum.d.ts +12 -0
- package/dist/ethereum.d.ts.map +1 -0
- package/dist/ethereum.js +193 -0
- package/dist/ethereum.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/solana.d.ts +9 -0
- package/dist/solana.d.ts.map +1 -0
- package/dist/solana.js +55 -0
- package/dist/solana.js.map +1 -0
- package/dist/thorchain.d.ts +6 -0
- package/dist/thorchain.d.ts.map +1 -0
- package/dist/thorchain.js +46 -0
- package/dist/thorchain.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/vultisig.d.ts +123 -0
- package/dist/vultisig.d.ts.map +1 -0
- package/dist/vultisig.js +482 -0
- package/dist/vultisig.js.map +1 -0
- package/package.json +31 -0
- package/src/adapter.ts +72 -0
- package/src/bitcoin.ts +171 -0
- package/src/cosmos.ts +43 -0
- package/src/ethereum.ts +169 -0
- package/src/index.ts +2 -0
- package/src/solana.ts +31 -0
- package/src/thorchain.ts +43 -0
- package/src/types.ts +62 -0
- package/src/vultisig.test.ts +253 -0
- package/src/vultisig.ts +459 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
package/src/bitcoin.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import * as bitcoin from "@shapeshiftoss/bitcoinjs-lib";
|
|
2
|
+
import * as core from "@shapeshiftoss/hdwallet-core";
|
|
3
|
+
import { BTCInputScriptType } from "@shapeshiftoss/hdwallet-core";
|
|
4
|
+
|
|
5
|
+
import { VultisigUtxoProvider } from "./types";
|
|
6
|
+
|
|
7
|
+
const getNetwork = (coin: string): bitcoin.networks.Network => {
|
|
8
|
+
switch (coin.toLowerCase()) {
|
|
9
|
+
case "bitcoin":
|
|
10
|
+
return bitcoin.networks.bitcoin;
|
|
11
|
+
default:
|
|
12
|
+
throw new Error(`Unsupported coin: ${coin}`);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function btcGetAddress(provider: VultisigUtxoProvider, msg: core.BTCGetAddress): Promise<string | null> {
|
|
17
|
+
const value = await (async () => {
|
|
18
|
+
switch (msg.coin.toLowerCase()) {
|
|
19
|
+
case "bitcoin": {
|
|
20
|
+
const accounts = await provider.request<"request_accounts">({
|
|
21
|
+
method: "request_accounts",
|
|
22
|
+
params: [],
|
|
23
|
+
});
|
|
24
|
+
return accounts.length > 0 ? accounts[0] : null;
|
|
25
|
+
}
|
|
26
|
+
default:
|
|
27
|
+
throw new Error("Vultisig does not support");
|
|
28
|
+
}
|
|
29
|
+
})();
|
|
30
|
+
if (!value || typeof value !== "string") return null;
|
|
31
|
+
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const btcGetAccountPaths = (msg: core.BTCGetAccountPaths): Array<core.BTCAccountPath> => {
|
|
36
|
+
const slip44 = core.slip44ByCoin(msg.coin);
|
|
37
|
+
if (slip44 === undefined) return [];
|
|
38
|
+
|
|
39
|
+
const bip84 = core.segwitNativeAccount(msg.coin, slip44, msg.accountIdx);
|
|
40
|
+
|
|
41
|
+
const coinPaths = {
|
|
42
|
+
bitcoin: [bip84],
|
|
43
|
+
} as Partial<Record<string, Array<core.BTCAccountPath>>>;
|
|
44
|
+
|
|
45
|
+
let paths: Array<core.BTCAccountPath> = coinPaths[msg.coin.toLowerCase()] || [];
|
|
46
|
+
|
|
47
|
+
if (msg.scriptType !== undefined) {
|
|
48
|
+
paths = paths.filter((path) => {
|
|
49
|
+
return path.scriptType === msg.scriptType;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return paths;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
async function addInput(psbt: bitcoin.Psbt, input: core.BTCSignTxInput): Promise<void> {
|
|
57
|
+
switch (input.scriptType) {
|
|
58
|
+
case BTCInputScriptType.SpendWitness: {
|
|
59
|
+
psbt.addInput({
|
|
60
|
+
hash: input.txid,
|
|
61
|
+
index: input.vout,
|
|
62
|
+
nonWitnessUtxo: Buffer.from(input.hex, "hex"),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
throw new Error(`Unsupported script type: ${input.scriptType}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function addOutput(
|
|
73
|
+
provider: VultisigUtxoProvider,
|
|
74
|
+
psbt: bitcoin.Psbt,
|
|
75
|
+
output: core.BTCSignTxOutput,
|
|
76
|
+
coin: string
|
|
77
|
+
): Promise<void> {
|
|
78
|
+
if (!output.amount) throw new Error("Invalid output - missing amount.");
|
|
79
|
+
|
|
80
|
+
const address = await (async () => {
|
|
81
|
+
if (output.address) return output.address;
|
|
82
|
+
|
|
83
|
+
if (output.addressNList) {
|
|
84
|
+
const outputAddress = await btcGetAddress(provider, {
|
|
85
|
+
addressNList: output.addressNList,
|
|
86
|
+
coin,
|
|
87
|
+
showDisplay: false,
|
|
88
|
+
});
|
|
89
|
+
if (!outputAddress) throw new Error("Could not get address from wallet");
|
|
90
|
+
return outputAddress;
|
|
91
|
+
}
|
|
92
|
+
})();
|
|
93
|
+
|
|
94
|
+
if (!address) throw new Error("Invalid output - no address");
|
|
95
|
+
|
|
96
|
+
psbt.addOutput({ address, value: BigInt(output.amount) });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function bitcoinSignTx(
|
|
100
|
+
msg: core.BTCSignTx,
|
|
101
|
+
provider: VultisigUtxoProvider
|
|
102
|
+
): Promise<core.BTCSignedTx | null> {
|
|
103
|
+
try {
|
|
104
|
+
const network = getNetwork(msg.coin);
|
|
105
|
+
const psbt = new bitcoin.Psbt({ network });
|
|
106
|
+
|
|
107
|
+
psbt.setVersion(msg.version ?? 2);
|
|
108
|
+
if (msg.locktime) {
|
|
109
|
+
psbt.setLocktime(msg.locktime);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const input of msg.inputs) {
|
|
113
|
+
await addInput(psbt, input);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const output of msg.outputs) {
|
|
117
|
+
await addOutput(provider, psbt, output, msg.coin);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (msg.opReturnData) {
|
|
121
|
+
const data = Buffer.from(msg.opReturnData, "utf-8");
|
|
122
|
+
const embed = bitcoin.payments.embed({ data: [data] });
|
|
123
|
+
const script = embed.output;
|
|
124
|
+
if (!script) throw new Error("unable to build OP_RETURN script");
|
|
125
|
+
// OP_RETURN_DATA output is always 0 value
|
|
126
|
+
psbt.addOutput({ script, value: BigInt(0) });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const inputsToSign = await Promise.all(
|
|
130
|
+
msg.inputs.map(async (input, index) => {
|
|
131
|
+
const address = await btcGetAddress(provider, {
|
|
132
|
+
addressNList: input.addressNList,
|
|
133
|
+
coin: msg.coin,
|
|
134
|
+
showDisplay: false,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (!address) throw new Error("Could not get address from wallet");
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
address,
|
|
141
|
+
signingIndexes: [index],
|
|
142
|
+
sigHash: bitcoin.Transaction.SIGHASH_ALL,
|
|
143
|
+
};
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const signedPsbtBuffer = await provider.signPSBT(psbt.toBuffer(), { inputsToSign }, false);
|
|
148
|
+
const signedPsbt = bitcoin.Psbt.fromBuffer(signedPsbtBuffer, { network });
|
|
149
|
+
|
|
150
|
+
signedPsbt.finalizeAllInputs();
|
|
151
|
+
|
|
152
|
+
const tx = signedPsbt.extractTransaction();
|
|
153
|
+
|
|
154
|
+
// If this is a THORChain transaction, validate the vout ordering
|
|
155
|
+
if (msg.vaultAddress && !core.validateVoutOrdering(msg, tx)) {
|
|
156
|
+
throw new Error("Improper vout ordering for BTC Thorchain transaction");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const signatures = signedPsbt.data.inputs.map((input) =>
|
|
160
|
+
input.partialSig ? Buffer.from(input.partialSig[0].signature).toString("hex") : ""
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
signatures,
|
|
165
|
+
serializedTx: tx.toHex(),
|
|
166
|
+
};
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error("Error signing with Vultisig:", error);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
package/src/cosmos.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { StdTx } from "@cosmjs/amino";
|
|
2
|
+
import { SignerData } from "@cosmjs/stargate";
|
|
3
|
+
import {
|
|
4
|
+
CosmosAccountPath,
|
|
5
|
+
CosmosGetAccountPaths,
|
|
6
|
+
CosmosSignedTx,
|
|
7
|
+
CosmosSignTx,
|
|
8
|
+
slip44ByCoin,
|
|
9
|
+
} from "@shapeshiftoss/hdwallet-core";
|
|
10
|
+
import { sign } from "@shapeshiftoss/proto-tx-builder";
|
|
11
|
+
|
|
12
|
+
import { VultisigOfflineProvider } from "./types";
|
|
13
|
+
|
|
14
|
+
const ATOM_CHAIN = "cosmoshub-4";
|
|
15
|
+
|
|
16
|
+
export function cosmosGetAccountPaths(msg: CosmosGetAccountPaths): Array<CosmosAccountPath> {
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
addressNList: [0x80000000 + 44, 0x80000000 + slip44ByCoin("Atom"), 0x80000000 + msg.accountIdx, 0, 0],
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function cosmosGetAddress(provider: VultisigOfflineProvider): Promise<string | undefined> {
|
|
25
|
+
const offlineSigner = provider.getOfflineSigner(ATOM_CHAIN);
|
|
26
|
+
const accounts = await offlineSigner.getAccounts();
|
|
27
|
+
return accounts[0]?.address;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function cosmosSignTx(provider: VultisigOfflineProvider, msg: CosmosSignTx): Promise<CosmosSignedTx> {
|
|
31
|
+
const offlineSigner = provider.getOfflineSigner(ATOM_CHAIN);
|
|
32
|
+
|
|
33
|
+
const address = await cosmosGetAddress(provider);
|
|
34
|
+
if (!address) throw new Error("failed to get address");
|
|
35
|
+
|
|
36
|
+
const signerData: SignerData = {
|
|
37
|
+
sequence: Number(msg.sequence),
|
|
38
|
+
accountNumber: Number(msg.account_number),
|
|
39
|
+
chainId: msg.chain_id,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return await sign(address, msg.tx as StdTx, offlineSigner, signerData, "cosmos");
|
|
43
|
+
}
|
package/src/ethereum.ts
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import * as core from "@shapeshiftoss/hdwallet-core";
|
|
2
|
+
import { ETHSignedMessage } from "@shapeshiftoss/hdwallet-core";
|
|
3
|
+
import { AddEthereumChainParameter } from "@shapeshiftoss/hdwallet-core";
|
|
4
|
+
import { ethErrors, serializeError } from "eth-rpc-errors";
|
|
5
|
+
import { isHexString } from "ethers/lib/utils";
|
|
6
|
+
|
|
7
|
+
import { VultisigEvmProvider } from "./types";
|
|
8
|
+
|
|
9
|
+
export function ethGetAccountPaths(msg: core.ETHGetAccountPath): Array<core.ETHAccountPath> {
|
|
10
|
+
const slip44 = core.slip44ByCoin(msg.coin);
|
|
11
|
+
if (slip44 === undefined) return [];
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0],
|
|
15
|
+
hardenedPath: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx],
|
|
16
|
+
relPath: [0, 0],
|
|
17
|
+
description: "Vultisig",
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function ethSendTx(
|
|
23
|
+
msg: core.ETHSignTx,
|
|
24
|
+
vultisig: VultisigEvmProvider,
|
|
25
|
+
from: string
|
|
26
|
+
): Promise<core.ETHTxHash | null> {
|
|
27
|
+
try {
|
|
28
|
+
const utxBase = {
|
|
29
|
+
from: from,
|
|
30
|
+
to: msg.to,
|
|
31
|
+
value: msg.value,
|
|
32
|
+
nonce: msg.nonce,
|
|
33
|
+
chainId: msg.chainId,
|
|
34
|
+
data: msg.data,
|
|
35
|
+
gas: msg.gasLimit,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const utx = msg.maxFeePerGas
|
|
39
|
+
? {
|
|
40
|
+
...utxBase,
|
|
41
|
+
maxFeePerGas: msg.maxFeePerGas,
|
|
42
|
+
maxPriorityFeePerGas: msg.maxPriorityFeePerGas,
|
|
43
|
+
}
|
|
44
|
+
: { ...utxBase, gasPrice: msg.gasPrice };
|
|
45
|
+
|
|
46
|
+
const signedTx = await vultisig.request?.({
|
|
47
|
+
method: "eth_sendTransaction",
|
|
48
|
+
params: [utx],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return { hash: signedTx } as core.ETHTxHash;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error(error);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function ethSignMessage(
|
|
59
|
+
msg: core.ETHSignMessage,
|
|
60
|
+
vultisig: VultisigEvmProvider,
|
|
61
|
+
address: string
|
|
62
|
+
): Promise<core.ETHSignedMessage | null> {
|
|
63
|
+
try {
|
|
64
|
+
if (!isHexString(msg.message)) throw new Error("data is not an hex string");
|
|
65
|
+
const signedMsg = await vultisig.request?.({
|
|
66
|
+
method: "personal_sign",
|
|
67
|
+
params: [msg.message, address],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
address: address,
|
|
72
|
+
signature: signedMsg,
|
|
73
|
+
} as ETHSignedMessage;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(error);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function ethSignTypedData(
|
|
81
|
+
msg: core.ETHSignTypedData,
|
|
82
|
+
vultisig: VultisigEvmProvider,
|
|
83
|
+
address: string
|
|
84
|
+
): Promise<core.ETHSignedMessage | null> {
|
|
85
|
+
try {
|
|
86
|
+
const signedMsg = await vultisig.request?.({
|
|
87
|
+
method: "eth_signTypedData_v4",
|
|
88
|
+
params: [address, JSON.stringify(msg.typedData)],
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
address: address,
|
|
93
|
+
signature: signedMsg,
|
|
94
|
+
} as ETHSignedMessage;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(error);
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function ethGetAddress(vultisig: VultisigEvmProvider): Promise<core.Address | null> {
|
|
102
|
+
if (!(vultisig && vultisig.request)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const ethAccounts = await vultisig.request({
|
|
107
|
+
method: "eth_accounts",
|
|
108
|
+
});
|
|
109
|
+
return ethAccounts[0];
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(error);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export async function ethGetChainId(evmProvider: VultisigEvmProvider): Promise<number | null> {
|
|
116
|
+
try {
|
|
117
|
+
// chainId as hex string
|
|
118
|
+
const chainId: string = (await evmProvider?.request?.({ method: "eth_chainId" })) || "";
|
|
119
|
+
return parseInt(chainId, 16);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.error(e);
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function ethAddChain(evmProvider: VultisigEvmProvider, params: AddEthereumChainParameter): Promise<void> {
|
|
127
|
+
// at this point, we know that we're in the context of a valid Coinbase provider
|
|
128
|
+
await evmProvider?.request?.({ method: "wallet_addEthereumChain", params: [params] });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function ethSwitchChain(
|
|
132
|
+
evmProvider: VultisigEvmProvider,
|
|
133
|
+
params: AddEthereumChainParameter
|
|
134
|
+
): Promise<void> {
|
|
135
|
+
try {
|
|
136
|
+
// at this point, we know that we're in the context of a valid Coinbase provider
|
|
137
|
+
await evmProvider?.request?.({ method: "wallet_switchEthereumChain", params: [{ chainId: params.chainId }] });
|
|
138
|
+
} catch (e: any) {
|
|
139
|
+
const error = serializeError(e);
|
|
140
|
+
if (error.code === -32603) {
|
|
141
|
+
try {
|
|
142
|
+
await ethAddChain(evmProvider, params);
|
|
143
|
+
return;
|
|
144
|
+
} catch (addChainE: any) {
|
|
145
|
+
const addChainError = serializeError(addChainE);
|
|
146
|
+
|
|
147
|
+
if (addChainError.code === 4001) {
|
|
148
|
+
throw ethErrors.provider.userRejectedRequest();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
throw (addChainError.data as any).originalError as {
|
|
152
|
+
code: number;
|
|
153
|
+
message: string;
|
|
154
|
+
stack: string;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (error.code === 4001) {
|
|
160
|
+
throw ethErrors.provider.userRejectedRequest();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
throw (error.data as any).originalError as {
|
|
164
|
+
code: number;
|
|
165
|
+
message: string;
|
|
166
|
+
stack: string;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
package/src/index.ts
ADDED
package/src/solana.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as core from "@shapeshiftoss/hdwallet-core";
|
|
2
|
+
import { PublicKey } from "@solana/web3.js";
|
|
3
|
+
|
|
4
|
+
import { VultisigSolanaProvider } from "./types";
|
|
5
|
+
|
|
6
|
+
export type SolanaAccount = {
|
|
7
|
+
publicKey: PublicKey;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export async function solanaSignTx(
|
|
11
|
+
msg: core.SolanaSignTx,
|
|
12
|
+
provider: VultisigSolanaProvider,
|
|
13
|
+
address: string
|
|
14
|
+
): Promise<core.SolanaSignedTx | null> {
|
|
15
|
+
const transaction = core.solanaBuildTransaction(msg, address);
|
|
16
|
+
const signedTransaction = await provider.signTransaction(transaction);
|
|
17
|
+
return {
|
|
18
|
+
serialized: Buffer.from(signedTransaction.serialize()).toString("base64"),
|
|
19
|
+
signatures: signedTransaction.signatures.map((signature) => Buffer.from(signature).toString("base64")),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function solanaSendTx(
|
|
24
|
+
msg: core.SolanaSignTx,
|
|
25
|
+
provider: VultisigSolanaProvider,
|
|
26
|
+
address: string
|
|
27
|
+
): Promise<core.SolanaTxSignature | null> {
|
|
28
|
+
const transaction = core.solanaBuildTransaction(msg, address);
|
|
29
|
+
const { signature } = await provider.signAndSendTransaction(transaction);
|
|
30
|
+
return { signature };
|
|
31
|
+
}
|
package/src/thorchain.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { StdTx } from "@cosmjs/amino";
|
|
2
|
+
import { SignerData } from "@cosmjs/stargate";
|
|
3
|
+
import {
|
|
4
|
+
CosmosAccountPath,
|
|
5
|
+
CosmosGetAccountPaths,
|
|
6
|
+
CosmosSignedTx,
|
|
7
|
+
CosmosSignTx,
|
|
8
|
+
slip44ByCoin,
|
|
9
|
+
} from "@shapeshiftoss/hdwallet-core";
|
|
10
|
+
import { sign } from "@shapeshiftoss/proto-tx-builder";
|
|
11
|
+
|
|
12
|
+
import { VultisigOfflineProvider } from "./types";
|
|
13
|
+
|
|
14
|
+
export function thorchainGetAccountPaths(msg: CosmosGetAccountPaths): Array<CosmosAccountPath> {
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
addressNList: [0x80000000 + 44, 0x80000000 + slip44ByCoin("Thorchain"), 0x80000000 + msg.accountIdx, 0, 0],
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function thorchainGetAddress(provider: VultisigOfflineProvider): Promise<string | undefined> {
|
|
23
|
+
const offlineSigner = provider.getOfflineSigner("thorchain-1");
|
|
24
|
+
const accounts = await offlineSigner.getAccounts();
|
|
25
|
+
return accounts[0].address;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function thorchainSignTx(provider: VultisigOfflineProvider, msg: CosmosSignTx): Promise<CosmosSignedTx> {
|
|
29
|
+
const offlineSigner = provider.getOfflineSigner(msg.chain_id);
|
|
30
|
+
|
|
31
|
+
const address = await thorchainGetAddress(provider);
|
|
32
|
+
if (!address) throw new Error("failed to get address");
|
|
33
|
+
|
|
34
|
+
const signerData: SignerData = {
|
|
35
|
+
sequence: Number(msg.sequence),
|
|
36
|
+
accountNumber: Number(msg.account_number),
|
|
37
|
+
chainId: msg.chain_id,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const result = await sign(address, msg.tx as StdTx, offlineSigner, signerData, "thor");
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { OfflineSigner } from "@cosmjs/proto-signing";
|
|
2
|
+
import { PublicKey, VersionedTransaction } from "@solana/web3.js";
|
|
3
|
+
import { TransactionSignature } from "@solana/web3.js";
|
|
4
|
+
import { providers } from "ethers";
|
|
5
|
+
|
|
6
|
+
import { SolanaAccount } from "./solana";
|
|
7
|
+
|
|
8
|
+
export type VultisigRequestParams = {
|
|
9
|
+
get_accounts: [];
|
|
10
|
+
request_accounts: [];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type VultisigRequestReturn = {
|
|
14
|
+
get_accounts: Promise<string[]>;
|
|
15
|
+
request_accounts: Promise<string[]>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type VultisigRequestMethod = keyof VultisigRequestParams;
|
|
19
|
+
|
|
20
|
+
export type VultisigRequestPayload<M extends VultisigRequestMethod> = {
|
|
21
|
+
method: M;
|
|
22
|
+
params: VultisigRequestParams[M];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type VultisigEvmProvider = providers.ExternalProvider;
|
|
26
|
+
|
|
27
|
+
export type VultisigUtxoProvider = {
|
|
28
|
+
request<M extends VultisigRequestMethod>(payload: VultisigRequestPayload<M>): Promise<VultisigRequestReturn[M]>;
|
|
29
|
+
signPSBT(
|
|
30
|
+
psbt: Uint8Array,
|
|
31
|
+
{
|
|
32
|
+
inputsToSign,
|
|
33
|
+
}: {
|
|
34
|
+
inputsToSign: {
|
|
35
|
+
address: string;
|
|
36
|
+
signingIndexes: number[];
|
|
37
|
+
sigHash?: number;
|
|
38
|
+
}[];
|
|
39
|
+
},
|
|
40
|
+
broadcast: boolean
|
|
41
|
+
): Promise<Uint8Array>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type VultisigSolanaProvider = providers.ExternalProvider & {
|
|
45
|
+
publicKey?: PublicKey;
|
|
46
|
+
connect(): Promise<SolanaAccount>;
|
|
47
|
+
signTransaction(transaction: VersionedTransaction): Promise<VersionedTransaction>;
|
|
48
|
+
signAndSendTransaction(transaction: VersionedTransaction): Promise<{ signature: TransactionSignature }>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type VultisigOfflineProvider = providers.ExternalProvider & {
|
|
52
|
+
getOfflineSigner(chainId: string): OfflineSigner;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type VultisigGetVault = {
|
|
56
|
+
hexChainCode: string;
|
|
57
|
+
isFastVault: boolean;
|
|
58
|
+
name: string;
|
|
59
|
+
publicKeyEcdsa: string;
|
|
60
|
+
publicKeyEddsa: string;
|
|
61
|
+
uid: string;
|
|
62
|
+
};
|