///
The Quicksilver SDK empowers developers to define sophisticated financial interactions with elegant, type-safe code. This example demonstrates how to set up a conditional escrow using the SDK's fluent
32 views
~32 views from guests
Guest views are estimated from total page views. These include anonymous visitors and users who weren't logged in when they viewed the page.
The Quicksilver SDK empowers developers to define sophisticated financial interactions with elegant, type-safe code. This example demonstrates how to set up a conditional escrow using the SDK's fluent Domain-Specific Language (DSL) for managing payments based on specific events, such as milestone approvals.
This page builds upon concepts introduced in [Core Concepts: Fluent DSL], particularly the Builder Pattern for constructing complex logic and Active Records for interacting with financial entities. You can find detailed API specifications for the builders used here in [API Reference: Builders (Condition, Product, Action)] and for the transaction model in [API Reference: Transaction Model].
Here's the complete code from examples/3-conditional-escrow.ts:
The demo begins by initializing the QuicksilverClient and setting up two Account models: a clientAccount and a freelancerAccount. These Account instances are "active records" that allow direct interaction and creation of related financial entities.
ConditionBuilderThe core of this example is the creation of intelligent, programmable money logic using the ConditionBuilder. This builder, accessed via client.condition(), allows you to express rules in a human-readable, fluent manner, abstracting away the underlying JSON structure.
Let's break down the ConditionBuilder's usage:
client.condition(): This is the entry point for defining conditional logic, creating a new ConditionBuilder instance..when(QuickSilverEvent.MilestoneApproved, ctx => ctx.milestone === 'design'):
.when() method specifies a condition's trigger. It takes a QuickSilverEvent (a predefined event like MilestoneApproved) as its first argument.ctx => ctx.milestone === 'design'). This function receives a context object (ctx) when the event is triggered and allows for more granular control over when the condition is actually met (e.g., only release funds if the design milestone is approved). This exemplifies the power of a DSL for expressing domain-specific logic..then(Action.release(1000).to(freelancerAccount.id)):
.then() method defines the actions to be executed if the preceding when condition evaluates to true.Action.release(1000) is a static factory method on the Action class that initiates a fund release. It returns an ActionBuilder, allowing for fluent chaining..to(freelancerAccount.id) is part of the ActionBuilder, specifying the recipient of the funds..then(Action.notify(clientAccount.id, 'Design milestone paid.')): Another action, Action.notify(), is chained to send a message to a specific account when the condition is met..when(QuickSilverEvent.MilestoneApproved, ctx => ctx.milestone === 'deploy'): Multiple .when() clauses can be added to define different sets of actions for different conditions. Here, another MilestoneApproved event, but for the 'deploy' milestone, triggers a different release amount..otherwise(Action.hold('Awaiting milestone approval.')): The .otherwise() method defines fallback actions if none of the preceding when conditions are met. In this case, if no specific milestone is approved, the funds remain on hold with a descriptive message.Once the conditional logic is defined using the ConditionBuilder, it needs to be attached to an actual Transaction.
clientAccount.transaction({...}): This demonstrates the "Active Record" pattern. Instead of calling client.transactions.create(), we call .transaction() directly on the clientAccount object. This implicitly sets the from field of the transaction to clientAccount.id, enhancing readability and reducing errors.transaction_type: 'Escrow': Specifies that this is an escrow transaction, meaning funds are held by the Quicksilver Engine until conditions are met.conditions: projectCondition.getConditions(): This is the crucial part that integrates the fluent ConditionBuilder with the Transaction model. The projectCondition.getConditions() method compiles the defined when/then/otherwise logic into an array of structured Condition objects, which is the format the Quicksilver API expects. This highlights how the SDK hides the complexity of API payload formatting.meta: {...}: Additional metadata can be attached to the transaction for contextual information.Calling await escrowTx.execute() places the funds into escrow, making the conditional logic active. The transaction is now in a pending state, awaiting external events to trigger its conditions.
To trigger the conditional logic in a real-world scenario, an authorized agent or system would invoke escrowTx.triggerEvent(). For example, after the "design" milestone is approved:
When this triggerEvent call occurs, the ConditionBuilder's predicate (ctx => ctx.milestone === 'design') would evaluate to true, and the associated Action.release(1000) and Action.notify() actions would be performed automatically by the Quicksilver Engine.
This example vividly illustrates how the Quicksilver SDK provides a powerful, intuitive, and type-safe way to define and manage complex financial flows, transforming programmable money into an accessible and elegant development experience.