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)]
46#[allow(missing_docs)]
47pub struct InitialState {
48 pub accounts: BTreeMap<AccountOwner, U128>,
49}
50
51/// Builder for [`InitialState`].
52#[derive(Debug, Default)]
53pub struct InitialStateBuilder {
54 account_balances: BTreeMap<AccountOwner, U128>,
55}
56
57impl InitialStateBuilder {
58 /// Adds an account with the given initial balance.
59 pub fn with_account(mut self, account: AccountOwner, balance: U128) -> Self {
60 self.account_balances.insert(account, balance);
61 self
62 }
63
64 /// Builds the [`InitialState`] from the configured accounts.
65 pub fn build(&self) -> InitialState {
66 InitialState {
67 accounts: self.account_balances.clone(),
68 }
69 }
70}
71
72/// Response variants returned by the wrapped fungible token application.
73#[derive(Debug, StableEnumInCrate, Default)]
74#[allow(missing_docs)]
75pub enum FungibleResponse {
76 #[default]
77 Ok,
78 Balance(U128),
79 TickerSymbol(String),
80}
81
82/// Operations for the wrapped fungible token application.
83#[derive(Debug, StableEnumInCrate, GraphQLMutationRootInCrate)]
84#[allow(missing_docs)]
85pub enum WrappedFungibleOperation {
86 /// Requests an account balance.
87 Balance { owner: AccountOwner },
88 /// Requests this fungible token's ticker symbol.
89 TickerSymbol,
90 /// Approve the transfer of tokens.
91 Approve {
92 owner: AccountOwner,
93 spender: AccountOwner,
94 allowance: U128,
95 },
96 /// Transfers tokens from a (locally owned) account to a (possibly remote) account.
97 Transfer {
98 owner: AccountOwner,
99 amount: U128,
100 target_account: Account,
101 },
102 /// Transfers tokens from a (locally owned) account using a previously approved allowance.
103 TransferFrom {
104 owner: AccountOwner,
105 spender: AccountOwner,
106 amount: U128,
107 target_account: Account,
108 },
109 /// Same as `Transfer` but the source account may be remote.
110 Claim {
111 source_account: Account,
112 amount: U128,
113 target_account: Account,
114 },
115 /// Mints new tokens and transfers them to a target account. Driven by the
116 /// registered authorized caller (see [`Self::RegisterAuthorizedCaller`]) on the
117 /// designated mint chain.
118 MintAndTransfer {
119 target_account: Account,
120 amount: U128,
121 },
122 /// Burns tokens from an account. Authorized only via the registered authorized
123 /// caller (see [`Self::RegisterAuthorizedCaller`]) on the designated mint chain.
124 Burn { owner: AccountOwner, amount: U128 },
125 /// Registers the application authorized to drive `MintAndTransfer`/`Burn`. Must run on
126 /// the designated `mint_chain_id` — the only chain where it is consulted — and
127 /// requires an authenticated signer. Because an authorized caller may take
128 /// this token's id as a creation parameter, the two cannot reference each
129 /// other at creation; this token is created first and registers its caller
130 /// afterwards.
131 RegisterAuthorizedCaller { app_id: ApplicationId },
132}
133
134/// Cross-chain message used by the wrapped fungible token application.
135/// Amounts are [`U128`] in the source ERC-20's decimal scale.
136#[derive(Debug, Deserialize, Serialize)]
137#[allow(missing_docs)]
138pub enum Message {
139 /// Credits the given `target` account, unless the message is bouncing, in which case
140 /// `source` is credited instead.
141 Credit {
142 target: AccountOwner,
143 amount: U128,
144 source: AccountOwner,
145 },
146
147 /// Withdraws from the given account and starts a transfer to the target account.
148 Withdraw {
149 owner: AccountOwner,
150 amount: U128,
151 target_account: Account,
152 },
153}
154
155/// ABI for the wrapped fungible token application.
156pub struct WrappedFungibleTokenAbi;
157
158impl ContractAbi for WrappedFungibleTokenAbi {
159 type Operation = WrappedFungibleOperation;
160 type Response = FungibleResponse;
161}
162
163impl ServiceAbi for WrappedFungibleTokenAbi {
164 type Query = Request;
165 type QueryResponse = Response;
166}