Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/funding-receipt-field-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hypercerts-org/lexicon": minor
---

Normalize funding receipt fields: `from`/`to` as union of DID ref + strongRef, `from` optional, `for` changed to strongRef
26 changes: 13 additions & 13 deletions SCHEMAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,19 +250,19 @@ Hypercerts-specific lexicons for tracking impact work and claims.

#### Properties

| Property | Type | Required | Description | Comments |
| ---------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| `from` | `ref` | ❌ | DID of the sender who transferred the funds. This field is optional, and can be left undefined to represent anonymity. | |
| `to` | `string` | ✅ | The recipient of the funds. Can be identified by DID or a clear-text name. | maxLength: 2048 |
| `amount` | `string` | ✅ | Amount of funding received as a numeric string (e.g. '1000.50'). | maxLength: 50 |
| `currency` | `string` | ✅ | Currency of the payment (e.g. EUR, USD, ETH). | maxLength: 10 |
| `paymentRail` | `string` | ❌ | How the funds were transferred (e.g. bank_transfer, credit_card, onchain, cash, check, payment_processor). | maxLength: 50 |
| `paymentNetwork` | `string` | ❌ | Optional network within the payment rail (e.g. arbitrum, ethereum, sepa, visa, paypal). | maxLength: 50 |
| `transactionId` | `string` | ❌ | Identifier of the underlying payment transaction (e.g. bank reference, onchain transaction hash, or processor-specific ID). Use paymentNetwork to specify the network where applicable. | maxLength: 256 |
| `for` | `string` | ❌ | Optional reference to the activity, project, or organization this funding relates to. | |
| `notes` | `string` | ❌ | Optional notes or additional context for this funding receipt. | maxLength: 500 |
| `occurredAt` | `string` | ❌ | Timestamp when the payment occurred. | |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this receipt record was created. | |
| Property | Type | Required | Description | Comments |
| ---------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
| `from` | `union` | ❌ | The sender of the funds (either an account DID or a strong reference to a record). Optional — omit to represent anonymity. | |
| `to` | `union` | ✅ | The recipient of the funds (either an account DID or a strong reference to a record). | |
| `amount` | `string` | ✅ | Amount of funding received as a numeric string (e.g. '1000.50'). | maxLength: 50 |
| `currency` | `string` | ✅ | Currency of the payment (e.g. EUR, USD, ETH). | maxLength: 10 |
| `paymentRail` | `string` | ❌ | How the funds were transferred (e.g. bank_transfer, credit_card, onchain, cash, check, payment_processor). | maxLength: 50 |
| `paymentNetwork` | `string` | ❌ | Optional network within the payment rail (e.g. arbitrum, ethereum, sepa, visa, paypal). | maxLength: 50 |
| `transactionId` | `string` | ❌ | Identifier of the underlying payment transaction (e.g. bank reference, onchain transaction hash, or processor-specific ID). Use paymentNetwork to specify the network where applicable. | maxLength: 256 |
| `for` | `ref` | ❌ | Optional strong reference to the activity, project, or organization this funding relates to. | |
| `notes` | `string` | ❌ | Optional notes or additional context for this funding receipt. | maxLength: 500 |
| `occurredAt` | `string` | ❌ | Timestamp when the payment occurred. | |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this receipt record was created. | |

---

Expand Down
18 changes: 9 additions & 9 deletions lexicons/org/hypercerts/funding/receipt.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
"required": ["to", "amount", "currency", "createdAt"],
"properties": {
"from": {
"type": "ref",
"ref": "app.certified.defs#did",
"description": "DID of the sender who transferred the funds. This field is optional, and can be left undefined to represent anonymity."
"type": "union",
"description": "The sender of the funds (either an account DID or a strong reference to a record). Optional — omit to represent anonymity.",
"refs": ["app.certified.defs#did", "com.atproto.repo.strongRef"]
},
"to": {
"type": "string",
"description": "The recipient of the funds. Can be identified by DID or a clear-text name.",
"maxLength": 2048
"type": "union",
"description": "The recipient of the funds (either an account DID or a strong reference to a record).",
"refs": ["app.certified.defs#did", "com.atproto.repo.strongRef"]
},
"amount": {
"type": "string",
Expand Down Expand Up @@ -46,9 +46,9 @@
"maxLength": 256
},
"for": {
"type": "string",
"format": "at-uri",
"description": "Optional reference to the activity, project, or organization this funding relates to."
"type": "ref",
"ref": "com.atproto.repo.strongRef",
"description": "Optional strong reference to the activity, project, or organization this funding relates to."
},
"notes": {
"type": "string",
Expand Down
61 changes: 52 additions & 9 deletions tests/validate-funding-receipt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { validate, ids } from "../generated/lexicons.js";
import * as FundingReceipt from "../generated/types/org/hypercerts/funding/receipt.js";

const validBase = {
to: "Alice",
to: { $type: "app.certified.defs#did", did: "did:plc:recipient123" },
amount: "1000.00",
currency: "USD",
createdAt: "2024-01-01T00:00:00Z",
Expand All @@ -17,7 +17,7 @@ describe("org.hypercerts.funding.receipt", () => {
});
expect(result.success).toBe(true);
if (result.success) {
expect(result.value.to).toBe("Alice");
expect(result.value.to).toMatchObject({ did: "did:plc:recipient123" });
expect(result.value.amount).toBe("1000.00");
expect(result.value.currency).toBe("USD");
}
Expand All @@ -34,27 +34,58 @@ describe("org.hypercerts.funding.receipt", () => {
}
});

it("should accept a valid record with the optional 'from' field present", () => {
it("should accept a valid record with 'from' as a DID", () => {
const result = FundingReceipt.validateMain({
$type: ids.OrgHypercertsFundingReceipt,
...validBase,
from: { did: "did:plc:abc123" },
from: { $type: "app.certified.defs#did", did: "did:plc:abc123" },
});
expect(result.success).toBe(true);
if (result.success) {
expect(result.value.from).toMatchObject({ did: "did:plc:abc123" });
}
});

it("should accept 'to' as a strongRef", () => {
const result = FundingReceipt.validateMain({
$type: ids.OrgHypercertsFundingReceipt,
to: {
$type: "com.atproto.repo.strongRef",
uri: "at://did:plc:recipient123/org.hypercerts.contributor.information/tid456",
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
},
amount: "1000.00",
currency: "USD",
createdAt: "2024-01-01T00:00:00Z",
});
expect(result.success).toBe(true);
});

it("should accept 'from' as a strongRef", () => {
const result = FundingReceipt.validateMain({
$type: ids.OrgHypercertsFundingReceipt,
...validBase,
from: {
$type: "com.atproto.repo.strongRef",
uri: "at://did:plc:abc123/org.hypercerts.contributor.information/tid789",
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
},
});
expect(result.success).toBe(true);
});

it("should accept a valid record with all optional fields present", () => {
const result = FundingReceipt.validateMain({
$type: ids.OrgHypercertsFundingReceipt,
...validBase,
from: { did: "did:plc:abc123" },
from: { $type: "app.certified.defs#did", did: "did:plc:abc123" },
paymentRail: "onchain",
paymentNetwork: "ethereum",
transactionId: "0xabc123",
for: "at://did:plc:abc123/org.hypercerts.claim.activity/tid123",
for: {
uri: "at://did:plc:abc123/org.hypercerts.claim.activity/tid123",
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
},
notes: "Quarterly donation",
occurredAt: "2024-01-01T00:00:00Z",
});
Expand All @@ -73,7 +104,11 @@ describe("org.hypercerts.funding.receipt", () => {

it("should reject a record missing the required 'amount' field", () => {
const result = validate(
{ to: "Alice", currency: "EUR", createdAt: "2024-01-01T00:00:00Z" },
{
to: { $type: "app.certified.defs#did", did: "did:plc:recipient123" },
currency: "EUR",
createdAt: "2024-01-01T00:00:00Z",
},
ids.OrgHypercertsFundingReceipt,
"main",
false,
Expand All @@ -83,7 +118,11 @@ describe("org.hypercerts.funding.receipt", () => {

it("should reject a record missing the required 'currency' field", () => {
const result = validate(
{ to: "Alice", amount: "500", createdAt: "2024-01-01T00:00:00Z" },
{
to: { $type: "app.certified.defs#did", did: "did:plc:recipient123" },
amount: "500",
createdAt: "2024-01-01T00:00:00Z",
},
ids.OrgHypercertsFundingReceipt,
"main",
false,
Expand All @@ -93,7 +132,11 @@ describe("org.hypercerts.funding.receipt", () => {

it("should reject a record missing the required 'createdAt' field", () => {
const result = validate(
{ to: "Alice", amount: "500", currency: "USD" },
{
to: { $type: "app.certified.defs#did", did: "did:plc:recipient123" },
amount: "500",
currency: "USD",
},
ids.OrgHypercertsFundingReceipt,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"main",
false,
Expand Down
Loading