Where Each Platform Excels
Canton/DAML's Primary Domain
Atomic delivery-versus-payment settlement between custodians, eliminating counterparty risk and enabling same-day settlement.
template DVPSettlement
with
seller, buyer : Party
sellerCustodian, buyerCustodian : Party
security : SecurityRef
payment : PaymentRef
where
signatory seller, buyer, sellerCustodian, buyerCustodian
choice Settle : (ContractId Security, ContractId Cash)
controller seller, buyer
do
-- Both legs execute atomically or neither does
securityCid <- exercise security.cid Transfer
with newHolder = buyer
cashCid <- exercise payment.cid Transfer
with newOwner = seller
return (securityCid, cashCid)
Multi-bank loan origination and servicing with real-time position updates across all participants.
template LoanFacility
with
borrower : Party
agent : Party
lenders : [Party]
commitments : [(Party, Decimal)]
totalAmount : Decimal
where
signatory agent, borrower
observer lenders
choice Drawdown : ContractId FundedLoan
with amount : Decimal
controller borrower
do
-- Pro-rata funding from each lender
fundings <- forA commitments \(lender, commitment) -> do
let share = amount * (commitment / totalAmount)
exercise (lookupFunding lender) Fund with share
create FundedLoan with ..
Collateralized short-term borrowing with automatic margin calls and substitution rights.
template RepoAgreement
with
lender, borrower : Party
collateral : [SecurityId]
principal : Decimal
rate : Decimal
maturity : Date
where
signatory lender, borrower
choice MarginCall : ContractId RepoAgreement
with additionalCollateral : [SecurityId]
controller lender
do
-- Automatically triggered when collateral value drops
create this with
collateral = collateral <> additionalCollateral
CosmWasm's Primary Domain
Permissionless token swaps with liquidity pools, following the constant product formula (x * y = k).
pub fn execute_swap(
deps: DepsMut,
info: MessageInfo,
offer_asset: Asset,
min_return: Uint128,
) -> Result<Response, ContractError> {
let pool = POOL.load(deps.storage)?;
// Constant product formula: x * y = k
let offer_amount = offer_asset.amount;
let (offer_pool, ask_pool) = match offer_asset.info {
AssetInfo::Token { .. } => (pool.token_reserve, pool.native_reserve),
AssetInfo::Native { .. } => (pool.native_reserve, pool.token_reserve),
};
// Calculate return amount (with 0.3% fee)
let fee = offer_amount * Decimal::percent(3) / Decimal::percent(1000);
let offer_after_fee = offer_amount - fee;
let return_amount = ask_pool - (offer_pool * ask_pool) / (offer_pool + offer_after_fee);
ensure!(return_amount >= min_return, ContractError::SlippageExceeded {});
// Update pool reserves
POOL.update(deps.storage, |mut pool| -> StdResult<_> {
pool.update_reserves(offer_asset.info.clone(), offer_amount, return_amount);
Ok(pool)
})?;
// Transfer tokens
let messages = vec![
offer_asset.transfer_from(&info.sender, &env.contract.address)?,
Asset::new(ask_asset_info, return_amount).transfer(&info.sender)?,
];
Ok(Response::new()
.add_messages(messages)
.add_attribute("action", "swap")
.add_attribute("return_amount", return_amount))
}
Collateralized borrowing with dynamic interest rates based on utilization.
pub fn execute_borrow(
deps: DepsMut,
env: Env,
info: MessageInfo,
amount: Uint128,
) -> Result<Response, ContractError> {
let user = USER_STATE.load(deps.storage, &info.sender)?;
let market = MARKET.load(deps.storage)?;
// Check collateral ratio
let collateral_value = calculate_collateral_value(deps.as_ref(), &user)?;
let borrow_value = user.borrowed + amount;
let ltv = borrow_value * PRECISION / collateral_value;
ensure!(ltv <= market.max_ltv, ContractError::InsufficientCollateral {});
// Update interest rates based on utilization
let utilization = (market.total_borrowed + amount) * PRECISION / market.total_supplied;
let new_rate = calculate_interest_rate(utilization, &market.rate_model);
MARKET.update(deps.storage, |mut m| -> StdResult<_> {
m.total_borrowed += amount;
m.interest_rate = new_rate;
Ok(m)
})?;
Ok(Response::new()
.add_message(BankMsg::Send {
to_address: info.sender.to_string(),
amount: coins(amount.u128(), market.denom),
}))
}
CW721-compatible NFT trading with auctions and instant sales.
pub fn execute_list_nft(
deps: DepsMut,
info: MessageInfo,
nft_contract: String,
token_id: String,
price: Coin,
) -> Result<Response, ContractError> {
// Verify ownership via CW721 query
let owner: OwnerOfResponse = deps.querier.query_wasm_smart(
&nft_contract,
&Cw721QueryMsg::OwnerOf { token_id: token_id.clone(), .. },
)?;
ensure!(owner.owner == info.sender, ContractError::NotOwner {});
// Create listing
LISTINGS.save(deps.storage, (&nft_contract, &token_id), &Listing {
seller: info.sender,
price,
listed_at: env.block.time,
})?;
Ok(Response::new().add_attribute("action", "list_nft"))
}
Bank-guaranteed trade finance with document verification and payment automation.
template LetterOfCredit
with
applicant : Party -- Buyer
beneficiary : Party -- Seller
issuingBank : Party
advisingBank : Party
amount : Decimal
documents : [DocumentType]
expiryDate : Date
where
signatory issuingBank, applicant
observer beneficiary, advisingBank
choice PresentDocuments : ContractId DocumentPresentation
with docs : [Document]
controller beneficiary
do
-- Validate all required documents present
assertMsg "Missing documents" $
all (`elem` map docType docs) documents
create DocumentPresentation with ..
choice ApproveAndPay : ()
controller issuingBank
do
-- Verify documents match LC terms
-- Release payment to beneficiary
exercise paymentObligation Release with
recipient = beneficiary
Track product journey from source to consumer with immutable on-chain records.
pub struct ProductNFT {
pub product_id: String,
pub origin: Location,
pub certifications: Vec<Certification>,
pub journey: Vec<CheckpointEvent>,
}
pub fn execute_add_checkpoint(
deps: DepsMut,
info: MessageInfo,
token_id: String,
checkpoint: CheckpointEvent,
) -> Result<Response, ContractError> {
// Verify caller is authorized handler
let handlers = AUTHORIZED_HANDLERS.load(deps.storage)?;
ensure!(handlers.contains(&info.sender), ContractError::Unauthorized {});
// Add checkpoint to NFT metadata
PRODUCT_NFTS.update(deps.storage, &token_id, |nft| -> StdResult<_> {
let mut nft = nft.ok_or(StdError::not_found("NFT"))?;
nft.journey.push(checkpoint);
Ok(nft)
})?;
Ok(Response::new())
}
Decentralized autonomous organizations with proposal creation, voting, and execution.
pub fn execute_vote(
deps: DepsMut,
env: Env,
info: MessageInfo,
proposal_id: u64,
vote: VoteOption,
) -> Result<Response, ContractError> {
let proposal = PROPOSALS.load(deps.storage, proposal_id)?;
// Check voting period
ensure!(
env.block.time < proposal.voting_end,
ContractError::VotingEnded {}
);
// Get voter's token balance (voting power)
let voting_power = query_token_balance(deps.as_ref(), &info.sender)?;
ensure!(!voting_power.is_zero(), ContractError::NoVotingPower {});
// Record vote
VOTES.save(deps.storage, (proposal_id, &info.sender), &Vote {
option: vote.clone(),
power: voting_power,
})?;
// Update proposal tallies
PROPOSALS.update(deps.storage, proposal_id, |p| -> StdResult<_> {
let mut p = p.unwrap();
match vote {
VoteOption::Yes => p.yes_votes += voting_power,
VoteOption::No => p.no_votes += voting_power,
VoteOption::Abstain => p.abstain_votes += voting_power,
}
Ok(p)
})?;
Ok(Response::new()
.add_attribute("action", "vote")
.add_attribute("proposal_id", proposal_id.to_string())
.add_attribute("voter", info.sender)
.add_attribute("vote", format!("{:?}", vote)))
}
pub fn execute_execute_proposal(
deps: DepsMut,
env: Env,
proposal_id: u64,
) -> Result<Response, ContractError> {
let proposal = PROPOSALS.load(deps.storage, proposal_id)?;
// Check voting ended and quorum reached
ensure!(env.block.time >= proposal.voting_end, ContractError::VotingNotEnded {});
let total_votes = proposal.yes_votes + proposal.no_votes + proposal.abstain_votes;
ensure!(total_votes >= proposal.quorum, ContractError::QuorumNotReached {});
ensure!(proposal.yes_votes > proposal.no_votes, ContractError::ProposalRejected {});
// Execute proposal messages
let messages: Vec<CosmosMsg> = proposal.messages;
PROPOSALS.update(deps.storage, proposal_id, |p| -> StdResult<_> {
let mut p = p.unwrap();
p.status = ProposalStatus::Executed;
Ok(p)
})?;
Ok(Response::new()
.add_messages(messages)
.add_attribute("action", "execute_proposal"))
}
Shareholder voting, dividend distribution, and corporate governance with verified identities.
template ShareholderVote
with
company : Party
registrar : Party
shareholders : [(Party, Int)] -- (shareholder, shares)
proposal : Text
deadline : Time
where
signatory company, registrar
observer map fst shareholders
choice CastVote : ContractId ShareholderVote
with
voter : Party
vote : Bool
controller voter
do
assertMsg "Not a shareholder" $
voter `elem` map fst shareholders
-- Record vote weighted by shares
create this with votes = votes <> [(voter, vote)]
choice TallyVotes : VoteResult
controller company
do
now <- getTime
assertMsg "Voting not ended" $ now > deadline
-- Calculate weighted results
let weighted = [(shares, v) | ((p, shares), v) <- zip shareholders votes]
return $ computeResult weighted