linera_sdk/abis/wrapped_fungible.rs
1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! An ABI for applications that implement a wrapped (bridged) fungible token with Mint/Burn.
5
6use std::collections::BTreeMap;
7
8use async_graphql::{Request, Response};
9pub use linera_base::identifiers::Account;
10use linera_base::{
11 abi::{ContractAbi, ServiceAbi},
12 data_types::U128,
13 identifiers::{AccountOwner, ApplicationId, ChainId},
14};
15use linera_sdk_derive::{GraphQLMutationRootInCrate, StableEnumInCrate};
16use serde::{Deserialize, Serialize};
17
18/// Parameters for a wrapped fungible token backed by an EVM bridge.
19#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
20pub struct WrappedParameters {
21 /// Ticker symbol (e.g. "USDC")
22 pub ticker_symbol: String,
23 /// Number of decimal places used by the source ERC-20 (e.g. 6 for USDC).
24 pub decimals: u8,
25 /// The chain on which minting and burning are allowed (the bridge chain).
26 pub mint_chain_id: ChainId,
27 /// The ERC-20 token address on the source EVM chain
28 pub evm_token_address: [u8; 20],
29 /// The EVM chain ID of the source chain (e.g. 8453 for Base)
30 pub evm_source_chain_id: u64,
31}
32
33/// Event emitted by the bridge application on its "burns" stream when it burns
34/// wrapped tokens on the bridge chain. The relayer observes these and forwards
35/// them to EVM to release the corresponding ERC-20 tokens.
36#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
37pub struct BurnEvent {
38 /// The Ethereum address to receive the unlocked ERC-20 tokens
39 pub target: [u8; 20],
40 /// Amount of tokens burned, in raw sub-units of the source ERC-20.
41 pub amount: U128,
42}
43
44/// Initial accounts and balances for the wrapped fungible token application.
45#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
46pub struct InitialState {
47 pub accounts: BTreeMap<AccountOwner, U128>,
48}
49
50/// Builder for [`InitialState`].
51#[derive(Debug, Default)]
52pub struct InitialStateBuilder {
53 account_balances: BTreeMap<AccountOwner, U128>,
54}
55
56impl InitialStateBuilder {
57 pub fn with_account(mut self, account: AccountOwner, balance: U128) -> Self {
58 self.account_balances.insert(account, balance);
59 self
60 }
61
62 pub fn build(&self) -> InitialState {
63 InitialState {
64 accounts: self.account_balances.clone(),
65 }
66 }
67}
68
69/// Response variants returned by the wrapped fungible token application.
70#[derive(Debug, StableEnumInCrate, Default)]
71pub enum FungibleResponse {
72 #[default]
73 Ok,
74 Balance(U128),
75 TickerSymbol(String),
76}
77
78/// Operations for the wrapped fungible token application.
79#[derive(Debug, StableEnumInCrate, GraphQLMutationRootInCrate)]
80pub enum WrappedFungibleOperation {
81 /// Requests an account balance.
82 Balance { owner: AccountOwner },
83 /// Requests this fungible token's ticker symbol.
84 TickerSymbol,
85 /// Approve the transfer of tokens.
86 Approve {
87 owner: AccountOwner,
88 spender: AccountOwner,
89 allowance: U128,
90 },
91 /// Transfers tokens from a (locally owned) account to a (possibly remote) account.
92 Transfer {
93 owner: AccountOwner,
94 amount: U128,
95 target_account: Account,
96 },
97 /// Transfers tokens from a (locally owned) account using a previously approved allowance.
98 TransferFrom {
99 owner: AccountOwner,
100 spender: AccountOwner,
101 amount: U128,
102 target_account: Account,
103 },
104 /// Same as `Transfer` but the source account may be remote.
105 Claim {
106 source_account: Account,
107 amount: U128,
108 target_account: Account,
109 },
110 /// Mints new tokens and transfers them to a target account. Driven by the
111 /// registered authorized caller (see [`Self::RegisterAuthorizedCaller`]) on the
112 /// designated mint chain.
113 MintAndTransfer {
114 target_account: Account,
115 amount: U128,
116 },
117 /// Burns tokens from an account. Authorized only via the registered authorized
118 /// caller (see [`Self::RegisterAuthorizedCaller`]) on the designated mint chain.
119 Burn { owner: AccountOwner, amount: U128 },
120 /// Registers the application authorized to drive `MintAndTransfer`/`Burn`. Must run on
121 /// the designated `mint_chain_id` — the only chain where it is consulted — and
122 /// requires an authenticated signer. Because an authorized caller may take
123 /// this token's id as a creation parameter, the two cannot reference each
124 /// other at creation; this token is created first and registers its caller
125 /// afterwards.
126 RegisterAuthorizedCaller { app_id: ApplicationId },
127}
128
129/// Cross-chain message used by the wrapped fungible token application.
130/// Amounts are [`U128`] in the source ERC-20's decimal scale.
131#[derive(Debug, Deserialize, Serialize)]
132pub enum Message {
133 /// Credits the given `target` account, unless the message is bouncing, in which case
134 /// `source` is credited instead.
135 Credit {
136 target: AccountOwner,
137 amount: U128,
138 source: AccountOwner,
139 },
140
141 /// Withdraws from the given account and starts a transfer to the target account.
142 Withdraw {
143 owner: AccountOwner,
144 amount: U128,
145 target_account: Account,
146 },
147}
148
149/// ABI for the wrapped fungible token application.
150pub struct WrappedFungibleTokenAbi;
151
152impl ContractAbi for WrappedFungibleTokenAbi {
153 type Operation = WrappedFungibleOperation;
154 type Response = FungibleResponse;
155}
156
157impl ServiceAbi for WrappedFungibleTokenAbi {
158 type Query = Request;
159 type QueryResponse = Response;
160}