@getcofund/protocol 0.0.0 → 0.1.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/LICENSE +21 -0
- package/README.md +413 -1
- package/dist/btc/index.d.ts +12 -0
- package/dist/btc/index.js +13 -0
- package/dist/btc/psbt.d.ts +121 -0
- package/dist/btc/psbt.js +377 -0
- package/dist/btc/reader.d.ts +42 -0
- package/dist/btc/reader.js +85 -0
- package/dist/btc/select.d.ts +48 -0
- package/dist/btc/select.js +59 -0
- package/dist/btc/spend-rules.d.ts +24 -0
- package/dist/btc/spend-rules.js +43 -0
- package/dist/btc/spend.d.ts +136 -0
- package/dist/btc/spend.js +180 -0
- package/dist/btc/types.d.ts +65 -0
- package/dist/btc/types.js +62 -0
- package/dist/btc/vault/bip341-nums.d.ts +35 -0
- package/dist/btc/vault/bip341-nums.js +121 -0
- package/dist/btc/vault/build.d.ts +5 -0
- package/dist/btc/vault/build.js +136 -0
- package/dist/btc/vault/canonical-order.d.ts +10 -0
- package/dist/btc/vault/canonical-order.js +18 -0
- package/dist/btc/vault/from-contract-id.d.ts +9 -0
- package/dist/btc/vault/from-contract-id.js +74 -0
- package/dist/btc/vault/helpers.d.ts +73 -0
- package/dist/btc/vault/helpers.js +129 -0
- package/dist/btc/vault/index.d.ts +21 -0
- package/dist/btc/vault/index.js +21 -0
- package/dist/btc/vault/limits.d.ts +9 -0
- package/dist/btc/vault/limits.js +9 -0
- package/dist/btc/vault/participant-order.d.ts +69 -0
- package/dist/btc/vault/participant-order.js +135 -0
- package/dist/btc/vault/script.d.ts +13 -0
- package/dist/btc/vault/script.js +39 -0
- package/dist/btc/vault/spend.d.ts +63 -0
- package/dist/btc/vault/spend.js +93 -0
- package/dist/btc/vault/tx-size.d.ts +66 -0
- package/dist/btc/vault/tx-size.js +71 -0
- package/dist/btc/vault/types.d.ts +49 -0
- package/dist/btc/vault/types.js +1 -0
- package/dist/btc/vault/vault-policy.d.ts +109 -0
- package/dist/btc/vault/vault-policy.js +213 -0
- package/dist/btc/vault/vaultContractUtil.d.ts +80 -0
- package/dist/btc/vault/vaultContractUtil.js +87 -0
- package/dist/btc/vault/xpub.d.ts +28 -0
- package/dist/btc/vault/xpub.js +41 -0
- package/dist/btc/witness.d.ts +101 -0
- package/dist/btc/witness.js +439 -0
- package/dist/btc-policies/activation.d.ts +76 -0
- package/dist/btc-policies/activation.js +153 -0
- package/dist/btc-policies/index.d.ts +7 -0
- package/dist/btc-policies/index.js +8 -0
- package/dist/btc-policies/policy.d.ts +55 -0
- package/dist/btc-policies/policy.js +210 -0
- package/dist/evm/abis/index.d.ts +10 -0
- package/dist/evm/abis/index.js +11 -0
- package/dist/evm/abis/policies.d.ts +279 -0
- package/dist/evm/abis/policies.js +363 -0
- package/dist/evm/abis/sponsor-guard.d.ts +201 -0
- package/dist/evm/abis/sponsor-guard.js +263 -0
- package/dist/evm/abis/state.d.ts +989 -0
- package/dist/evm/abis/state.js +1290 -0
- package/dist/evm/abis/users.d.ts +408 -0
- package/dist/evm/abis/users.js +532 -0
- package/dist/evm/abis/vault-factory.d.ts +92 -0
- package/dist/evm/abis/vault-factory.js +126 -0
- package/dist/evm/abis/vault.d.ts +343 -0
- package/dist/evm/abis/vault.js +444 -0
- package/dist/evm/address.d.ts +2 -0
- package/dist/evm/address.js +4 -0
- package/dist/evm/calls/common.d.ts +30 -0
- package/dist/evm/calls/common.js +51 -0
- package/dist/evm/calls/index.d.ts +11 -0
- package/dist/evm/calls/index.js +4 -0
- package/dist/evm/calls/policies.d.ts +25 -0
- package/dist/evm/calls/policies.js +173 -0
- package/dist/evm/calls/users.d.ts +61 -0
- package/dist/evm/calls/users.js +353 -0
- package/dist/evm/calls/vault.d.ts +25 -0
- package/dist/evm/calls/vault.js +184 -0
- package/dist/evm/chain.d.ts +74 -0
- package/dist/evm/chain.js +48 -0
- package/dist/evm/company-name.d.ts +46 -0
- package/dist/evm/company-name.js +64 -0
- package/dist/evm/deployment.d.ts +47 -0
- package/dist/evm/deployment.js +110 -0
- package/dist/evm/eip712.d.ts +271 -0
- package/dist/evm/eip712.js +224 -0
- package/dist/evm/index.d.ts +20 -0
- package/dist/evm/index.js +21 -0
- package/dist/evm/reads.d.ts +64 -0
- package/dist/evm/reads.js +131 -0
- package/dist/evm/signatures.d.ts +57 -0
- package/dist/evm/signatures.js +105 -0
- package/dist/evm/simulate/balances.d.ts +76 -0
- package/dist/evm/simulate/balances.js +201 -0
- package/dist/evm/simulate/describe.d.ts +79 -0
- package/dist/evm/simulate/describe.js +188 -0
- package/dist/evm/simulate/index.d.ts +12 -0
- package/dist/evm/simulate/index.js +13 -0
- package/dist/evm/simulate/own-node.d.ts +62 -0
- package/dist/evm/simulate/own-node.js +122 -0
- package/dist/evm/simulate/request.d.ts +86 -0
- package/dist/evm/simulate/request.js +235 -0
- package/dist/evm/simulate/revert.d.ts +29 -0
- package/dist/evm/simulate/revert.js +192 -0
- package/dist/evm/simulate/run.d.ts +51 -0
- package/dist/evm/simulate/run.js +214 -0
- package/dist/evm/stocks/decode-stock-swap.d.ts +32 -0
- package/dist/evm/stocks/decode-stock-swap.js +79 -0
- package/dist/evm/stocks/encode-swap.d.ts +45 -0
- package/dist/evm/stocks/encode-swap.js +45 -0
- package/dist/evm/stocks/index.d.ts +9 -0
- package/dist/evm/stocks/index.js +10 -0
- package/dist/evm/stocks/quote.d.ts +19 -0
- package/dist/evm/stocks/quote.js +46 -0
- package/dist/evm/stocks/stock-pools.d.ts +61 -0
- package/dist/evm/stocks/stock-pools.js +179 -0
- package/dist/evm/submitters.d.ts +32 -0
- package/dist/evm/submitters.js +105 -0
- package/dist/evm/types.d.ts +146 -0
- package/dist/evm/types.js +128 -0
- package/dist/keys/index.d.ts +6 -0
- package/dist/keys/index.js +7 -0
- package/dist/keys/keys.d.ts +63 -0
- package/dist/keys/keys.js +80 -0
- package/dist/keys/xpub-ownership.d.ts +10 -0
- package/dist/keys/xpub-ownership.js +13 -0
- package/package.json +61 -3
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { PsbtSpendSummary } from "../btc/psbt.ts";
|
|
3
|
+
import type { VerifiedBtcVault } from "../btc/spend.ts";
|
|
4
|
+
/**
|
|
5
|
+
* A Bitcoin transfer policy, a plain value its users store: who signs (each
|
|
6
|
+
* by the account xpub the vault commits to), how many of them, the most one
|
|
7
|
+
* spend pays, and, when listed, the only addresses it may pay.
|
|
8
|
+
*/
|
|
9
|
+
export declare const BtcPolicySchema: z.ZodObject<{
|
|
10
|
+
id: z.ZodUUID;
|
|
11
|
+
name: z.ZodString;
|
|
12
|
+
type: z.ZodString;
|
|
13
|
+
signers: z.ZodArray<z.ZodString>;
|
|
14
|
+
threshold: z.ZodNumber;
|
|
15
|
+
maxAmount: z.ZodString;
|
|
16
|
+
recipients: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
export type BtcPolicy = z.infer<typeof BtcPolicySchema>;
|
|
19
|
+
/**
|
|
20
|
+
* Throws unless `policy` is a policy `vault` can carry out, as the app refuses
|
|
21
|
+
* one at creation (`assertSignersInBtcVault`, which the policy form mirrors
|
|
22
|
+
* in `getBtcPolicySignersIssue`, vault-signer-ids.ts:132-194): the
|
|
23
|
+
* policy parses (`BtcPolicySchema`), every signer is one of the vault's
|
|
24
|
+
* members, the threshold is at least the vault's, and on an admin template
|
|
25
|
+
* an admin signs.
|
|
26
|
+
*/
|
|
27
|
+
export declare function checkPolicyForVault(policy: BtcPolicy, vault: VerifiedBtcVault): void;
|
|
28
|
+
/**
|
|
29
|
+
* Throws unless the checked spend (`checkPsbt`'s summary) is one `policy`
|
|
30
|
+
* allows out of `vault`: the policy fits the vault (`checkPolicyForVault`), it
|
|
31
|
+
* pays only the policy's recipients when it lists any, and its payees' total
|
|
32
|
+
* is within the cap. The extra outputs (a fee you charge), the change and the
|
|
33
|
+
* network fee are not counted, as the app counts a transfer's amount alone.
|
|
34
|
+
*/
|
|
35
|
+
export declare function checkSpendAgainstPolicy(summary: PsbtSpendSummary, policy: BtcPolicy, vault: VerifiedBtcVault): void;
|
|
36
|
+
/** One tapleaf on which the policy's signers have signed enough. */
|
|
37
|
+
export type PolicyApproval = {
|
|
38
|
+
/** The leaf, hex. */
|
|
39
|
+
leafHash: string;
|
|
40
|
+
/** The policy's signers whose signatures cover every input on it, by account xpub. */
|
|
41
|
+
signers: string[];
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Whether the policy's signers, not just any of the vault's, have signed the
|
|
45
|
+
* PSBT enough: the app's server waits for this beyond the script's k
|
|
46
|
+
* (`findVerifiedApprovalPlan`, `app/api/bitcoin/nonces/finalizeTx.ts:316-361`).
|
|
47
|
+
* The first leaf on which at least `threshold` of the policy's signers each
|
|
48
|
+
* have a signature that verifies over every input, by distinct keys, and
|
|
49
|
+
* among whom the leaf's own rule (k, and its admin on `ADMINS_REQUIRED`) can
|
|
50
|
+
* be met; null when no leaf has that.
|
|
51
|
+
*
|
|
52
|
+
* `finalizePsbt` uses every signature that verifies, so to put exactly these
|
|
53
|
+
* signers' on the witness, combine only their PSBTs.
|
|
54
|
+
*/
|
|
55
|
+
export declare function policySignersSigned(psbt: Uint8Array, policy: BtcPolicy, vault: VerifiedBtcVault): PolicyApproval | null;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { bytesToHex } from "@noble/hashes/utils";
|
|
2
|
+
import * as btc from "@scure/btc-signer";
|
|
3
|
+
import { equalBytes } from "@scure/btc-signer/utils.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { BtcXpub, Uuid } from "../btc/types.js";
|
|
6
|
+
import { BTC_VAULT_MAX_PARTICIPANTS } from "../btc/vault/limits.js";
|
|
7
|
+
import { vaultSpendSighash } from "../btc/vault/spend.js";
|
|
8
|
+
import { leafScriptPolicy, selectHardThresholdSignerIds, signerSlotsForScript, verifyPartialSignatureAgainstMsg, } from "../btc/witness.js";
|
|
9
|
+
/**
|
|
10
|
+
* A Bitcoin spending policy as the same rules Cofund's app holds its Bitcoin
|
|
11
|
+
* transfers to. No script checks them: a vault's script asks for k of its
|
|
12
|
+
* keys and nothing else. They bind only the parties that check a spend
|
|
13
|
+
* against them before signing, and that wait for the policy's signers.
|
|
14
|
+
*
|
|
15
|
+
* The app files cited are under `apps/cofund/src/`:
|
|
16
|
+
* `actions/create-policy/assert-btc-vault-signers.ts`,
|
|
17
|
+
* `lib/bitcoin/vault-signer-ids.ts`,
|
|
18
|
+
* `app/api/documents/transfer/create-transfer-doc.ts` and
|
|
19
|
+
* `app/api/bitcoin/nonces/finalizeTx.ts`.
|
|
20
|
+
*/
|
|
21
|
+
const BtcAddress = z
|
|
22
|
+
.string()
|
|
23
|
+
.regex(/^(?:(?:bc|tb|bcrt)1[02-9ac-hj-np-z]{6,87}|[123mn][1-9A-HJ-NP-Za-km-z]{25,34})$/, "a Bitcoin address");
|
|
24
|
+
const distinct = (items, key) => new Set(items.map(key)).size === items.length;
|
|
25
|
+
/**
|
|
26
|
+
* A Bitcoin transfer policy, a plain value its users store: who signs (each
|
|
27
|
+
* by the account xpub the vault commits to), how many of them, the most one
|
|
28
|
+
* spend pays, and, when listed, the only addresses it may pay.
|
|
29
|
+
*/
|
|
30
|
+
export const BtcPolicySchema = z
|
|
31
|
+
.object({
|
|
32
|
+
id: Uuid,
|
|
33
|
+
name: z.string().min(2).max(100),
|
|
34
|
+
/** What the policy is for, e.g. `Contractor_Stipend`; its activation commits to it. */
|
|
35
|
+
type: z.string().min(1).max(100),
|
|
36
|
+
signers: z
|
|
37
|
+
.array(BtcXpub)
|
|
38
|
+
.min(1)
|
|
39
|
+
.max(BTC_VAULT_MAX_PARTICIPANTS)
|
|
40
|
+
// assert-btc-vault-signers.ts:24-27: the same signer twice.
|
|
41
|
+
.refine((xpubs) => distinct(xpubs, (xpub) => xpub), "Each signer once"),
|
|
42
|
+
/** Signatures by the policy's signers a spend waits for. */
|
|
43
|
+
threshold: z.number().int().min(1),
|
|
44
|
+
/** The most one spend pays its payees, in sats. */
|
|
45
|
+
maxAmount: z
|
|
46
|
+
.string()
|
|
47
|
+
.regex(/^[1-9]\d{0,29}$/, "sats, a positive integer of at most 30 digits"),
|
|
48
|
+
/** The only addresses a spend may pay; absent or empty: any. */
|
|
49
|
+
recipients: z
|
|
50
|
+
.array(BtcAddress)
|
|
51
|
+
.max(100)
|
|
52
|
+
.refine((list) => distinct(list, (address) => address.toLowerCase()), "Each recipient once")
|
|
53
|
+
.optional(),
|
|
54
|
+
})
|
|
55
|
+
// assert-btc-vault-signers.ts:37-45: no more than the signers can give.
|
|
56
|
+
.refine(({ threshold, signers }) => threshold <= signers.length, {
|
|
57
|
+
message: "The threshold is more than the policy's signers",
|
|
58
|
+
path: ["threshold"],
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Throws unless `policy` is a policy `vault` can carry out, as the app refuses
|
|
62
|
+
* one at creation (`assertSignersInBtcVault`, which the policy form mirrors
|
|
63
|
+
* in `getBtcPolicySignersIssue`, vault-signer-ids.ts:132-194): the
|
|
64
|
+
* policy parses (`BtcPolicySchema`), every signer is one of the vault's
|
|
65
|
+
* members, the threshold is at least the vault's, and on an admin template
|
|
66
|
+
* an admin signs.
|
|
67
|
+
*/
|
|
68
|
+
export function checkPolicyForVault(policy, vault) {
|
|
69
|
+
const { signers, threshold } = BtcPolicySchema.parse(policy);
|
|
70
|
+
const { members, vaultType } = vault.definition;
|
|
71
|
+
// assert-btc-vault-signers.ts:29-35 (vault-signer-ids.ts:163-167).
|
|
72
|
+
const outsiders = signers.filter((xpub) => !members.some(({ accountXpub }) => accountXpub === xpub));
|
|
73
|
+
if (outsiders.length > 0) {
|
|
74
|
+
throw new Error(`Signers not in vault ${vault.address}: ${outsiders.join(", ")}`);
|
|
75
|
+
}
|
|
76
|
+
// assert-btc-vault-signers.ts:37-45 (vault-signer-ids.ts:169-171): the
|
|
77
|
+
// script takes k signatures whatever the policy says, so never fewer.
|
|
78
|
+
if (threshold < vault.definition.threshold) {
|
|
79
|
+
throw new Error(`The policy's threshold of ${threshold} is below vault ${vault.address}'s ${vault.definition.threshold}`);
|
|
80
|
+
}
|
|
81
|
+
// assert-btc-vault-signers.ts:47-60 (vault-signer-ids.ts:99-110).
|
|
82
|
+
if (vaultType === "ADMINS_INCLUDED" || vaultType === "ADMINS_REQUIRED") {
|
|
83
|
+
const admin = members.some(({ role, accountXpub }) => role === "admin" && signers.includes(accountXpub));
|
|
84
|
+
if (!admin) {
|
|
85
|
+
throw new Error(`An ${vaultType} vault's policy needs at least one of its admins among its signers`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Throws unless the checked spend (`checkPsbt`'s summary) is one `policy`
|
|
91
|
+
* allows out of `vault`: the policy fits the vault (`checkPolicyForVault`), it
|
|
92
|
+
* pays only the policy's recipients when it lists any, and its payees' total
|
|
93
|
+
* is within the cap. The extra outputs (a fee you charge), the change and the
|
|
94
|
+
* network fee are not counted, as the app counts a transfer's amount alone.
|
|
95
|
+
*/
|
|
96
|
+
export function checkSpendAgainstPolicy(summary, policy, vault) {
|
|
97
|
+
if (summary.vault !== vault.address) {
|
|
98
|
+
throw new Error(`The spend is from ${summary.vault}, not vault ${vault.address}`);
|
|
99
|
+
}
|
|
100
|
+
checkPolicyForVault(policy, vault);
|
|
101
|
+
// create-transfer-doc.ts:191-196: the amount over the cap. One spend is one
|
|
102
|
+
// transfer, so its payees count together (the app's payroll run caps each
|
|
103
|
+
// contractor's own transfer, cron/payroll-run.worker.ts:806).
|
|
104
|
+
const total = summary.payees.reduce((sum, { amount }) => sum + amount, 0n);
|
|
105
|
+
if (total > BigInt(policy.maxAmount)) {
|
|
106
|
+
throw new Error(`The spend pays ${total} sats, over the policy's maximum of ${policy.maxAmount}`);
|
|
107
|
+
}
|
|
108
|
+
// create-transfer-doc.ts:224-240: an allowlist, when the policy has one,
|
|
109
|
+
// compared case-insensitively.
|
|
110
|
+
const allowlist = (policy.recipients ?? []).map((a) => a.toLowerCase());
|
|
111
|
+
if (allowlist.length > 0) {
|
|
112
|
+
for (const { address } of summary.payees) {
|
|
113
|
+
if (!allowlist.includes(address.toLowerCase())) {
|
|
114
|
+
throw new Error(`Recipient ${address} is not in the policy's recipient allowlist`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** leaf hash → member id → the inputs a signature of theirs that verifies covers (finalizeTx.ts:242-269). */
|
|
120
|
+
function signatureCoverage(tx, vault) {
|
|
121
|
+
const coverage = new Map();
|
|
122
|
+
const add = (leaf, index, pubKey) => {
|
|
123
|
+
const key = bytesToHex(leaf.leafHash);
|
|
124
|
+
const perLeaf = coverage.get(key) ?? new Map();
|
|
125
|
+
for (const { id, xonly } of leaf.signers) {
|
|
126
|
+
if (equalBytes(xonly, pubKey)) {
|
|
127
|
+
perLeaf.set(id, (perLeaf.get(id) ?? new Set()).add(index));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
coverage.set(key, perLeaf);
|
|
131
|
+
};
|
|
132
|
+
for (let index = 0; index < tx.inputsLength; index++) {
|
|
133
|
+
const sigs = tx.getInput(index).tapScriptSig ?? [];
|
|
134
|
+
for (const [{ pubKey, leafHash }, signature] of sigs) {
|
|
135
|
+
const leaf = vault.leaves.find((entry) => equalBytes(entry.leaf.leafHash, leafHash))?.leaf;
|
|
136
|
+
const sighash = leaf && vaultSpendSighash(tx, index, leaf.script);
|
|
137
|
+
if (sighash &&
|
|
138
|
+
verifyPartialSignatureAgainstMsg(sighash, pubKey, signature)) {
|
|
139
|
+
add(leaf, index, pubKey);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return coverage;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The leaf's script slots held by a policy signer whose signatures cover every
|
|
147
|
+
* input, each key counted once (`verifiedApproverIdsForLeaf`,
|
|
148
|
+
* finalizeTx.ts:271-303).
|
|
149
|
+
*/
|
|
150
|
+
function leafApprovers(leaf, perLeaf, policySignerIds, inputCount) {
|
|
151
|
+
const script = leafScriptPolicy(leaf);
|
|
152
|
+
if (!script) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
const countedKeys = new Set();
|
|
156
|
+
const approvers = [];
|
|
157
|
+
for (const [index, signer] of signerSlotsForScript(leaf, script.signatureKeys).entries()) {
|
|
158
|
+
const key = bytesToHex(script.signatureKeys[index]);
|
|
159
|
+
if (signer &&
|
|
160
|
+
policySignerIds.has(signer.id) &&
|
|
161
|
+
perLeaf.get(signer.id)?.size === inputCount &&
|
|
162
|
+
!countedKeys.has(key)) {
|
|
163
|
+
countedKeys.add(key);
|
|
164
|
+
approvers.push(signer.id);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return approvers;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Whether the policy's signers, not just any of the vault's, have signed the
|
|
171
|
+
* PSBT enough: the app's server waits for this beyond the script's k
|
|
172
|
+
* (`findVerifiedApprovalPlan`, `app/api/bitcoin/nonces/finalizeTx.ts:316-361`).
|
|
173
|
+
* The first leaf on which at least `threshold` of the policy's signers each
|
|
174
|
+
* have a signature that verifies over every input, by distinct keys, and
|
|
175
|
+
* among whom the leaf's own rule (k, and its admin on `ADMINS_REQUIRED`) can
|
|
176
|
+
* be met; null when no leaf has that.
|
|
177
|
+
*
|
|
178
|
+
* `finalizePsbt` uses every signature that verifies, so to put exactly these
|
|
179
|
+
* signers' on the witness, combine only their PSBTs.
|
|
180
|
+
*/
|
|
181
|
+
export function policySignersSigned(psbt, policy, vault) {
|
|
182
|
+
// finalizeTx.ts:323-325.
|
|
183
|
+
if (!Number.isInteger(policy.threshold) || policy.threshold < 1) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const tx = btc.Transaction.fromPSBT(psbt);
|
|
187
|
+
for (let index = 0; index < tx.inputsLength; index++) {
|
|
188
|
+
const prevout = tx.getInput(index).witnessUtxo;
|
|
189
|
+
if (!(prevout && equalBytes(prevout.script, vault.script))) {
|
|
190
|
+
throw new Error(`Input ${index} does not spend vault ${vault.address}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const xpubOf = new Map(vault.definition.members
|
|
194
|
+
.filter(({ accountXpub }) => policy.signers.includes(accountXpub))
|
|
195
|
+
.map(({ id, accountXpub }) => [id, accountXpub]));
|
|
196
|
+
const coverage = signatureCoverage(tx, vault);
|
|
197
|
+
for (const { leaf } of vault.leaves) {
|
|
198
|
+
const leafHash = bytesToHex(leaf.leafHash);
|
|
199
|
+
const approvers = leafApprovers(leaf, coverage.get(leafHash) ?? new Map(), new Set(xpubOf.keys()), tx.inputsLength);
|
|
200
|
+
// finalizeTx.ts:346-355.
|
|
201
|
+
if (approvers.length >= policy.threshold &&
|
|
202
|
+
selectHardThresholdSignerIds(leaf, vault.definition.vaultType, new Set(approvers))) {
|
|
203
|
+
return {
|
|
204
|
+
leafHash,
|
|
205
|
+
signers: approvers.flatMap((id) => xpubOf.get(id) ?? []),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contracts' ABIs, generated from cofund-solidity's Foundry artifacts
|
|
3
|
+
* (`scripts/gen-evm-abis.mjs`), typed `as const` for viem.
|
|
4
|
+
*/
|
|
5
|
+
export { policiesAbi } from "./policies.ts";
|
|
6
|
+
export { sponsorGuardAbi } from "./sponsor-guard.ts";
|
|
7
|
+
export { stateAbi } from "./state.ts";
|
|
8
|
+
export { usersAbi } from "./users.ts";
|
|
9
|
+
export { vaultAbi } from "./vault.ts";
|
|
10
|
+
export { vaultFactoryAbi } from "./vault-factory.ts";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contracts' ABIs, generated from cofund-solidity's Foundry artifacts
|
|
3
|
+
* (`scripts/gen-evm-abis.mjs`), typed `as const` for viem.
|
|
4
|
+
*/
|
|
5
|
+
// biome-ignore lint/performance/noBarrelFile: the ABIs' part of the `./evm` entry
|
|
6
|
+
export { policiesAbi } from "./policies.js";
|
|
7
|
+
export { sponsorGuardAbi } from "./sponsor-guard.js";
|
|
8
|
+
export { stateAbi } from "./state.js";
|
|
9
|
+
export { usersAbi } from "./users.js";
|
|
10
|
+
export { vaultAbi } from "./vault.js";
|
|
11
|
+
export { vaultFactoryAbi } from "./vault-factory.js";
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
export declare const policiesAbi: readonly [{
|
|
2
|
+
readonly type: "constructor";
|
|
3
|
+
readonly inputs: readonly [{
|
|
4
|
+
readonly name: "_stateContract";
|
|
5
|
+
readonly type: "address";
|
|
6
|
+
readonly internalType: "address";
|
|
7
|
+
}];
|
|
8
|
+
readonly stateMutability: "nonpayable";
|
|
9
|
+
}, {
|
|
10
|
+
readonly type: "function";
|
|
11
|
+
readonly name: "activateTransactionPolicy";
|
|
12
|
+
readonly inputs: readonly [{
|
|
13
|
+
readonly name: "args";
|
|
14
|
+
readonly type: "tuple";
|
|
15
|
+
readonly internalType: "struct CFHelpersPoliciesv1.TransactionPolicyArgs";
|
|
16
|
+
readonly components: readonly [{
|
|
17
|
+
readonly name: "clientId";
|
|
18
|
+
readonly type: "bytes32";
|
|
19
|
+
readonly internalType: "bytes32";
|
|
20
|
+
}, {
|
|
21
|
+
readonly name: "company";
|
|
22
|
+
readonly type: "string";
|
|
23
|
+
readonly internalType: "string";
|
|
24
|
+
}, {
|
|
25
|
+
readonly name: "policyId";
|
|
26
|
+
readonly type: "string";
|
|
27
|
+
readonly internalType: "string";
|
|
28
|
+
}, {
|
|
29
|
+
readonly name: "title";
|
|
30
|
+
readonly type: "string";
|
|
31
|
+
readonly internalType: "string";
|
|
32
|
+
}, {
|
|
33
|
+
readonly name: "policyType";
|
|
34
|
+
readonly type: "string";
|
|
35
|
+
readonly internalType: "string";
|
|
36
|
+
}, {
|
|
37
|
+
readonly name: "signerIds";
|
|
38
|
+
readonly type: "string[]";
|
|
39
|
+
readonly internalType: "string[]";
|
|
40
|
+
}, {
|
|
41
|
+
readonly name: "threshold";
|
|
42
|
+
readonly type: "uint256";
|
|
43
|
+
readonly internalType: "uint256";
|
|
44
|
+
}, {
|
|
45
|
+
readonly name: "wrapper";
|
|
46
|
+
readonly type: "address";
|
|
47
|
+
readonly internalType: "address";
|
|
48
|
+
}, {
|
|
49
|
+
readonly name: "func";
|
|
50
|
+
readonly type: "string";
|
|
51
|
+
readonly internalType: "string";
|
|
52
|
+
}];
|
|
53
|
+
}, {
|
|
54
|
+
readonly name: "authId";
|
|
55
|
+
readonly type: "string";
|
|
56
|
+
readonly internalType: "string";
|
|
57
|
+
}, {
|
|
58
|
+
readonly name: "signatures";
|
|
59
|
+
readonly type: "bytes[]";
|
|
60
|
+
readonly internalType: "bytes[]";
|
|
61
|
+
}];
|
|
62
|
+
readonly outputs: readonly [{
|
|
63
|
+
readonly name: "";
|
|
64
|
+
readonly type: "bool";
|
|
65
|
+
readonly internalType: "bool";
|
|
66
|
+
}];
|
|
67
|
+
readonly stateMutability: "nonpayable";
|
|
68
|
+
}, {
|
|
69
|
+
readonly type: "function";
|
|
70
|
+
readonly name: "activateTransferPolicy";
|
|
71
|
+
readonly inputs: readonly [{
|
|
72
|
+
readonly name: "args";
|
|
73
|
+
readonly type: "tuple";
|
|
74
|
+
readonly internalType: "struct CFHelpersPoliciesv1.TransferPolicyArgs";
|
|
75
|
+
readonly components: readonly [{
|
|
76
|
+
readonly name: "clientId";
|
|
77
|
+
readonly type: "bytes32";
|
|
78
|
+
readonly internalType: "bytes32";
|
|
79
|
+
}, {
|
|
80
|
+
readonly name: "company";
|
|
81
|
+
readonly type: "string";
|
|
82
|
+
readonly internalType: "string";
|
|
83
|
+
}, {
|
|
84
|
+
readonly name: "policyId";
|
|
85
|
+
readonly type: "string";
|
|
86
|
+
readonly internalType: "string";
|
|
87
|
+
}, {
|
|
88
|
+
readonly name: "title";
|
|
89
|
+
readonly type: "string";
|
|
90
|
+
readonly internalType: "string";
|
|
91
|
+
}, {
|
|
92
|
+
readonly name: "policyType";
|
|
93
|
+
readonly type: "string";
|
|
94
|
+
readonly internalType: "string";
|
|
95
|
+
}, {
|
|
96
|
+
readonly name: "signerIds";
|
|
97
|
+
readonly type: "string[]";
|
|
98
|
+
readonly internalType: "string[]";
|
|
99
|
+
}, {
|
|
100
|
+
readonly name: "threshold";
|
|
101
|
+
readonly type: "uint256";
|
|
102
|
+
readonly internalType: "uint256";
|
|
103
|
+
}, {
|
|
104
|
+
readonly name: "token";
|
|
105
|
+
readonly type: "address";
|
|
106
|
+
readonly internalType: "address";
|
|
107
|
+
}, {
|
|
108
|
+
readonly name: "maxAmount";
|
|
109
|
+
readonly type: "uint256";
|
|
110
|
+
readonly internalType: "uint256";
|
|
111
|
+
}, {
|
|
112
|
+
readonly name: "recipients";
|
|
113
|
+
readonly type: "address[]";
|
|
114
|
+
readonly internalType: "address[]";
|
|
115
|
+
}];
|
|
116
|
+
}, {
|
|
117
|
+
readonly name: "authId";
|
|
118
|
+
readonly type: "string";
|
|
119
|
+
readonly internalType: "string";
|
|
120
|
+
}, {
|
|
121
|
+
readonly name: "signatures";
|
|
122
|
+
readonly type: "bytes[]";
|
|
123
|
+
readonly internalType: "bytes[]";
|
|
124
|
+
}];
|
|
125
|
+
readonly outputs: readonly [{
|
|
126
|
+
readonly name: "";
|
|
127
|
+
readonly type: "bool";
|
|
128
|
+
readonly internalType: "bool";
|
|
129
|
+
}];
|
|
130
|
+
readonly stateMutability: "nonpayable";
|
|
131
|
+
}, {
|
|
132
|
+
readonly type: "function";
|
|
133
|
+
readonly name: "deactivatePolicy";
|
|
134
|
+
readonly inputs: readonly [{
|
|
135
|
+
readonly name: "clientId";
|
|
136
|
+
readonly type: "bytes32";
|
|
137
|
+
readonly internalType: "bytes32";
|
|
138
|
+
}, {
|
|
139
|
+
readonly name: "company";
|
|
140
|
+
readonly type: "string";
|
|
141
|
+
readonly internalType: "string";
|
|
142
|
+
}, {
|
|
143
|
+
readonly name: "policyId";
|
|
144
|
+
readonly type: "string";
|
|
145
|
+
readonly internalType: "string";
|
|
146
|
+
}, {
|
|
147
|
+
readonly name: "authId";
|
|
148
|
+
readonly type: "string";
|
|
149
|
+
readonly internalType: "string";
|
|
150
|
+
}, {
|
|
151
|
+
readonly name: "adminSignature";
|
|
152
|
+
readonly type: "bytes";
|
|
153
|
+
readonly internalType: "bytes";
|
|
154
|
+
}];
|
|
155
|
+
readonly outputs: readonly [{
|
|
156
|
+
readonly name: "";
|
|
157
|
+
readonly type: "bool";
|
|
158
|
+
readonly internalType: "bool";
|
|
159
|
+
}];
|
|
160
|
+
readonly stateMutability: "nonpayable";
|
|
161
|
+
}, {
|
|
162
|
+
readonly type: "function";
|
|
163
|
+
readonly name: "eip712Domain";
|
|
164
|
+
readonly inputs: readonly [];
|
|
165
|
+
readonly outputs: readonly [{
|
|
166
|
+
readonly name: "fields";
|
|
167
|
+
readonly type: "bytes1";
|
|
168
|
+
readonly internalType: "bytes1";
|
|
169
|
+
}, {
|
|
170
|
+
readonly name: "name";
|
|
171
|
+
readonly type: "string";
|
|
172
|
+
readonly internalType: "string";
|
|
173
|
+
}, {
|
|
174
|
+
readonly name: "version";
|
|
175
|
+
readonly type: "string";
|
|
176
|
+
readonly internalType: "string";
|
|
177
|
+
}, {
|
|
178
|
+
readonly name: "chainId";
|
|
179
|
+
readonly type: "uint256";
|
|
180
|
+
readonly internalType: "uint256";
|
|
181
|
+
}, {
|
|
182
|
+
readonly name: "verifyingContract";
|
|
183
|
+
readonly type: "address";
|
|
184
|
+
readonly internalType: "address";
|
|
185
|
+
}, {
|
|
186
|
+
readonly name: "salt";
|
|
187
|
+
readonly type: "bytes32";
|
|
188
|
+
readonly internalType: "bytes32";
|
|
189
|
+
}, {
|
|
190
|
+
readonly name: "extensions";
|
|
191
|
+
readonly type: "uint256[]";
|
|
192
|
+
readonly internalType: "uint256[]";
|
|
193
|
+
}];
|
|
194
|
+
readonly stateMutability: "view";
|
|
195
|
+
}, {
|
|
196
|
+
readonly type: "function";
|
|
197
|
+
readonly name: "requiredPolicyActivationSignatures";
|
|
198
|
+
readonly inputs: readonly [{
|
|
199
|
+
readonly name: "clientId";
|
|
200
|
+
readonly type: "bytes32";
|
|
201
|
+
readonly internalType: "bytes32";
|
|
202
|
+
}, {
|
|
203
|
+
readonly name: "threshold";
|
|
204
|
+
readonly type: "uint256";
|
|
205
|
+
readonly internalType: "uint256";
|
|
206
|
+
}];
|
|
207
|
+
readonly outputs: readonly [{
|
|
208
|
+
readonly name: "";
|
|
209
|
+
readonly type: "uint256";
|
|
210
|
+
readonly internalType: "uint256";
|
|
211
|
+
}];
|
|
212
|
+
readonly stateMutability: "view";
|
|
213
|
+
}, {
|
|
214
|
+
readonly type: "function";
|
|
215
|
+
readonly name: "stateContract";
|
|
216
|
+
readonly inputs: readonly [];
|
|
217
|
+
readonly outputs: readonly [{
|
|
218
|
+
readonly name: "";
|
|
219
|
+
readonly type: "address";
|
|
220
|
+
readonly internalType: "contract ICofundHelperState";
|
|
221
|
+
}];
|
|
222
|
+
readonly stateMutability: "view";
|
|
223
|
+
}, {
|
|
224
|
+
readonly type: "error";
|
|
225
|
+
readonly name: "DuplicatePolicySigner";
|
|
226
|
+
readonly inputs: readonly [];
|
|
227
|
+
}, {
|
|
228
|
+
readonly type: "error";
|
|
229
|
+
readonly name: "InactiveUser";
|
|
230
|
+
readonly inputs: readonly [];
|
|
231
|
+
}, {
|
|
232
|
+
readonly type: "error";
|
|
233
|
+
readonly name: "InvalidAdmin";
|
|
234
|
+
readonly inputs: readonly [];
|
|
235
|
+
}, {
|
|
236
|
+
readonly type: "error";
|
|
237
|
+
readonly name: "InvalidCompany";
|
|
238
|
+
readonly inputs: readonly [];
|
|
239
|
+
}, {
|
|
240
|
+
readonly type: "error";
|
|
241
|
+
readonly name: "InvalidPolicySigner";
|
|
242
|
+
readonly inputs: readonly [];
|
|
243
|
+
}, {
|
|
244
|
+
readonly type: "error";
|
|
245
|
+
readonly name: "InvalidPolicyType";
|
|
246
|
+
readonly inputs: readonly [];
|
|
247
|
+
}, {
|
|
248
|
+
readonly type: "error";
|
|
249
|
+
readonly name: "InvalidSignature";
|
|
250
|
+
readonly inputs: readonly [];
|
|
251
|
+
}, {
|
|
252
|
+
readonly type: "error";
|
|
253
|
+
readonly name: "InvalidSigner";
|
|
254
|
+
readonly inputs: readonly [];
|
|
255
|
+
}, {
|
|
256
|
+
readonly type: "error";
|
|
257
|
+
readonly name: "InvalidThreshold";
|
|
258
|
+
readonly inputs: readonly [];
|
|
259
|
+
}, {
|
|
260
|
+
readonly type: "error";
|
|
261
|
+
readonly name: "InvalidTitle";
|
|
262
|
+
readonly inputs: readonly [];
|
|
263
|
+
}, {
|
|
264
|
+
readonly type: "error";
|
|
265
|
+
readonly name: "InvalidType";
|
|
266
|
+
readonly inputs: readonly [];
|
|
267
|
+
}, {
|
|
268
|
+
readonly type: "error";
|
|
269
|
+
readonly name: "SignerCountMismatch";
|
|
270
|
+
readonly inputs: readonly [];
|
|
271
|
+
}, {
|
|
272
|
+
readonly type: "error";
|
|
273
|
+
readonly name: "SignersLength";
|
|
274
|
+
readonly inputs: readonly [];
|
|
275
|
+
}, {
|
|
276
|
+
readonly type: "error";
|
|
277
|
+
readonly name: "UnauthorizedCaller";
|
|
278
|
+
readonly inputs: readonly [];
|
|
279
|
+
}];
|