@aromedia/contracts-sdk 0.2.0 → 1.0.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.
Files changed (42) hide show
  1. package/dist/chains.d.cts +10 -0
  2. package/dist/chains.d.ts +10 -0
  3. package/dist/{chunk-7I5N3BGV.js → chunk-BENMJNYO.js} +3 -3
  4. package/dist/chunk-BENMJNYO.js.map +1 -0
  5. package/dist/{chunk-TZQHQLNY.js → chunk-EGUPIYHR.js} +2242 -199
  6. package/dist/chunk-EGUPIYHR.js.map +1 -0
  7. package/dist/{chunk-BYPGUFYV.js → chunk-VUYKYCOD.js} +1 -1
  8. package/dist/chunk-VUYKYCOD.js.map +1 -0
  9. package/dist/{chunk-FWZ7XKFC.js → chunk-WUHQC3GY.js} +22 -12
  10. package/dist/chunk-WUHQC3GY.js.map +1 -0
  11. package/dist/generated/abis.cjs +2245 -200
  12. package/dist/generated/abis.cjs.map +1 -1
  13. package/dist/generated/abis.d.cts +6046 -2916
  14. package/dist/generated/abis.d.ts +6046 -2916
  15. package/dist/generated/abis.js +7 -3
  16. package/dist/generated/addresses.cjs +21 -11
  17. package/dist/generated/addresses.cjs.map +1 -1
  18. package/dist/generated/addresses.js +1 -1
  19. package/dist/hooks/index.cjs +307 -27
  20. package/dist/hooks/index.cjs.map +1 -1
  21. package/dist/hooks/index.d.cts +46 -5
  22. package/dist/hooks/index.d.ts +46 -5
  23. package/dist/hooks/index.js +7 -3
  24. package/dist/hooks/index.js.map +1 -1
  25. package/dist/index.cjs +2421 -313
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +132 -14
  28. package/dist/index.d.ts +132 -14
  29. package/dist/index.js +60 -7
  30. package/dist/index.js.map +1 -1
  31. package/dist/{onboarding-DxL_LpM3.d.ts → onboarding-BD_g7Len.d.ts} +64 -9
  32. package/dist/{onboarding-BHpAJaNW.d.cts → onboarding-DDKwXy8O.d.cts} +64 -9
  33. package/dist/workflows/index.cjs +282 -17
  34. package/dist/workflows/index.cjs.map +1 -1
  35. package/dist/workflows/index.d.cts +1 -1
  36. package/dist/workflows/index.d.ts +1 -1
  37. package/dist/workflows/index.js +3 -3
  38. package/package.json +1 -1
  39. package/dist/chunk-7I5N3BGV.js.map +0 -1
  40. package/dist/chunk-BYPGUFYV.js.map +0 -1
  41. package/dist/chunk-FWZ7XKFC.js.map +0 -1
  42. package/dist/chunk-TZQHQLNY.js.map +0 -1
package/dist/chains.d.cts CHANGED
@@ -52,8 +52,10 @@ declare const aroChains: {
52
52
  fees?: viem.ChainFees<undefined> | undefined;
53
53
  formatters?: undefined;
54
54
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
55
+ client: viem.Client;
55
56
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
56
57
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
58
+ client: viem.Client;
57
59
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
58
60
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
59
61
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -101,8 +103,10 @@ declare const aroChains: {
101
103
  fees?: viem.ChainFees<undefined> | undefined;
102
104
  formatters?: undefined;
103
105
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
106
+ client: viem.Client;
104
107
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
105
108
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
109
+ client: viem.Client;
106
110
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
107
111
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
108
112
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -423,8 +427,10 @@ declare const aroChains: {
423
427
  };
424
428
  };
425
429
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
430
+ client: viem.Client;
426
431
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
427
432
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
433
+ client: viem.Client;
428
434
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
429
435
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
430
436
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -747,8 +753,10 @@ declare const aroChains: {
747
753
  };
748
754
  };
749
755
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
756
+ client: viem.Client;
750
757
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
751
758
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
759
+ client: viem.Client;
752
760
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
753
761
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
754
762
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -803,8 +811,10 @@ declare const aroChains: {
803
811
  fees?: viem.ChainFees<undefined> | undefined;
804
812
  formatters?: undefined;
805
813
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
814
+ client: viem.Client;
806
815
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
807
816
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
817
+ client: viem.Client;
808
818
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
809
819
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
810
820
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
package/dist/chains.d.ts CHANGED
@@ -52,8 +52,10 @@ declare const aroChains: {
52
52
  fees?: viem.ChainFees<undefined> | undefined;
53
53
  formatters?: undefined;
54
54
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
55
+ client: viem.Client;
55
56
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
56
57
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
58
+ client: viem.Client;
57
59
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
58
60
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
59
61
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -101,8 +103,10 @@ declare const aroChains: {
101
103
  fees?: viem.ChainFees<undefined> | undefined;
102
104
  formatters?: undefined;
103
105
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
106
+ client: viem.Client;
104
107
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
105
108
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
109
+ client: viem.Client;
106
110
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
107
111
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
108
112
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -423,8 +427,10 @@ declare const aroChains: {
423
427
  };
424
428
  };
425
429
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
430
+ client: viem.Client;
426
431
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
427
432
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
433
+ client: viem.Client;
428
434
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
429
435
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
430
436
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -747,8 +753,10 @@ declare const aroChains: {
747
753
  };
748
754
  };
749
755
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
756
+ client: viem.Client;
750
757
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
751
758
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
759
+ client: viem.Client;
752
760
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
753
761
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
754
762
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -803,8 +811,10 @@ declare const aroChains: {
803
811
  fees?: viem.ChainFees<undefined> | undefined;
804
812
  formatters?: undefined;
805
813
  prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
814
+ client: viem.Client;
806
815
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
807
816
  }) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
817
+ client: viem.Client;
808
818
  phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
809
819
  }) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
810
820
  runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AroNomination_ABI,
3
3
  AroSBT_ABI
4
- } from "./chunk-TZQHQLNY.js";
4
+ } from "./chunk-EGUPIYHR.js";
5
5
 
6
6
  // src/utils/errors.ts
7
7
  import { BaseError } from "viem";
@@ -171,7 +171,7 @@ async function mintSBTForApproved(sdk, opts) {
171
171
  throw new Error("mintSBTForApproved requires a walletClient on the SDK");
172
172
  }
173
173
  const wallet = sdk.walletClient;
174
- const tier = opts.tier ?? 0 /* STANDARD */;
174
+ const tier = opts.tier === void 0 ? 0n : typeof opts.tier === "bigint" ? opts.tier : BigInt(opts.tier);
175
175
  try {
176
176
  return await wallet.writeContract({
177
177
  account: pickAccount(wallet, opts.account),
@@ -268,4 +268,4 @@ export {
268
268
  computeKycHash,
269
269
  hashKycResult
270
270
  };
271
- //# sourceMappingURL=chunk-7I5N3BGV.js.map
271
+ //# sourceMappingURL=chunk-BENMJNYO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/errors.ts","../src/workflows/onboarding.ts","../src/utils/kycHash.ts","../src/workflows/kyc.ts"],"sourcesContent":["import { BaseError, type ContractFunctionRevertedError } from \"viem\";\n\n/**\n * Friendly error class wrapping a decoded custom error from one of the Aro\n * contracts. We keep both the raw cause (for retry/logging) and a friendly\n * message that maps cleanly to user-facing copy in the dapp.\n */\nexport class AroContractError extends Error {\n readonly errorName: string;\n readonly args: readonly unknown[];\n override readonly cause?: unknown;\n\n constructor(opts: {\n errorName: string;\n args: readonly unknown[];\n message: string;\n cause?: unknown;\n }) {\n super(opts.message);\n this.name = \"AroContractError\";\n this.errorName = opts.errorName;\n this.args = opts.args;\n this.cause = opts.cause;\n }\n}\n\n/**\n * Translate a viem ContractFunctionRevertedError into a known Aro-specific\n * message where possible. Unknown errors are returned as-is so callers\n * can still display the raw revert reason.\n */\nconst FRIENDLY: Record<string, (args: readonly unknown[]) => string> = {\n // AroSBT\n AlreadyHasSBT: ([who]) => `Address ${String(who)} already holds an SBT.`,\n SoulboundTransferBlocked: () => \"SBTs are soulbound — transfers are not permitted.\",\n NoSBT: ([who]) => `Address ${String(who)} does not hold an SBT.`,\n // AroNomination\n CallerHasNoSBT: () => \"Only existing SBT holders can nominate or vouch.\",\n AlreadyNominated: ([who]) => `Address ${String(who)} is already nominated.`,\n CandidateAlreadyMember: ([who]) => `Address ${String(who)} is already a member.`,\n NotNominated: ([who]) => `Address ${String(who)} has not been nominated.`,\n AlreadyVouched: ([voucher, candidate]) =>\n `Address ${String(voucher)} already vouched for ${String(candidate)}.`,\n NominatorCannotVouch: () => \"The original nominator cannot also vouch.\",\n ThresholdCannotBeZero: () => \"Vouch threshold must be at least 1.\",\n NominationNotPending: ([who]) =>\n `Nomination for ${String(who)} is not in PENDING status.`,\n};\n\nexport function decodeAroError(err: unknown): AroContractError | null {\n if (!(err instanceof BaseError)) return null;\n\n const reverted = err.walk(\n (e) => e instanceof Error && e.name === \"ContractFunctionRevertedError\",\n ) as ContractFunctionRevertedError | null;\n if (!reverted || !reverted.data) return null;\n\n const errorName = reverted.data.errorName ?? \"UnknownError\";\n const args = (reverted.data.args ?? []) as readonly unknown[];\n const friendly = FRIENDLY[errorName];\n const message = friendly ? friendly(args) : `Revert: ${errorName}`;\n\n return new AroContractError({ errorName, args, message, cause: err });\n}\n","import type { Account, Hex, WalletClient } from \"viem\";\n\nimport type { AroSdk } from \"../clients/createAroSdk.js\";\nimport { AroNomination_ABI, AroSBT_ABI } from \"../generated/abis.js\";\nimport { AroTier, NominationStatus } from \"../generated/types.js\";\nimport { decodeAroError } from \"../utils/errors.js\";\n\n/**\n * Onboarding workflow helpers.\n *\n * STATUS: The on-chain nomination + admin-mint helpers below are\n * @deprecated and slated for removal in a future major release. The\n * nomination flow has moved to the admin backend (see the admin portal's\n * /access-control + onboarding API), and SBT minting is now\n * candidate-triggered against an EIP-712 voucher\n * (`AroSBT.mintWithApproval`; see `../utils/mintApproval.ts` and\n * `../hooks/useAroSBT.ts::useMintWithApproval`). The on-chain\n * `AroNomination` contract is retained on Sepolia for compatibility but\n * is no longer deployed to new networks.\n *\n * Status-read helpers (`checkMembershipStatus`, `describeOnboardingState`)\n * remain useful regardless of the underlying flow and are NOT deprecated.\n *\n * Original mental model (kept for reference; pre-migration):\n *\n * 1. Existing member calls `nominateCandidate(candidate)`.\n * The nominator's vouch counts as the first vouch automatically.\n *\n * 2. Other existing members call `vouchForCandidate(candidate)`. Each\n * vouch increments the count; once `threshold` is reached (3 per\n * policy), the nomination moves to APPROVED.\n *\n * 3. Off-chain: candidate completes Didit KYC (per MEMO-KYC-PROVIDER).\n * Webhook posts back the verification result.\n *\n * 4. Compliance Officer reviews and either rejects (`clearNomination`)\n * or mints the SBT (`mintSBTForApproved`) with the KYC hash.\n *\n * 5. SBT minted → AccessManager grants member roles → access unlocked.\n *\n * The write paths use `walletClient.writeContract` directly rather than\n * the typed contract `.write` namespace. This is intentional: viem\n * narrows the contract handle's write surface away when the SDK is\n * instantiated without a walletClient, but workflows are run on demand\n * after the dapp's wallet connects.\n */\n\nexport interface MembershipStatus {\n hasSBT: boolean;\n /**\n * Opaque tier id (uint256) as returned by AroSBT.getMemberData. Compare\n * with `tierLabel`, `tierAtLeast`, or the AroTier enum values for the\n * conventional defaults; semantics live off-chain in the admin backend.\n */\n tier?: bigint;\n tokenId?: bigint;\n memberId?: bigint;\n issuanceDate?: bigint;\n kycHash?: `0x${string}`;\n}\n\n/**\n * One-call read for the dapp's access-gate page. Returns whether the\n * connected wallet holds an SBT and, if so, decoded member data.\n *\n * Implementation note: reads are issued via `publicClient.readContract`\n * with the ABI rather than the typed contract instance's `.read`\n * namespace. viem ≥2.50 narrows `contract.read.*` based on whether a\n * walletClient was bound at construction time; bypassing that gives us a\n * stable, version-independent signature.\n */\nexport async function checkMembershipStatus(\n sdk: AroSdk,\n account: `0x${string}`,\n): Promise<MembershipStatus> {\n const sbtAddress = sdk.addressOf(\"AroSBT\");\n\n const hasSBT = (await sdk.publicClient.readContract({\n address: sbtAddress,\n abi: AroSBT_ABI,\n functionName: \"hasSBT\",\n args: [account],\n })) as boolean;\n if (!hasSBT) return { hasSBT: false };\n\n const tokenId = (await sdk.publicClient.readContract({\n address: sbtAddress,\n abi: AroSBT_ABI,\n functionName: \"tokenOfMember\",\n args: [account],\n })) as bigint;\n const data = (await sdk.publicClient.readContract({\n address: sbtAddress,\n abi: AroSBT_ABI,\n functionName: \"getMemberData\",\n args: [account],\n })) as {\n memberId: bigint;\n issuanceDate: bigint;\n tier: bigint; // uint256 since the Tier enum was retired; semantics off-chain\n kycHash: `0x${string}`;\n };\n\n return {\n hasSBT: true,\n tokenId,\n tier: data.tier,\n memberId: data.memberId,\n issuanceDate: data.issuanceDate,\n kycHash: data.kycHash,\n };\n}\n\nexport interface NominationSnapshot {\n status: NominationStatus;\n nominator: `0x${string}`;\n vouchers: readonly `0x${string}`[];\n nominatedAt: bigint;\n voucherCount: number;\n threshold: number;\n remaining: number;\n ready: boolean;\n}\n\n/**\n * Snapshot of the candidate's progress through the vouch flow. The dapp\n * shows this as \"you need N more vouches\" in the onboarding UI.\n *\n * @deprecated On-chain nomination is retired. Read this status from the\n * admin portal's onboarding API instead. Removal planned in a future\n * major release.\n */\nexport async function getNominationSnapshot(\n sdk: AroSdk,\n candidate: `0x${string}`,\n): Promise<NominationSnapshot> {\n const nominationAddress = sdk.addressOf(\"AroNomination\");\n const [nomination, threshold] = (await Promise.all([\n sdk.publicClient.readContract({\n address: nominationAddress,\n abi: AroNomination_ABI,\n functionName: \"getNomination\",\n args: [candidate],\n }),\n sdk.publicClient.readContract({\n address: nominationAddress,\n abi: AroNomination_ABI,\n functionName: \"threshold\",\n }),\n ])) as [\n [\n `0x${string}`,\n readonly `0x${string}`[],\n bigint,\n number,\n ],\n bigint,\n ];\n\n const [nominator, vouchers, nominatedAt, status] = nomination;\n const voucherCount = vouchers.length;\n const thresholdN = Number(threshold);\n\n return {\n status: status as NominationStatus,\n nominator,\n vouchers,\n nominatedAt,\n voucherCount,\n threshold: thresholdN,\n remaining: Math.max(0, thresholdN - voucherCount),\n ready: status === NominationStatus.APPROVED,\n };\n}\n\n/** Resolve the account to use when sending a write transaction. */\nfunction pickAccount(\n wallet: WalletClient,\n passed?: Account | `0x${string}`,\n): Account | `0x${string}` {\n if (passed) return passed;\n if (wallet.account) return wallet.account;\n throw new Error(\n \"No account available — pass one explicitly or use a walletClient bound to an account.\",\n );\n}\n\n/**\n * @deprecated On-chain nomination is retired. Use the admin portal's\n * onboarding API instead. Removal planned in a future major release.\n *\n * Send `nominate(candidate)` from the connected wallet. The contract\n * gates this behind \"caller must already be an SBT holder\"; we surface\n * the right user-facing message via `AroContractError`.\n */\nexport async function nominateCandidate(\n sdk: AroSdk,\n candidate: `0x${string}`,\n opts: { account?: Account | `0x${string}` } = {},\n): Promise<Hex> {\n if (!sdk.walletClient) {\n throw new Error(\"nominateCandidate requires a walletClient on the SDK\");\n }\n const wallet = sdk.walletClient;\n try {\n return await wallet.writeContract({\n account: pickAccount(wallet, opts.account),\n chain: wallet.chain,\n address: sdk.addressOf(\"AroNomination\"),\n abi: AroNomination_ABI,\n functionName: \"nominate\",\n args: [candidate],\n });\n } catch (err) {\n throw decodeAroError(err) ?? err;\n }\n}\n\n/**\n * @deprecated On-chain vouching is retired. Use the admin portal's\n * onboarding API instead. Removal planned in a future major release.\n */\nexport async function vouchForCandidate(\n sdk: AroSdk,\n candidate: `0x${string}`,\n opts: { account?: Account | `0x${string}` } = {},\n): Promise<Hex> {\n if (!sdk.walletClient) {\n throw new Error(\"vouchForCandidate requires a walletClient on the SDK\");\n }\n const wallet = sdk.walletClient;\n try {\n return await wallet.writeContract({\n account: pickAccount(wallet, opts.account),\n chain: wallet.chain,\n address: sdk.addressOf(\"AroNomination\"),\n abi: AroNomination_ABI,\n functionName: \"vouch\",\n args: [candidate],\n });\n } catch (err) {\n throw decodeAroError(err) ?? err;\n }\n}\n\n/**\n * @deprecated On-chain nomination is retired. The admin portal handles\n * rejections in its backend. Removal planned in a future major release.\n *\n * Admin path: clear (reject) a nomination. Restricted to authorized roles\n * via AccessManager; will revert with AccessManagedUnauthorized if the\n * caller lacks the role.\n */\nexport async function clearNomination(\n sdk: AroSdk,\n candidate: `0x${string}`,\n opts: { account?: Account | `0x${string}` } = {},\n): Promise<Hex> {\n if (!sdk.walletClient) {\n throw new Error(\"clearNomination requires a walletClient on the SDK\");\n }\n const wallet = sdk.walletClient;\n try {\n return await wallet.writeContract({\n account: pickAccount(wallet, opts.account),\n chain: wallet.chain,\n address: sdk.addressOf(\"AroNomination\"),\n abi: AroNomination_ABI,\n functionName: \"clearNomination\",\n args: [candidate],\n });\n } catch (err) {\n throw decodeAroError(err) ?? err;\n }\n}\n\nexport interface MintSBTOpts {\n to: `0x${string}`;\n /** keccak256 of the verified KYC payload — see utils/kycHash.ts. */\n kycHash: `0x${string}`;\n /** Off-chain profile metadata URI (ipfs://... or https://...). */\n metadataURI: string;\n /**\n * Initial tier id (opaque uint256; defaults to 0 / \"Standard\"). Accepts\n * AroTier enum values for the conventional defaults, or any bigint id\n * defined off-chain by the admin backend.\n */\n tier?: bigint | AroTier;\n account?: Account | `0x${string}`;\n}\n\n/**\n * @deprecated SBT minting is now candidate-triggered against an EIP-712\n * voucher (`AroSBT.mintWithApproval`). The admin backend issues vouchers\n * via `signMintApproval` in `../utils/mintApproval`, and the candidate\n * submits via the `useMintWithApproval` hook in `../hooks/useAroSBT`.\n * The direct admin mint below remains gated by ROLE_MINTER but is the\n * legacy path; removal planned in a future major release.\n *\n * Admin-only: mint the SBT for an approved candidate. The contract gates\n * this behind ROLE_MINTER via AccessManager (per AroMediaAccessManager).\n *\n * Calls the 4-arg mint overload explicitly so the tier is always\n * specified — the 3-arg overload silently defaults to STANDARD, which\n * we don't want callers to rely on implicitly.\n */\nexport async function mintSBTForApproved(\n sdk: AroSdk,\n opts: MintSBTOpts,\n): Promise<Hex> {\n if (!sdk.walletClient) {\n throw new Error(\"mintSBTForApproved requires a walletClient on the SDK\");\n }\n const wallet = sdk.walletClient;\n const tier: bigint =\n opts.tier === undefined\n ? 0n\n : typeof opts.tier === \"bigint\"\n ? opts.tier\n : BigInt(opts.tier);\n try {\n return await wallet.writeContract({\n account: pickAccount(wallet, opts.account),\n chain: wallet.chain,\n address: sdk.addressOf(\"AroSBT\"),\n abi: AroSBT_ABI,\n // Disambiguate the overload by writing the explicit 4-arg form.\n functionName: \"mint\",\n args: [opts.to, opts.kycHash, opts.metadataURI, tier],\n });\n } catch (err) {\n throw decodeAroError(err) ?? err;\n }\n}\n\n/**\n * Convenience: enumerate the policy-derived next action for a wallet so\n * the onboarding UI can render the correct step (Connect → Nominate →\n * Vouch → KYC → Mint).\n */\nexport type OnboardingStep =\n | \"needs-wallet\"\n | \"needs-nomination\"\n | \"needs-vouches\"\n | \"needs-kyc\"\n | \"needs-admin-mint\"\n | \"member\";\n\nexport interface OnboardingState {\n step: OnboardingStep;\n membership: MembershipStatus;\n nomination?: NominationSnapshot;\n}\n\nexport async function describeOnboardingState(\n sdk: AroSdk,\n wallet: `0x${string}` | undefined,\n): Promise<OnboardingState> {\n if (!wallet) {\n return {\n step: \"needs-wallet\",\n membership: { hasSBT: false },\n };\n }\n const membership = await checkMembershipStatus(sdk, wallet);\n if (membership.hasSBT) return { step: \"member\", membership };\n\n const nomination = await getNominationSnapshot(sdk, wallet);\n if (nomination.status === NominationStatus.NONE) {\n return { step: \"needs-nomination\", membership, nomination };\n }\n if (nomination.status === NominationStatus.PENDING) {\n return { step: \"needs-vouches\", membership, nomination };\n }\n if (nomination.status === NominationStatus.APPROVED) {\n // The dapp distinguishes these client-side: if the user has finished\n // Didit, we move to needs-admin-mint; otherwise needs-kyc. The SDK\n // returns the on-chain reading and lets the UI overlay KYC state.\n return { step: \"needs-kyc\", membership, nomination };\n }\n // CLEARED — admin rejected. Surfaced as \"needs-nomination\" because the\n // candidate would need a fresh nomination from another member.\n return { step: \"needs-nomination\", membership, nomination };\n}\n","import { keccak256, stringToBytes } from \"viem\";\nimport type { Hex } from \"viem\";\n\n/**\n * KYC hash computation as required by the ARO KYC/AML/CFT Policy §6.2.\n *\n * The on-chain `kycHash` (bytes32) is a keccak256 over a deterministic\n * serialization of the verified identity payload returned by the KYC\n * provider (Didit, per MEMO-KYC-PROVIDER). Storing only the hash on-chain\n * gives us tamper-evidence without ever exposing PII on a public ledger.\n *\n * Why deterministic serialization: the same identity payload must hash to\n * the same value across processes, languages, and timezones — JSON object\n * key order is otherwise implementation-dependent, which would silently\n * change the hash and break audit-trail equivalence.\n *\n * The serializer:\n * • Sorts object keys recursively.\n * • Coerces undefined → null so it survives JSON.stringify.\n * • Refuses functions, symbols, bigints, and class instances.\n */\nexport interface KycPayload {\n /** Provider's verification session id (e.g. Didit `session_id`). */\n sessionId: string;\n /** Provider name — lets us roll over to a different vendor later. */\n provider: string;\n /** Verification status returned by the provider. */\n status: string;\n /** ISO-8601 timestamp of the provider's verification decision. */\n verifiedAt: string;\n /** Wallet address the verification is bound to. */\n walletAddress: `0x${string}`;\n /** Anything else the provider returned that we want anchored on-chain. */\n attributes?: Record<string, JsonScalar | JsonScalar[]>;\n}\n\ntype JsonScalar = string | number | boolean | null;\n\nfunction stableStringify(value: unknown): string {\n if (value === undefined) return \"null\";\n if (value === null) return \"null\";\n if (typeof value === \"string\") return JSON.stringify(value);\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) {\n throw new Error(\"KYC payload must not contain Infinity or NaN\");\n }\n return JSON.stringify(value);\n }\n if (typeof value === \"boolean\") return JSON.stringify(value);\n if (Array.isArray(value)) {\n return `[${value.map(stableStringify).join(\",\")}]`;\n }\n if (typeof value === \"object\") {\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);\n return `{${parts.join(\",\")}}`;\n }\n throw new Error(`Unsupported type in KYC payload: ${typeof value}`);\n}\n\n/**\n * Compute the bytes32 hash stored on-chain in AroSBT.MemberData.kycHash.\n *\n * @param payload Verified identity payload from the KYC provider.\n * @returns Hex-encoded keccak256 (`0x...`, 66 chars).\n */\nexport function computeKycHash(payload: KycPayload): Hex {\n const serialized = stableStringify(payload);\n return keccak256(stringToBytes(serialized));\n}\n\n/** Re-export for convenience when the dapp needs to hash arbitrary blobs. */\nexport { keccak256, stringToBytes } from \"viem\";\n","import { computeKycHash, type KycPayload } from \"../utils/kycHash.js\";\n\n/**\n * KYC workflow helpers. These are intentionally provider-agnostic — the\n * SDK does not call Didit directly (that's the dapp/backend's job per\n * MEMO-KYC-PROVIDER), but it owns the canonical mapping from a verified\n * payload to the on-chain `kycHash` bytes32.\n *\n * Splitting concerns this way means we can swap KYC vendors later without\n * touching anything that hashes or anchors data on-chain.\n */\nexport { computeKycHash, type KycPayload };\n\n/**\n * Normalized representation of a KYC verification result. Whatever vendor\n * the dapp uses, results should be mapped into this shape before hashing\n * so the on-chain hash is vendor-stable.\n */\nexport interface NormalizedKycResult {\n /** Vendor name — \"didit\" today. */\n provider: string;\n /** Vendor session id. */\n sessionId: string;\n /** Pass-through status string from the vendor. */\n status: \"verified\" | \"failed\" | \"pending\" | \"expired\";\n /** ISO-8601 timestamp from the vendor's verification decision. */\n verifiedAt: string;\n /** Wallet the verification is bound to. */\n walletAddress: `0x${string}`;\n /** Optional extras the dapp wants anchored on-chain. */\n attributes?: Record<string, string | number | boolean | null>;\n}\n\n/**\n * Hash a normalized KYC result for SBT minting. Wrapper around\n * `computeKycHash` that enforces the shape we expect from the dapp.\n */\nexport function hashKycResult(result: NormalizedKycResult): `0x${string}` {\n if (result.status !== \"verified\") {\n throw new Error(\n `Refusing to hash an un-verified KYC result (status=${result.status}).`,\n );\n }\n return computeKycHash({\n provider: result.provider,\n sessionId: result.sessionId,\n status: result.status,\n verifiedAt: result.verifiedAt,\n walletAddress: result.walletAddress,\n attributes: result.attributes,\n });\n}\n"],"mappings":"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","names":["keccak256","stringToBytes"]}