Skip to content
Open
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/silly-owls-attest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/server": patch
---

馃 add travel rule to offramp transfers
14 changes: 14 additions & 0 deletions server/test/utils/bridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2775,6 +2775,13 @@ describe("bridge utils", () => {
source: { currency: "usdc", payment_rail: "optimism" },
destination: { currency: "usd", payment_rail: "ach", external_account_id: "ext-acc-1" },
features: { flexible_amount: true, static_template: true, allow_any_from_address: true },
travel_rule_data: {
originator: {
is_self: true,
wallet_type: "self_custodied", // cspell:ignore custodied
wallet_attested_ownership_at: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/) as unknown,
},
},
});
});

Expand All @@ -2795,6 +2802,13 @@ describe("bridge utils", () => {
source: { currency: "usdc", payment_rail: "optimism" },
destination: { currency: "eur", payment_rail: "sepa", external_account_id: "ext-acc-1" },
features: { flexible_amount: true, static_template: true, allow_any_from_address: true },
travel_rule_data: {
originator: {
is_self: true,
wallet_type: "self_custodied", // cspell:ignore custodied
wallet_attested_ownership_at: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/) as unknown,
},
},
});
});

Expand Down
16 changes: 16 additions & 0 deletions server/utils/ramps/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,13 @@ export async function getOfframpDepositDetails(
external_account_id: externalAccountId,
},
features: { flexible_amount: true, static_template: true, allow_any_from_address: true },
travel_rule_data: {
Comment thread
mainqueg marked this conversation as resolved.
originator: {
is_self: true,
wallet_type: "self_custodied", // cspell:ignore custodied
wallet_attested_ownership_at: new Date().toISOString(),
Comment on lines +962 to +963

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not attest arbitrary source wallets as self-custodied

When users fund these instructions from an exchange or any other address, this template still sends wallet_type: "self_custodied" with a fresh attestation timestamp even though allow_any_from_address explicitly allows deposits regardless of from_address. Bridge's docs define self_custodied as customer-owned and require ownership validation before sending wallet_attested_ownership_at (https://apidocs.bridge.xyz/get-started/guides/move-money/crypto-travel-rule), so this can misreport or reject otherwise valid off-ramp deposits unless the source is constrained to a verified wallet or travel-rule data is submitted per movement once the real source is known.

Useful? React with 馃憤聽/ 馃憥.

},
},
});

return [
Expand Down Expand Up @@ -1933,6 +1940,15 @@ const CreateTransfer = object({
blockchain_memo: optional(string()),
swift_charges: optional(picklist(["ben", "our", "sha"])),
}),
travel_rule_data: optional(
object({
originator: object({
is_self: boolean(),
wallet_type: picklist(["external", "hosted", "self_custodied"]), // cspell:ignore custodied
wallet_attested_ownership_at: string(),
}),
}),
),
});

const Transfer = object({
Expand Down