@cubist-labs/cubesigner-sdk-viem 0.4.183-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/README.md +51 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +129 -0
- package/package.json +38 -0
- package/src/index.ts +158 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# CubeSigner Plugin for Viem
|
|
2
|
+
|
|
3
|
+
This package exposes a single `CubeSignerSource` class which implements
|
|
4
|
+
a custom source in Viem, backed by CubeSigner.
|
|
5
|
+
|
|
6
|
+
## Simple example usage
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { type CustomSource, createWalletClient, http, publicActions } from "viem";
|
|
10
|
+
import { CubeSignerClient } from "@cubist-labs/cubesigner-sdk";
|
|
11
|
+
import { CubeSignerSource } from "@cubist-labs/cubesigner-sdk-viem";
|
|
12
|
+
import { sepolia } from "viem/chains";
|
|
13
|
+
import { toAccount } from "viem/accounts";
|
|
14
|
+
|
|
15
|
+
const client = await CubeSignerClient.create(...); // must have permissions to sign with `key`
|
|
16
|
+
const key = ...; // A CubeSigner key object or a string of an address in your org
|
|
17
|
+
const chain = sepolia; // use an object from "viem/chains"
|
|
18
|
+
|
|
19
|
+
const account = toAccount(new CubeSignerSource(key, client) as CustomSource);
|
|
20
|
+
|
|
21
|
+
// Create a WalletClient to perform actions
|
|
22
|
+
const walletClient = createWalletClient({
|
|
23
|
+
account,
|
|
24
|
+
chain,
|
|
25
|
+
transport: http(), // uses Viem's default RPC provider
|
|
26
|
+
}).extend(publicActions);
|
|
27
|
+
|
|
28
|
+
// Sign transaction as usual:
|
|
29
|
+
const tx = await walletClient.prepareTransactionRequest({
|
|
30
|
+
to: "0x96be1e4c198ecb1a55e769f653b1934950294f19",
|
|
31
|
+
value: 0n,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const signature = await walletClient.signTransaction(tx);
|
|
35
|
+
...
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Check out our [Viem example](../../examples/viem/src/index.ts) or the unit tests
|
|
39
|
+
in the `test` folder for more examples.
|
|
40
|
+
|
|
41
|
+
The [@cubist-labs/cubesigner-sdk](https://www.npmjs.com/package/@cubist-labs/cubesigner-sdk) contains more details on how to create signer sessions.
|
|
42
|
+
|
|
43
|
+
## Supported Transactions
|
|
44
|
+
|
|
45
|
+
Please note that CubeSigner only supports legacy or EIP-1559 EVM
|
|
46
|
+
transactions with an explicitly defined `type` field. It's recommended to use
|
|
47
|
+
`prepareTransactionRequest` before signing to fill this field.
|
|
48
|
+
|
|
49
|
+
Transactions must take place on a specific chain---either use a client which
|
|
50
|
+
holds a specific chain, or ensure your transaction has the `chainId` field
|
|
51
|
+
defined.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type Address, type CustomSource } from "viem";
|
|
2
|
+
import { type CubeSignerClient, type EvmSignerOptions, type Key } from "@cubist-labs/cubesigner-sdk";
|
|
3
|
+
/**
|
|
4
|
+
* A class to wrap a CubeSigner key and client into a Viem {@link CustomSource}.
|
|
5
|
+
* Use Viem's `toAccount` to convert this to a Viem Account.
|
|
6
|
+
*/
|
|
7
|
+
export declare class CubeSignerSource implements CustomSource {
|
|
8
|
+
#private;
|
|
9
|
+
/** The address the wallet is associated with. */
|
|
10
|
+
readonly address: Address;
|
|
11
|
+
/**
|
|
12
|
+
* Construct a Viem {@link CustomSource} around a CubeSigner key and client.
|
|
13
|
+
* Use Viem's `toAccount` to convert this to a Viem Account.
|
|
14
|
+
*
|
|
15
|
+
* @param address The EVM address this wallet is associated with
|
|
16
|
+
* @param client The session used for signing. Must have necessary scopes.
|
|
17
|
+
* @param options MFA options for the client to respect
|
|
18
|
+
* @throws {Error} if the address is not a valid EVM address
|
|
19
|
+
*/
|
|
20
|
+
constructor(address: Key | string, client: CubeSignerClient, options?: EvmSignerOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Signs arbitrary messages. This uses CubeSigner's EIP-191 signing endpoint.
|
|
23
|
+
* The key (for this session) must have the `"AllowRawBlobSigning"` or
|
|
24
|
+
* `"AllowEip191Signing"` policy attached.
|
|
25
|
+
*
|
|
26
|
+
* @param root Object around the message
|
|
27
|
+
* @param root.message The message to sign
|
|
28
|
+
* @returns The signature
|
|
29
|
+
*/
|
|
30
|
+
signMessage: CustomSource["signMessage"];
|
|
31
|
+
/**
|
|
32
|
+
* Signs EIP-712 typed data. This uses CubeSigner's EIP-712 signing endpoint.
|
|
33
|
+
* The key (for this session) must have the `"AllowRawBlobSigning"` or
|
|
34
|
+
* `"AllowEip712Signing"` policy attached.
|
|
35
|
+
* `chainId` must be specified within the `domain`.
|
|
36
|
+
*
|
|
37
|
+
* @param parameters Typed data
|
|
38
|
+
* @returns signature for the typed data
|
|
39
|
+
*/
|
|
40
|
+
signTypedData: CustomSource["signTypedData"];
|
|
41
|
+
/**
|
|
42
|
+
* It is recommended to use `prepareTransactionRequest` on your request
|
|
43
|
+
* before calling this function.
|
|
44
|
+
*
|
|
45
|
+
* Sign a transaction. This method will block if the key requires MFA approval.
|
|
46
|
+
* `type` and `chainId` must be defined. Only supports type "legacy" or "eip1559".
|
|
47
|
+
*
|
|
48
|
+
* @param transaction The transaction to sign
|
|
49
|
+
* @param options Contains an optional custom serializer
|
|
50
|
+
* @returns Signed transaction
|
|
51
|
+
* @throws {Error} if transaction.type isn't "legacy" or "eip1559", or if "chainId" isn't specified
|
|
52
|
+
*/
|
|
53
|
+
signTransaction: CustomSource["signTransaction"];
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,YAAY,EAUlB,MAAM,MAAM,CAAC;AACd,OAAO,EACL,KAAK,gBAAgB,EAIrB,KAAK,gBAAgB,EACrB,KAAK,GAAG,EACT,MAAM,6BAA6B,CAAC;AAErC;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,YAAY;;IAInD,iDAAiD;IACjD,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC;;;;;;;;OAQG;gBACS,OAAO,EAAE,GAAG,GAAG,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,gBAAgB;IAYvF;;;;;;;;OAQG;IACI,WAAW,EAAE,YAAY,CAAC,aAAa,CAAC,CAa7C;IAEF;;;;;;;;OAQG;IACI,aAAa,EAAE,YAAY,CAAC,eAAe,CAAC,CAYjD;IAEF;;;;;;;;;;;OAWG;IACI,eAAe,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAsBrD;CACH"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _CubeSignerSource_signer;
|
|
13
|
+
import { bytesToHex, formatTransactionRequest, getAddress, isHex, parseTransaction, serializeTransaction, stringToHex, } from "viem";
|
|
14
|
+
import { EvmSigner, } from "@cubist-labs/cubesigner-sdk";
|
|
15
|
+
/**
|
|
16
|
+
* A class to wrap a CubeSigner key and client into a Viem {@link CustomSource}.
|
|
17
|
+
* Use Viem's `toAccount` to convert this to a Viem Account.
|
|
18
|
+
*/
|
|
19
|
+
export class CubeSignerSource {
|
|
20
|
+
/**
|
|
21
|
+
* Construct a Viem {@link CustomSource} around a CubeSigner key and client.
|
|
22
|
+
* Use Viem's `toAccount` to convert this to a Viem Account.
|
|
23
|
+
*
|
|
24
|
+
* @param address The EVM address this wallet is associated with
|
|
25
|
+
* @param client The session used for signing. Must have necessary scopes.
|
|
26
|
+
* @param options MFA options for the client to respect
|
|
27
|
+
* @throws {Error} if the address is not a valid EVM address
|
|
28
|
+
*/
|
|
29
|
+
constructor(address, client, options) {
|
|
30
|
+
/** The internal CubeSigner signer used. */
|
|
31
|
+
_CubeSignerSource_signer.set(this, void 0);
|
|
32
|
+
/**
|
|
33
|
+
* Signs arbitrary messages. This uses CubeSigner's EIP-191 signing endpoint.
|
|
34
|
+
* The key (for this session) must have the `"AllowRawBlobSigning"` or
|
|
35
|
+
* `"AllowEip191Signing"` policy attached.
|
|
36
|
+
*
|
|
37
|
+
* @param root Object around the message
|
|
38
|
+
* @param root.message The message to sign
|
|
39
|
+
* @returns The signature
|
|
40
|
+
*/
|
|
41
|
+
this.signMessage = async ({ message }) => {
|
|
42
|
+
let hex;
|
|
43
|
+
if (typeof message === "string") {
|
|
44
|
+
hex = stringToHex(message);
|
|
45
|
+
}
|
|
46
|
+
else if (isHex(message.raw)) {
|
|
47
|
+
hex = message.raw;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
hex = bytesToHex(message.raw);
|
|
51
|
+
}
|
|
52
|
+
const signature = await __classPrivateFieldGet(this, _CubeSignerSource_signer, "f").signEip191({ data: hex });
|
|
53
|
+
return ensureHex(signature);
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Signs EIP-712 typed data. This uses CubeSigner's EIP-712 signing endpoint.
|
|
57
|
+
* The key (for this session) must have the `"AllowRawBlobSigning"` or
|
|
58
|
+
* `"AllowEip712Signing"` policy attached.
|
|
59
|
+
* `chainId` must be specified within the `domain`.
|
|
60
|
+
*
|
|
61
|
+
* @param parameters Typed data
|
|
62
|
+
* @returns signature for the typed data
|
|
63
|
+
*/
|
|
64
|
+
this.signTypedData = async (parameters) => {
|
|
65
|
+
assert(parameters.domain, "`domain` must be defined");
|
|
66
|
+
const castedParameters = parameters;
|
|
67
|
+
assert(castedParameters.domain.chainId, "`domain.chainId` must be defined");
|
|
68
|
+
const signature = await __classPrivateFieldGet(this, _CubeSignerSource_signer, "f").signEip712({
|
|
69
|
+
chain_id: Number(castedParameters.domain.chainId),
|
|
70
|
+
typed_data: castedParameters,
|
|
71
|
+
});
|
|
72
|
+
return ensureHex(signature);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* It is recommended to use `prepareTransactionRequest` on your request
|
|
76
|
+
* before calling this function.
|
|
77
|
+
*
|
|
78
|
+
* Sign a transaction. This method will block if the key requires MFA approval.
|
|
79
|
+
* `type` and `chainId` must be defined. Only supports type "legacy" or "eip1559".
|
|
80
|
+
*
|
|
81
|
+
* @param transaction The transaction to sign
|
|
82
|
+
* @param options Contains an optional custom serializer
|
|
83
|
+
* @returns Signed transaction
|
|
84
|
+
* @throws {Error} if transaction.type isn't "legacy" or "eip1559", or if "chainId" isn't specified
|
|
85
|
+
*/
|
|
86
|
+
this.signTransaction = async (transaction, options) => {
|
|
87
|
+
assert(transaction.type === "legacy" || transaction.type === "eip1559", `Unsupported transaction type '${transaction.type}', CubeSigner only supports type 'legacy' or 'eip1559'")}'`);
|
|
88
|
+
assert(transaction.chainId, "`chainId` must be defined");
|
|
89
|
+
const formatted = formatTransactionRequest(transaction);
|
|
90
|
+
const rlpSignedTransaction = await __classPrivateFieldGet(this, _CubeSignerSource_signer, "f").signTransaction({
|
|
91
|
+
chain_id: transaction.chainId,
|
|
92
|
+
tx: formatted,
|
|
93
|
+
});
|
|
94
|
+
// CubeSigner returns an RLP-encoded transaction. Since Viem allows users to pass a custom serializer, we will
|
|
95
|
+
// now unserialize and reserialize with Viem's serializer.
|
|
96
|
+
const { r, s, v, yParity, ...parsedTransaction } = parseTransaction(ensureHex(rlpSignedTransaction));
|
|
97
|
+
const serializer = options?.serializer ?? serializeTransaction;
|
|
98
|
+
return await serializer(parsedTransaction, { r, s, v, yParity });
|
|
99
|
+
};
|
|
100
|
+
__classPrivateFieldSet(this, _CubeSignerSource_signer, new EvmSigner(address, client, options), "f");
|
|
101
|
+
// NOTE: `getAddress` will checksum the address and throw if it's an invalid EVM address.
|
|
102
|
+
this.address = getAddress(__classPrivateFieldGet(this, _CubeSignerSource_signer, "f").address);
|
|
103
|
+
// Scope these functions to properly resolve `this`
|
|
104
|
+
this.signMessage = this.signMessage.bind(this);
|
|
105
|
+
this.signTransaction = this.signTransaction.bind(this);
|
|
106
|
+
this.signTypedData = this.signTypedData.bind(this);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
_CubeSignerSource_signer = new WeakMap();
|
|
110
|
+
/**
|
|
111
|
+
* @param input A hex string
|
|
112
|
+
* @returns the input, type narrowed to Hex
|
|
113
|
+
* @throws {Error} if input is not Hex
|
|
114
|
+
*/
|
|
115
|
+
function ensureHex(input) {
|
|
116
|
+
assert(isHex(input), `${input} is not hex`);
|
|
117
|
+
return input;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* @param value A value that is expected to be truthy
|
|
121
|
+
* @param message The error message if the value is falsy
|
|
122
|
+
* @throws {Error} if value is not truthy
|
|
123
|
+
*/
|
|
124
|
+
function assert(value, message) {
|
|
125
|
+
if (!value) {
|
|
126
|
+
throw new Error(message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQUEsT0FBTyxFQUtMLFVBQVUsRUFDVix3QkFBd0IsRUFDeEIsVUFBVSxFQUNWLEtBQUssRUFDTCxnQkFBZ0IsRUFDaEIsb0JBQW9CLEVBQ3BCLFdBQVcsR0FDWixNQUFNLE1BQU0sQ0FBQztBQUNkLE9BQU8sRUFJTCxTQUFTLEdBR1YsTUFBTSw2QkFBNkIsQ0FBQztBQUVyQzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sZ0JBQWdCO0lBTzNCOzs7Ozs7OztPQVFHO0lBQ0gsWUFBWSxPQUFxQixFQUFFLE1BQXdCLEVBQUUsT0FBMEI7UUFmdkYsMkNBQTJDO1FBQ2xDLDJDQUFtQjtRQTBCNUI7Ozs7Ozs7O1dBUUc7UUFDSSxnQkFBVyxHQUFnQyxLQUFLLEVBQUUsRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFO1lBQ3RFLElBQUksR0FBRyxDQUFDO1lBQ1IsSUFBSSxPQUFPLE9BQU8sS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDaEMsR0FBRyxHQUFHLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUM3QixDQUFDO2lCQUFNLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUM5QixHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQztZQUNwQixDQUFDO2lCQUFNLENBQUM7Z0JBQ04sR0FBRyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDaEMsQ0FBQztZQUVELE1BQU0sU0FBUyxHQUFHLE1BQU0sdUJBQUEsSUFBSSxnQ0FBUSxDQUFDLFVBQVUsQ0FBQyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDO1lBRS9ELE9BQU8sU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzlCLENBQUMsQ0FBQztRQUVGOzs7Ozs7OztXQVFHO1FB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QUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgdHlwZSBBZGRyZXNzLFxuICB0eXBlIEN1c3RvbVNvdXJjZSxcbiAgdHlwZSBIZXgsXG4gIHR5cGUgU2lnbmF0dXJlLFxuICBieXRlc1RvSGV4LFxuICBmb3JtYXRUcmFuc2FjdGlvblJlcXVlc3QsXG4gIGdldEFkZHJlc3MsXG4gIGlzSGV4LFxuICBwYXJzZVRyYW5zYWN0aW9uLFxuICBzZXJpYWxpemVUcmFuc2FjdGlvbixcbiAgc3RyaW5nVG9IZXgsXG59IGZyb20gXCJ2aWVtXCI7XG5pbXBvcnQge1xuICB0eXBlIEN1YmVTaWduZXJDbGllbnQsXG4gIHR5cGUgRWlwNzEyU2lnblJlcXVlc3QsXG4gIHR5cGUgRXZtU2lnblJlcXVlc3QsXG4gIEV2bVNpZ25lcixcbiAgdHlwZSBFdm1TaWduZXJPcHRpb25zLFxuICB0eXBlIEtleSxcbn0gZnJvbSBcIkBjdWJpc3QtbGFicy9jdWJlc2lnbmVyLXNka1wiO1xuXG4vKipcbiAqIEEgY2xhc3MgdG8gd3JhcCBhIEN1YmVTaWduZXIga2V5IGFuZCBjbGllbnQgaW50byBhIFZpZW0ge0BsaW5rIEN1c3RvbVNvdXJjZX0uXG4gKiBVc2UgVmllbSdzIGB0b0FjY291bnRgIHRvIGNvbnZlcnQgdGhpcyB0byBhIFZpZW0gQWNjb3VudC5cbiAqL1xuZXhwb3J0IGNsYXNzIEN1YmVTaWduZXJTb3VyY2UgaW1wbGVtZW50cyBDdXN0b21Tb3VyY2Uge1xuICAvKiogVGhlIGludGVybmFsIEN1YmVTaWduZXIgc2lnbmVyIHVzZWQuICovXG4gIHJlYWRvbmx5ICNzaWduZXI6IEV2bVNpZ25lcjtcblxuICAvKiogVGhlIGFkZHJlc3MgdGhlIHdhbGxldCBpcyBhc3NvY2lhdGVkIHdpdGguICovXG4gIHB1YmxpYyByZWFkb25seSBhZGRyZXNzOiBBZGRyZXNzO1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3QgYSBWaWVtIHtAbGluayBDdXN0b21Tb3VyY2V9IGFyb3VuZCBhIEN1YmVTaWduZXIga2V5IGFuZCBjbGllbnQuXG4gICAqIFVzZSBWaWVtJ3MgYHRvQWNjb3VudGAgdG8gY29udmVydCB0aGlzIHRvIGEgVmllbSBBY2NvdW50LlxuICAgKlxuICAgKiBAcGFyYW0gYWRkcmVzcyBUaGUgRVZNIGFkZHJlc3MgdGhpcyB3YWxsZXQgaXMgYXNzb2NpYXRlZCB3aXRoXG4gICAqIEBwYXJhbSBjbGllbnQgVGhlIHNlc3Npb24gdXNlZCBmb3Igc2lnbmluZy4gTXVzdCBoYXZlIG5lY2Vzc2FyeSBzY29wZXMuXG4gICAqIEBwYXJhbSBvcHRpb25zIE1GQSBvcHRpb25zIGZvciB0aGUgY2xpZW50IHRvIHJlc3BlY3RcbiAgICogQHRocm93cyB7RXJyb3J9IGlmIHRoZSBhZGRyZXNzIGlzIG5vdCBhIHZhbGlkIEVWTSBhZGRyZXNzXG4gICAqL1xuICBjb25zdHJ1Y3RvcihhZGRyZXNzOiBLZXkgfCBzdHJpbmcsIGNsaWVudDogQ3ViZVNpZ25lckNsaWVudCwgb3B0aW9ucz86IEV2bVNpZ25lck9wdGlvbnMpIHtcbiAgICB0aGlzLiNzaWduZXIgPSBuZXcgRXZtU2lnbmVyKGFkZHJlc3MsIGNsaWVudCwgb3B0aW9ucyk7XG5cbiAgICAvLyBOT1RFOiBgZ2V0QWRkcmVzc2Agd2lsbCBjaGVja3N1bSB0aGUgYWRkcmVzcyBhbmQgdGhyb3cgaWYgaXQncyBhbiBpbnZhbGlkIEVWTSBhZGRyZXNzLlxuICAgIHRoaXMuYWRkcmVzcyA9IGdldEFkZHJlc3ModGhpcy4jc2lnbmVyLmFkZHJlc3MpO1xuXG4gICAgLy8gU2NvcGUgdGhlc2UgZnVuY3Rpb25zIHRvIHByb3Blcmx5IHJlc29sdmUgYHRoaXNgXG4gICAgdGhpcy5zaWduTWVzc2FnZSA9IHRoaXMuc2lnbk1lc3NhZ2UuYmluZCh0aGlzKTtcbiAgICB0aGlzLnNpZ25UcmFuc2FjdGlvbiA9IHRoaXMuc2lnblRyYW5zYWN0aW9uLmJpbmQodGhpcyk7XG4gICAgdGhpcy5zaWduVHlwZWREYXRhID0gdGhpcy5zaWduVHlwZWREYXRhLmJpbmQodGhpcyk7XG4gIH1cblxuICAvKipcbiAgICogU2lnbnMgYXJiaXRyYXJ5IG1lc3NhZ2VzLiBUaGlzIHVzZXMgQ3ViZVNpZ25lcidzIEVJUC0xOTEgc2lnbmluZyBlbmRwb2ludC5cbiAgICogVGhlIGtleSAoZm9yIHRoaXMgc2Vzc2lvbikgbXVzdCBoYXZlIHRoZSBgXCJBbGxvd1Jhd0Jsb2JTaWduaW5nXCJgIG9yXG4gICAqIGBcIkFsbG93RWlwMTkxU2lnbmluZ1wiYCBwb2xpY3kgYXR0YWNoZWQuXG4gICAqXG4gICAqIEBwYXJhbSByb290IE9iamVjdCBhcm91bmQgdGhlIG1lc3NhZ2VcbiAgICogQHBhcmFtIHJvb3QubWVzc2FnZSBUaGUgbWVzc2FnZSB0byBzaWduXG4gICAqIEByZXR1cm5zIFRoZSBzaWduYXR1cmVcbiAgICovXG4gIHB1YmxpYyBzaWduTWVzc2FnZTogQ3VzdG9tU291cmNlW1wic2lnbk1lc3NhZ2VcIl0gPSBhc3luYyAoeyBtZXNzYWdlIH0pID0+IHtcbiAgICBsZXQgaGV4O1xuICAgIGlmICh0eXBlb2YgbWVzc2FnZSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgaGV4ID0gc3RyaW5nVG9IZXgobWVzc2FnZSk7XG4gICAgfSBlbHNlIGlmIChpc0hleChtZXNzYWdlLnJhdykpIHtcbiAgICAgIGhleCA9IG1lc3NhZ2UucmF3O1xuICAgIH0gZWxzZSB7XG4gICAgICBoZXggPSBieXRlc1RvSGV4KG1lc3NhZ2UucmF3KTtcbiAgICB9XG5cbiAgICBjb25zdCBzaWduYXR1cmUgPSBhd2FpdCB0aGlzLiNzaWduZXIuc2lnbkVpcDE5MSh7IGRhdGE6IGhleCB9KTtcblxuICAgIHJldHVybiBlbnN1cmVIZXgoc2lnbmF0dXJlKTtcbiAgfTtcblxuICAvKipcbiAgICogU2lnbnMgRUlQLTcxMiB0eXBlZCBkYXRhLiBUaGlzIHVzZXMgQ3ViZVNpZ25lcidzIEVJUC03MTIgc2lnbmluZyBlbmRwb2ludC5cbiAgICogVGhlIGtleSAoZm9yIHRoaXMgc2Vzc2lvbikgbXVzdCBoYXZlIHRoZSBgXCJBbGxvd1Jhd0Jsb2JTaWduaW5nXCJgIG9yXG4gICAqIGBcIkFsbG93RWlwNzEyU2lnbmluZ1wiYCBwb2xpY3kgYXR0YWNoZWQuXG4gICAqIGBjaGFpbklkYCBtdXN0IGJlIHNwZWNpZmllZCB3aXRoaW4gdGhlIGBkb21haW5gLlxuICAgKlxuICAgKiBAcGFyYW0gcGFyYW1ldGVycyBUeXBlZCBkYXRhXG4gICAqIEByZXR1cm5zIHNpZ25hdHVyZSBmb3IgdGhlIHR5cGVkIGRhdGFcbiAgICovXG4gIHB1YmxpYyBzaWduVHlwZWREYXRhOiBDdXN0b21Tb3VyY2VbXCJzaWduVHlwZWREYXRhXCJdID0gYXN5bmMgKHBhcmFtZXRlcnMpID0+IHtcbiAgICBhc3NlcnQocGFyYW1ldGVycy5kb21haW4sIFwiYGRvbWFpbmAgbXVzdCBiZSBkZWZpbmVkXCIpO1xuXG4gICAgY29uc3QgY2FzdGVkUGFyYW1ldGVycyA9IHBhcmFtZXRlcnMgYXMgRWlwNzEyU2lnblJlcXVlc3RbXCJ0eXBlZF9kYXRhXCJdO1xuICAgIGFzc2VydChjYXN0ZWRQYXJhbWV0ZXJzLmRvbWFpbi5jaGFpbklkLCBcImBkb21haW4uY2hhaW5JZGAgbXVzdCBiZSBkZWZpbmVkXCIpO1xuXG4gICAgY29uc3Qgc2lnbmF0dXJlID0gYXdhaXQgdGhpcy4jc2lnbmVyLnNpZ25FaXA3MTIoe1xuICAgICAgY2hhaW5faWQ6IE51bWJlcihjYXN0ZWRQYXJhbWV0ZXJzLmRvbWFpbi5jaGFpbklkKSxcbiAgICAgIHR5cGVkX2RhdGE6IGNhc3RlZFBhcmFtZXRlcnMsXG4gICAgfSk7XG5cbiAgICByZXR1cm4gZW5zdXJlSGV4KHNpZ25hdHVyZSk7XG4gIH07XG5cbiAgLyoqXG4gICAqIEl0IGlzIHJlY29tbWVuZGVkIHRvIHVzZSBgcHJlcGFyZVRyYW5zYWN0aW9uUmVxdWVzdGAgb24geW91ciByZXF1ZXN0XG4gICAqIGJlZm9yZSBjYWxsaW5nIHRoaXMgZnVuY3Rpb24uXG4gICAqXG4gICAqIFNpZ24gYSB0cmFuc2FjdGlvbi4gVGhpcyBtZXRob2Qgd2lsbCBibG9jayBpZiB0aGUga2V5IHJlcXVpcmVzIE1GQSBhcHByb3ZhbC5cbiAgICogYHR5cGVgIGFuZCBgY2hhaW5JZGAgbXVzdCBiZSBkZWZpbmVkLiBPbmx5IHN1cHBvcnRzIHR5cGUgXCJsZWdhY3lcIiBvciBcImVpcDE1NTlcIi5cbiAgICpcbiAgICogQHBhcmFtIHRyYW5zYWN0aW9uIFRoZSB0cmFuc2FjdGlvbiB0byBzaWduXG4gICAqIEBwYXJhbSBvcHRpb25zIENvbnRhaW5zIGFuIG9wdGlvbmFsIGN1c3RvbSBzZXJpYWxpemVyXG4gICAqIEByZXR1cm5zIFNpZ25lZCB0cmFuc2FjdGlvblxuICAgKiBAdGhyb3dzIHtFcnJvcn0gaWYgdHJhbnNhY3Rpb24udHlwZSBpc24ndCBcImxlZ2FjeVwiIG9yIFwiZWlwMTU1OVwiLCBvciBpZiBcImNoYWluSWRcIiBpc24ndCBzcGVjaWZpZWRcbiAgICovXG4gIHB1YmxpYyBzaWduVHJhbnNhY3Rpb246IEN1c3RvbVNvdXJjZVtcInNpZ25UcmFuc2FjdGlvblwiXSA9IGFzeW5jICh0cmFuc2FjdGlvbiwgb3B0aW9ucykgPT4ge1xuICAgIGFzc2VydChcbiAgICAgIHRyYW5zYWN0aW9uLnR5cGUgPT09IFwibGVnYWN5XCIgfHwgdHJhbnNhY3Rpb24udHlwZSA9PT0gXCJlaXAxNTU5XCIsXG4gICAgICBgVW5zdXBwb3J0ZWQgdHJhbnNhY3Rpb24gdHlwZSAnJHt0cmFuc2FjdGlvbi50eXBlfScsIEN1YmVTaWduZXIgb25seSBzdXBwb3J0cyB0eXBlICdsZWdhY3knIG9yICdlaXAxNTU5J1wiKX0nYCxcbiAgICApO1xuXG4gICAgYXNzZXJ0KHRyYW5zYWN0aW9uLmNoYWluSWQsIFwiYGNoYWluSWRgIG11c3QgYmUgZGVmaW5lZFwiKTtcblxuICAgIGNvbnN0IGZvcm1hdHRlZCA9IGZvcm1hdFRyYW5zYWN0aW9uUmVxdWVzdCh0cmFuc2FjdGlvbik7XG4gICAgY29uc3QgcmxwU2lnbmVkVHJhbnNhY3Rpb24gPSBhd2FpdCB0aGlzLiNzaWduZXIuc2lnblRyYW5zYWN0aW9uKHtcbiAgICAgIGNoYWluX2lkOiB0cmFuc2FjdGlvbi5jaGFpbklkLFxuICAgICAgdHg6IGZvcm1hdHRlZCBhcyBFdm1TaWduUmVxdWVzdFtcInR4XCJdLFxuICAgIH0pO1xuXG4gICAgLy8gQ3ViZVNpZ25lciByZXR1cm5zIGFuIFJMUC1lbmNvZGVkIHRyYW5zYWN0aW9uLiBTaW5jZSBWaWVtIGFsbG93cyB1c2VycyB0byBwYXNzIGEgY3VzdG9tIHNlcmlhbGl6ZXIsIHdlIHdpbGxcbiAgICAvLyBub3cgdW5zZXJpYWxpemUgYW5kIHJlc2VyaWFsaXplIHdpdGggVmllbSdzIHNlcmlhbGl6ZXIuXG4gICAgY29uc3QgeyByLCBzLCB2LCB5UGFyaXR5LCAuLi5wYXJzZWRUcmFuc2FjdGlvbiB9ID0gcGFyc2VUcmFuc2FjdGlvbihcbiAgICAgIGVuc3VyZUhleChybHBTaWduZWRUcmFuc2FjdGlvbiksXG4gICAgKTtcbiAgICBjb25zdCBzZXJpYWxpemVyID0gb3B0aW9ucz8uc2VyaWFsaXplciA/PyBzZXJpYWxpemVUcmFuc2FjdGlvbjtcblxuICAgIHJldHVybiBhd2FpdCBzZXJpYWxpemVyKHBhcnNlZFRyYW5zYWN0aW9uLCB7IHIsIHMsIHYsIHlQYXJpdHkgfSBhcyBTaWduYXR1cmUpO1xuICB9O1xufVxuXG4vKipcbiAqIEBwYXJhbSBpbnB1dCBBIGhleCBzdHJpbmdcbiAqIEByZXR1cm5zIHRoZSBpbnB1dCwgdHlwZSBuYXJyb3dlZCB0byBIZXhcbiAqIEB0aHJvd3Mge0Vycm9yfSBpZiBpbnB1dCBpcyBub3QgSGV4XG4gKi9cbmZ1bmN0aW9uIGVuc3VyZUhleChpbnB1dDogc3RyaW5nKTogSGV4IHtcbiAgYXNzZXJ0KGlzSGV4KGlucHV0KSwgYCR7aW5wdXR9IGlzIG5vdCBoZXhgKTtcbiAgcmV0dXJuIGlucHV0O1xufVxuXG4vKipcbiAqIEBwYXJhbSB2YWx1ZSBBIHZhbHVlIHRoYXQgaXMgZXhwZWN0ZWQgdG8gYmUgdHJ1dGh5XG4gKiBAcGFyYW0gbWVzc2FnZSBUaGUgZXJyb3IgbWVzc2FnZSBpZiB0aGUgdmFsdWUgaXMgZmFsc3lcbiAqIEB0aHJvd3Mge0Vycm9yfSBpZiB2YWx1ZSBpcyBub3QgdHJ1dGh5XG4gKi9cbmZ1bmN0aW9uIGFzc2VydCh2YWx1ZTogdW5rbm93biwgbWVzc2FnZTogc3RyaW5nKTogYXNzZXJ0cyB2YWx1ZSB7XG4gIGlmICghdmFsdWUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IobWVzc2FnZSk7XG4gIH1cbn1cbiJdfQ==
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cubist-labs/cubesigner-sdk-viem",
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "git+https://github.com/cubist-labs/CubeSigner-TypeScript-SDK.git",
|
|
6
|
+
"directory": "packages/viem"
|
|
7
|
+
},
|
|
8
|
+
"version": "0.4.183-0",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"description": "Viem CustomSource implementation",
|
|
11
|
+
"license": "MIT OR Apache-2.0",
|
|
12
|
+
"author": "Cubist, Inc.",
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"tsconfig.json",
|
|
17
|
+
"src/**",
|
|
18
|
+
"dist/**",
|
|
19
|
+
"../../NOTICE",
|
|
20
|
+
"../../LICENSE-APACHE",
|
|
21
|
+
"../../LICENSE-MIT"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"prepack": "tsc",
|
|
26
|
+
"test": "jest --maxWorkers=1"
|
|
27
|
+
},
|
|
28
|
+
"directories": {
|
|
29
|
+
"test": "test"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@cubist-labs/cubesigner-sdk": "^0.4.183-0",
|
|
33
|
+
"viem": "^2.38.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@cubist-labs/cubesigner-sdk-fs-storage": "^0.4.183-0"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Address,
|
|
3
|
+
type CustomSource,
|
|
4
|
+
type Hex,
|
|
5
|
+
type Signature,
|
|
6
|
+
bytesToHex,
|
|
7
|
+
formatTransactionRequest,
|
|
8
|
+
getAddress,
|
|
9
|
+
isHex,
|
|
10
|
+
parseTransaction,
|
|
11
|
+
serializeTransaction,
|
|
12
|
+
stringToHex,
|
|
13
|
+
} from "viem";
|
|
14
|
+
import {
|
|
15
|
+
type CubeSignerClient,
|
|
16
|
+
type Eip712SignRequest,
|
|
17
|
+
type EvmSignRequest,
|
|
18
|
+
EvmSigner,
|
|
19
|
+
type EvmSignerOptions,
|
|
20
|
+
type Key,
|
|
21
|
+
} from "@cubist-labs/cubesigner-sdk";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A class to wrap a CubeSigner key and client into a Viem {@link CustomSource}.
|
|
25
|
+
* Use Viem's `toAccount` to convert this to a Viem Account.
|
|
26
|
+
*/
|
|
27
|
+
export class CubeSignerSource implements CustomSource {
|
|
28
|
+
/** The internal CubeSigner signer used. */
|
|
29
|
+
readonly #signer: EvmSigner;
|
|
30
|
+
|
|
31
|
+
/** The address the wallet is associated with. */
|
|
32
|
+
public readonly address: Address;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Construct a Viem {@link CustomSource} around a CubeSigner key and client.
|
|
36
|
+
* Use Viem's `toAccount` to convert this to a Viem Account.
|
|
37
|
+
*
|
|
38
|
+
* @param address The EVM address this wallet is associated with
|
|
39
|
+
* @param client The session used for signing. Must have necessary scopes.
|
|
40
|
+
* @param options MFA options for the client to respect
|
|
41
|
+
* @throws {Error} if the address is not a valid EVM address
|
|
42
|
+
*/
|
|
43
|
+
constructor(address: Key | string, client: CubeSignerClient, options?: EvmSignerOptions) {
|
|
44
|
+
this.#signer = new EvmSigner(address, client, options);
|
|
45
|
+
|
|
46
|
+
// NOTE: `getAddress` will checksum the address and throw if it's an invalid EVM address.
|
|
47
|
+
this.address = getAddress(this.#signer.address);
|
|
48
|
+
|
|
49
|
+
// Scope these functions to properly resolve `this`
|
|
50
|
+
this.signMessage = this.signMessage.bind(this);
|
|
51
|
+
this.signTransaction = this.signTransaction.bind(this);
|
|
52
|
+
this.signTypedData = this.signTypedData.bind(this);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Signs arbitrary messages. This uses CubeSigner's EIP-191 signing endpoint.
|
|
57
|
+
* The key (for this session) must have the `"AllowRawBlobSigning"` or
|
|
58
|
+
* `"AllowEip191Signing"` policy attached.
|
|
59
|
+
*
|
|
60
|
+
* @param root Object around the message
|
|
61
|
+
* @param root.message The message to sign
|
|
62
|
+
* @returns The signature
|
|
63
|
+
*/
|
|
64
|
+
public signMessage: CustomSource["signMessage"] = async ({ message }) => {
|
|
65
|
+
let hex;
|
|
66
|
+
if (typeof message === "string") {
|
|
67
|
+
hex = stringToHex(message);
|
|
68
|
+
} else if (isHex(message.raw)) {
|
|
69
|
+
hex = message.raw;
|
|
70
|
+
} else {
|
|
71
|
+
hex = bytesToHex(message.raw);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const signature = await this.#signer.signEip191({ data: hex });
|
|
75
|
+
|
|
76
|
+
return ensureHex(signature);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Signs EIP-712 typed data. This uses CubeSigner's EIP-712 signing endpoint.
|
|
81
|
+
* The key (for this session) must have the `"AllowRawBlobSigning"` or
|
|
82
|
+
* `"AllowEip712Signing"` policy attached.
|
|
83
|
+
* `chainId` must be specified within the `domain`.
|
|
84
|
+
*
|
|
85
|
+
* @param parameters Typed data
|
|
86
|
+
* @returns signature for the typed data
|
|
87
|
+
*/
|
|
88
|
+
public signTypedData: CustomSource["signTypedData"] = async (parameters) => {
|
|
89
|
+
assert(parameters.domain, "`domain` must be defined");
|
|
90
|
+
|
|
91
|
+
const castedParameters = parameters as Eip712SignRequest["typed_data"];
|
|
92
|
+
assert(castedParameters.domain.chainId, "`domain.chainId` must be defined");
|
|
93
|
+
|
|
94
|
+
const signature = await this.#signer.signEip712({
|
|
95
|
+
chain_id: Number(castedParameters.domain.chainId),
|
|
96
|
+
typed_data: castedParameters,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return ensureHex(signature);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* It is recommended to use `prepareTransactionRequest` on your request
|
|
104
|
+
* before calling this function.
|
|
105
|
+
*
|
|
106
|
+
* Sign a transaction. This method will block if the key requires MFA approval.
|
|
107
|
+
* `type` and `chainId` must be defined. Only supports type "legacy" or "eip1559".
|
|
108
|
+
*
|
|
109
|
+
* @param transaction The transaction to sign
|
|
110
|
+
* @param options Contains an optional custom serializer
|
|
111
|
+
* @returns Signed transaction
|
|
112
|
+
* @throws {Error} if transaction.type isn't "legacy" or "eip1559", or if "chainId" isn't specified
|
|
113
|
+
*/
|
|
114
|
+
public signTransaction: CustomSource["signTransaction"] = async (transaction, options) => {
|
|
115
|
+
assert(
|
|
116
|
+
transaction.type === "legacy" || transaction.type === "eip1559",
|
|
117
|
+
`Unsupported transaction type '${transaction.type}', CubeSigner only supports type 'legacy' or 'eip1559'")}'`,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
assert(transaction.chainId, "`chainId` must be defined");
|
|
121
|
+
|
|
122
|
+
const formatted = formatTransactionRequest(transaction);
|
|
123
|
+
const rlpSignedTransaction = await this.#signer.signTransaction({
|
|
124
|
+
chain_id: transaction.chainId,
|
|
125
|
+
tx: formatted as EvmSignRequest["tx"],
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// CubeSigner returns an RLP-encoded transaction. Since Viem allows users to pass a custom serializer, we will
|
|
129
|
+
// now unserialize and reserialize with Viem's serializer.
|
|
130
|
+
const { r, s, v, yParity, ...parsedTransaction } = parseTransaction(
|
|
131
|
+
ensureHex(rlpSignedTransaction),
|
|
132
|
+
);
|
|
133
|
+
const serializer = options?.serializer ?? serializeTransaction;
|
|
134
|
+
|
|
135
|
+
return await serializer(parsedTransaction, { r, s, v, yParity } as Signature);
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param input A hex string
|
|
141
|
+
* @returns the input, type narrowed to Hex
|
|
142
|
+
* @throws {Error} if input is not Hex
|
|
143
|
+
*/
|
|
144
|
+
function ensureHex(input: string): Hex {
|
|
145
|
+
assert(isHex(input), `${input} is not hex`);
|
|
146
|
+
return input;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @param value A value that is expected to be truthy
|
|
151
|
+
* @param message The error message if the value is falsy
|
|
152
|
+
* @throws {Error} if value is not truthy
|
|
153
|
+
*/
|
|
154
|
+
function assert(value: unknown, message: string): asserts value {
|
|
155
|
+
if (!value) {
|
|
156
|
+
throw new Error(message);
|
|
157
|
+
}
|
|
158
|
+
}
|