@gvnrdao/dh-sdk 0.0.347 → 0.0.349
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/browser/dist/browser.js +1 -1
- package/dist/contracts/typechain-contracts/factories/src/agent/AgentDelegationRegistry__factory.d.ts +80 -1
- package/dist/contracts/typechain-contracts/src/agent/AgentDelegationRegistry.d.ts +85 -8
- package/dist/deployments.js +20 -8
- package/dist/deployments.mjs +20 -8
- package/dist/index.d.ts +3 -1
- package/dist/index.js +948 -417
- package/dist/index.mjs +855 -335
- package/dist/interfaces/chunks/loan-operations.i.d.ts +0 -6
- package/dist/modules/diamond-hands-sdk.d.ts +8 -4
- package/dist/modules/withdrawal-address/withdrawal-address.module.d.ts +9 -1
- package/dist/safe-delegation.js +20 -8
- package/dist/safe-delegation.mjs +20 -8
- package/dist/sign-guard.js +737 -0
- package/dist/sign-guard.mjs +734 -0
- package/dist/utils/address-conversion.utils.d.ts +16 -3
- package/dist/utils/agent-delegation.utils.d.ts +19 -4
- package/dist/utils/btc-spend-request-712.d.ts +150 -0
- package/dist/utils/btc-withdrawal-message.d.ts +40 -27
- package/dist/utils/sign-guard/index.d.ts +1 -0
- package/dist/utils/sign-guard/typed-data-guard.d.ts +68 -0
- package/dist/utils/stale-spend-invalidate-message.d.ts +28 -15
- package/package.json +5 -4
|
@@ -38,16 +38,29 @@ type BitcoinAddressNetwork = 'mainnet' | 'testnet' | 'regtest';
|
|
|
38
38
|
* @throws if the address is malformed, fails its checksum, or is not on `network`
|
|
39
39
|
*/
|
|
40
40
|
export declare function safeValidateBitcoinAddress(address: string, network: BitcoinAddressNetwork): string;
|
|
41
|
+
/**
|
|
42
|
+
* Why `address` has no canonical spelling, or null when it has one. Pure — for callers that
|
|
43
|
+
* report failures as values (Result) rather than exceptions.
|
|
44
|
+
*
|
|
45
|
+
* BIP-173: a bech32/bech32m string is valid all-lowercase or all-uppercase, never mixed. A mixed
|
|
46
|
+
* string is not a typo we may repair by lowercasing — it is invalid, and folding it would let a
|
|
47
|
+
* corrupted address hash to a real allowlist entry. Base58 is case-sensitive by design and is
|
|
48
|
+
* never folded, so it is never a case error.
|
|
49
|
+
*/
|
|
50
|
+
export declare function bitcoinAddressCaseError(address: string): string | null;
|
|
41
51
|
/**
|
|
42
52
|
* Canonicalize a Bitcoin address so that the SAME string is used on both the
|
|
43
53
|
* approve path (registry.addAddress) and the withdraw path. The on-chain
|
|
44
54
|
* allowlist is keyed by keccak256(bytes(address)), so the two strings MUST
|
|
45
|
-
* byte-match
|
|
46
|
-
*
|
|
47
|
-
*
|
|
55
|
+
* byte-match, and the contract accepts lowercase-only bech32.
|
|
56
|
+
*
|
|
57
|
+
* - bech32 (bc1/tb1/bcrt1): lowercase is canonical; an ALL-uppercase string
|
|
58
|
+
* (BIP-173 allows it, e.g. QR codes) is lowercased; MIXED case throws.
|
|
59
|
+
* - base58 (1…/3…/m…/n…/2…): case-sensitive, returned unchanged.
|
|
48
60
|
*
|
|
49
61
|
* @param address Bitcoin address to canonicalize
|
|
50
62
|
* @returns The canonical form to store/compare
|
|
63
|
+
* @throws on mixed-case bech32 — see `bitcoinAddressCaseError`
|
|
51
64
|
*/
|
|
52
65
|
export declare function normalizeBitcoinAddress(address: string): string;
|
|
53
66
|
/**
|
|
@@ -5,9 +5,21 @@
|
|
|
5
5
|
* this file owns the feature-specific orchestration (the lit-ops-server mint-agent endpoint).
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
|
|
8
|
+
* EIP-712 types of the agent's binding consent (`contracts/docs/EIP712_DOMAIN_SPEC.md` §3.9).
|
|
9
|
+
* Domain: "DiamondHands.Protocol" v1, verifyingContract = the AgentDelegationRegistry proxy.
|
|
10
|
+
*/
|
|
11
|
+
export declare const AGENT_BINDING_CONSENT_TYPES: {
|
|
12
|
+
AgentBindingConsent: {
|
|
13
|
+
name: string;
|
|
14
|
+
type: string;
|
|
15
|
+
}[];
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Mint a fresh per-user AGENT PKP via lit-ops-server and return its Ethereum address together
|
|
19
|
+
* with the agent's binding consent — the EIP-712 `AgentBindingConsent(user, agent)` signature
|
|
20
|
+
* AgentDelegationRegistry 3.0.0's `registerAgent` / `rotateAgent` require (issue #1197). The
|
|
21
|
+
* server signs it for the authenticated session address only. Each call yields a unique address
|
|
22
|
+
* (satisfies the registry's AR-1 non-reuse). Throws on failure.
|
|
11
23
|
*
|
|
12
24
|
* @param serviceEndpoint lit-ops-server base URL (the SDK's `serviceEndpoint`).
|
|
13
25
|
* @param authHeader Optional async/sync provider of auth headers (the SDK's session token).
|
|
@@ -17,7 +29,10 @@ export declare function mintAgentPkp(params: {
|
|
|
17
29
|
serviceEndpoint: string;
|
|
18
30
|
authHeader?: () => Promise<Record<string, string>> | Record<string, string>;
|
|
19
31
|
timeoutMs?: number;
|
|
20
|
-
}): Promise<
|
|
32
|
+
}): Promise<{
|
|
33
|
+
walletAddress: string;
|
|
34
|
+
agentConsent: string;
|
|
35
|
+
}>;
|
|
21
36
|
/** `AgentStatus` in `IAgentDelegateRegistryBase.sol`. */
|
|
22
37
|
export declare const AGENT_STATUS: {
|
|
23
38
|
readonly None: 0;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EIP-712 types for the BTC-custody owner authorizations — SDK copy.
|
|
3
|
+
*
|
|
4
|
+
* The withdrawal **execute** step and the stale-spend **invalidate** step
|
|
5
|
+
* (`contracts/docs/EIP712_DOMAIN_SPEC.md` §3.8). Before these types, both were
|
|
6
|
+
* `personal_sign` over a 32-byte packed hash that no wallet can decode
|
|
7
|
+
* (watchlist C-46); typed data lets the wallet show the destination, what the
|
|
8
|
+
* recipient receives, the fee and the total itself.
|
|
9
|
+
*
|
|
10
|
+
* ## Why this is a copy, and why that is safe
|
|
11
|
+
*
|
|
12
|
+
* The single source lives in `@gvnrdao/dh-lit-actions`
|
|
13
|
+
* (`lit-actions/src/constants/btc-spend-request-types.ts`), which the
|
|
14
|
+
* `btc-transaction-signer` / `btc-utxo-invalidator` verifiers read. The SDK's
|
|
15
|
+
* published `dh-lit-actions` dependency predates it, so this module re-declares
|
|
16
|
+
* the two field lists; `sdk/tests/shared/unit/btc-spend-request-712.test.ts`
|
|
17
|
+
* reads the golden vectors (`lit-actions/tests/vectors/btc-spend-request.v1.json`)
|
|
18
|
+
* and asserts every digest is byte-identical. Fold this into a re-export once
|
|
19
|
+
* the SDK depends on a `dh-lit-actions` that exports these names.
|
|
20
|
+
*
|
|
21
|
+
* ## The message comes from the chain, not the caller
|
|
22
|
+
*
|
|
23
|
+
* `readAuthorizedSpend` reads the `BTCSpendAuthorizer` record on the pinned
|
|
24
|
+
* proxy; the builders take destination, total deduction and outpoint from it.
|
|
25
|
+
* The Lit Action builds its message from the same record, so the wallet shows
|
|
26
|
+
* the borrower exactly what the transaction pays — never a server's claim.
|
|
27
|
+
*/
|
|
28
|
+
import { type ContractRunner, type Signer, type TypedDataField } from 'ethers';
|
|
29
|
+
export declare const BTC_SPEND_REQUEST_712_DOMAIN_NAME = "DiamondHands.Protocol";
|
|
30
|
+
export declare const BTC_SPEND_REQUEST_712_DOMAIN_VERSION = "1";
|
|
31
|
+
export type BtcSpendRequest712PrimaryType = 'ExecuteBtcWithdrawalRequest' | 'InvalidateStaleSpendRequest';
|
|
32
|
+
/**
|
|
33
|
+
* Field lists per primary type. **Order is part of the digest** — a change
|
|
34
|
+
* lands with the lit-actions constants, the descriptor and a CID rotation.
|
|
35
|
+
*/
|
|
36
|
+
export declare const BTC_SPEND_REQUEST_712_FIELDS: Readonly<Record<BtcSpendRequest712PrimaryType, readonly TypedDataField[]>>;
|
|
37
|
+
export interface BtcSpendRequest712Domain {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly version: string;
|
|
40
|
+
readonly chainId: number;
|
|
41
|
+
readonly verifyingContract: string;
|
|
42
|
+
}
|
|
43
|
+
/** A complete `eth_signTypedData_v4` payload, without the `EIP712Domain` entry. */
|
|
44
|
+
export interface BtcSpendRequest712Payload {
|
|
45
|
+
domain: BtcSpendRequest712Domain;
|
|
46
|
+
types: Record<string, TypedDataField[]>;
|
|
47
|
+
primaryType: BtcSpendRequest712PrimaryType;
|
|
48
|
+
message: Record<string, string | number>;
|
|
49
|
+
}
|
|
50
|
+
/** The `BTCSpendAuthorizer.getAuthorizedSpends` row a signature consumes. */
|
|
51
|
+
export interface AuthorizedSpendRecord {
|
|
52
|
+
txid: string;
|
|
53
|
+
vout: number;
|
|
54
|
+
satoshis: bigint;
|
|
55
|
+
targetAddress: string;
|
|
56
|
+
targetAmount: bigint;
|
|
57
|
+
authorizedAt: bigint;
|
|
58
|
+
}
|
|
59
|
+
/** Returns ONLY the requested primary type. @throws on an unknown type. */
|
|
60
|
+
export declare function btcSpendRequest712Types(primaryType: BtcSpendRequest712PrimaryType): Record<string, TypedDataField[]>;
|
|
61
|
+
/** The pinned BTCSpendAuthorizer proxy for a chain. @throws for an unknown chain. */
|
|
62
|
+
export declare function btcSpendAuthorizerAddress(chainId: number): string;
|
|
63
|
+
/** Build the EIP-712 domain: the pinned BTCSpendAuthorizer proxy, never caller-supplied. */
|
|
64
|
+
export declare function buildBtcSpendRequest712Domain(chainId: number): BtcSpendRequest712Domain;
|
|
65
|
+
/** Canonical txid: lowercase, no `0x`, exactly 64 hex. @throws otherwise. */
|
|
66
|
+
export declare function canonicalBtcTxid(txid: unknown): string;
|
|
67
|
+
/**
|
|
68
|
+
* Read the on-chain spend record for one outpoint from the pinned
|
|
69
|
+
* BTCSpendAuthorizer. The outpoint is matched in canonical txid form.
|
|
70
|
+
*
|
|
71
|
+
* @throws if no record exists for the outpoint under this position.
|
|
72
|
+
*/
|
|
73
|
+
export declare function readAuthorizedSpend(params: {
|
|
74
|
+
runner: ContractRunner;
|
|
75
|
+
chainId: number;
|
|
76
|
+
positionId: string;
|
|
77
|
+
txid: string;
|
|
78
|
+
vout: number;
|
|
79
|
+
}): Promise<AuthorizedSpendRecord>;
|
|
80
|
+
/** @throws if `record` is not the record for `txid:vout` (canonical txid form). */
|
|
81
|
+
export declare function assertRecordIsOutpoint(record: Pick<AuthorizedSpendRecord, 'txid' | 'vout'>, txid: string, vout: number): void;
|
|
82
|
+
/**
|
|
83
|
+
* The typed execute payload, built from the on-chain record, the fee the
|
|
84
|
+
* transaction will pay and the input-set hash. Exported for Safe/precomputed
|
|
85
|
+
* signing and for tests.
|
|
86
|
+
*
|
|
87
|
+
* @throws on a non-integer or negative fee, or one not below the total.
|
|
88
|
+
*/
|
|
89
|
+
export declare function buildExecuteBtcWithdrawalTypedData(params: {
|
|
90
|
+
chainId: number;
|
|
91
|
+
positionId: string;
|
|
92
|
+
record: Pick<AuthorizedSpendRecord, 'txid' | 'vout' | 'targetAddress' | 'targetAmount'>;
|
|
93
|
+
networkFee: number;
|
|
94
|
+
inputSetHash: string;
|
|
95
|
+
quantumTimestamp: number;
|
|
96
|
+
}): BtcSpendRequest712Payload;
|
|
97
|
+
/** The typed invalidate payload over the on-chain record's outpoint. */
|
|
98
|
+
export declare function buildInvalidateStaleSpendTypedData(params: {
|
|
99
|
+
chainId: number;
|
|
100
|
+
positionId: string;
|
|
101
|
+
record: Pick<AuthorizedSpendRecord, 'txid' | 'vout'>;
|
|
102
|
+
quantumTimestamp: number;
|
|
103
|
+
}): BtcSpendRequest712Payload;
|
|
104
|
+
/** EIP-712 digest of a payload — the hash a contract wallet's `isValidSignature` receives. */
|
|
105
|
+
export declare function btcSpendRequest712Digest(payload: BtcSpendRequest712Payload): string;
|
|
106
|
+
export declare const BTC_SPEND_QUANTUM_SECONDS = 60;
|
|
107
|
+
/**
|
|
108
|
+
* Optional signer capability: re-stamp at the moment the wallet is actually
|
|
109
|
+
* asked. The hosted connector implements it, because the borrower may open
|
|
110
|
+
* the request long after the SDK built it. The signer calls `build` with the
|
|
111
|
+
* quantum start it stamps (and may call it again to re-stamp) and returns the
|
|
112
|
+
* signature with the stamp it signed.
|
|
113
|
+
*/
|
|
114
|
+
export interface RestampableTypedDataSigner {
|
|
115
|
+
dhRestampTypedData(build: (quantumTimestamp: number) => BtcSpendRequest712Payload): Promise<{
|
|
116
|
+
signature: string;
|
|
117
|
+
quantumTimestamp: number;
|
|
118
|
+
}>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Latest moment, in seconds after the stamp, at which the SDK still relays a
|
|
122
|
+
* signature.
|
|
123
|
+
*
|
|
124
|
+
* The Lit Action accepts a stamp from the current or previous quantum, but
|
|
125
|
+
* its shared verifier first waits out the dead zone (seconds 52–59) and then
|
|
126
|
+
* re-checks the window, so a stamp from quantum S is only accepted when
|
|
127
|
+
* verification STARTS by second 51 of quantum S+1 — 111 s after the stamp.
|
|
128
|
+
* Verification starts after the server's chain reads and the signer's own
|
|
129
|
+
* RPC steps; 90 s leaves ~20 s for that trip. Past it, the refusal is
|
|
130
|
+
* certain, so nothing is relayed.
|
|
131
|
+
*/
|
|
132
|
+
export declare const BTC_SPEND_RELAY_DEADLINE_SECONDS = 90;
|
|
133
|
+
/**
|
|
134
|
+
* Sign a BTC spend request as typed data, stamping the CURRENT quantum.
|
|
135
|
+
*
|
|
136
|
+
* The verifier accepts a typed execute stamp from the current or previous
|
|
137
|
+
* quantum (DR-6), so there is no pre-wait: the borrower has between 30 and
|
|
138
|
+
* 90 s from the prompt (depending on where in the quantum it opens), and a
|
|
139
|
+
* re-stamping signer that stamps at dispatch gets the full 90 s. After the
|
|
140
|
+
* wallet returns, a signature older than `BTC_SPEND_RELAY_DEADLINE_SECONDS`,
|
|
141
|
+
* or stamped in a quantum that has not started, is refused here ("signed too
|
|
142
|
+
* late") rather than relayed into a certain refusal.
|
|
143
|
+
*
|
|
144
|
+
* No `personal_sign` fallback: a signer without `signTypedData` throws.
|
|
145
|
+
*/
|
|
146
|
+
export declare function signBtcSpendRequest712(signer: Signer, build: (quantumTimestamp: number) => BtcSpendRequest712Payload): Promise<{
|
|
147
|
+
signature: string;
|
|
148
|
+
quantumTimestamp: number;
|
|
149
|
+
payload: BtcSpendRequest712Payload;
|
|
150
|
+
}>;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Helpers for building the
|
|
2
|
+
* Helpers for building the `execute-btc-withdrawal` envelope that the
|
|
3
3
|
* `btc-transaction-signer` LIT Action verifies.
|
|
4
4
|
*
|
|
5
5
|
* Pulled out of the CLI's executePendingWithdrawal util so the SDK and CLI
|
|
6
6
|
* use the same logic and signing rules.
|
|
7
|
+
*
|
|
8
|
+
* Since A-2 (EIP712_DOMAIN_SPEC.md §3.8) the borrower signs an EIP-712
|
|
9
|
+
* `ExecuteBtcWithdrawalRequest` the wallet can display — destination, what
|
|
10
|
+
* the recipient receives, the fee and the total — instead of `personal_sign`
|
|
11
|
+
* over a 32-byte hash. The envelope shape sent to the server is unchanged.
|
|
7
12
|
*/
|
|
8
|
-
import { type Signer } from "ethers";
|
|
13
|
+
import { type ContractRunner, type Signer } from "ethers";
|
|
14
|
+
import { type AuthorizedSpendRecord } from "./btc-spend-request-712";
|
|
9
15
|
export interface BtcExecuteSignParams {
|
|
10
16
|
positionId: string;
|
|
11
17
|
txid: string;
|
|
@@ -37,17 +43,17 @@ export interface BtcExecuteSignedEnvelope {
|
|
|
37
43
|
txid: string;
|
|
38
44
|
vout: number;
|
|
39
45
|
satoshis: number;
|
|
40
|
-
/** Bitcoin destination
|
|
46
|
+
/** Bitcoin destination — the on-chain record's `targetAddress`. */
|
|
41
47
|
targetAddress: string;
|
|
42
|
-
/**
|
|
48
|
+
/** Total deduction in sats — the on-chain record's `targetAmount`. */
|
|
43
49
|
targetAmount: number;
|
|
44
50
|
networkFee: number;
|
|
45
51
|
chainId: number;
|
|
46
|
-
/** Quantum-aligned timestamp (start of a 60s quantum). */
|
|
52
|
+
/** Quantum-aligned timestamp (start of a 60s quantum) the signature binds. */
|
|
47
53
|
timestamp: number;
|
|
48
|
-
/** EIP-
|
|
54
|
+
/** EIP-712 `ExecuteBtcWithdrawalRequest` signature. */
|
|
49
55
|
userSignature: string;
|
|
50
|
-
/** Address
|
|
56
|
+
/** Address of the signer (borrower, or PKP owner for a liquidated position). */
|
|
51
57
|
borrowerAddress: string;
|
|
52
58
|
/**
|
|
53
59
|
* The FULL input set bound into the signature (P6/#8). The server must
|
|
@@ -57,30 +63,37 @@ export interface BtcExecuteSignedEnvelope {
|
|
|
57
63
|
utxos: BtcExecuteInput[];
|
|
58
64
|
}
|
|
59
65
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
* Where the on-chain spend record comes from: a chain reader (the builder
|
|
67
|
+
* reads `BTCSpendAuthorizer.getAuthorizedSpends` itself) or a record the
|
|
68
|
+
* caller already read from the chain. Exactly one.
|
|
69
|
+
*/
|
|
70
|
+
export type AuthorizedSpendSource = {
|
|
71
|
+
provider: ContractRunner;
|
|
72
|
+
authorizedSpend?: never;
|
|
73
|
+
} | {
|
|
74
|
+
authorizedSpend: AuthorizedSpendRecord;
|
|
75
|
+
provider?: never;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Build and sign the envelope the `/api/lit/pending-withdrawals/execute`
|
|
79
|
+
* endpoint expects.
|
|
71
80
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
81
|
+
* The destination and total come from the ON-CHAIN `BTCSpendAuthorizer`
|
|
82
|
+
* record for the outpoint, never from the caller: the borrower's wallet
|
|
83
|
+
* displays them, and the Lit Action builds the identical typed message from
|
|
84
|
+
* the same record. Caller-supplied `targetAddress` / `targetAmount` (e.g. from
|
|
85
|
+
* a server's pending-withdrawals list) are ASSERTIONS — a disagreement with
|
|
86
|
+
* the record throws before anything is signed.
|
|
75
87
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* Lit Action does not accept older SDK signatures. Deploy together.
|
|
88
|
+
* The stamp is the current quantum start; the signer accepts the current or
|
|
89
|
+
* previous quantum, so a borrower has 60–120 s to read and approve.
|
|
79
90
|
*/
|
|
80
|
-
export declare function buildBtcExecuteEnvelope(params: BtcExecuteSignParams & {
|
|
91
|
+
export declare function buildBtcExecuteEnvelope(params: BtcExecuteSignParams & AuthorizedSpendSource & {
|
|
81
92
|
satoshis: number;
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
/** Expected destination; must equal the on-chain record. */
|
|
94
|
+
targetAddress?: string;
|
|
95
|
+
/** Expected total in sats; must equal the on-chain record. */
|
|
96
|
+
targetAmount?: number;
|
|
84
97
|
/**
|
|
85
98
|
* FULL input set the phase-2 signer may spend (P6/#8). Omit for the
|
|
86
99
|
* common single-UTXO withdrawal — it defaults to exactly the authorized
|
|
@@ -8,3 +8,4 @@ export { REQUEST_TIMEOUT_MS, LitOpsHttpError, LitOpsTransportError, fetchWithTim
|
|
|
8
8
|
export { signAndBroadcast, type SignGuardSigner } from "./sign-and-broadcast";
|
|
9
9
|
export { computeExtendFeeUpperBound, type ExtendFeeBound } from "./extend-fee-bound";
|
|
10
10
|
export { buildPsmApproveTx, buildPsmSwapTx, buildPsmRedeemTx, planPsmExchange, executePsmPlan, psmExchangeReadsFromProvider, type PsmUnsignedTx, type PsmLeg, type PsmExchangeAddresses, type PsmExchangeReads, type PsmExchangePlan, } from "./psm-exchange";
|
|
11
|
+
export { assertSignableTypedData, SIGNABLE_TYPED_DATA, TypedDataRefused, } from "./typed-data-guard";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed-data allowlist — the check between an EIP-712 request and a
|
|
3
|
+
* borrower's wallet (A-2 plan, Step 8).
|
|
4
|
+
*
|
|
5
|
+
* A typed-data signature is only as safe as the struct it covers. Without a
|
|
6
|
+
* guard, any party that can put a request in front of the signer (a hostile
|
|
7
|
+
* server, a prompt-injected tool call) could ask for an EIP-2612 `Permit` or
|
|
8
|
+
* any other contract's typed data, and the borrower would see a well-formed
|
|
9
|
+
* prompt for something the protocol never asks for. This guard admits ONLY
|
|
10
|
+
* the typed requests Diamond Hands clients make, each pinned to its own
|
|
11
|
+
* domain, exact ordered field list and exact message keys, and returns the
|
|
12
|
+
* human label a signing card should show for it.
|
|
13
|
+
*
|
|
14
|
+
* Inventory (2026-10-19, every typed request a connector / CLI signer is
|
|
15
|
+
* asked for): the server login `DiamondHands` (three field-list variants:
|
|
16
|
+
* base, `audience`, `statement`+`audience`; `sdk/src/utils/eip712-login.ts`),
|
|
17
|
+
* the four loan `*Request` types under the LoanOperationsManager domain
|
|
18
|
+
* (`sdk/src/utils/borrower-authorization-712.ts`), and the two BTC-custody
|
|
19
|
+
* `*Request` types under the BTCSpendAuthorizer domain
|
|
20
|
+
* (`sdk/src/utils/btc-spend-request-712.ts`).
|
|
21
|
+
*
|
|
22
|
+
* PURE: imports only `ethers` and the address table
|
|
23
|
+
* (`scripts/check-sign-guard-imports.mjs`), so the field lists are declared
|
|
24
|
+
* here and `tests/shared/unit/sign-guard/typed-data-guard.test.ts` holds them
|
|
25
|
+
* in lockstep with the modules that build the requests.
|
|
26
|
+
*/
|
|
27
|
+
type Fields = ReadonlyArray<{
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly type: string;
|
|
30
|
+
}>;
|
|
31
|
+
type DomainKind = "login" | "loan-operations" | "btc-spend-authorizer";
|
|
32
|
+
interface AllowedType {
|
|
33
|
+
readonly label: string;
|
|
34
|
+
readonly domain: DomainKind;
|
|
35
|
+
/** Accepted field lists, each exact and ordered. */
|
|
36
|
+
readonly variants: ReadonlyArray<Fields>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Every typed request a Diamond Hands client may put in front of a wallet.
|
|
40
|
+
* Deep-frozen: it is exported (tests and consumers read it), and a writable
|
|
41
|
+
* export would let any caller widen the guard's policy.
|
|
42
|
+
*/
|
|
43
|
+
export declare const SIGNABLE_TYPED_DATA: Readonly<Record<string, AllowedType>>;
|
|
44
|
+
/** Refusal for a typed request outside the allowlist; the wallet is never asked. */
|
|
45
|
+
export declare class TypedDataRefused extends Error {
|
|
46
|
+
constructor(reason: string);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Admit a typed-data request or throw `TypedDataRefused`.
|
|
50
|
+
*
|
|
51
|
+
* Checks, all required: the primary type `TypedDataEncoder.getPrimaryType`
|
|
52
|
+
* derives equals the one sent; it is on the allowlist and is the ONLY struct
|
|
53
|
+
* (besides an `EIP712Domain` entry); its field list is one allowed variant,
|
|
54
|
+
* exact and ordered; the domain has exactly the keys its kind allows (login:
|
|
55
|
+
* name, version, chainId; protocol: + verifyingContract; never `salt`), the
|
|
56
|
+
* expected name and version, `chainId` equal to the session's as a number,
|
|
57
|
+
* and the pinned contract for that kind; the message has exactly the field
|
|
58
|
+
* names as keys.
|
|
59
|
+
*
|
|
60
|
+
* @returns the human action label for the signing card.
|
|
61
|
+
*/
|
|
62
|
+
export declare function assertSignableTypedData(chainId: number, domain: Record<string, unknown>, types: Record<string, ReadonlyArray<{
|
|
63
|
+
name: string;
|
|
64
|
+
type: string;
|
|
65
|
+
}>>, primaryType: string, message: Record<string, unknown>): {
|
|
66
|
+
label: string;
|
|
67
|
+
};
|
|
68
|
+
export {};
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Borrower authorization envelope for the btc-utxo-invalidator Lit Action
|
|
3
|
-
* the SDK-side mirror of `AuthorizationModule.verifyRecoveryAuthorization`
|
|
4
|
-
* (lit-actions/src/modules/authorization.module.ts:539). Field order and
|
|
5
|
-
* types MUST match byte-for-byte; the action recomputes this hash and
|
|
6
|
-
* EIP-191-recovers the borrower/delegate from it.
|
|
2
|
+
* Borrower authorization envelope for the btc-utxo-invalidator Lit Action.
|
|
7
3
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* Since A-2 (EIP712_DOMAIN_SPEC.md §3.8) the borrower signs an EIP-712
|
|
5
|
+
* `InvalidateStaleSpendRequest` over the on-chain record's outpoint, which
|
|
6
|
+
* the wallet can display, instead of `personal_sign` over a 32-byte hash.
|
|
7
|
+
*
|
|
8
|
+
* `buildInvalidateMessageHash` is kept: it is the legacy packed hash the Lit
|
|
9
|
+
* Action still accepts until the legacy arm is switched off, and the
|
|
10
|
+
* cross-package parity suites pin it. Field order and types MUST match
|
|
11
|
+
* `AuthorizationModule.verifyRecoveryAuthorization`
|
|
12
|
+
* (lit-actions/src/modules/authorization.module.ts) byte-for-byte; the
|
|
13
|
+
* lit-actions side hashes with ethers v5 `solidityKeccak256`, this side with
|
|
14
|
+
* v6 `solidityPackedKeccak256` — identical packed encoding.
|
|
11
15
|
*/
|
|
12
|
-
import { type Signer } from "ethers";
|
|
16
|
+
import { type ContractRunner, type Signer } from "ethers";
|
|
17
|
+
import { type AuthorizedSpendRecord } from "./btc-spend-request-712";
|
|
13
18
|
/** Must equal RECOVERY_INVALIDATE_ACTION_STRING in lit-actions constants. */
|
|
14
19
|
export declare const RECOVERY_INVALIDATE_ACTION = "btc-utxo-invalidator";
|
|
15
20
|
export interface InvalidateStaleSpendEnvelope {
|
|
@@ -20,10 +25,11 @@ export interface InvalidateStaleSpendEnvelope {
|
|
|
20
25
|
timestamp: number;
|
|
21
26
|
chainId: number;
|
|
22
27
|
action: typeof RECOVERY_INVALIDATE_ACTION;
|
|
28
|
+
/** EIP-712 `InvalidateStaleSpendRequest` signature. */
|
|
23
29
|
signature: string;
|
|
24
30
|
borrowerAddress: string;
|
|
25
31
|
}
|
|
26
|
-
/** The
|
|
32
|
+
/** The legacy packed hash `verifyRecoveryAuthorization` recomputes in-TEE. */
|
|
27
33
|
export declare function buildInvalidateMessageHash(params: {
|
|
28
34
|
positionId: string;
|
|
29
35
|
timestamp: number;
|
|
@@ -32,10 +38,11 @@ export declare function buildInvalidateMessageHash(params: {
|
|
|
32
38
|
utxoVout: number;
|
|
33
39
|
}): string;
|
|
34
40
|
/**
|
|
35
|
-
* Build + sign the invalidate envelope
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
41
|
+
* Build + sign the invalidate envelope. The outpoint the wallet displays is
|
|
42
|
+
* the on-chain record's: the builder reads it (or takes a record the caller
|
|
43
|
+
* already read from the chain) and refuses an outpoint with no record. The
|
|
44
|
+
* signature is scoped to exactly one (positionId, outpoint) recovery on one
|
|
45
|
+
* chain, and its primary type cannot be replayed as any other authorization.
|
|
39
46
|
*/
|
|
40
47
|
export declare function buildInvalidateStaleSpendEnvelope(params: {
|
|
41
48
|
positionId: string;
|
|
@@ -43,4 +50,10 @@ export declare function buildInvalidateStaleSpendEnvelope(params: {
|
|
|
43
50
|
utxoVout: number;
|
|
44
51
|
chainId: number;
|
|
45
52
|
signer: Signer;
|
|
46
|
-
}
|
|
53
|
+
} & ({
|
|
54
|
+
provider: ContractRunner;
|
|
55
|
+
authorizedSpend?: never;
|
|
56
|
+
} | {
|
|
57
|
+
authorizedSpend: AuthorizedSpendRecord;
|
|
58
|
+
provider?: never;
|
|
59
|
+
})): Promise<InvalidateStaleSpendEnvelope>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gvnrdao/dh-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.349",
|
|
4
4
|
"description": "TypeScript SDK for Diamond Hands Protocol",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -77,9 +77,10 @@
|
|
|
77
77
|
"test:watch": "jest --watch",
|
|
78
78
|
"test:coverage": "jest --coverage",
|
|
79
79
|
"authorize:pkp": "node scripts/authorize-pkp-actions.js",
|
|
80
|
-
"lint": "eslint src --ext .ts && npm run lint:server-boundary && npm run lint:sign-guard",
|
|
80
|
+
"lint": "eslint src --ext .ts && npm run lint:server-boundary && npm run lint:sign-guard && npm run lint:no-blind-sign",
|
|
81
81
|
"lint:server-boundary": "node scripts/check-pkp-mint-server-imports.mjs",
|
|
82
82
|
"lint:sign-guard": "node scripts/check-sign-guard-imports.mjs",
|
|
83
|
+
"lint:no-blind-sign": "node scripts/check-no-blind-borrower-sign.mjs",
|
|
83
84
|
"clean": "rm -rf dist && rm -rf browser/dist",
|
|
84
85
|
"gen:contract-errors": "node scripts/gen-contract-errors.mjs"
|
|
85
86
|
},
|
|
@@ -100,8 +101,8 @@
|
|
|
100
101
|
},
|
|
101
102
|
"sideEffects": false,
|
|
102
103
|
"dependencies": {
|
|
103
|
-
"@gvnrdao/dh-lit-actions": "^0.0.
|
|
104
|
-
"@gvnrdao/dh-lit-ops": "^0.0.
|
|
104
|
+
"@gvnrdao/dh-lit-actions": "^0.0.326",
|
|
105
|
+
"@gvnrdao/dh-lit-ops": "^0.0.323",
|
|
105
106
|
"@noble/hashes": "^1.5.0",
|
|
106
107
|
"axios": "^1.17.0",
|
|
107
108
|
"bech32": "^2.0.0",
|